ensured footer was pinned to bottom of page for new-ish billboard.html & room_scroll.html pages; introduced mobile landscape layout, incl. leftward 'navbar', rightward 'footer'; ensured z-index primacy of #id_kit_btn, which would here appear behind the kit bar when open; other fixes introduced by problems stemming largely from new landscape styling
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
Disco DeDisco
2026-03-23 01:06:14 -04:00
parent 2fd3ec9ab2
commit eecb6c2be6
11 changed files with 208 additions and 40 deletions

View File

@@ -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`. All tools require a `tabId` except `firefox_create_window` and `firefox_diagnose`.
### If tools aren't available in a session ### 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) ### Setup (already done — for reference)
The native messaging host requires a `.bat` wrapper on Windows (Firefox can't execute `.js` directly): 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" %*` - 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 - Manifest: `C:\Users\adamc\AppData\Roaming\claudezilla\claudezilla.json` — points to the `.bat` file
- Registry: `HKCU\SOFTWARE\Mozilla\NativeMessagingHosts\claudezilla` → manifest path - 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` - 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`. Native host: `E:\ClaudeLibrary\claudezilla\host\`. Extension: `claudezilla@boot.industries`.
## Stack ## Stack
@@ -91,7 +103,7 @@ Test runner is `core.runner.RobustCompressorTestRunner` — handles the Windows
- Push to `main` triggers Woodpecker → deploys to staging - Push to `main` triggers Woodpecker → deploys to staging
## SCSS Import Order ## 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 ## Critical Gotchas

View File

@@ -104,6 +104,30 @@
z-index: 201; 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 (shared across all boards) ────────────────
%applets-grid { %applets-grid {
container-type: inline-size; container-type: inline-size;

View File

@@ -21,7 +21,7 @@ body {
max-width: 960px; max-width: 960px;
width: 100%; width: 100%;
margin: 0 auto; margin: 0 auto;
padding: 0 1rem; // padding: 0 1rem;
flex: 1; flex: 1;
.navbar { .navbar {
@@ -29,6 +29,7 @@ body {
border-bottom: 0.1rem solid rgba(var(--secUser), 0.4); border-bottom: 0.1rem solid rgba(var(--secUser), 0.4);
.navbar-brand { .navbar-brand {
margin-left: 1rem;
h1 { h1 {
font-size: 2rem; font-size: 2rem;
} }
@@ -38,6 +39,7 @@ body {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 1rem; gap: 1rem;
margin-right: 0.5rem;
> form { flex-shrink: 0; margin-left: auto; } > form { flex-shrink: 0; margin-left: auto; }
} }
@@ -172,45 +174,118 @@ body {
} }
@media (orientation: landscape) and (max-width: 1023px) { @media (orientation: landscape) and (max-width: 1023px) {
body .container { $sidebar-w: 4rem;
padding: 0.4rem 1rem;
.navbar { // ── Sidebar layout: navbar ← left, footer → right ────────────────────────────
padding: 0.2rem 0; body {
flex-direction: row;
}
// 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
}
.navbar-brand { order: 1; } // brand at bottom
.navbar-brand h1 { .navbar-brand h1 {
font-size: 1.2rem; 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 { .btn-primary {
width: 3rem; width: 3rem;
height: 3rem; height: 3rem;
font-size: 0.75rem; font-size: 0.75rem;
border-width: 0.125rem; border-width: 0.125rem;
} margin-left: 0.75rem;
} }
.row { // Login form: email input can't fit in narrow sidebar
padding: 0.5rem 0; .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 { .col-lg-6 h2 {
font-size: 2rem; font-size: 1.5rem;
margin-bottom: 0.5rem; margin: 0 0 0.25rem;
// text-justify: inter-character is Firefox-only; approximate for Safari/Chrome letter-spacing: 0.4em;
letter-spacing: 1em;
text-align: center; text-align: center;
text-align-last: center; text-align-last: center;
} }
} }
// 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 {
flex-direction: column;
width: auto;
max-width: none;
gap: 1.5rem;
a {
font-size: 1.75rem;
display: flex;
justify-content: center;
align-items: center;
}
} }
#id_footer { .footer-container { display: none; }
height: 3rem;
padding: 0.4rem 1rem;
gap: 0.2rem;
#id_footer_nav a {
font-size: 1.4rem;
}
} }
} }

View File

@@ -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;
}

View File

@@ -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) { @media (max-height: 500px) {
body.page-dashboard { body.page-dashboard {
.container { .container {

View File

@@ -2,7 +2,14 @@
position: fixed; position: fixed;
bottom: 0.5rem; bottom: 0.5rem;
right: 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; font-size: 1.75rem;
cursor: pointer; cursor: pointer;
color: rgba(var(--secUser), 1); color: rgba(var(--secUser), 1);
@@ -37,6 +44,10 @@
background: rgba(var(--priUser), 0.97); background: rgba(var(--priUser), 0.97);
z-index: 204; z-index: 204;
overflow: hidden; overflow: hidden;
@media (orientation: landscape) and (max-width: 1023px) {
z-index: 301; // above navbar sidebar (z-index: 300) in landscape
}
// Closed state // Closed state
max-height: 0; max-height: 0;
visibility: hidden; visibility: hidden;

View File

@@ -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 { #id_applet_game_kit {
display: flex; display: flex;
flex-direction: column; flex-direction: column;

View File

@@ -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 // Landscape mobile — aggressively scale down to fit short viewport
@media (orientation: landscape) and (max-width: 1023px) { @media (orientation: landscape) and (max-width: 1023px) {
.room-page .gear-btn {
bottom: 3.5rem;
}
.gate-modal { .gate-modal {
padding: 0.6rem 1.25rem; padding: 0.6rem 1.25rem;

View File

@@ -6,6 +6,7 @@
@import 'gameboard'; @import 'gameboard';
@import 'palette-picker'; @import 'palette-picker';
@import 'room'; @import 'room';
@import 'billboard';
@import 'game-kit'; @import 'game-kit';
@import 'wallet-tokens'; @import 'wallet-tokens';

View File

@@ -4,7 +4,7 @@
{% block header_text %}<span>Bill</span>board{% endblock header_text %} {% block header_text %}<span>Bill</span>board{% endblock header_text %}
{% block content %} {% block content %}
<div class="content billboard-page"> <div class="billboard-page">
<h2>My Games</h2> <h2>My Games</h2>
<ul class="game-list"> <ul class="game-list">
{% for room in my_rooms %} {% for room in my_rooms %}

View File

@@ -3,7 +3,7 @@
{% block title_text %}{{ room.name }} — Drama Log{% endblock %} {% block title_text %}{{ room.name }} — Drama Log{% endblock %}
{% block content %} {% block content %}
<div class="content dashboard-page"> <div class="billboard-page">
<h2>{{ room.name }}</h2> <h2>{{ room.name }}</h2>
{% include "core/_partials/_scroll.html" %} {% include "core/_partials/_scroll.html" %}
</div> </div>