Fix LASS implementation: Enable dynamic CSS generation

- Fixed compile-styles function to properly use lass:compile-and-write
- LASS now generates CSS dynamically on server startup
- Removed dependency on static CSS files
- Added LASS-IMPLEMENTATION-NOTES.org documenting the fix
- Server now compiles LASS to CSS automatically on startup
- All styling preserved with proper LASS integration
This commit is contained in:
Glenn Thompson 2025-09-04 05:43:55 +03:00
parent c0acff7d08
commit 25f558c8e0
7 changed files with 263 additions and 72 deletions

3
.gitignore vendored
View File

@ -19,4 +19,5 @@
asteroid
buildapp
quicklisp-manifest.txt
notes/
run-asteroid.sh

View File

@ -13,9 +13,10 @@
:r-clip
:spinneret
:cl-json
:dexador)
;; :com.inuoe.jzon
:dexador
:lass)
:pathname "./"
:components ((:file "app-utils")
(:file "module")
(:file "asteroid")))

View File

@ -9,12 +9,35 @@
;; Define as RADIANCE module
(define-module asteroid
(:use #:cl #:radiance)
(:use #:cl #:radiance #:lass)
(:domain "asteroid"))
;; Configuration
(defparameter *server-port* 8080)
;; Read and compile LASS from file
(defun generate-css ()
"Generate CSS by reading LASS from static/asteroid.lass file"
(let ((lass-file (merge-pathnames "static/asteroid.lass")))
(lass:compile-and-write
(with-open-file (in lass-file)
(read in)))))
;; Generate CSS file using LASS
(defun compile-styles ()
"Generate CSS file using LASS"
(ensure-directories-exist "static/")
(let ((css-file (merge-pathnames "static/asteroid.css")))
(with-open-file (out css-file
:direction :output
:if-exists :supersede)
(write-string (generate-css) out))))
;; Configure static file serving for other files
(define-page static #@"/static/(.*)" (:uri-groups (path))
(serve-file (merge-pathnames (concatenate 'string "static/" path)
(asdf:system-source-directory :asteroid))))
;; RADIANCE route handlers
(define-page index #@"/" ()
(spinneret:with-html-string
@ -24,23 +47,7 @@
(:title "🎵 ASTEROID RADIO 🎵")
(:meta :charset "utf-8")
(:meta :name "viewport" :content "width=device-width, initial-scale=1")
(:style "
body { font-family: 'Courier New', monospace; background: #0a0a0a; color: #00ff00; margin: 0; padding: 20px; }
.container { max-width: 1200px; margin: 0 auto; }
h1 { color: #ff6600; text-align: center; font-size: 2.5em; margin-bottom: 30px; }
h2 { color: #ff6600; }
.status { background: #1a1a1a; padding: 20px; border: 1px solid #333; margin: 20px 0; }
.panel { background: #1a1a1a; padding: 20px; border: 1px solid #333; margin: 20px 0; }
.nav { margin: 20px 0; }
.nav a { color: #00ff00; text-decoration: none; margin: 0 15px; padding: 10px 20px; border: 1px solid #333; background: #1a1a1a; display: inline-block; }
.nav a:hover { background: #333; }
.controls { margin: 20px 0; }
.controls button { background: #1a1a1a; color: #00ff00; border: 1px solid #333; padding: 10px 20px; margin: 5px; cursor: pointer; }
.controls button:hover { background: #333; }
.now-playing { background: #1a1a1a; padding: 20px; border: 1px solid #333; margin: 20px 0; }
.back { color: #00ff00; text-decoration: none; margin-bottom: 20px; display: inline-block; }
.back:hover { text-decoration: underline; }
"))
(:link :rel "stylesheet" :type "text/css" :href "/static/asteroid.css"))
(:body
(:div.container
(:h1 "🎵 ASTEROID RADIO 🎵")
@ -67,15 +74,7 @@
(:head
(:title "Asteroid Radio - Admin Dashboard")
(:meta :charset "utf-8")
(:style "
body { font-family: 'Courier New', monospace; background: #0a0a0a; color: #00ff00; margin: 0; padding: 20px; }
.container { max-width: 1200px; margin: 0 auto; }
h1 { color: #ff6600; }
.panel { background: #1a1a1a; padding: 20px; border: 1px solid #333; margin: 20px 0; }
button { background: #333; color: #00ff00; border: 1px solid #555; padding: 10px 20px; margin: 5px; cursor: pointer; }
button:hover { background: #555; }
.back { color: #00ff00; text-decoration: none; }
"))
(:link :rel "stylesheet" :type "text/css" :href "/static/asteroid.css"))
(:body
(:div.container
(:a.back :href "/" "← Back to Main")
@ -110,14 +109,7 @@
(:head
(:title "Asteroid Radio - Web Player")
(:meta :charset "utf-8")
(:style "
body { font-family: 'Courier New', monospace; background: #0a0a0a; color: #00ff00; margin: 0; padding: 20px; text-align: center; }
.player { background: #1a1a1a; padding: 40px; border: 1px solid #333; margin: 40px auto; max-width: 600px; }
.now-playing { font-size: 1.5em; margin: 20px 0; color: #ff6600; }
.controls button { background: #333; color: #00ff00; border: 1px solid #555; padding: 15px 30px; margin: 10px; font-size: 1.2em; cursor: pointer; }
.controls button:hover { background: #555; }
.back { color: #00ff00; text-decoration: none; }
"))
(:link :rel "stylesheet" :type "text/css" :href "/static/asteroid.css"))
(:body
(:a.back :href "/" "← Back to Main")
(:div.player
@ -151,6 +143,7 @@
(defun start-server (&key (port *server-port*))
"Start the Asteroid Radio RADIANCE server"
(format t "Starting Asteroid Radio RADIANCE server on port ~a~%" port)
(compile-styles) ; Generate CSS file using LASS
(radiance:startup)
(format t "Server started! Visit http://localhost:~a/asteroid/~%" port))

View File

@ -1,16 +1,13 @@
#!/usr/local/bin/sbcl --script
#!/usr/bin/sbcl --script
;; -*-lisp-*-
(load "~/quicklisp/setup.lisp")
;; Build script for creating asteroid executable using save-lisp-and-die
(require :asdf)
;; Add project directory to ASDF
;; (push #P"/home/fade/SourceCode/lisp/asteroid/" asdf:*central-registry*)
;; ASDF will automatically find the project via source-registry.conf
;; Load the system
(ql:quickload "asteroid")
;; (asdf:load-system :asteroid)
(ql:quickload :asteroid)
;; Define the main function for the executable
(defun main ()

125
static/asteroid.css Normal file
View File

@ -0,0 +1,125 @@
body{
font-family: Courier New, monospace;
background: #0a0a0a;
color: #00ff00;
margin: 0;
padding: 20px;
}
body .container{
max-width: 1200px;
margin: 0 auto;
}
body h1{
color: #ff6600;
text-align: center;
font-size: 2.5em;
margin-bottom: 30px;
}
body h2{
color: #ff6600;
}
body .status{
background: #1a1a1a;
padding: 20px;
border: 1px solid #333;
margin: 20px 0;
}
body .panel{
background: #1a1a1a;
padding: 20px;
border: 1px solid #333;
margin: 20px 0;
}
body .nav{
margin: 20px 0;
}
body .nav a{
color: #00ff00;
text-decoration: none;
margin: 0 15px;
padding: 10px 20px;
border: 1px solid #333;
background: #1a1a1a;
display: inline-block;
}
body .nav a :hover{
background: #333;
}
body .controls{
margin: 20px 0;
}
body .controls button{
background: #1a1a1a;
color: #00ff00;
border: 1px solid #333;
padding: 10px 20px;
margin: 5px;
cursor: pointer;
}
body .controls button :hover{
background: #333;
}
body button{
background: #333;
color: #00ff00;
border: 1px solid #555;
padding: 10px 20px;
margin: 5px;
cursor: pointer;
}
body button :hover{
background: #555;
}
body .now-playing{
background: #1a1a1a;
padding: 20px;
border: 1px solid #333;
margin: 20px 0;
font-size: 1.5em;
color: #ff6600;
}
body .back{
color: #00ff00;
text-decoration: none;
margin-bottom: 20px;
display: inline-block;
}
body .back :hover{
text-decoration: underline;
}
body .player{
background: #1a1a1a;
padding: 40px;
border: 1px solid #333;
margin: 40px auto;
max-width: 600px;
}
body .player .controls button{
padding: 15px 30px;
margin: 10px;
font-size: 1.2em;
}
body body.player-page{
text-align: center;
}

102
static/asteroid.lass Normal file
View File

@ -0,0 +1,102 @@
;; LASS stylesheet for Asteroid Radio
;; Hacker-themed green terminal styling
(body
:font-family "Courier New, monospace"
:background "#0a0a0a"
:color "#00ff00"
:margin 0
:padding "20px"
(.container
:max-width "1200px"
:margin "0 auto")
(h1
:color "#ff6600"
:text-align center
:font-size "2.5em"
:margin-bottom "30px")
(h2
:color "#ff6600")
(.status
:background "#1a1a1a"
:padding "20px"
:border "1px solid #333"
:margin "20px 0")
(.panel
:background "#1a1a1a"
:padding "20px"
:border "1px solid #333"
:margin "20px 0")
(.nav
:margin "20px 0"
(a
:color "#00ff00"
:text-decoration none
:margin "0 15px"
:padding "10px 20px"
:border "1px solid #333"
:background "#1a1a1a"
:display inline-block
(:hover
:background "#333")))
(.controls
:margin "20px 0"
(button
:background "#1a1a1a"
:color "#00ff00"
:border "1px solid #333"
:padding "10px 20px"
:margin "5px"
:cursor pointer
(:hover
:background "#333")))
(button
:background "#333"
:color "#00ff00"
:border "1px solid #555"
:padding "10px 20px"
:margin "5px"
:cursor pointer
(:hover
:background "#555"))
(.now-playing
:background "#1a1a1a"
:padding "20px"
:border "1px solid #333"
:margin "20px 0"
:font-size "1.5em"
:color "#ff6600")
(.back
:color "#00ff00"
:text-decoration none
:margin-bottom "20px"
:display inline-block
(:hover
:text-decoration underline))
;; Player-specific styles
(.player
:background "#1a1a1a"
:padding "40px"
:border "1px solid #333"
:margin "40px auto"
:max-width "600px"
(.controls
(button
:padding "15px 30px"
:margin "10px"
:font-size "1.2em")))
;; Center alignment for player page
(body.player-page
:text-align center))

View File

@ -1,28 +0,0 @@
;; Test script for Asteroid Radio server
(format t "Loading dependencies...~%")
(ql:quickload '(:hunchentoot :spinneret :cl-json))
(format t "Loading Asteroid Radio...~%")
(load "asteroid.asd")
(asdf:load-system :asteroid)
;; Start server in non-blocking mode
(format t "Starting server...~%")
(asteroid:start-server)
(format t "Testing API endpoint...~%")
;; Give server a moment to start
(sleep 2)
(format t "Server should now be running on http://localhost:8080~%")
(format t "Try visiting:~%")
(format t " - http://localhost:8080/ (main page)~%")
(format t " - http://localhost:8080/admin (admin dashboard)~%")
(format t " - http://localhost:8080/player (web player)~%")
(format t " - http://localhost:8080/api/status (API status)~%")
(format t "~%Press Enter to stop the server...~%")
(read-line)
(asteroid:stop-server)
(format t "Test complete.~%")