- Implement Web Audio API-based spectrum analyzer with green gradient bars
- Add spectrum analyzer to front page and persistent player content frames
- Support cross-frame audio element access for frameset mode
- Add CORS headers to Icecast configuration for Web Audio API
- Serve Parenscript-generated JavaScript dynamically via API endpoint
- Canvas visualization appears below page title, above navigation
Changed from static file generation to Radiance API endpoint.
Parenscript now generates JavaScript in-memory at runtime when
the /api/asteroid/spectrum-analyzer.js endpoint is requested.
This is the correct approach for Parenscript integration with Radiance.
Implements real-time audio visualization for the main page:
- Added Parenscript dependency to asteroid.asd
- Created spectrum-analyzer.lisp with Parenscript code
- Added canvas element to front-page.ctml above nav buttons
- Auto-generates JavaScript at compile time
- Green gradient bars matching Asteroid aesthetic
- Uses Web Audio API for FFT analysis
This is the first Parenscript component in Asteroid Radio,
demonstrating the approach for future JavaScript conversions.
The 'sequential' mode in Liquidsoap starts playback at a random position
in the playlist, causing tracks to play out of order. Switching to 'normal'
mode ensures the playlist starts from the beginning and plays sequentially
through all tracks in order.
- Change audio player background from #1a1a1a to #1a2332
- Matches the blue-grey color used throughout the app
- Addresses Fade's feedback about brownish grey appearance
- Change track link colors: cyan default, green hover, blue visited
- Add equal left/right padding (12px) to track-list for centered separators
- Increase track-item top/bottom padding from 6px to 10px for better spacing
- Move track-list and track-item inside recently-played-list block for proper CSS nesting
- Addresses feedback from easilok on symmetry and breathing room
- Move track-link styles inside recently-played-list block for proper CSS nesting
- Use (:and .track-link :hover) at correct nesting level per LASS documentation
- Fixes hover color change to green when mousing over track names
- Generates correct CSS selector: .recently-played-list .track-link:hover
- Replace separate MusicBrainz link with clickable track name
- Add external link icon next to track name
- Simplify grid layout by removing track-meta column
- Update CSS styling for track-link with hover effects
- Improves UX by making the primary action (clicking track) more intuitive
- Display last 3 played tracks on front page
- Auto-updates every 30 seconds
- Shows track title, artist, and time ago
- Links to MusicBrainz search for each track
- Thread-safe in-memory storage
- Works in both normal and frameset modes
- Hacker-themed green styling
Implements feature request from fade to show recently played tracks
with linkage to track info at music database.
- Fix user management API endpoints to match backend changes
- Add user-id to form data for role updates and activation/deactivation
- Change endpoints from /users/{id}/action to /user/action format
- Handle Radiance API data wrapping properly
- Add confirmation prompt for user activation
- Update audio player background color to match app panels (#1a2332)
Incorporates changes from main branch users.js updates
- Resolve conflicts in frontend-partials.lisp, asteroid.lass, and templates
- Keep improved recently-played styling from parenscript-conversion branch
- Incorporate user management updates from main
- Change track link colors: cyan default, green hover, blue visited
- Add equal left/right padding (12px) to track-list for centered separators
- Increase track-item top/bottom padding from 6px to 10px for better spacing
- Move track-list and track-item inside recently-played-list block for proper CSS nesting
- Matches styling changes from recently-played-tracks branch
- Update ParenScript to make track name a clickable link
- Add external link icon next to track name
- Remove separate MusicBrainz link to reduce clutter
- Fix LASS hover syntax using :and combinator at correct nesting level
- Update CSS styling for track-link with proper hover effects
- Improves UX by making the primary action (clicking track) more intuitive
- Add favicon.ico, favicon-16x16.png, and favicon-32x32.png
- Add asteroid.png graphic to header on both front-page.ctml and front-page-content.ctml
- Update header styling to display asteroid graphics flanking the station name
- Use 2-column grid layout: track/artist left, time/link right
- Match color scheme with now-playing section (blue text)
- Tighter row spacing (6px padding)
- Simplified MusicBrainz search query (no field prefixes)
- Fix CSS selector for proper link styling
- Right-align time and MusicBrainz link
- Display last 3 played tracks on front page
- Auto-updates every 30 seconds
- Shows track title, artist, and time ago
- Links to MusicBrainz search for each track
- Thread-safe in-memory storage
- Works in both normal and frameset modes
- Hacker-themed green styling
Implements feature request from fade to show recently played tracks
with linkage to track info at music database.
- Convert recently-played.js to ParenScript in parenscript/recently-played.lisp
- Add API endpoint /api/asteroid/recently-played
- Add track monitoring in icecast-now-playing to populate recently-played list
- Add recently-played panel to front-page.ctml and front-page-content.ctml
- Add LASS styling for recently-played section
- Fix ParenScript issues: use ps:ps instead of ps:ps* with quote, use aref for innerHTML
- Display last 3 tracks with time ago formatting and MusicBrainz search links
- Fixed missing closing paren in save-queue-as-playlist function
- Fixed extra closing paren in icecast-now-playing function
- Updated player.lisp with upstream changes from player.js:
* Removed array indexing for track properties
* Added RADIANCE API wrapper handling
* Complete save-queue-as-playlist implementation
- Build and server startup now working correctly
- Replace uri-to-url with :representation :external with uri-path
- Fixes issue where full URLs like http://asteroid.radio.localhost were generated
- .localhost domains resolve to 127.0.0.1 which breaks on remote servers
- Path-only approach works for both local and remote deployments
- Follows Radiance best practices: :external is only for redirect URLs
- Converted player.js to parenscript/player.lisp
- Converted admin.js to parenscript/admin.lisp
- Fixed ParenScript compilation errors (push macro, != operator, error handlers)
- Fixed now-playing display with proper Icecast stats parsing
- Aggregated listener counts across all three stream mount points (mp3, aac, low)
- Updated documentation with all lessons learned and ParenScript patterns
- All JavaScript files now successfully converted to ParenScript
- Application maintains 100% original functionality
Successfully converted users.js with all functionality:
- User stats display (total, active, admin, DJ counts)
- Load users list with table display
- Change user role (UI working, backend may need fixes)
- Activate/deactivate users
- Create new user form
- Auto-refresh stats every 30 seconds
Generated JavaScript working correctly.
Files:
- parenscript/users.lisp - ParenScript source
- asteroid.asd - Added users to parenscript module
- asteroid.lisp - Added users.js to static route interception
- static/js/users.js - Removed from git (backed up as .original)
Four files successfully converted to ParenScript!
Remaining: admin.js, player.js
Successfully converted profile.js with all functionality:
- Profile data loading (username, role, join date, last active)
- Listening statistics display
- Recent tracks display
- Top artists display
- Password change form
- Export listening data
- Clear listening history
- Toast notifications
Generated JavaScript working correctly after fixing modulo operator.
Key learning: Use 'rem' instead of '%' for modulo in ParenScript.
Files:
- parenscript/profile.lisp - ParenScript source
- asteroid.asd - Added profile to parenscript module
- asteroid.lisp - Added profile.js to static route interception
- static/js/profile.js - Removed from git (backed up as .original)
- static/js/player.js - Restored (skipped for now, too complex)
Three files successfully converted to ParenScript\!
Moved PARENSCRIPT-EXPERIMENT.org to docs/ directory.
Updates:
- Marked front-page.js as complete in Phase 2
- Removed duplicate front-page.js from Phase 3
- Added conversion progress section with both files
- Documented front-page.js specific patterns:
* Global variables with defvar
* String concatenation with +
* Conditional logic with cond
* Object property access with ps:getprop
- Listed all tested features for front-page.js
Status: 2 of 6 JavaScript files converted successfully
Successfully converted front-page.js with all functionality:
- Stream quality configuration and switching
- Now playing updates (every 10 seconds)
- Pop-out player functionality
- Frameset mode toggle
- Auto-reconnect on stream errors
Generated JavaScript: 6900 characters
No browser errors, all features working
Files:
- parenscript/front-page.lisp - ParenScript source
- asteroid.asd - Added front-page to parenscript module
- asteroid.lisp - Added front-page.js to static route interception
- static/js/front-page.js - Removed from git (backed up as .original)
Two files successfully converted to ParenScript!
ParenScript doesn't support async/await syntax properly. Changed to use
promise chains with .then() which compiles correctly.
Result:
- No JavaScript errors
- Auth UI working correctly
- Generated JS: 1386 characters
- First successful ParenScript replacement complete\!
Next: Can convert more JS files (profile.js, users.js, etc.)
The issue was route ordering. Since Radiance matches routes in load order,
we couldn't override the static file route. Solution: intercept the static
route and check if path is 'js/auth-ui.js', then serve ParenScript-compiled
JavaScript instead.
Changes:
- Compile ParenScript to string at load time (stored in *auth-ui-js*)
- Intercept static route to serve ParenScript for auth-ui.js
- JavaScript successfully generated (1290 chars)
- Ready for browser testing
The ParenScript route must come before the catch-all static route
to properly override /static/js/auth-ui.js with dynamically compiled
JavaScript. Routes are matched in order, so specific routes must
precede general patterns.
- Removed static/js/auth-ui.js (backed up as .original)
- Templates still reference /asteroid/static/js/auth-ui.js
- Route now serves dynamically compiled ParenScript
- Ready to test ParenScript replacement
- Created parenscript/auth-ui.lisp with ParenScript version
- Added route to serve compiled JavaScript at /static/js/auth-ui.js
- Updated asteroid.asd to include parenscript module
- First conversion: auth-ui.js (authentication UI state management)
The ParenScript code compiles to equivalent JavaScript and is served
dynamically. This allows us to write client-side code in Lisp.
- Add parenscript dependency to asteroid.asd
- Create parenscript-utils.lisp with helper functions and macros
- Add PARENSCRIPT-EXPERIMENT.org documenting the conversion plan
- Goal: Replace all JavaScript with ParenScript for full-stack Lisp