Skip to content

Commit 0d9bd8a

Browse files
jbouderclaude
andauthored
Re-sync frontend UI primitives with the @Nebari registry (#565)
* Move app-owned components and helpers out of registry-managed paths confirm-dialog, split-button and user-badge are nebi-specific wrappers, not @Nebari registry items, so they now live in src/components/ instead of src/components/ui/. Likewise capitalize and the status colour helpers move from src/lib/utils.ts (registry-owned, cn() only) into src/lib/strings.ts and src/lib/status.ts. Importers and tests follow; split-button gains the explicit button type Biome requires now that it is linted. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Re-sync frontend UI primitives with the @Nebari registry Re-add every installed registry item with --overwrite (code-block, select and table had drifted; the rest were already current), re-apply @nebari/theme (adds the --info tokens) and install the @nebari/claude-skill so Claude Code sessions in frontend/ get the nebari-ui skill. The repo-wide .claude/ ignore now lets .claude/skills/ through so the installed skill is tracked. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Adopt the @Nebari theme hook and align the header with the Nebari recipe Replace the hand-rolled useThemePreference/useLocalStorageState pair with the registry's use-theme-preference + theme-provider, mounted once in main.tsx with the existing nebi:themeMode storage key so saved preferences survive. App reads theme state via useTheme(); Layout/ProfileMenu take ThemeMode from the registry hook. index.html gains the pre-paint bootstrap script generated by themeBootstrapScript('nebi:themeMode') so dark users no longer see a light flash. The registry hook files are excluded from Biome like components/ui. Header: drop the last hard-coded rgba shadows in favour of shadow-sm and shadow-primary/40, size the profile menu at the recipe's 248px, and label the brand link "Go to homepage". Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Fix theme picker overflow in the profile menu Drop the horizontal padding around the Light/Dark/System control so it matches the Nebari header recipe. The extra 16px left the muted track too narrow for the three pills and the System option ran past its edge. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * Move the Nebari skill to .agents/skills with a .claude symlink Store the registry-installed skill at frontend/.agents/skills so it is agent-agnostic, and track frontend/.claude/skills as a symlink to it so Claude Code (and shadcn re-syncs, which target .claude/skills) still find it. Only the skills directory is linked so per-directory settings.local.json files stay out of the tracked tree. Generalize the .gitignore rules to **/.claude/* and **/.agents/*; the previous .claude/* pattern was root-anchored and never applied to frontend/.claude/. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
1 parent 75dfa23 commit 0d9bd8a

39 files changed

Lines changed: 1092 additions & 219 deletions

.gitignore

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,15 @@ dist/
6868
go.work
6969
go.work.sum
7070

71-
# Claude
71+
# AI agents (any directory level)
7272
CLAUDE.md
73-
.claude/
73+
**/.claude/*
74+
**/.agents/*
75+
# Keep registry-installed agent skills (e.g. frontend/.agents/skills/nebari-ui).
76+
# Claude Code reads .claude/skills, so frontend/.claude/skills is a symlink to
77+
# ../.agents/skills; the symlink itself is tracked.
78+
!**/.agents/skills/
79+
!**/.claude/skills
7480

7581
# Git worktrees
7682
.worktrees/

frontend/.agents/skills/nebari-ui/SKILL.md

Lines changed: 655 additions & 0 deletions
Large diffs are not rendered by default.

frontend/.claude/skills

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../.agents/skills

frontend/biome.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
"vite.config.ts",
1818
"!!**/dist",
1919
"!wailsjs",
20-
"!src/components/ui"
20+
"!src/components/ui",
21+
"!src/hooks/use-theme-preference.ts",
22+
"!src/hooks/theme-provider.tsx"
2123
]
2224
},
2325

frontend/index.html

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,26 @@
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
66
<meta name="referrer" content="no-referrer-when-downgrade" />
77

8+
<!-- Apply the saved (or OS) theme before first paint. Generated by
9+
themeBootstrapScript('nebi:themeMode') from src/hooks/use-theme-preference.ts;
10+
regenerate rather than hand-editing if the storage key changes. -->
11+
<script>
12+
(function () {
13+
var mode = null;
14+
var prefersDark = false;
15+
try {
16+
mode = localStorage.getItem("nebi:themeMode");
17+
} catch (e) {}
18+
try {
19+
prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
20+
} catch (e) {}
21+
var isDark = mode === 'dark' || (mode !== 'light' && prefersDark);
22+
try {
23+
document.documentElement.classList.toggle('dark', isDark);
24+
} catch (e) {}
25+
})();
26+
</script>
27+
828
<!-- Favicons -->
929
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />
1030
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />

frontend/src/App.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
import { adminApi } from './api/admin';
1313
import { AdminLayout } from './components/layout/AdminLayout';
1414
import { Layout } from './components/layout/Layout';
15-
import { useThemePreference } from './hooks/useThemePreference';
15+
import { useTheme } from './hooks/theme-provider';
1616
import { getBasePath } from './lib/basePath';
1717
import { queryClient } from './lib/queryClient';
1818
import { AdminDashboard } from './pages/admin/AdminDashboard';
@@ -88,7 +88,7 @@ const AdminRoute = () => {
8888
};
8989

9090
function App() {
91-
const { themeMode, isDarkMode, setThemeMode } = useThemePreference();
91+
const { themeMode, isDarkMode, setThemeMode } = useTheme();
9292

9393
return (
9494
<QueryClientProvider client={queryClient}>

frontend/src/components/ui/confirm-dialog.test.tsx renamed to frontend/src/components/confirm-dialog.test.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import { describe, it, expect, vi } from 'vitest';
21
import { screen } from '@testing-library/react';
32
import userEvent from '@testing-library/user-event';
4-
import { ConfirmDialog } from './confirm-dialog';
3+
import { describe, expect, it, vi } from 'vitest';
54
import { renderWithProviders } from '@/test/utils';
5+
import { ConfirmDialog } from './confirm-dialog';
66

7-
function renderDialog(overrides?: Partial<Parameters<typeof ConfirmDialog>[0]>) {
7+
function renderDialog(
8+
overrides?: Partial<Parameters<typeof ConfirmDialog>[0]>,
9+
) {
810
const props = {
911
open: true,
1012
onOpenChange: vi.fn(),
@@ -26,7 +28,9 @@ describe('ConfirmDialog', () => {
2628

2729
it('shows default button labels', () => {
2830
renderDialog();
29-
expect(screen.getByRole('button', { name: 'Continue' })).toBeInTheDocument();
31+
expect(
32+
screen.getByRole('button', { name: 'Continue' }),
33+
).toBeInTheDocument();
3034
expect(screen.getByRole('button', { name: 'Cancel' })).toBeInTheDocument();
3135
});
3236

frontend/src/components/ui/confirm-dialog.tsx renamed to frontend/src/components/confirm-dialog.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,7 @@ export const ConfirmDialog = ({
4040
};
4141

4242
return (
43-
<Dialog
44-
open={open}
45-
onOpenChange={onOpenChange}
46-
disablePointerDismissal
47-
>
43+
<Dialog open={open} onOpenChange={onOpenChange} disablePointerDismissal>
4844
<DialogContent role="alertdialog" showCloseButton={false}>
4945
<DialogHeader>
5046
<DialogTitle>{title}</DialogTitle>

frontend/src/components/jobs/Jobs.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { CodeBlock, CodeBlockBody } from '@/components/ui/code-block';
77
import { useJobLogStream } from '@/hooks/useJobLogStream';
88
import { useJobs } from '@/hooks/useJobs';
99
import { useRemoteJobs, useRemoteView } from '@/hooks/useRemote';
10-
import { capitalize } from '@/lib/utils';
10+
import { capitalize } from '@/lib/strings';
1111
import type { Job, JobType } from '@/types';
1212

1313
const statusColors = {

frontend/src/components/layout/Layout.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ import {
1313
NavigationMenu,
1414
NavLink,
1515
} from '@/components/ui/navigation-menu';
16+
import type { ThemeMode } from '@/hooks/use-theme-preference';
1617
import { useIsAdmin } from '@/hooks/useAdmin';
1718
import { useHostJobNotifications } from '@/hooks/useHostJobNotifications';
1819
import { useRemoteView } from '@/hooks/useRemote';
19-
import type { ThemeMode } from '@/hooks/useThemePreference';
2020
import { useVersion } from '@/hooks/useVersion';
2121
import { getBrandingLogoUrl } from '@/lib/brandingConfig';
2222
import { openExternal } from '@/lib/openExternal';
@@ -91,7 +91,7 @@ export const Layout = ({
9191
<NavigationMenu className="h-14 shrink-0 justify-between border-border bg-header pl-4 text-header-foreground">
9292
<MenuBarBrand
9393
href="/workspaces"
94-
aria-label="Go to workspaces"
94+
aria-label="Go to homepage"
9595
onClick={(event) => {
9696
event.preventDefault();
9797
navigate('/workspaces');
@@ -144,7 +144,7 @@ export const Layout = ({
144144
<span
145145
className={`w-1.5 h-1.5 rounded-full transition-all ${
146146
viewMode === 'local'
147-
? 'bg-primary shadow-[0_0_6px_rgba(155,61,204,0.4)]'
147+
? 'bg-primary shadow-[0_0_6px] shadow-primary/40'
148148
: 'bg-muted-foreground/50'
149149
}`}
150150
/>
@@ -162,7 +162,7 @@ export const Layout = ({
162162
<span
163163
className={`w-1.5 h-1.5 rounded-full transition-all ${
164164
viewMode === 'remote'
165-
? 'bg-primary shadow-[0_0_6px_rgba(155,61,204,0.4)]'
165+
? 'bg-primary shadow-[0_0_6px] shadow-primary/40'
166166
: 'bg-muted-foreground/50'
167167
}`}
168168
/>

0 commit comments

Comments
 (0)