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
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
18
CLAUDE.md
18
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
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
28
src/static_src/scss/_billboard.scss
Normal file
28
src/static_src/scss/_billboard.scss
Normal 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;
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
@import 'gameboard';
|
||||
@import 'palette-picker';
|
||||
@import 'room';
|
||||
@import 'billboard';
|
||||
@import 'game-kit';
|
||||
@import 'wallet-tokens';
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
{% block header_text %}<span>Bill</span>board{% endblock header_text %}
|
||||
|
||||
{% block content %}
|
||||
<div class="content billboard-page">
|
||||
<div class="billboard-page">
|
||||
<h2>My Games</h2>
|
||||
<ul class="game-list">
|
||||
{% for room in my_rooms %}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
{% block title_text %}{{ room.name }} — Drama Log{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="content dashboard-page">
|
||||
<div class="billboard-page">
|
||||
<h2>{{ room.name }}</h2>
|
||||
{% include "core/_partials/_scroll.html" %}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user