diff --git a/packages/app/src/components/file-tree.tsx b/packages/app/src/components/file-tree.tsx index 425379f10c8f..0cf3928ef823 100644 --- a/packages/app/src/components/file-tree.tsx +++ b/packages/app/src/components/file-tree.tsx @@ -19,7 +19,7 @@ import { } from "solid-js" import { Dynamic } from "solid-js/web" import type { FileNode } from "@opencode-ai/sdk/v2" -import { FirlawFileActions } from "./firlaw-file-actions" +import { FirlawFileActions } from "@/firlaw/file-actions/file-actions" const MAX_DEPTH = 128 @@ -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)} > -
+
Promise diff --git a/packages/app/src/components/firlaw-file-actions.tsx b/packages/app/src/firlaw/file-actions/file-actions.tsx similarity index 100% rename from packages/app/src/components/firlaw-file-actions.tsx rename to packages/app/src/firlaw/file-actions/file-actions.tsx diff --git a/packages/app/src/components/firlaw-logo.tsx b/packages/app/src/firlaw/logo/logo.tsx similarity index 100% rename from packages/app/src/components/firlaw-logo.tsx rename to packages/app/src/firlaw/logo/logo.tsx diff --git a/packages/app/src/pages/session/firlaw-side-panel-actions.tsx b/packages/app/src/firlaw/side-panel/side-panel-actions.tsx similarity index 100% rename from packages/app/src/pages/session/firlaw-side-panel-actions.tsx rename to packages/app/src/firlaw/side-panel/side-panel-actions.tsx diff --git a/packages/app/src/firlaw/styles/overrides.css b/packages/app/src/firlaw/styles/overrides.css new file mode 100644 index 000000000000..22e544be7f04 --- /dev/null +++ b/packages/app/src/firlaw/styles/overrides.css @@ -0,0 +1,135 @@ +/** + * Firlaw UI overrides — monochrome court-luxury theme. + * Keep upstream JSX files clean; override visuals here. + */ + +/* ─── Firlaw Monochrome Palette ───────────────────────────────────────────── */ +:root { + /* Bump this when forcing a new asset hash (CDN cache-bust). */ + --firlaw-build: 260529; + --firlaw-titlebar-height: 48px; + --firlaw-titlebar-btn-size: 32px; + --firlaw-titlebar-btn-radius: 6px; + --firlaw-font: 'Times New Roman', Times, serif; +} + +/* ─── Titlebar ─────────────────────────────────────────────────────────────── */ +header[data-tauri-drag-region] { + height: var(--firlaw-titlebar-height) !important; + border-bottom: 1px solid var(--border-weak-base, rgba(0,0,0,.07)); +} + +/* NOTE: do NOT override upstream .titlebar-icon — upstream uses h-6 (24px) for nav buttons */ + +/* ─── 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; +} + +.firlaw-dir-label { + display: inline; + font-size: 0.875rem; + padding-left: 0.5rem; +} + +.firlaw-header-action-btn { + width: auto !important; + height: var(--firlaw-titlebar-btn-size) !important; + border-radius: var(--firlaw-titlebar-btn-radius) !important; + padding-left: 0.75rem !important; + padding-right: 0.75rem !important; + gap: 0.5rem !important; +} + +.firlaw-btn-label { + display: inline; + font-size: 0.875rem; +} + +.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; +} diff --git a/packages/app/src/firlaw/titlebar/titlebar-extras.tsx b/packages/app/src/firlaw/titlebar/titlebar-extras.tsx new file mode 100644 index 000000000000..bc36c159b10b --- /dev/null +++ b/packages/app/src/firlaw/titlebar/titlebar-extras.tsx @@ -0,0 +1,106 @@ +import { createSignal, onMount, Show } from "solid-js" +import { Portal } from "solid-js/web" +import { useTheme } from "@opencode-ai/ui/theme/context" +import { Tooltip } from "@opencode-ai/ui/tooltip" + +export function FirlawTitlebarExtras() { + const theme = useTheme() + const [mount, setMount] = createSignal(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() + 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" }) + } catch { + return + } + window.location.href = "/login.html" + } + + return ( + + {(el) => ( + +
+ + + + + + + + + + + + + + + + + +
+
+ )} +
+ ) +} diff --git a/packages/app/src/index.css b/packages/app/src/index.css index a98a9b35d878..7b1930880fba 100644 --- a/packages/app/src/index.css +++ b/packages/app/src/index.css @@ -1,5 +1,5 @@ @import "@opencode-ai/ui/styles/tailwind"; -@import "./styles/firlaw-overrides.css"; +@import "./firlaw/styles/overrides.css"; @font-face { font-family: "JetBrainsMono Nerd Font Mono"; diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index d56e16a8a949..8a4c5bbb0f2a 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 "@/firlaw/titlebar/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..a5543a276f9c 100644 --- a/packages/app/src/pages/session/session-side-panel.tsx +++ b/packages/app/src/pages/session/session-side-panel.tsx @@ -13,7 +13,7 @@ import { ConstrainDragYAxis, getDraggableId } from "@/utils/solid-dnd" import { useDialog } from "@opencode-ai/ui/context/dialog" import FileTree from "@/components/file-tree" -import { FirlawSidePanelActions } from "./firlaw-side-panel-actions" +import { FirlawSidePanelActions } from "@/firlaw/side-panel/side-panel-actions" import { SessionContextUsage } from "@/components/session-context-usage" import { SessionContextTab, SortableTab, FileVisual } from "@/components/session" import { useCommand } from "@/context/command" @@ -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 deleted file mode 100644 index db036a32263e..000000000000 --- a/packages/app/src/styles/firlaw-overrides.css +++ /dev/null @@ -1,64 +0,0 @@ -/** - * 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. - */ - -/* ─── Titlebar ─────────────────────────────────────────────────────────────── */ -: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; -} - -/* Override header height */ -header[data-tauri-drag-region] { - height: var(--firlaw-titlebar-height); -} - -/* 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) */ -.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 */ - 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; - border-radius: var(--firlaw-titlebar-btn-radius) !important; - padding-left: 0.75rem !important; - padding-right: 0.75rem !important; - gap: 0.5rem !important; -} - -/* Show "Review" label text next to icon */ -.firlaw-btn-label { - display: inline; - font-size: 0.875rem; /* text-sm */ -} - -/* 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; -}