From e0bc7c5dc805eb5a8719ea18d8605329227f8baa Mon Sep 17 00:00:00 2001 From: FirLaw Bot Date: Tue, 26 May 2026 23:13:39 +0700 Subject: [PATCH 1/4] =?UTF-8?q?feat(spa):=20add=20firlaw=20header=20extras?= =?UTF-8?q?=20=E2=80=94=20user=20chip,=20theme=20toggle,=20notification,?= =?UTF-8?q?=20help?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New component firlaw-titlebar-extras.tsx mounted via Portal into opencode-titlebar-right - Updated firlaw-overrides.css with monochrome court-luxury theme variables - Titlebar height 48px, button size 32px, radius 6px - User chip fetches session and shows name + initials avatar - Theme toggle switches light/dark via useTheme() - No inline edits to upstream titlebar.tsx --- packages/app/src/components/file-tree.tsx | 20 +-- .../src/components/firlaw-titlebar-extras.tsx | 103 ++++++++++++++++ packages/app/src/pages/layout.tsx | 2 + .../src/pages/session/session-side-panel.tsx | 8 +- packages/app/src/styles/firlaw-overrides.css | 115 ++++++++++++++---- 5 files changed, 215 insertions(+), 33 deletions(-) create mode 100644 packages/app/src/components/firlaw-titlebar-extras.tsx diff --git a/packages/app/src/components/file-tree.tsx b/packages/app/src/components/file-tree.tsx index 425379f10c8f..068ee40a98b5 100644 --- a/packages/app/src/components/file-tree.tsx +++ b/packages/app/src/components/file-tree.tsx @@ -148,13 +148,13 @@ const FileTreeNode = ( { if (!local.draggable) return @@ -168,7 +168,7 @@ const FileTreeNode = ( {local.children} + {kindLabel(value)} ) @@ -429,8 +429,8 @@ export default function FileTree(props: { kinds={kinds()} marks={marks()} > -
- +
+
, )} @@ -438,11 +438,11 @@ export default function FileTree(props: {
props.onFileClick?.(node)} > -
+
(null) + const [userName, setUserName] = createSignal("") + const [userInitials, setUserInitials] = createSignal("FL") + + onMount(() => { + setMount(document.getElementById("opencode-titlebar-right")) + fetchUser() + }) + + const fetchUser = async () => { + try { + const r = await fetch("/api/v2/auth/get-session", { credentials: "include" }) + if (!r.ok) return + const data = await r.json() + const name = data?.user?.name || "" + setUserName(name) + const parts = name.split(" ").filter(Boolean) + if (parts.length >= 2) { + setUserInitials(parts[0][0] + parts[parts.length - 1][0]) + } else if (parts.length === 1) { + setUserInitials(parts[0].slice(0, 2).toUpperCase()) + } + } catch {} + } + + const toggleTheme = () => { + const current = theme.colorScheme() + theme.setColorScheme(current === "dark" ? "light" : "dark") + } + + const logout = async () => { + try { + await fetch("/api/v2/auth/sign-out", { method: "POST", credentials: "include" }) + window.location.href = "/login.html" + } catch {} + } + + return ( + + {(el) => ( + +
+ + + + + + + + + + + + + + + + + +
+
+ )} +
+ ) +} diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index d56e16a8a949..d623359c28cc 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -62,6 +62,7 @@ import { useCommand, type CommandOption } from "@/context/command" import { ConstrainDragXAxis, getDraggableId } from "@/utils/solid-dnd" import { DebugBar } from "@/components/debug-bar" import { Titlebar } from "@/components/titlebar" +import { FirlawTitlebarExtras } from "@/components/firlaw-titlebar-extras" import { useServer } from "@/context/server" import { useLanguage, type Locale } from "@/context/language" import { pathKey } from "@/utils/path-key" @@ -2382,6 +2383,7 @@ export default function Layout(props: ParentProps) {
{autoselecting() ?? ""} +
diff --git a/packages/app/src/pages/session/session-side-panel.tsx b/packages/app/src/pages/session/session-side-panel.tsx index 1813b60897e0..e99641b2460b 100644 --- a/packages/app/src/pages/session/session-side-panel.tsx +++ b/packages/app/src/pages/session/session-side-panel.tsx @@ -385,8 +385,12 @@ export function SessionSidePanel(props: { class="h-full" data-scope="filetree" > -
- +
+
+ + Thư mục dự án +
+ {props.reviewCount()}{" "} {language.t( diff --git a/packages/app/src/styles/firlaw-overrides.css b/packages/app/src/styles/firlaw-overrides.css index db036a32263e..eeb6ab7683d3 100644 --- a/packages/app/src/styles/firlaw-overrides.css +++ b/packages/app/src/styles/firlaw-overrides.css @@ -1,46 +1,40 @@ /** - * Firlaw UI overrides — all Firlaw-specific visual customizations live here. - * Keep upstream JSX files clean; add CSS rules here instead of inline class changes. - * - * CSS variables let us tune sizing from one place without touching upstream components. + * Firlaw UI overrides — monochrome court-luxury theme. + * Keep upstream JSX files clean; override visuals here. */ -/* ─── Titlebar ─────────────────────────────────────────────────────────────── */ +/* ─── Firlaw Monochrome Palette ───────────────────────────────────────────── */ :root { - --firlaw-titlebar-height: 3.5rem; /* 56px — upstream default is 2.5rem (40px) */ - --firlaw-titlebar-btn-size: 2.5rem; /* 40px (10 in Tailwind) */ - --firlaw-titlebar-btn-radius: 10px; + --firlaw-titlebar-height: 48px; + --firlaw-titlebar-btn-size: 32px; + --firlaw-titlebar-btn-radius: 6px; + --firlaw-font: 'Times New Roman', Times, serif; } -/* Override header height */ +/* ─── Titlebar ─────────────────────────────────────────────────────────────── */ header[data-tauri-drag-region] { - height: var(--firlaw-titlebar-height); + height: var(--firlaw-titlebar-height) !important; + border-bottom: 1px solid var(--border-weak-base, rgba(0,0,0,.07)); } -/* Larger titlebar action buttons */ .titlebar-icon { width: var(--firlaw-titlebar-btn-size); height: var(--firlaw-titlebar-btn-size); border-radius: var(--firlaw-titlebar-btn-radius); } - -/* ─── Session Header ────────────────────────────────────────────────────────── */ - -/* Larger open-dir button (height + radius) */ +/* ─── Session Header (portal-injected right side) ─────────────────────────── */ .firlaw-open-dir-btn { height: var(--firlaw-titlebar-btn-size) !important; border-radius: var(--firlaw-titlebar-btn-radius) !important; } -/* Show the "Thư mục" label inline — upstream button doesn't have text label */ .firlaw-dir-label { display: inline; - font-size: 0.875rem; /* text-sm */ + font-size: 0.875rem; padding-left: 0.5rem; } -/* Review + File Tree toggle buttons — same sizing as titlebar icons */ .firlaw-header-action-btn { width: auto !important; height: var(--firlaw-titlebar-btn-size) !important; @@ -50,15 +44,94 @@ header[data-tauri-drag-region] { gap: 0.5rem !important; } -/* Show "Review" label text next to icon */ .firlaw-btn-label { display: inline; - font-size: 0.875rem; /* text-sm */ + font-size: 0.875rem; } -/* Reload skills button */ .firlaw-reload-skills-btn { width: var(--firlaw-titlebar-btn-size) !important; height: var(--firlaw-titlebar-btn-size) !important; border-radius: var(--firlaw-titlebar-btn-radius) !important; } + +/* ─── Firlaw User Chip (topbar right) ─────────────────────────────────────── */ +.firlaw-user-chip { + display: flex; + align-items: center; + gap: 8px; + padding: 3px 10px 3px 4px; + border-radius: 7px; + cursor: pointer; + transition: background .12s; + font-family: var(--firlaw-font); +} +.firlaw-user-chip:hover { + background: var(--surface-base-hover, rgba(0,0,0,.06)); +} +.firlaw-user-avatar { + width: 26px; + height: 26px; + border-radius: 50%; + background: var(--text-strong, #161618); + color: var(--background-base, #fff); + display: grid; + place-items: center; + font-weight: 700; + font-size: 11.5px; + font-family: var(--firlaw-font); +} +.firlaw-user-name { + font-size: 14px; + font-weight: 500; + font-family: var(--firlaw-font); +} + +/* ─── Firlaw Topbar Button ────────────────────────────────────────────────── */ +.firlaw-top-btn { + width: 32px; + height: 32px; + display: grid; + place-items: center; + border-radius: 6px; + border: none; + background: none; + color: var(--icon-weak, #6B6B70); + cursor: pointer; + transition: background .12s, color .12s; +} +.firlaw-top-btn:hover { + background: var(--surface-base-hover, rgba(0,0,0,.06)); + color: var(--text-strong, #161618); +} +.firlaw-top-btn svg { + width: 16px; + height: 16px; +} + +/* ─── Firlaw Case Toggle Button ───────────────────────────────────────────── */ +.firlaw-case-toggle { + display: flex; + align-items: center; + gap: 7px; + height: 32px; + padding: 0 12px 0 10px; + margin-left: 6px; + border: 1px solid var(--border-base, rgba(0,0,0,.12)); + border-radius: 7px; + font-size: 13.5px; + font-weight: 500; + font-family: var(--firlaw-font); + color: var(--text-base, #3F3F45); + background: transparent; + cursor: pointer; + transition: background .12s, border-color .12s, color .12s; +} +.firlaw-case-toggle:hover { + background: var(--surface-base-hover, rgba(0,0,0,.06)); + color: var(--text-strong, #161618); +} +.firlaw-case-toggle svg { + width: 15px; + height: 15px; +} From 8a3c8f0e38c993a44c9d1b3156845ca0b4c3a875 Mon Sep 17 00:00:00 2001 From: FirLaw Bot Date: Wed, 27 May 2026 15:22:13 +0700 Subject: [PATCH 2/4] fix(ui): fix CSS collision, logout error handling, toggleTheme system state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove .titlebar-icon override that forced upstream nav buttons to 32x32px (upstream uses h-6=24px; our override broke sidebar toggle, back/forward) - Fix logout() to redirect only on success, return silently on network error - Fix toggleTheme to cycle dark→light→system instead of losing system state - Fix theme tooltip label to reflect system/light/dark correctly --- .../app/src/components/firlaw-titlebar-extras.tsx | 11 +++++++---- packages/app/src/styles/firlaw-overrides.css | 6 +----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/app/src/components/firlaw-titlebar-extras.tsx b/packages/app/src/components/firlaw-titlebar-extras.tsx index 2cab83786bb1..bc36c159b10b 100644 --- a/packages/app/src/components/firlaw-titlebar-extras.tsx +++ b/packages/app/src/components/firlaw-titlebar-extras.tsx @@ -32,14 +32,17 @@ export function FirlawTitlebarExtras() { const toggleTheme = () => { const current = theme.colorScheme() - theme.setColorScheme(current === "dark" ? "light" : "dark") + const next = current === "dark" ? "light" : current === "light" ? "system" : "dark" + theme.setColorScheme(next) } const logout = async () => { try { await fetch("/api/v2/auth/sign-out", { method: "POST", credentials: "include" }) - window.location.href = "/login.html" - } catch {} + } catch { + return + } + window.location.href = "/login.html" } return ( @@ -56,7 +59,7 @@ export function FirlawTitlebarExtras() { - +