From e992d8247d186e0ff62dd58c73182cd13721da36 Mon Sep 17 00:00:00 2001 From: Alex Dunmow Date: Tue, 2 Jun 2026 23:11:11 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20LCARS=20theme=20plugin=20=E2=80=94=20St?= =?UTF-8?q?ar=20Trek=20computer=20interface?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First-class pongo2 theme with 4 color presets (Federation, Red Alert, Sickbay, Engineering), 3 custom blocks (header, sidebar, panel), 2 page templates, heading/text overrides, email wrapper, bundled Antonio font, and full LCARS CSS with elbow brackets, pill buttons, and rounded bars. Co-Authored-By: Claude Opus 4.6 --- .gitignore | 1 + assets/fonts/antonio-latin-ext.woff2 | Bin 0 -> 16288 bytes assets/fonts/antonio-latin.woff2 | Bin 0 -> 26592 bytes assets/style.css | 423 +++++++++++++++++++++++++ blocks.go | 53 ++++ embed.go | 60 ++++ fonts.json | 28 ++ go.mod | 19 ++ go.sum | 52 +++ master_pages.go | 57 ++++ plugin.mod | 3 + presets.json | 202 ++++++++++++ register.go | 50 +++ registration.go | 24 ++ schemas/lcars_header.schema.json | 41 +++ schemas/lcars_panel.schema.json | 29 ++ schemas/lcars_sidebar.schema.json | 33 ++ templates/blocks/header.html | 23 ++ templates/blocks/heading_override.html | 1 + templates/blocks/panel.html | 14 + templates/blocks/sidebar.html | 11 + templates/blocks/text_override.html | 1 + templates/default.html | 45 +++ templates/email_wrapper.html | 43 +++ templates/full_display.html | 31 ++ 25 files changed, 1244 insertions(+) create mode 100644 .gitignore create mode 100644 assets/fonts/antonio-latin-ext.woff2 create mode 100644 assets/fonts/antonio-latin.woff2 create mode 100644 assets/style.css create mode 100644 blocks.go create mode 100644 embed.go create mode 100644 fonts.json create mode 100644 go.mod create mode 100644 go.sum create mode 100644 master_pages.go create mode 100644 plugin.mod create mode 100644 presets.json create mode 100644 register.go create mode 100644 registration.go create mode 100644 schemas/lcars_header.schema.json create mode 100644 schemas/lcars_panel.schema.json create mode 100644 schemas/lcars_sidebar.schema.json create mode 100644 templates/blocks/header.html create mode 100644 templates/blocks/heading_override.html create mode 100644 templates/blocks/panel.html create mode 100644 templates/blocks/sidebar.html create mode 100644 templates/blocks/text_override.html create mode 100644 templates/default.html create mode 100644 templates/email_wrapper.html create mode 100644 templates/full_display.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..140f8cf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.so diff --git a/assets/fonts/antonio-latin-ext.woff2 b/assets/fonts/antonio-latin-ext.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..282553595781c80dc690e0e88e64dd9cbb82c843 GIT binary patch literal 16288 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

*!K5cU_36)g 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 literal 0 HcmV?d00001 diff --git a/assets/style.css b/assets/style.css new file mode 100644 index 0000000..7860eb1 --- /dev/null +++ b/assets/style.css @@ -0,0 +1,423 @@ +/* ============================================================ + LCARS Theme — Star Trek Computer Interface + ============================================================ */ + +/* --- Typography --- */ + +.lcars-page { + font-family: 'Antonio', sans-serif; + letter-spacing: 0.05em; +} + +/* --- Color Utilities --- */ + +.lcars-bg-primary { background-color: hsl(var(--primary)); } +.lcars-bg-secondary { background-color: hsl(var(--secondary)); } +.lcars-bg-accent { background-color: hsl(var(--accent)); } +.lcars-bg-muted { background-color: hsl(var(--muted)); } +.lcars-text-primary { color: hsl(var(--primary)); } +.lcars-text-secondary { color: hsl(var(--secondary)); } +.lcars-text-accent { color: hsl(var(--accent)); } + +/* --- Frame Layout --- */ + +.lcars-frame { + display: flex; + flex-direction: column; + min-height: 100vh; + padding: 0.5rem; + gap: 0.25rem; +} + +.lcars-body { + display: grid; + grid-template-columns: 1fr; + grid-template-rows: auto 1fr auto; + flex: 1; + gap: 0.25rem; +} + +.lcars-content-area { + display: grid; + grid-template-columns: 12rem 0.25rem 1fr; + gap: 0; + flex: 1; +} + +.lcars-frame-full .lcars-body-full { + display: flex; + flex-direction: column; + flex: 1; + gap: 0.25rem; +} + +/* --- Elbow Brackets --- */ + +.lcars-elbow-top, +.lcars-elbow-bottom { + display: flex; + align-items: stretch; + height: 2rem; +} + +.lcars-elbow-corner { + width: 12rem; + min-width: 12rem; + background-color: hsl(var(--primary)); +} + +.lcars-elbow-tl { + border-bottom-left-radius: 0; + border-bottom-right-radius: 2rem; + border-top-left-radius: 0; + border-top-right-radius: 0; +} + +.lcars-elbow-bl { + border-top-left-radius: 0; + border-top-right-radius: 2rem; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} + +.lcars-bar { + flex: 1; + margin-left: 0.25rem; +} + +.lcars-bar-top { + background-color: hsl(var(--secondary)); + border-radius: 0 0 1rem 0; +} + +.lcars-bar-bottom { + background-color: hsl(var(--secondary)); + border-radius: 0 1rem 0 0; +} + +.lcars-bar-accent { + height: 0.5rem; + background: linear-gradient( + to right, + hsl(var(--primary)) 0%, + hsl(var(--primary)) 30%, + hsl(var(--secondary)) 30%, + hsl(var(--secondary)) 60%, + hsl(var(--accent)) 60%, + hsl(var(--accent)) 100% + ); + border-radius: 0.25rem; +} + +/* --- Divider (between sidebar and main) --- */ + +.lcars-divider { + background-color: hsl(var(--primary)); + width: 0.25rem; +} + +/* --- Sidebar --- */ + +.lcars-sidebar-area { + display: flex; + flex-direction: column; + overflow: hidden; +} + +.lcars-sidebar { + display: flex; + flex-direction: column; + gap: 0.25rem; + padding: 0.25rem 0; +} + +.lcars-sidebar-btn { + display: flex; + align-items: center; + justify-content: flex-end; + padding: 0.5rem 1rem; + border-radius: 0 1.5rem 1.5rem 0; + text-decoration: none; + color: hsl(var(--background)); + font-weight: 700; + font-size: 0.875rem; + text-transform: uppercase; + letter-spacing: 0.1em; + transition: filter 0.15s ease; + min-height: 2.5rem; +} + +.lcars-sidebar-btn:hover { + filter: brightness(1.2); +} + +.lcars-sidebar-btn-label { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* --- Main Content --- */ + +.lcars-main-area { + padding: 1.5rem 2rem; +} + +.lcars-main-full { + flex: 1; + padding: 2rem; +} + +.lcars-empty-state { + display: flex; + align-items: center; + justify-content: center; + min-height: 20rem; +} + +/* --- Header Block --- */ + +.lcars-header-bar { + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 0; +} + +.lcars-header-pill { + width: 6rem; + height: 2.5rem; + border-radius: 1.25rem; + flex-shrink: 0; +} + +.lcars-header-title-area { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; +} + +.lcars-title { + font-size: 2rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.15em; + color: hsl(var(--primary)); + line-height: 1; + margin: 0; +} + +.lcars-subtitle { + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.2em; + color: hsl(var(--secondary)); +} + +.lcars-header-indicators { + display: flex; + align-items: center; + gap: 1rem; + flex-shrink: 0; +} + +.lcars-stardate { + font-size: 0.875rem; + font-weight: 600; + color: hsl(var(--accent)); + text-transform: uppercase; + letter-spacing: 0.1em; +} + +.lcars-status { + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.15em; + padding: 0.25rem 0.75rem; + border-radius: 0.75rem; +} + +.lcars-status-online { + background-color: hsl(var(--primary)); + color: hsl(var(--background)); + animation: lcars-pulse 3s ease-in-out infinite; +} + +.lcars-status-standby { + background-color: hsl(var(--secondary)); + color: hsl(var(--background)); +} + +.lcars-status-alert { + background-color: hsl(var(--destructive)); + color: hsl(var(--destructive-foreground)); + animation: lcars-blink 1s step-end infinite; +} + +.lcars-status-offline { + background-color: hsl(var(--muted)); + color: hsl(var(--muted-foreground)); +} + +.lcars-editor-badge { + position: absolute; + top: 0.25rem; + right: 0.5rem; + font-size: 0.625rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.2em; + color: hsl(var(--accent)); + opacity: 0.7; +} + +/* --- Panel Block --- */ + +.lcars-panel { + border-left: 0.25rem solid hsl(var(--primary)); + margin: 1rem 0; +} + +.lcars-panel-secondary { border-left-color: hsl(var(--secondary)); } +.lcars-panel-accent { border-left-color: hsl(var(--accent)); } + +.lcars-panel-header { + display: flex; + align-items: center; + gap: 0.5rem; + margin-bottom: 0.5rem; +} + +.lcars-panel-header-pill { + width: 3rem; + height: 1.25rem; + background-color: inherit; + border-radius: 0.625rem; + background-color: hsl(var(--primary)); +} + +.lcars-panel-secondary .lcars-panel-header-pill { background-color: hsl(var(--secondary)); } +.lcars-panel-accent .lcars-panel-header-pill { background-color: hsl(var(--accent)); } + +.lcars-panel-title { + font-size: 0.875rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.15em; + color: hsl(var(--foreground)); + white-space: nowrap; +} + +.lcars-panel-header-bar { + flex: 1; + height: 0.125rem; + background-color: hsl(var(--border)); +} + +.lcars-panel-body { + padding: 0.75rem 1rem 0.75rem 1.25rem; +} + +/* --- Heading Override --- */ + +.lcars-heading { + font-family: 'Antonio', sans-serif; + text-transform: uppercase; + letter-spacing: 0.12em; + color: hsl(var(--primary)); + border-bottom: 0.125rem solid hsl(var(--primary)); + padding-bottom: 0.25rem; + margin-bottom: 1rem; +} + +.lcars-heading-1 { font-size: 2.25rem; } +.lcars-heading-2 { font-size: 1.75rem; } +.lcars-heading-3 { font-size: 1.375rem; } +.lcars-heading-4 { font-size: 1.125rem; } + +/* --- Text Override --- */ + +.lcars-prose { + color: hsl(var(--foreground)); + line-height: 1.7; + font-size: 1rem; +} + +.lcars-prose a { + color: hsl(var(--secondary)); + text-decoration: underline; + text-underline-offset: 0.2em; +} + +.lcars-prose a:hover { + color: hsl(var(--primary)); +} + +.lcars-prose strong { + color: hsl(var(--primary)); +} + +.lcars-prose code { + background-color: hsl(var(--muted)); + padding: 0.125rem 0.375rem; + border-radius: 0.25rem; + font-size: 0.875em; +} + +/* --- Footer --- */ + +.lcars-footer-area { + padding: 0.5rem 0; +} + +/* --- Animations --- */ + +@keyframes lcars-pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.7; } +} + +@keyframes lcars-blink { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.3; } +} + +/* --- Responsive --- */ + +@media (max-width: 768px) { + .lcars-content-area { + grid-template-columns: 1fr; + } + + .lcars-sidebar-area { + display: none; + } + + .lcars-divider { + display: none; + } + + .lcars-elbow-corner { + width: 4rem; + min-width: 4rem; + } + + .lcars-sidebar-btn { + border-radius: 1.5rem; + justify-content: center; + } + + .lcars-header-pill { + width: 3rem; + } + + .lcars-title { + font-size: 1.5rem; + } + + .lcars-main-area { + padding: 1rem; + } +} diff --git a/blocks.go b/blocks.go new file mode 100644 index 0000000..9bb2043 --- /dev/null +++ b/blocks.go @@ -0,0 +1,53 @@ +package main + +import ( + "git.dev.alexdunmow.com/block/core/blocks" +) + +// --- LCARS Header Block --- + +var LCARSHeaderMeta = blocks.BlockMeta{ + Key: "lcars_header", + Title: "LCARS Header", + Description: "Top bar with elbow bracket, status indicators, and stardate", + Category: blocks.CategoryNavigation, + Source: "lcars", +} + +var headerDefaults = map[string]any{ + "title": "LCARS", + "subtitle": "Library Computer Access/Retrieval System", + "stardate": "", + "status": "online", + "menu_name": "main", +} + +// --- LCARS Sidebar Block --- + +var LCARSSidebarMeta = blocks.BlockMeta{ + Key: "lcars_sidebar", + Title: "LCARS Sidebar", + Description: "Left panel with rounded-rectangle navigation buttons", + Category: blocks.CategoryNavigation, + Source: "lcars", +} + +var sidebarDefaults = map[string]any{ + "items": []any{ + map[string]any{"label": "Personnel", "url": "#", "color": "primary"}, + map[string]any{"label": "Operations", "url": "#", "color": "secondary"}, + map[string]any{"label": "Sciences", "url": "#", "color": "accent"}, + map[string]any{"label": "Engineering", "url": "#", "color": "primary"}, + map[string]any{"label": "Medical", "url": "#", "color": "secondary"}, + }, +} + +// --- LCARS Panel Block --- + +var LCARSPanelMeta = blocks.BlockMeta{ + Key: "lcars_panel", + Title: "LCARS Panel", + Description: "Framed content area with LCARS border treatment", + Category: blocks.CategoryLayout, + Source: "lcars", +} diff --git a/embed.go b/embed.go new file mode 100644 index 0000000..0a6316f --- /dev/null +++ b/embed.go @@ -0,0 +1,60 @@ +package main + +import ( + "embed" + "io/fs" + "net/http" + + "git.dev.alexdunmow.com/block/core/plugin" +) + +//go:embed assets/* +var assetsFS embed.FS + +//go:embed schemas/* +var schemasFS embed.FS + +//go:embed templates/* +var templateFS embed.FS + +//go:embed presets.json +var presetsData []byte + +//go:embed fonts.json +var fontsData []byte + +//go:embed plugin.mod +var pluginModBytes []byte + +func Assets() fs.FS { + sub, _ := fs.Sub(assetsFS, "assets") + return sub +} + +func SchemasFS() fs.FS { + sub, _ := fs.Sub(schemasFS, "schemas") + return sub +} + +func TemplatesFS() fs.FS { + sub, _ := fs.Sub(templateFS, "templates") + return sub +} + +func AssetsHandler() http.Handler { + return http.FileServer(http.FS(Assets())) +} + +func ThemePresets() []byte { return presetsData } + +func BundledFonts() []byte { return fontsData } + +func ThemeCSSManifest() *plugin.CSSManifest { + css, err := assetsFS.ReadFile("assets/style.css") + if err != nil { + return &plugin.CSSManifest{} + } + return &plugin.CSSManifest{ + InputCSSAppend: string(css), + } +} diff --git a/fonts.json b/fonts.json new file mode 100644 index 0000000..1806db6 --- /dev/null +++ b/fonts.json @@ -0,0 +1,28 @@ +[ + { + "name": "Antonio", + "family": "Antonio", + "variants": [ + { + "weight": "300", + "style": "normal", + "file": "fonts/antonio-latin.woff2" + }, + { + "weight": "400", + "style": "normal", + "file": "fonts/antonio-latin.woff2" + }, + { + "weight": "600", + "style": "normal", + "file": "fonts/antonio-latin.woff2" + }, + { + "weight": "700", + "style": "normal", + "file": "fonts/antonio-latin.woff2" + } + ] + } +] diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..31184bc --- /dev/null +++ b/go.mod @@ -0,0 +1,19 @@ +module git.dev.alexdunmow.com/block/lcars + +go 1.26.2 + +require git.dev.alexdunmow.com/block/core v0.10.0 + +require ( + connectrpc.com/connect v1.20.0 // indirect + github.com/BurntSushi/toml v1.6.0 // indirect + github.com/a-h/templ v0.3.1020 // indirect + github.com/flosch/pongo2/v6 v6.1.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.9.2 // indirect + golang.org/x/mod v0.34.0 // indirect + golang.org/x/text v0.36.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..fb68555 --- /dev/null +++ b/go.sum @@ -0,0 +1,52 @@ +connectrpc.com/connect v1.20.0 h1:6TNDAB+WeNd2uolWNlYczB5E0KNNaVMNUEx8JEUsPmQ= +connectrpc.com/connect v1.20.0/go.mod h1:A2ygJrukXwWy32vkCAAHNVguZrqZ+jeZ9rGRnGR4dN4= +git.dev.alexdunmow.com/block/core v0.10.0 h1:dWfYVbGuJOnvE58GcGGd5c71dAAKcjxrSEfjNepu4ro= +git.dev.alexdunmow.com/block/core v0.10.0/go.mod h1:y1/Q9UMG29AplbExecnq9M7y16PZ7cYd24bjZO1SCBQ= +github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= +github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/a-h/templ v0.3.1020 h1:ypAT/L5ySWEnZ6Zft/5yfoWXYYkhFNvEFOeeqecg4tw= +github.com/a-h/templ v0.3.1020/go.mod h1:A2DlK61v+K+NRoGnhmYbNYVmtYHcFO5/AisMvBdDxTM= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/flosch/pongo2/v6 v6.1.0 h1:A/NJbrQJJD2B2mbpw3DRFwBYG0xpCr3vwFlEr46y1HQ= +github.com/flosch/pongo2/v6 v6.1.0/go.mod h1:CuDpFm47R0uGGE7z13/tTlt1Y6zdxvr2RLT5LJhsHEU= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw= +github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.15.0 h1:D0RCU5rMAp+SpgkiNdrjfJ+LX4J1M32V2NeCY7EJ6hc= +github.com/rogpeppe/go-internal v1.15.0/go.mod h1:DrUVZyrJU+txYW5/1kwtXQSMFio52ZOxX7yM1VHvnxs= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= +golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg= +golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/master_pages.go b/master_pages.go new file mode 100644 index 0000000..15451c5 --- /dev/null +++ b/master_pages.go @@ -0,0 +1,57 @@ +package main + +import "git.dev.alexdunmow.com/block/core/plugin" + +func DefaultMasterPages() []plugin.MasterPageDefinition { + return []plugin.MasterPageDefinition{ + { + Key: "lcars:standard-display", + Title: "LCARS Standard Display", + PageTemplates: []string{"default"}, + Blocks: []plugin.MasterPageBlock{ + { + BlockKey: "lcars:lcars_header", + Title: "LCARS Header", + Content: map[string]any{"title": "LCARS", "subtitle": "Library Computer Access/Retrieval System"}, + Slot: "header", + SortOrder: 0, + }, + { + BlockKey: "lcars:lcars_sidebar", + Title: "LCARS Sidebar", + Content: map[string]any{}, + Slot: "sidebar", + SortOrder: 0, + }, + { + BlockKey: "slot", + Title: "Main Content Slot", + Content: map[string]any{"slotName": "main", "placeholder": "Enter display data…"}, + Slot: "main", + SortOrder: 0, + }, + }, + }, + { + Key: "lcars:full-display", + Title: "LCARS Full Display", + PageTemplates: []string{"full-display"}, + Blocks: []plugin.MasterPageBlock{ + { + BlockKey: "lcars:lcars_header", + Title: "LCARS Header", + Content: map[string]any{"title": "LCARS", "subtitle": "Main Viewer"}, + Slot: "header", + SortOrder: 0, + }, + { + BlockKey: "slot", + Title: "Main Content Slot", + Content: map[string]any{"slotName": "main", "placeholder": "Enter display data…"}, + Slot: "main", + SortOrder: 0, + }, + }, + }, + } +} diff --git a/plugin.mod b/plugin.mod new file mode 100644 index 0000000..2e87e71 --- /dev/null +++ b/plugin.mod @@ -0,0 +1,3 @@ +[plugin] +name = "lcars" +version = "0.1.0" diff --git a/presets.json b/presets.json new file mode 100644 index 0000000..0fba260 --- /dev/null +++ b/presets.json @@ -0,0 +1,202 @@ +[ + { + "id": "federation", + "name": "Federation", + "description": "Classic LCARS — gold, periwinkle, and coral on black", + "theme": { + "lightColors": { + "background": "0 0% 2%", + "foreground": "40 33% 92%", + "card": "0 0% 5%", + "cardForeground": "40 33% 92%", + "popover": "0 0% 5%", + "popoverForeground": "40 33% 92%", + "primary": "39 90% 65%", + "primaryForeground": "0 0% 0%", + "secondary": "230 55% 70%", + "secondaryForeground": "0 0% 0%", + "muted": "0 0% 10%", + "mutedForeground": "40 20% 55%", + "accent": "10 70% 65%", + "accentForeground": "0 0% 0%", + "destructive": "0 85% 55%", + "destructiveForeground": "0 0% 98%", + "border": "0 0% 15%", + "input": "0 0% 15%", + "ring": "39 90% 65%" + }, + "darkColors": { + "background": "0 0% 2%", + "foreground": "40 33% 92%", + "card": "0 0% 5%", + "cardForeground": "40 33% 92%", + "popover": "0 0% 5%", + "popoverForeground": "40 33% 92%", + "primary": "39 90% 65%", + "primaryForeground": "0 0% 0%", + "secondary": "230 55% 70%", + "secondaryForeground": "0 0% 0%", + "muted": "0 0% 10%", + "mutedForeground": "40 20% 55%", + "accent": "10 70% 65%", + "accentForeground": "0 0% 0%", + "destructive": "0 85% 55%", + "destructiveForeground": "0 0% 98%", + "border": "0 0% 15%", + "input": "0 0% 15%", + "ring": "39 90% 65%" + }, + "mode": "dark" + } + }, + { + "id": "red-alert", + "name": "Red Alert", + "description": "Emergency operations — red and amber on black", + "theme": { + "lightColors": { + "background": "0 0% 2%", + "foreground": "0 0% 92%", + "card": "0 0% 5%", + "cardForeground": "0 0% 92%", + "popover": "0 0% 5%", + "popoverForeground": "0 0% 92%", + "primary": "0 80% 50%", + "primaryForeground": "0 0% 100%", + "secondary": "35 95% 55%", + "secondaryForeground": "0 0% 0%", + "muted": "0 0% 10%", + "mutedForeground": "0 15% 55%", + "accent": "35 95% 55%", + "accentForeground": "0 0% 0%", + "destructive": "0 85% 55%", + "destructiveForeground": "0 0% 98%", + "border": "0 30% 18%", + "input": "0 30% 18%", + "ring": "0 80% 50%" + }, + "darkColors": { + "background": "0 0% 2%", + "foreground": "0 0% 92%", + "card": "0 0% 5%", + "cardForeground": "0 0% 92%", + "popover": "0 0% 5%", + "popoverForeground": "0 0% 92%", + "primary": "0 80% 50%", + "primaryForeground": "0 0% 100%", + "secondary": "35 95% 55%", + "secondaryForeground": "0 0% 0%", + "muted": "0 0% 10%", + "mutedForeground": "0 15% 55%", + "accent": "35 95% 55%", + "accentForeground": "0 0% 0%", + "destructive": "0 85% 55%", + "destructiveForeground": "0 0% 98%", + "border": "0 30% 18%", + "input": "0 30% 18%", + "ring": "0 80% 50%" + }, + "mode": "dark" + } + }, + { + "id": "sickbay", + "name": "Sickbay", + "description": "Medical operations — teal, cyan, and blue on black", + "theme": { + "lightColors": { + "background": "0 0% 2%", + "foreground": "180 20% 90%", + "card": "0 0% 5%", + "cardForeground": "180 20% 90%", + "popover": "0 0% 5%", + "popoverForeground": "180 20% 90%", + "primary": "174 72% 50%", + "primaryForeground": "0 0% 0%", + "secondary": "199 80% 55%", + "secondaryForeground": "0 0% 0%", + "muted": "0 0% 10%", + "mutedForeground": "180 15% 50%", + "accent": "199 80% 55%", + "accentForeground": "0 0% 0%", + "destructive": "0 85% 55%", + "destructiveForeground": "0 0% 98%", + "border": "180 15% 15%", + "input": "180 15% 15%", + "ring": "174 72% 50%" + }, + "darkColors": { + "background": "0 0% 2%", + "foreground": "180 20% 90%", + "card": "0 0% 5%", + "cardForeground": "180 20% 90%", + "popover": "0 0% 5%", + "popoverForeground": "180 20% 90%", + "primary": "174 72% 50%", + "primaryForeground": "0 0% 0%", + "secondary": "199 80% 55%", + "secondaryForeground": "0 0% 0%", + "muted": "0 0% 10%", + "mutedForeground": "180 15% 50%", + "accent": "199 80% 55%", + "accentForeground": "0 0% 0%", + "destructive": "0 85% 55%", + "destructiveForeground": "0 0% 98%", + "border": "180 15% 15%", + "input": "180 15% 15%", + "ring": "174 72% 50%" + }, + "mode": "dark" + } + }, + { + "id": "engineering", + "name": "Engineering", + "description": "Engineering deck — orange, amber, and tan on black", + "theme": { + "lightColors": { + "background": "0 0% 2%", + "foreground": "35 30% 88%", + "card": "0 0% 5%", + "cardForeground": "35 30% 88%", + "popover": "0 0% 5%", + "popoverForeground": "35 30% 88%", + "primary": "25 95% 55%", + "primaryForeground": "0 0% 0%", + "secondary": "40 80% 60%", + "secondaryForeground": "0 0% 0%", + "muted": "0 0% 10%", + "mutedForeground": "30 20% 50%", + "accent": "33 65% 70%", + "accentForeground": "0 0% 0%", + "destructive": "0 85% 55%", + "destructiveForeground": "0 0% 98%", + "border": "25 20% 16%", + "input": "25 20% 16%", + "ring": "25 95% 55%" + }, + "darkColors": { + "background": "0 0% 2%", + "foreground": "35 30% 88%", + "card": "0 0% 5%", + "cardForeground": "35 30% 88%", + "popover": "0 0% 5%", + "popoverForeground": "35 30% 88%", + "primary": "25 95% 55%", + "primaryForeground": "0 0% 0%", + "secondary": "40 80% 60%", + "secondaryForeground": "0 0% 0%", + "muted": "0 0% 10%", + "mutedForeground": "30 20% 50%", + "accent": "33 65% 70%", + "accentForeground": "0 0% 0%", + "destructive": "0 85% 55%", + "destructiveForeground": "0 0% 98%", + "border": "25 20% 16%", + "input": "25 20% 16%", + "ring": "25 95% 55%" + }, + "mode": "dark" + } + } +] diff --git a/register.go b/register.go new file mode 100644 index 0000000..372cd8f --- /dev/null +++ b/register.go @@ -0,0 +1,50 @@ +package main + +import ( + "git.dev.alexdunmow.com/block/core/blocks" + "git.dev.alexdunmow.com/block/core/templates" + "git.dev.alexdunmow.com/block/core/templates/pongo" +) + +var engine = pongo.NewEngine(TemplatesFS(), "/templates/lcars/style.css") + +func Register(tr templates.TemplateRegistry, br blocks.BlockRegistry) error { + tr.RegisterSystemTemplate(templates.SystemTemplateMeta{ + Key: "lcars", + Title: "LCARS", + Description: "Star Trek LCARS computer interface theme", + }) + + if err := tr.RegisterPageTemplate("lcars", templates.PageTemplateMeta{ + Key: "default", + Title: "Standard Display", + Description: "Classic LCARS layout with sidebar and header frame", + Slots: []string{"header", "sidebar", "main", "footer"}, + }, engine.MustPageTemplate("default.html")); err != nil { + return err + } + + if err := tr.RegisterPageTemplate("lcars", templates.PageTemplateMeta{ + Key: "full-display", + Title: "Full Display", + Description: "Full-width LCARS display without sidebar", + Slots: []string{"header", "main", "footer"}, + }, engine.MustPageTemplate("full_display.html")); err != nil { + return err + } + + if err := br.LoadSchemasFromFS(SchemasFS()); err != nil { + return err + } + + br.Register(LCARSHeaderMeta, engine.MustBlockTemplateWithDefaults("blocks/header.html", headerDefaults)) + br.Register(LCARSSidebarMeta, engine.MustBlockTemplateWithDefaults("blocks/sidebar.html", sidebarDefaults)) + br.Register(LCARSPanelMeta, engine.MustBlockTemplate("blocks/panel.html")) + + br.RegisterTemplateOverride("lcars", "heading", engine.MustTemplateOverride("blocks/heading_override.html")) + br.RegisterTemplateOverride("lcars", "text", engine.MustTemplateOverride("blocks/text_override.html")) + + tr.RegisterEmailWrapper("lcars", engine.MustEmailWrapper("email_wrapper.html")) + + return nil +} diff --git a/registration.go b/registration.go new file mode 100644 index 0000000..127bf8b --- /dev/null +++ b/registration.go @@ -0,0 +1,24 @@ +package main + +import ( + "io/fs" + "net/http" + + "git.dev.alexdunmow.com/block/core/blocks" + "git.dev.alexdunmow.com/block/core/plugin" + "git.dev.alexdunmow.com/block/core/templates" +) + +var Registration = plugin.PluginRegistration{ + Name: "lcars", + Version: plugin.ParseModVersion(pluginModBytes), + Register: func(tr templates.TemplateRegistry, br blocks.BlockRegistry) error { + return Register(tr, br) + }, + Assets: func() http.Handler { return AssetsHandler() }, + Schemas: func() fs.FS { return SchemasFS() }, + ThemePresets: func() []byte { return ThemePresets() }, + BundledFonts: func() []byte { return BundledFonts() }, + MasterPages: func() []plugin.MasterPageDefinition { return DefaultMasterPages() }, + CSSManifest: func() *plugin.CSSManifest { return ThemeCSSManifest() }, +} diff --git a/schemas/lcars_header.schema.json b/schemas/lcars_header.schema.json new file mode 100644 index 0000000..70a5cf5 --- /dev/null +++ b/schemas/lcars_header.schema.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "LCARS Header", + "description": "Top bar with elbow bracket, status indicators, and stardate", + "type": "object", + "properties": { + "title": { + "type": "string", + "title": "Title", + "description": "Main display title", + "x-editor": "text" + }, + "subtitle": { + "type": "string", + "title": "Subtitle", + "description": "Secondary text below the title", + "x-editor": "text" + }, + "stardate": { + "type": "string", + "title": "Stardate", + "description": "Stardate display (leave empty for auto)", + "x-editor": "text" + }, + "status": { + "type": "string", + "title": "Status", + "description": "System status indicator", + "enum": ["online", "standby", "alert", "offline"], + "default": "online", + "x-editor": "select" + }, + "menu_name": { + "type": "string", + "title": "Menu Name", + "description": "Navigation menu to display", + "x-editor": "text" + } + }, + "required": ["title"] +} diff --git a/schemas/lcars_panel.schema.json b/schemas/lcars_panel.schema.json new file mode 100644 index 0000000..a97fe4e --- /dev/null +++ b/schemas/lcars_panel.schema.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "LCARS Panel", + "description": "Framed content area with LCARS border treatment", + "type": "object", + "properties": { + "title": { + "type": "string", + "title": "Panel Title", + "description": "Title shown in the panel header bar", + "x-editor": "text" + }, + "content": { + "type": "string", + "title": "Content", + "description": "Panel body content (HTML)", + "x-editor": "richtext" + }, + "border_color": { + "type": "string", + "title": "Border Color", + "description": "Which theme color for the panel border", + "enum": ["primary", "secondary", "accent"], + "default": "primary", + "x-editor": "select" + } + }, + "required": ["title"] +} diff --git a/schemas/lcars_sidebar.schema.json b/schemas/lcars_sidebar.schema.json new file mode 100644 index 0000000..42425e5 --- /dev/null +++ b/schemas/lcars_sidebar.schema.json @@ -0,0 +1,33 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "LCARS Sidebar", + "description": "Left panel with rounded-rectangle navigation buttons", + "type": "object", + "properties": { + "items": { + "type": "array", + "title": "Navigation Items", + "description": "List of sidebar navigation buttons", + "items": { + "type": "object", + "properties": { + "label": { + "type": "string", + "title": "Label" + }, + "url": { + "type": "string", + "title": "URL" + }, + "color": { + "type": "string", + "title": "Color", + "enum": ["primary", "secondary", "accent"], + "default": "primary" + } + }, + "required": ["label", "url"] + } + } + } +} diff --git a/templates/blocks/header.html b/templates/blocks/header.html new file mode 100644 index 0000000..a5bb160 --- /dev/null +++ b/templates/blocks/header.html @@ -0,0 +1,23 @@ +

+
+
+
+

{{ title }}

+ {% if subtitle %} + {{ subtitle }} + {% endif %} +
+
+ {% if stardate %} + SD {{ stardate }} + {% endif %} + + {{ status|default:"online"|upper }} + +
+
+
+ {% if ctx.isEditor %} +
EDIT MODE
+ {% endif %} +
diff --git a/templates/blocks/heading_override.html b/templates/blocks/heading_override.html new file mode 100644 index 0000000..8d59fbd --- /dev/null +++ b/templates/blocks/heading_override.html @@ -0,0 +1 @@ +<{{ tag|default:"h2" }} class="lcars-heading lcars-heading-{{ level|default:2 }}">{{ text }} diff --git a/templates/blocks/panel.html b/templates/blocks/panel.html new file mode 100644 index 0000000..9d2e658 --- /dev/null +++ b/templates/blocks/panel.html @@ -0,0 +1,14 @@ +
+ {% if title %} +
+
+ {{ title }} +
+
+ {% endif %} +
+ {% if content %} + {{ content|safe }} + {% endif %} +
+
diff --git a/templates/blocks/sidebar.html b/templates/blocks/sidebar.html new file mode 100644 index 0000000..7e72262 --- /dev/null +++ b/templates/blocks/sidebar.html @@ -0,0 +1,11 @@ + diff --git a/templates/blocks/text_override.html b/templates/blocks/text_override.html new file mode 100644 index 0000000..71c0f22 --- /dev/null +++ b/templates/blocks/text_override.html @@ -0,0 +1 @@ +
{{ html|safe }}
diff --git a/templates/default.html b/templates/default.html new file mode 100644 index 0000000..8d9c694 --- /dev/null +++ b/templates/default.html @@ -0,0 +1,45 @@ +{% extends "base.html" %} + +{% block body_class %}lcars-page lcars-default bg-background text-foreground antialiased min-h-screen{% endblock %} + +{% block body %} +
+
+ {{ slots.header|safe }} +
+ +
+
+
+
+
+ +
+ + +
+ +
+ {% if slots.main %} + {{ slots.main|safe }} + {% else %} +
+

No data on file

+
+ {% endif %} +
+
+ +
+
+
+
+
+ +
+ {{ slots.footer|safe }} +
+
+{% endblock %} diff --git a/templates/email_wrapper.html b/templates/email_wrapper.html new file mode 100644 index 0000000..7fba0a6 --- /dev/null +++ b/templates/email_wrapper.html @@ -0,0 +1,43 @@ + + + + + + {% if preview_text %} + {{ preview_text }} + {% endif %} + + + +
+
+ + + +
{{ site_name }}
+
+ +
+ +
+ {{ body|safe }} +
+ + +
+ + diff --git a/templates/full_display.html b/templates/full_display.html new file mode 100644 index 0000000..4b7e4d0 --- /dev/null +++ b/templates/full_display.html @@ -0,0 +1,31 @@ +{% extends "base.html" %} + +{% block body_class %}lcars-page lcars-full-display bg-background text-foreground antialiased min-h-screen{% endblock %} + +{% block body %} +
+
+ {{ slots.header|safe }} +
+ +
+
+ +
+ {% if slots.main %} + {{ slots.main|safe }} + {% else %} +
+

No data on file

+
+ {% endif %} +
+ +
+
+ + +
+{% endblock %}