diff --git a/CLAUDE.md b/CLAUDE.md index 2c3243e..1b13422 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,16 +17,28 @@ Tools are available as `mcp__claudezilla__firefox_*`, e.g.: All tools require a `tabId` except `firefox_create_window` and `firefox_diagnose`. ### If tools aren't available in a session -MCP servers load at session startup only. **Start a new Claude Code session** to pick them up. +MCP servers load at session startup only. **Start a new Claude Code conversation** (hit "+" in the sidebar) — no need to reboot VSCode, just open a fresh chat. Always call `firefox_diagnose` first to confirm the connection is live. + +### Correct startup sequence +1. Firefox open with Claudezilla extension active (native host must be running) +2. Open a new Claude Code conversation → tools appear as `mcp__claudezilla__firefox_*` +3. Call `firefox_diagnose` to confirm before depending on any tool ### Setup (already done — for reference) The native messaging host requires a `.bat` wrapper on Windows (Firefox can't execute `.js` directly): - Wrapper: `E:\ClaudeLibrary\claudezilla\host\claudezilla.bat` — contains `@echo off` / `node "%~dp0index.js" %*` - Manifest: `C:\Users\adamc\AppData\Roaming\claudezilla\claudezilla.json` — points to the `.bat` file - Registry: `HKCU\SOFTWARE\Mozilla\NativeMessagingHosts\claudezilla` → manifest path -- MCP server: `~/.claude/settings.json` and `~/.claude/mcp.json` — both register `node E:/ClaudeLibrary/claudezilla/mcp/server.js` +- MCP server: registered in `~/.claude.json` (NOT `~/.claude/settings.json` or `~/.claude/mcp.json`) — use the CLI to register: + ``` + claude mcp add --scope user claudezilla "D:/Program Files/nodejs/node.exe" "E:/ClaudeLibrary/claudezilla/mcp/server.js" + ``` - Permission: `mcp__claudezilla__*` in `~/.claude/settings.json` `permissions.allow` +**Config file gotcha:** The Claude Code CLI and VSCode extension read user-level MCP servers from `~/.claude.json` (home dir, single file) — NOT from `~/.claude/settings.json` or `~/.claude/mcp.json`. Always use `claude mcp add --scope user` to register; never hand-edit. Verify registration with `claude mcp list`. + +**BOM gotcha:** PowerShell writes JSON files with a UTF-8 BOM, which causes `JSON.parse` to throw. Never use PowerShell `Set-Content` to write any Claude config JSON — use the Write tool or the CLI instead. + Native host: `E:\ClaudeLibrary\claudezilla\host\`. Extension: `claudezilla@boot.industries`. ## Stack @@ -91,7 +103,7 @@ Test runner is `core.runner.RobustCompressorTestRunner` — handles the Windows - Push to `main` triggers Woodpecker → deploys to staging ## SCSS Import Order -`core.scss`: `rootvars → applets → base → button-pad → dashboard → gameboard → room → palette-picker → wallet-tokens` +`core.scss`: `rootvars → applets → base → button-pad → dashboard → gameboard → palette-picker → room → billboard → game-kit → wallet-tokens` ## Critical Gotchas diff --git a/src/static_src/scss/_applets.scss b/src/static_src/scss/_applets.scss index 445b604..5e7492a 100644 --- a/src/static_src/scss/_applets.scss +++ b/src/static_src/scss/_applets.scss @@ -104,6 +104,30 @@ z-index: 201; } +// In landscape: shift gear btn and applet menus left of the footer right sidebar +@media (orientation: landscape) and (max-width: 1023px) { + $sidebar-w: 4rem; + + .gameboard-page, + .dashboard-page, + .wallet-page, + .room-page { + > .gear-btn { + right: calc(#{$sidebar-w} + 0.5rem); + bottom: 4.2rem; // same gap above kit btn as portrait; no page-specific overrides needed + top: auto; + } + } + + #id_dash_applet_menu, + #id_game_applet_menu, + #id_wallet_applet_menu { + right: calc(#{$sidebar-w} + 1rem); + bottom: 6.6rem; // same as portrait, just shifted right of footer sidebar + top: auto; + } +} + // ── Applets grid (shared across all boards) ──────────────── %applets-grid { container-type: inline-size; diff --git a/src/static_src/scss/_base.scss b/src/static_src/scss/_base.scss index 7ce8e07..3b281e0 100644 --- a/src/static_src/scss/_base.scss +++ b/src/static_src/scss/_base.scss @@ -21,7 +21,7 @@ body { max-width: 960px; width: 100%; margin: 0 auto; - padding: 0 1rem; + // padding: 0 1rem; flex: 1; .navbar { @@ -29,6 +29,7 @@ body { border-bottom: 0.1rem solid rgba(var(--secUser), 0.4); .navbar-brand { + margin-left: 1rem; h1 { font-size: 2rem; } @@ -38,6 +39,7 @@ body { display: flex; align-items: center; gap: 1rem; + margin-right: 0.5rem; > form { flex-shrink: 0; margin-left: auto; } } @@ -172,45 +174,118 @@ body { } @media (orientation: landscape) and (max-width: 1023px) { - body .container { - padding: 0.4rem 1rem; + $sidebar-w: 4rem; - .navbar { - padding: 0.2rem 0; + // ── Sidebar layout: navbar ← left, footer → right ──────────────────────────── + body { + flex-direction: row; + } - .navbar-brand h1 { - font-size: 1.2rem; - } - .btn-primary { - width: 3rem; - height: 3rem; - font-size: 0.75rem; - border-width: 0.125rem; - } + // Navbar → fixed left sidebar + body .container .navbar { + position: fixed; + left: 0; + top: 0; + height: 100vh; + width: $sidebar-w; + padding: 0.5rem 0; + border-bottom: none; + border-right: 0.1rem solid rgba(var(--secUser), 0.4); + background-color: rgba(var(--priUser), 1); + z-index: 300; + overflow: hidden; + + .container-fluid { + flex-direction: column; + height: 100%; + align-items: center; + justify-content: space-between; + gap: 0.5rem; + padding: 0 0.25rem; + + > form { margin-left: 0; flex-shrink: 0; order: -1; } // logout at top } - .row { - padding: 0.5rem 0; + .navbar-brand { order: 1; } // brand at bottom - .col-lg-6 h2 { - font-size: 2rem; - margin-bottom: 0.5rem; - // text-justify: inter-character is Firefox-only; approximate for Safari/Chrome - letter-spacing: 1em; - text-align: center; - text-align-last: center; - } + .navbar-brand h1 { + writing-mode: vertical-rl; + transform: rotate(180deg); + font-size: 1rem; + line-height: 1.2; + white-space: nowrap; + margin-right: 2.75rem; + } + + .navbar-text, + .navbar-link { display: none; } + + .btn-primary { + width: 3rem; + height: 3rem; + font-size: 0.75rem; + border-width: 0.125rem; + margin-left: 0.75rem; + } + + // Login form: email input can't fit in narrow sidebar + .input-group { display: none; } + } + + // Container: fill centre, compensate for fixed sidebars on both sides + body .container { + flex: 1; + min-width: 0; + margin-left: $sidebar-w; + margin-right: $sidebar-w; + padding: 0 0.5rem; + } + + // Header row: compact in landscape + body .container .row { + padding: 0.25rem 0; + + .col-lg-6 h2 { + font-size: 1.5rem; + margin: 0 0 0.25rem; + letter-spacing: 0.4em; + text-align: center; + text-align-last: center; } } - #id_footer { - height: 3rem; - padding: 0.4rem 1rem; - gap: 0.2rem; + // Footer → fixed right sidebar (mirrors navbar approach — explicit right boundary) + // Use body #id_footer (specificity 0,1,0,1) to beat base #id_footer (0,1,0,0) + // which compiles later in the output and would otherwise override height: 100vh. + body #id_footer { + position: fixed; + right: 0; + top: 0; + width: $sidebar-w; + height: 100vh; + flex-direction: column; + justify-content: center; + align-items: center; + border-top: none; + border-left: 0.1rem solid rgba(var(--secUser), 0.3); + padding: 1rem 0; + gap: 0; - #id_footer_nav a { - font-size: 1.4rem; + #id_footer_nav { + flex-direction: column; + width: auto; + max-width: none; + gap: 1.5rem; + + a { + font-size: 1.75rem; + display: flex; + justify-content: center; + align-items: center; + } } + + .footer-container { display: none; } } } diff --git a/src/static_src/scss/_billboard.scss b/src/static_src/scss/_billboard.scss new file mode 100644 index 0000000..21efb62 --- /dev/null +++ b/src/static_src/scss/_billboard.scss @@ -0,0 +1,28 @@ +html:has(body.page-billboard) { + overflow: hidden; +} + +body.page-billboard { + overflow: hidden; + + .container { + overflow: hidden; + display: flex; + flex-direction: column; + flex: 1; + min-height: 0; + } + + .row { + flex-shrink: 0; + margin-bottom: -1rem; + } +} + +.billboard-page { + flex: 1; + min-width: 0; + overflow-y: auto; + position: relative; + padding: 0.75rem; +} diff --git a/src/static_src/scss/_dashboard.scss b/src/static_src/scss/_dashboard.scss index b3fd418..d3ab652 100644 --- a/src/static_src/scss/_dashboard.scss +++ b/src/static_src/scss/_dashboard.scss @@ -153,6 +153,14 @@ body.page-dashboard { } } +@media (orientation: landscape) and (max-width: 1023px) { + // Reset the 666px min-width so #id_dash_content shrinks to fit within the + // sidebar-bounded container rather than overflowing into the footer sidebar. + #id_dash_content { + min-width: 0; + } +} + @media (max-height: 500px) { body.page-dashboard { .container { diff --git a/src/static_src/scss/_game-kit.scss b/src/static_src/scss/_game-kit.scss index 8435580..64a1000 100644 --- a/src/static_src/scss/_game-kit.scss +++ b/src/static_src/scss/_game-kit.scss @@ -2,7 +2,14 @@ position: fixed; bottom: 0.5rem; right: 0.5rem; - z-index: 205; + + @media (orientation: landscape) and (max-width: 1023px) { + right: calc(4rem + 0.5rem); + bottom: 0.75rem; + top: auto; + } + + z-index: 305; font-size: 1.75rem; cursor: pointer; color: rgba(var(--secUser), 1); @@ -37,6 +44,10 @@ background: rgba(var(--priUser), 0.97); z-index: 204; overflow: hidden; + + @media (orientation: landscape) and (max-width: 1023px) { + z-index: 301; // above navbar sidebar (z-index: 300) in landscape + } // Closed state max-height: 0; visibility: hidden; diff --git a/src/static_src/scss/_gameboard.scss b/src/static_src/scss/_gameboard.scss index 612a132..9fba3e2 100644 --- a/src/static_src/scss/_gameboard.scss +++ b/src/static_src/scss/_gameboard.scss @@ -46,6 +46,19 @@ body.page-gameboard { } } +@media (orientation: landscape) and (max-width: 1023px) { + // Restore clip in landscape — overrides the >738px overflow:visible above, + // preventing the gameboard applets from bleeding into the footer sidebar. + body.page-gameboard .container { + overflow: clip; + } + // Reset the 666px min-width so gameboard-page shrinks to fit within the + // sidebar-bounded container rather than overflowing into the footer sidebar. + .gameboard-page { + min-width: 0; + } +} + #id_applet_game_kit { display: flex; flex-direction: column; diff --git a/src/static_src/scss/_room.scss b/src/static_src/scss/_room.scss index ca22354..d171c70 100644 --- a/src/static_src/scss/_room.scss +++ b/src/static_src/scss/_room.scss @@ -633,10 +633,6 @@ $inv-strip: 30px; // visible height of each stacked card after the first // Landscape mobile — aggressively scale down to fit short viewport @media (orientation: landscape) and (max-width: 1023px) { - .room-page .gear-btn { - bottom: 3.5rem; - } - .gate-modal { padding: 0.6rem 1.25rem; diff --git a/src/static_src/scss/core.scss b/src/static_src/scss/core.scss index f215bb5..fdb06e1 100644 --- a/src/static_src/scss/core.scss +++ b/src/static_src/scss/core.scss @@ -6,6 +6,7 @@ @import 'gameboard'; @import 'palette-picker'; @import 'room'; +@import 'billboard'; @import 'game-kit'; @import 'wallet-tokens'; diff --git a/src/templates/apps/billboard/billboard.html b/src/templates/apps/billboard/billboard.html index ea90425..940be24 100644 --- a/src/templates/apps/billboard/billboard.html +++ b/src/templates/apps/billboard/billboard.html @@ -4,7 +4,7 @@ {% block header_text %}Billboard{% endblock header_text %} {% block content %} -