diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-07 10:35:09 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-07 10:35:09 +0200 |
| commit | 2c0903c648e24b4e2adf20492398e8b67d033b49 (patch) | |
| tree | 0435f298010f0f946362f28baebbe88337ca8768 /packages/meshbay-hub/src/meshbay_hub/static/style.css | |
| parent | 0ed078c92cabab1dab0f70f321562032ea549ce6 (diff) | |
| parent | eeda274d751c537f4ecef3087994a16a9517478f (diff) | |
| download | meshbay-2c0903c648e24b4e2adf20492398e8b67d033b49.tar.gz | |
Merge branch 'refactor/groups-phase1'
Groups refactor, phases 1-3.
The root model replaces the old `upload` flag and group-wide `member_upload`
with per-root `writable`/`removable`/`ejected`, carried by a `RootSet` that
both front doors — the loopback API and signed MNP — reach through the same
`ops` functions. MNP goes to 1.1, additively: the roots table now rides on
`index_delta`, so a root added, removed, ejected or plugged reaches every
connected client instead of only whoever reloaded.
The group UI becomes a plugin architecture: an application is a registry
entry in `apps.js` plus its own files, with directories stored generically
by `ops.set_app_directories` under whatever the app is called. A reference
application, hidden behind `?dev=1`, is what makes that claim testable —
adding it is what found the two places still naming apps by hand.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011pvMdvLBG92jyhvD5pD6us
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/style.css')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/style.css | 231 |
1 files changed, 227 insertions, 4 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/style.css b/packages/meshbay-hub/src/meshbay_hub/static/style.css index c183d7e..d1d5732 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/style.css +++ b/packages/meshbay-hub/src/meshbay_hub/static/style.css @@ -24,6 +24,11 @@ --nav-brand: #23b1f0; --sidebar-bg: #f1f5f9; --sidebar-hover: #e2e8f0; + /* Used with a literal fallback in a handful of rules before they were + defined here — same values, so nothing moves; now themeable, and the + names in those rules are real. */ + --warn: #d97706; + --accent-bg: rgba(59, 130, 246, 0.1); --shadow: 0 1px 3px rgba(0, 0, 0, 0.08); --shadow-lg: 0 4px 16px rgba(0, 0, 0, 0.1); } @@ -36,6 +41,8 @@ --text-secondary: #94a3b8; --text-dim: #64748b; --border: #334155; + --warn: #f59e0b; + --accent-bg: rgba(59, 130, 246, 0.18); --error-bg: #450a0a; --nav-bg: #020617; --sidebar-bg: #1e293b; @@ -826,6 +833,22 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } .tb-btn:disabled { opacity: 0.45; cursor: not-allowed; } .tb-btn:disabled:hover { border-color: var(--border); color: var(--text); } .tb-btn .icon { width: 15px; height: 15px; } +/* Square, for a toolbar button whose name is in its tooltip rather than + beside it. The horizontal padding goes; the height does not, so it lines up + with the labelled buttons next to it. */ +.tb-btn-icon { padding: 0; width: 32px; justify-content: center; gap: 0; } + +/* Naming a new folder, in the toolbar. Not `window.prompt`, which throws in + Electron — and an inline field can put the node's refusal next to the input + rather than after a dialog has closed. */ +.tb-mkdir { display: inline-flex; align-items: center; gap: 6px; } +.tb-mkdir-input { + height: 32px; width: 170px; padding: 0 9px; + border: 1px solid var(--border-focus); border-radius: 6px; + background: var(--bg-surface); color: var(--text); + font-family: inherit; font-size: 0.83em; +} +.tb-mkdir-input:focus { outline: none; } .tb-btn.primary { background: var(--accent); border-color: var(--accent); @@ -1240,6 +1263,46 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } } .settings-collapsible-header .settings-heading { margin-bottom: 0; } .settings-collapsible-body { margin-top: 12px; } +/* The header button stretches; the action (an app's on/off switch) keeps its + own width at the end, and its own click. */ +.settings-collapsible-bar { display: flex; align-items: center; gap: 12px; } +.settings-collapsible-bar .settings-collapsible-header { flex: 1 1 auto; min-width: 0; } +.settings-collapsible-action { flex: 0 0 auto; } + +/* One app's settings pane, inside its section. */ +.app-settings > .settings-row:first-child { margin-top: 0; } +/* The Save button of an app's settings pane. + * + * It was `btn btn-small btn-secondary`, and there is no `.btn` rule at all — + * so it inherited `.btn-secondary`: no background, a transparent border, dim + * grey text. Enabled, it already looked like a disabled control; disabled, it + * was the same thing at 40% opacity. "You cannot always click Save, you do not + * notice, and it does not work" was one sentence describing all of that. + * + * It looks like a button now, and a disabled one looks disabled. */ +.app-save { + display: inline-flex; align-items: center; gap: 6px; + height: 30px; padding: 0 14px; margin-top: 4px; + border: 1px solid var(--accent); border-radius: 6px; + background: var(--accent); color: var(--accent-text); + font-family: inherit; font-size: 0.83em; cursor: pointer; +} +.app-save:hover:not(:disabled) { background: var(--accent-hover); } +/* No transition on the enabled/disabled swap. It animates a colour change that + coincides with a text-colour change, which reads as a flicker — and it made + the state genuinely hard to observe: a measurement taken during it reports a + transparent background on a button that is not transparent. */ +.app-save:disabled { + background: none; color: var(--text-dim); + border-color: var(--border); opacity: 1; cursor: default; +} +/* Why it is inert, beside it — a disabled control that explains itself is the + difference between "nothing changed" and "this is broken". */ + +.app-settings-sub { + margin: 18px 0 4px; font-size: 0.9em; font-weight: 600; + padding-top: 12px; border-top: 1px solid var(--border); +} .settings-meta-title { display: flex; @@ -1267,6 +1330,7 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } /* A modern on/off switch — replaces a checkbox or a "Turn on/off" button wherever the setting is a straight binary. */ .toggle-switch { + position: relative; display: inline-flex; align-items: center; gap: 10px; @@ -1897,7 +1961,8 @@ button:disabled { opacity: 0.5; cursor: not-allowed; } white-space: nowrap; } a.transfer-name { - color: var(--link); + /* `--link` is not defined here; the palette's link colour is `--accent`. */ + color: var(--accent); text-decoration: underline; cursor: pointer; } @@ -2125,7 +2190,11 @@ a.transfer-name { position: absolute; top: -6px; right: -8px; - background: var(--danger); + /* `--danger` is not one of this stylesheet's variables — the palette calls + it `--error`. The declaration was invalid, so the unread-count badge had + white text on no background at all. Pre-existing; found while fixing four + more of the same in the folder picker. */ + background: var(--error); color: #fff; font-size: 0.65rem; padding: 1px 5px; @@ -2147,7 +2216,8 @@ a.transfer-name { align-items: center; gap: 8px; padding: 8px 12px; - border-radius: var(--radius); + /* `--radius` was never defined — 8px is what the other cards use. */ + border-radius: 8px; background: var(--bg-surface); margin-bottom: 4px; cursor: pointer; @@ -2157,7 +2227,8 @@ a.transfer-name { background: var(--bg-raised); } .notif-unread { - border-left: 3px solid var(--primary); + /* `--primary` is not defined here either; it is `--accent`. */ + border-left: 3px solid var(--accent); font-weight: 600; } .notif-kind { @@ -2542,6 +2613,66 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } .node-roots-header { margin-bottom: 8px; } +/* Shared directories table */ +.shared-dirs-tbl { width: 100%; border-collapse: collapse; border: none; } +.shared-dirs-tbl th { + text-align: left; font-size: 0.78em; font-weight: 500; + color: var(--text-dim); padding: 0 12px 6px 0; + text-transform: uppercase; letter-spacing: 0.04em; + border: none; +} +.shared-dirs-tbl td { padding: 7px 12px 7px 0; border: none; vertical-align: middle; } +.shared-dirs-tbl tbody tr + tr td { border-top: 1px solid var(--border); } +.sdt-col-dir { min-width: 140px; } +/* The path is the only thing separating two libraries whose folders happen to + share a basename, so it is shown — truncated, because it is usually long and + rarely the thing being read. */ +.sdt-col-path { + color: var(--text-dim); font-size: 0.85em; + max-width: 260px; overflow: hidden; text-overflow: ellipsis; + white-space: nowrap; +} +.sdt-add-row { display: flex; gap: 6px; align-items: center; margin-top: 8px; } +.sdt-add-input { + flex: 1 1 auto; min-width: 0; padding: 5px 8px; + border: 1px solid var(--border); border-radius: 4px; + background: var(--bg-surface); color: var(--text); + font-family: inherit; font-size: 0.9em; +} +.sdt-col-toggle { width: 90px; text-align: center; } +.sdt-col-toggle th { text-align: center; } +.sdt-col-toggle .toggle-switch { justify-content: center; } +.sdt-col-actions { white-space: nowrap; text-align: right; } +.sdt-action-btn { + background: none; border: 1px solid var(--border); border-radius: 4px; + padding: 4px 7px; cursor: pointer; font-size: 0.85em; color: var(--text-dim); + line-height: 1; vertical-align: middle; +} +.sdt-action-btn + .sdt-action-btn { margin-left: 6px; } +.sdt-action-btn:hover:not(:disabled) { background: var(--bg-raised); color: var(--text); } +.sdt-action-btn:disabled { opacity: 0.3; cursor: default; } +.sdt-action-danger:hover:not(:disabled) { color: var(--error); border-color: var(--error); } +.sdt-dir-name { display: inline-flex; align-items: center; gap: 6px; font-weight: 500; } +.sdt-dir-name .icon { width: 16px; height: 16px; flex-shrink: 0; } +.sdt-row-ejected { opacity: 0.5; } +.sdt-row-unavail { opacity: 0.6; } +.root-eject-btn { + background: none; border: 1px solid var(--border); border-radius: 4px; + padding: 2px 6px; cursor: pointer; font-size: 0.85em; color: var(--text-dim); + line-height: 1; +} +.root-eject-btn:hover { background: var(--bg-raised); } +.root-eject-btn { margin-left: 8px; } +/* An ejected root in the Files table: still listed, deliberately — its files + are frozen, not gone — but not somewhere you can walk into. */ +.file-row.root-ejected { opacity: 0.5; } +.file-row.root-ejected td:not(.sel-cell) { cursor: default; } + +/* The paperclip with no writable directory to write to. Shown rather than + hidden, so the reason is discoverable instead of the control just being + absent. */ +.chat-attach-off { opacity: 0.35; cursor: not-allowed; } + .node-root { display: flex; align-items: center; @@ -3993,3 +4124,95 @@ h2 .gn-owner, h3 .gn-owner { font-size: 0.55em; } background: linear-gradient(to top, rgba(0, 0, 0, 0.55), transparent); } .photo-lightbox-count { color: #94a3b8; } + +/* ── Folder tree picker (folder-tree.js) ─────────────────────────────────── */ + +.ftp-backdrop { + position: fixed; inset: 0; z-index: 1200; + background: rgba(0, 0, 0, 0.55); + display: flex; align-items: center; justify-content: center; + padding: 16px; +} +.ftp-panel { + background: var(--bg-surface); color: var(--text); + border: 1px solid var(--border); border-radius: 8px; + width: min(520px, 100%); max-height: min(70vh, 640px); + display: flex; flex-direction: column; padding: 16px; + box-shadow: 0 12px 40px rgba(0, 0, 0, 0.4); +} +.ftp-panel:focus { outline: none; } +.ftp-title { margin: 0 0 8px; font-size: 1em; } +/* The tree is the only part that scrolls: the title, the selection and the + buttons stay put, so OK never travels off-screen in a deep library. */ +.ftp-tree, .ftp-children { list-style: none; margin: 0; padding: 0; } +.ftp-tree { + flex: 1 1 auto; overflow-y: auto; overflow-x: hidden; + border: 1px solid var(--border); border-radius: 4px; + padding: 4px 0; margin: 8px 0; +} +.ftp-row { display: flex; align-items: center; gap: 2px; } +.ftp-row.chosen { background: var(--bg-raised); } +/* 0.4 was low enough that a greyed-out root read as an artefact rather than + a disabled control — the names were barely legible in the screenshot that + prompted this. */ +.ftp-row.blocked { opacity: 0.55; } +.ftp-twisty { + flex: 0 0 auto; width: 20px; height: 20px; padding: 0; + background: none; border: none; color: var(--text-dim); + font-family: inherit; font-size: 0.95em; line-height: 1; cursor: pointer; +} +.ftp-twisty:disabled { cursor: default; opacity: 0; } +.ftp-label { + flex: 1 1 auto; min-width: 0; + display: flex; align-items: center; gap: 6px; + background: none; border: none; color: inherit; + font: inherit; text-align: left; padding: 3px 6px; cursor: pointer; +} +.ftp-label:disabled { cursor: not-allowed; } +.ftp-label .icon { width: 15px; height: 15px; flex-shrink: 0; } +.ftp-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.ftp-badge { + flex: 0 0 auto; margin-left: 8px; + font-size: 0.72em; padding: 1px 5px; border-radius: 3px; + border: 1px solid var(--border); background: var(--bg-raised); + color: var(--text-dim); white-space: nowrap; + text-transform: uppercase; letter-spacing: 0.03em; +} +.ftp-badge.rw { color: var(--accent); border-color: currentColor; } +.ftp-badge.warn { color: var(--error); border-color: currentColor; } +.ftp-check { margin-left: auto; flex: 0 0 auto; } +.ftp-selection { + display: flex; flex-wrap: wrap; gap: 4px; + max-height: 84px; overflow-y: auto; margin-bottom: 10px; +} +.ftp-chip { + font-size: 0.82em; padding: 2px 6px; border-radius: 3px; + background: var(--bg-raised); border: 1px solid var(--border); +} +.ftp-actions { display: flex; justify-content: flex-end; gap: 8px; } +/* An app's chosen folders. Deliberately not a .settings-row: that is a + space-between flex row, and a label, a hint and a list laid out inside one + end up spread across a single line in source order. */ +.folder-field { padding: 8px 0; } +.folder-field-head { margin-bottom: 6px; } +.folder-field-label { + margin: 0 0 2px; font-size: 0.9em; font-weight: 600; color: var(--text); +} +.folder-field-head .settings-hint { margin-bottom: 0; } +/* Borrows .shared-dirs-tbl so the two lists of directories on this page read + as the same kind of thing. */ +.folder-field-tbl { margin-bottom: 8px; } +.folder-field-tbl .sdt-col-dir { font-size: 0.9em; } +.folder-field-actions { display: flex; align-items: center; gap: 10px; } + +/* HelloWorld (docs/refactor-groups.md §4.1) — the reference app, hidden + behind ?dev=1. Deliberately plain: it exists to prove the plumbing, and + anything decorative here would be a second thing to keep working. */ +.hw-list { list-style: none; margin: 12px 0 0; padding: 0; } +.hw-item { + display: flex; align-items: center; gap: 8px; + padding: 6px 0; border-bottom: 1px solid var(--border); +} +.hw-item .icon { width: 15px; height: 15px; flex-shrink: 0; } +.hw-name { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.hw-size { flex: 0 0 auto; color: var(--text-dim); font-size: 0.85em; } |