@polderlabs/bizar 7.0.0 → 7.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +125 -47
  2. package/bizar-dash/dist/assets/{EnvVarsSection-CW6fWKds.js → EnvVarsSection-weVnLz3Z.js} +3 -3
  3. package/bizar-dash/dist/assets/EnvVarsSection-weVnLz3Z.js.map +1 -0
  4. package/bizar-dash/dist/assets/{MobileChat-D7zRhU9K.js → MobileChat-BRFjer8p.js} +1 -1
  5. package/bizar-dash/dist/assets/{MobileChat-D7zRhU9K.js.map → MobileChat-BRFjer8p.js.map} +1 -1
  6. package/bizar-dash/dist/assets/MobileSettings-Dw5jyJWi.js +1 -0
  7. package/bizar-dash/dist/assets/MobileSettings-Dw5jyJWi.js.map +1 -0
  8. package/bizar-dash/dist/assets/flow-BpU0-gK2.js.map +1 -1
  9. package/bizar-dash/dist/assets/icons-B0AKnYI3.js.map +1 -1
  10. package/bizar-dash/dist/assets/main-BE4Uh9im.css +1 -0
  11. package/bizar-dash/dist/assets/{main-CO8fIxQK.js → main-DqC58FEZ.js} +11 -11
  12. package/bizar-dash/dist/assets/main-DqC58FEZ.js.map +1 -0
  13. package/bizar-dash/dist/assets/markdown-w2bDDkAH.js.map +1 -1
  14. package/bizar-dash/dist/assets/{mobile-DXYCA0hC.js → mobile-DRZsK3ic.js} +1 -1
  15. package/bizar-dash/dist/assets/{mobile-DXYCA0hC.js.map → mobile-DRZsK3ic.js.map} +1 -1
  16. package/bizar-dash/dist/assets/mobile-layout-BWLtwtfQ.css +1 -0
  17. package/bizar-dash/dist/assets/{mobile-layout-DSmtNPja.js → mobile-layout-Ckru3HKJ.js} +2 -2
  18. package/bizar-dash/dist/assets/{mobile-layout-DSmtNPja.js.map → mobile-layout-Ckru3HKJ.js.map} +1 -1
  19. package/bizar-dash/dist/assets/react-vendor-D3pra6w-.js.map +1 -1
  20. package/bizar-dash/dist/assets/{useSlashCommands-3DFLOfpk.js → useSlashCommands-BAxVOU_a.js} +2 -2
  21. package/bizar-dash/dist/assets/{useSlashCommands-3DFLOfpk.js.map → useSlashCommands-BAxVOU_a.js.map} +1 -1
  22. package/bizar-dash/dist/assets/vendor-Di7Cp-rw.js.map +1 -1
  23. package/bizar-dash/dist/index.html +6 -6
  24. package/bizar-dash/dist/mobile.html +3 -3
  25. package/bizar-dash/src/server/memory-consolidator.mjs +8 -1
  26. package/bizar-dash/src/web/mobile/MobileApp.tsx +33 -2
  27. package/bizar-dash/src/web/mobile/MobileBottomNav.tsx +2 -2
  28. package/bizar-dash/src/web/mobile/MobileEval.tsx +4 -4
  29. package/bizar-dash/src/web/mobile/MobileMemory.tsx +2 -2
  30. package/bizar-dash/src/web/mobile/MobileSettings.tsx +3 -3
  31. package/bizar-dash/src/web/mobile/MobileTasks.tsx +2 -2
  32. package/bizar-dash/src/web/mobile/components/MobileListItem.tsx +3 -3
  33. package/bizar-dash/src/web/styles/mobile.css +41 -0
  34. package/bizar-dash/src/web/styles/tasks-redesign.css +34 -12
  35. package/bizar-dash/src/web/ui/controls/Textarea.tsx +56 -0
  36. package/bizar-dash/src/web/ui/controls/controls.css +22 -0
  37. package/bizar-dash/src/web/ui/controls/index.ts +2 -0
  38. package/bizar-dash/src/web/ui/layout/ViewHeader.tsx +48 -0
  39. package/bizar-dash/src/web/ui/layout/index.ts +1 -0
  40. package/bizar-dash/src/web/ui/layout/layout.css +51 -0
  41. package/bizar-dash/src/web/ui/styles/tokens.css +13 -0
  42. package/bizar-dash/src/web/views/Agents.tsx +10 -2
  43. package/bizar-dash/src/web/views/Overview.tsx +2 -13
  44. package/bizar-dash/src/web/views/Tasks.tsx +1 -7
  45. package/bizar-dash/tests/ui/controls/Textarea.test.tsx +56 -0
  46. package/bizar-dash/tests/ui/layout/ViewHeader.test.tsx +47 -0
  47. package/cli/provision-claude.mjs +94 -0
  48. package/package.json +1 -1
  49. package/bizar-dash/dist/assets/EnvVarsSection-CW6fWKds.js.map +0 -1
  50. package/bizar-dash/dist/assets/MobileSettings-K-sNsn_r.js +0 -1
  51. package/bizar-dash/dist/assets/MobileSettings-K-sNsn_r.js.map +0 -1
  52. package/bizar-dash/dist/assets/main-C-Md3DaY.css +0 -1
  53. package/bizar-dash/dist/assets/main-CO8fIxQK.js.map +0 -1
  54. package/bizar-dash/dist/assets/mobile-layout-CJnZNLy3.css +0 -1
  55. package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +0 -1
@@ -66,6 +66,7 @@ import {
66
66
  Sparkline,
67
67
  BarChart,
68
68
  KeyValueList,
69
+ Textarea,
69
70
  IconButton,
70
71
  Button as UiButton,
71
72
  Badge,
@@ -968,23 +969,11 @@ function OverviewInner({
968
969
  }}
969
970
  >
970
971
  <Stack gap={3}>
971
- <textarea
972
+ <Textarea
972
973
  ref={inputRef}
973
974
  disabled={submitting}
974
975
  aria-label="Describe what you want Odin to do"
975
976
  rows={4}
976
- style={{
977
- width: '100%',
978
- resize: 'vertical',
979
- fontFamily: 'var(--font-sans)',
980
- fontSize: 'var(--text-base)',
981
- lineHeight: 'var(--leading-normal)',
982
- padding: 'var(--space-3) var(--space-4)',
983
- border: '1px solid var(--border-default)',
984
- borderRadius: 'var(--radius-md)',
985
- background: 'var(--surface-1)',
986
- color: 'var(--text-primary)',
987
- }}
988
977
  placeholder="e.g. Implement user authentication with email + password, including registration, login, password reset, and integration tests. Use Bcrypt, JWT tokens, and the existing API style."
989
978
  data-testid="overview-prompt-input"
990
979
  />
@@ -119,12 +119,6 @@ const PRIORITY_BADGE: Record<PriorityKind, BadgeVariant> = {
119
119
  low: 'neutral',
120
120
  };
121
121
 
122
- const PRIORITY_ROW_CLASS: Record<PriorityKind, string> = {
123
- high: 'tasks-wave3__row--priority-high',
124
- normal: 'tasks-wave3__row--priority-normal',
125
- low: 'tasks-wave3__row--priority-low',
126
- };
127
-
128
122
  function TasksInner({
129
123
  snapshot,
130
124
  refreshSnapshot,
@@ -442,7 +436,7 @@ function TaskRow({
442
436
  : 'normal';
443
437
  return (
444
438
  <div
445
- className={cx('tasks-wave3__row', PRIORITY_ROW_CLASS[priority])}
439
+ className={cx('tasks-wave3__row')}
446
440
  data-task-id={task.id}
447
441
  data-tick={tick}
448
442
  role="button"
@@ -0,0 +1,56 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import { render, screen } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
4
+ import { Textarea } from '../../../src/web/ui/controls/Textarea';
5
+
6
+ describe('Textarea', () => {
7
+ it('renders and accepts typed input', async () => {
8
+ const onChange = vi.fn();
9
+ const user = userEvent.setup();
10
+ render(<Textarea value="" onChange={onChange} aria-label="Description" />);
11
+ const input = screen.getByRole('textbox');
12
+ await user.type(input, 'hello');
13
+ expect(onChange).toHaveBeenCalled();
14
+ expect(input).toBeInTheDocument();
15
+ });
16
+
17
+ it('applies the size modifier class on the wrapper', () => {
18
+ render(<Textarea value="" onChange={() => {}} inputSize="lg" aria-label="Lg" />);
19
+ // .field-size-lg sits on the .field wrapper, not the inner textarea.
20
+ expect(screen.getByLabelText('Lg').parentElement).toHaveClass('field-size-lg');
21
+ });
22
+
23
+ it('marks itself aria-invalid when error is set', () => {
24
+ render(<Textarea value="" onChange={() => {}} error="Required" aria-label="Err" />);
25
+ const input = screen.getByLabelText('Err');
26
+ expect(input).toHaveAttribute('aria-invalid', 'true');
27
+ expect(input).toHaveClass('field-error');
28
+ });
29
+
30
+ it('is disabled when disabled is set', () => {
31
+ render(<Textarea value="" onChange={() => {}} disabled aria-label="Dis" />);
32
+ expect(screen.getByLabelText('Dis')).toBeDisabled();
33
+ });
34
+
35
+ it('renders the hint slot when provided', () => {
36
+ render(
37
+ <Textarea
38
+ value=""
39
+ onChange={() => {}}
40
+ hint={<span data-testid="hint">Markdown ok</span>}
41
+ aria-label="With hint"
42
+ />,
43
+ );
44
+ expect(screen.getByTestId('hint')).toBeInTheDocument();
45
+ });
46
+
47
+ it('defaults rows to 4 when not provided', () => {
48
+ render(<Textarea value="" onChange={() => {}} aria-label="R" />);
49
+ expect(screen.getByLabelText('R')).toHaveAttribute('rows', '4');
50
+ });
51
+
52
+ it('honours an explicit rows override', () => {
53
+ render(<Textarea value="" onChange={() => {}} rows={8} aria-label="R8" />);
54
+ expect(screen.getByLabelText('R8')).toHaveAttribute('rows', '8');
55
+ });
56
+ });
@@ -0,0 +1,47 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import { describe, it, expect } from 'vitest';
3
+ import { ViewHeader } from '../../../src/web/ui/layout/ViewHeader';
4
+
5
+ describe('ViewHeader', () => {
6
+ it('renders title and subtitle', () => {
7
+ render(<ViewHeader title="Tasks" subtitle="Live kanban board" />);
8
+ expect(screen.getByText('Tasks')).toBeInTheDocument();
9
+ expect(screen.getByText('Live kanban board')).toBeInTheDocument();
10
+ });
11
+
12
+ it('renders the actions slot', () => {
13
+ render(
14
+ <ViewHeader
15
+ title="Tasks"
16
+ actions={<button>Refresh</button>}
17
+ />,
18
+ );
19
+ expect(screen.getByText('Refresh')).toBeInTheDocument();
20
+ });
21
+
22
+ it('renders an eyebrow row above the title when provided', () => {
23
+ const { container } = render(
24
+ <ViewHeader title="Overview" eyebrow={<span>Dashboard</span>} />,
25
+ );
26
+ const eyebrow = container.querySelector('.bizar-view-header__eyebrow');
27
+ expect(eyebrow).toBeInTheDocument();
28
+ expect(eyebrow?.textContent).toBe('Dashboard');
29
+ });
30
+
31
+ it('uses <h1> for the page title (not a panel-level h3)', () => {
32
+ const { container } = render(<ViewHeader title="Overview" />);
33
+ const h1 = container.querySelector('h1');
34
+ expect(h1).toBeInTheDocument();
35
+ expect(h1?.textContent).toBe('Overview');
36
+ });
37
+
38
+ it('accepts an extra className for view-specific tweaks', () => {
39
+ const { container } = render(<ViewHeader title="X" className="agents-header" />);
40
+ expect(container.querySelector('.bizar-view-header')).toHaveClass('agents-header');
41
+ });
42
+
43
+ it('renders nothing in the actions slot when not provided', () => {
44
+ const { container } = render(<ViewHeader title="No actions" />);
45
+ expect(container.querySelector('.bizar-view-header__actions')).not.toBeInTheDocument();
46
+ });
47
+ });
@@ -116,6 +116,93 @@ function haveCmd(cmd) {
116
116
  }
117
117
  }
118
118
 
119
+ /**
120
+ * Build the SDK workspace so its compiled JS output lives at
121
+ * `<pkg>/packages/sdk/dist/`. The dashboard imports the SDK memory
122
+ * module from `dist/memory/index.js`, not from `src/memory/index.js`,
123
+ * so without this build step the npm-installed package ships a TS
124
+ * source tree that the dashboard cannot run.
125
+ *
126
+ * v7.0.1 — `install.sh` + `bizar update` previously skipped this step
127
+ * entirely. Symptom: `bizar dash start` crashes with
128
+ * `Cannot find module .../packages/sdk/dist/memory/index.js`
129
+ * (or .../src/memory/index.js for older import paths). Root cause: the
130
+ * SDK's `package.json` advertises `./dist/*` exports, but the SDK's
131
+ * `tsconfig.json` is never compiled by the installer.
132
+ *
133
+ * Bun resolution: when bun is invoked from a globally npm-installed
134
+ * package, its lifecycle script runs with a minimal PATH that does
135
+ * NOT include `~/.bun/bin`. So `bun run build:sdk` (which shells out to
136
+ * `tsc` via bun's bin shim) fails with `tsc: command not found`. To
137
+ * dodge that, we resolve bun to its absolute path
138
+ * (`$HOME/.bun/bin/bun`) and prepend that dir to the child process
139
+ * PATH so bun's tsc shim is visible.
140
+ *
141
+ * Idempotent: skips when `dist/memory/index.js` already exists.
142
+ * Non-fatal: returns `{ ok: false, message }` rather than throwing —
143
+ * the rest of the provision flow should still complete (some operators
144
+ * run a separate build pipeline, e.g. a CI step).
145
+ */
146
+ export async function buildSdk({ dryRun = false } = {}) {
147
+ const sdkRoot = join(REPO_ROOT, 'packages', 'sdk');
148
+ const srcEntry = join(sdkRoot, 'src', 'memory', 'index.ts');
149
+ const distEntry = join(sdkRoot, 'dist', 'memory', 'index.js');
150
+
151
+ if (!existsSync(srcEntry)) {
152
+ // No SDK TS source — this is a stripped install or an unusual layout.
153
+ // Don't fail the whole provision over it; just report.
154
+ return { ok: true, skipped: true, message: `SDK src not found at ${srcEntry} — skipping build (likely a non-bundled install)` };
155
+ }
156
+
157
+ if (existsSync(distEntry)) {
158
+ return { ok: true, skipped: true, message: `SDK dist already present — skipping build (delete ${join(sdkRoot, 'dist')} to force)` };
159
+ }
160
+
161
+ if (dryRun) {
162
+ return { ok: true, skipped: false, message: `would build SDK at ${sdkRoot}` };
163
+ }
164
+
165
+ // Resolve bun to an absolute path. `command -v bun` finds whatever is
166
+ // on PATH, but bun's bin dir (typically `~/.bun/bin`) is NOT always on
167
+ // PATH — especially when invoked from a globally npm-installed package
168
+ // whose lifecycle scripts run with a minimal env. Without the absolute
169
+ // path, `bun run build:sdk` shells out to `tsc`, finds no shim on PATH,
170
+ // and fails with `tsc: command not found`. Prefer the absolute path so
171
+ // we don't depend on PATH inheritance.
172
+ const bunFromHome = join(process.env.HOME || '', '.bun', 'bin', 'bun');
173
+ let cmd, args, childEnv;
174
+ if (existsSync(bunFromHome)) {
175
+ cmd = bunFromHome;
176
+ args = ['run', 'build:sdk'];
177
+ // Make bun's bin dir visible to the child so its tsc shim resolves.
178
+ const bunBin = dirname(bunFromHome);
179
+ const pathDelim = process.platform === 'win32' ? ';' : ':';
180
+ childEnv = { ...process.env, PATH: `${bunBin}${pathDelim}${process.env.PATH || ''}` };
181
+ } else if (haveCmd('bun')) {
182
+ cmd = 'bun';
183
+ args = ['run', 'build:sdk'];
184
+ childEnv = process.env;
185
+ } else {
186
+ // Fallback: npx tsc. Requires a network round-trip on first use but
187
+ // works on systems without bun installed (CI, minimal containers).
188
+ cmd = 'npx';
189
+ args = ['--yes', 'tsc', '-p', 'packages/sdk/tsconfig.json'];
190
+ childEnv = process.env;
191
+ }
192
+
193
+ try {
194
+ logInfo(`building SDK with \`${cmd} ${args.join(' ')}\` (REPO_ROOT=${REPO_ROOT})`);
195
+ execFileSync(cmd, args, { cwd: REPO_ROOT, stdio: 'pipe', timeout: 180_000, env: childEnv });
196
+ if (existsSync(distEntry)) {
197
+ return { ok: true, skipped: false, message: 'SDK built (dist/memory/index.js present)' };
198
+ }
199
+ return { ok: false, message: `build completed but ${distEntry} still missing — check tsconfig outDir` };
200
+ } catch (err) {
201
+ const stderr = err.stderr ? err.stderr.toString().split('\n').slice(0, 5).join(' | ') : '(no stderr)';
202
+ return { ok: false, message: `SDK build failed: ${stderr}` };
203
+ }
204
+ }
205
+
119
206
  function readTextSafe(file, fallback = '') {
120
207
  try {
121
208
  if (!existsSync(file)) return fallback;
@@ -798,6 +885,13 @@ export async function runProvision(opts = {}) {
798
885
  await runStep('Syncing rules', () => syncRulesFiles({ dryRun, force }));
799
886
  await runStep('Syncing hooks', () => syncHookFiles({ dryRun, force }));
800
887
 
888
+ // ── 4b. Build the SDK workspace so dist/ exists. ────────────────────
889
+ // The dashboard imports from packages/sdk/dist/* at runtime; without
890
+ // this step v7.0.0+ npm installs crash on dashboard startup because
891
+ // the SDK ships as TS source only. See `buildSdk()` JSDoc for the
892
+ // full story.
893
+ await runStep('Building SDK', () => buildSdk({ dryRun }));
894
+
801
895
  // ── 9. settings.json (MCP + hooks + permissions + env) ──────────────
802
896
  section('Writing settings.json');
803
897
  const settingsStep = writeClaudeSettings({ dryRun, force });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@polderlabs/bizar",
3
- "version": "7.0.0",
3
+ "version": "7.0.1",
4
4
  "description": "Norse-pantheon multi-agent system for Claude Code — 14 agents across 4 cost tiers with cost-aware routing, plans, and a configurable agent harness. Ships as a single npm package with the dashboard server, Claude Code MCP server, and typed SDK.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1 +0,0 @@
1
- {"version":3,"mappings":"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","names":["Button","forwardRef","variant","size","iconOnly","loading","disabled","className","children","rest","ref","jsxs","cn","Card","interactive","jsx","CardTitle","CardMeta","PACKAGES","UpdatesSection","toast","useToast","status","setStatus","useState","useEffect","ws","Ws","msg","s","m","existing","api","e","check","err","applyUpdate","restart","isBusy","Download","p","cur","lat","_a","isOutdated","pkgStatus","CheckCircle","AlertCircle","AlertTriangle","RefreshCw","useAutosave","initialValue","saveFn","delay","onSaved","onError","value","setValue","timeoutRef","useRef","pendingValueRef","lastSavedValue","hasPendingDebounceRef","save","useCallback","v","setValueAndSave","toSave","clearDebounce","flush","AutosaveField","render","handleBlur","React","Loader","Check","LAYOUTS","GeneralSection","settings","patchUi","patchTop","autoSave","Fragment","LayoutIcon","l","onChange","onBlur","TailscaleSettings","initialStatus","authKey","setAuthKey","setLoading","error","setError","r","loadStatus","handleAuthenticate","handleSetupServe","_b","_c","handleRemoveServe","isInstalled","isAuthenticated","isServeEnabled","serveUrl","Plug","LinkIcon","SystemLlmSection","Sparkles","BACKEND_OPTIONS","HeadroomSettingsCard","onPatch","stats","setStats","loadingStatus","setLoadingStatus","installing","setInstalling","wrapping","setWrapping","unwrapping","setUnwrapping","startingProxy","setStartingProxy","stoppingProxy","setStoppingProxy","st","handleInstall","handleWrap","handleUnwrap","handleStartProxy","handleStopProxy","handleOpenDashboard","spawn","__vitePreload","installed","version","proxyRunning","proxyPort","wrapped","ratio","saved","Activity","XCircle","Zap","Square","Play","ExternalLink","DEFAULT_HEADROOM_SETTINGS","HeadroomSection","setSettings","setDirty","patch","ActivityLogCard","items","setItems","hidden","setHidden","filter","setFilter","showHidden","setShowHidden","refresh","log","hid","keyOf","it","idx","k","h","i","onRestoreAll","onRestoreOne","key","next","filtered","q","Search","Eye","isHidden","AboutCard","about","Info","ActivitySection","EnvVarManager","vars","setVars","saving","setSaving","revealed","setRevealed","editing","setEditing","editValue","setEditValue","showAdd","setShowAdd","newName","setNewName","newValue","setNewValue","showBulk","setShowBulk","bulkText","setBulkText","bulkImporting","setBulkImporting","searchQ","setSearchQ","collapsed","setCollapsed","load","toggleReveal","name","prev","handleAdd","startEdit","handleUpdate","handleDelete","handleBulkImport","handleExport","blob","url","a","useMemo","grouped","groups","prefix","toggleGroup","sortedGroupKeys","known","b","ai","bi","groupMode","Plus","Upload","X","KeyRound","isCollapsed","ChevronRight","ChevronDown","VarRow","isRevealed","onToggleReveal","onStartEdit","onEditValueChange","onHandleUpdate","onCancelEdit","onDelete","isEditing","EyeOff","Pencil","Trash2","EnvVarsSection"],"ignoreList":[],"sources":["../../src/web/components/Button.tsx","../../src/web/components/Card.tsx","../../src/web/views/settings/UpdatesSection.tsx","../../src/web/hooks/useAutosave.ts","../../src/web/components/AutosaveField.tsx","../../src/web/views/settings/GeneralSection.tsx","../../src/web/components/TailscaleSettings.tsx","../../src/web/views/settings/SystemLlmSection.tsx","../../src/web/components/HeadroomSettings.tsx","../../src/web/views/settings/HeadroomSection.tsx","../../src/web/views/settings/ActivitySection.tsx","../../src/web/components/EnvVarManager.tsx","../../src/web/views/settings/EnvVarsSection.tsx"],"sourcesContent":["// src/components/Button.tsx — typed button with variants + sizes.\n\nimport { type ButtonHTMLAttributes, forwardRef } from 'react';\nimport { cn } from '../lib/utils';\n\nexport type ButtonVariant =\n | 'primary'\n | 'secondary'\n | 'ghost'\n | 'danger'\n | 'accent'\n | 'success';\nexport type ButtonSize = 'sm' | 'md' | 'lg';\n\nexport type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {\n variant?: ButtonVariant;\n size?: ButtonSize;\n iconOnly?: boolean;\n loading?: boolean;\n};\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(\n function Button(\n {\n variant = 'secondary',\n size = 'md',\n iconOnly = false,\n loading = false,\n disabled,\n className,\n children,\n ...rest\n },\n ref,\n ) {\n return (\n <button\n ref={ref}\n type={rest.type ?? 'button'}\n disabled={disabled || loading}\n className={cn(\n 'btn',\n `btn-${variant}`,\n `btn-size-${size}`,\n iconOnly && 'btn-icon',\n loading && 'btn-loading',\n className,\n )}\n {...rest}\n >\n {loading ? <span className=\"btn-spinner\" aria-hidden /> : null}\n {children}\n </button>\n );\n },\n);\n","// src/components/Card.tsx — wrapper card.\n\nimport type { HTMLAttributes, ReactNode } from 'react';\nimport { cn } from '../lib/utils';\n\nexport type CardProps = HTMLAttributes<HTMLDivElement> & {\n variant?: 'elevated' | 'outlined' | 'filled';\n interactive?: boolean;\n children?: ReactNode;\n};\n\nexport function Card({\n variant = 'elevated',\n interactive = false,\n className,\n children,\n ...rest\n}: CardProps) {\n return (\n <div\n className={cn(\n 'card',\n `card-${variant}`,\n interactive && 'card-interactive',\n className,\n )}\n {...rest}\n >\n {children}\n </div>\n );\n}\n\nexport function CardTitle({ children, className }: { children: ReactNode; className?: string }) {\n return <h3 className={cn('card-title', className)}>{children}</h3>;\n}\n\nexport function CardMeta({ children, className }: { children: ReactNode; className?: string }) {\n return <div className={cn('card-meta', className)}>{children}</div>;\n}\n","// src/web/views/settings/UpdatesSection.tsx\nimport React, { useEffect, useState } from 'react';\nimport { Download, RefreshCw, CheckCircle, AlertCircle, AlertTriangle } from 'lucide-react';\nimport { Card, CardTitle, CardMeta } from '../../components/Card';\nimport { Button } from '../../components/Button';\nimport { useToast } from '../../components/Toast';\nimport { api } from '../../lib/api';\nimport { Ws } from '../../lib/ws';\n\ntype PkgStatus = {\n status: 'idle' | 'starting' | 'installing' | 'done' | 'error';\n error?: string;\n newVersion?: string;\n logs: string[];\n};\n\ntype StatusState = {\n current: Record<string, string | null>;\n latest: Record<string, string | null> | null;\n checking: boolean;\n updating: boolean;\n hasUpdates: boolean;\n requiresRestart: boolean;\n perPackage: Record<string, PkgStatus>;\n error?: string;\n};\n\nconst PACKAGES = [\n { id: 'bizar', name: 'Bizar CLI' },\n { id: 'bizar-dash', name: 'Dashboard' },\n { id: 'bizar-plugin', name: 'Opencode Plugin' },\n];\n\nexport function UpdatesSection() {\n const toast = useToast();\n const [status, setStatus] = useState<StatusState>({\n current: {},\n latest: null,\n checking: false,\n updating: false,\n hasUpdates: false,\n requiresRestart: false,\n perPackage: {},\n });\n\n // WebSocket subscription for live update progress\n useEffect(() => {\n const ws = new Ws();\n ws.on((msg: Record<string, unknown>) => {\n if (msg.type === 'update:progress' || msg.type === 'update:log' || msg.type === 'update:complete') {\n setStatus((s) => {\n if (msg.type === 'update:complete') {\n return {\n ...s,\n updating: false,\n requiresRestart: Boolean((msg as { requiresRestart?: boolean }).requiresRestart),\n };\n }\n if (msg.type === 'update:log') {\n const m = msg as { pkg: string; line: string };\n const existing = s.perPackage[m.pkg] || { logs: [] };\n return {\n ...s,\n perPackage: {\n ...s.perPackage,\n [m.pkg]: {\n ...existing,\n logs: [...(existing.logs || []).slice(-50), m.line],\n },\n },\n };\n }\n // update:progress\n const m = msg as { pkg: string; status: PkgStatus['status']; error?: string; newVersion?: string };\n return {\n ...s,\n perPackage: {\n ...s.perPackage,\n [m.pkg]: {\n ...s.perPackage[m.pkg],\n status: m.status,\n error: m.error,\n newVersion: m.newVersion,\n },\n },\n };\n });\n }\n });\n return () => ws.close();\n }, []);\n\n // Load current versions on mount\n useEffect(() => {\n api.get<{ current: Record<string, string | null> }>('/updates/status')\n .then((r) => setStatus((s) => ({ ...s, current: r.current })))\n .catch((e) => setStatus((s) => ({ ...s, error: e.message })));\n }, []);\n\n const check = async () => {\n setStatus((s) => ({ ...s, checking: true, error: undefined }));\n try {\n const r = await api.get<{\n current: Record<string, string | null>;\n latest: Record<string, string | null>;\n hasUpdates: boolean;\n }>('/updates/check');\n setStatus((s) => ({\n ...s,\n checking: false,\n current: r.current,\n latest: r.latest,\n hasUpdates: r.hasUpdates,\n }));\n } catch (err) {\n setStatus((s) => ({ ...s, checking: false, error: (err as Error).message }));\n }\n };\n\n const applyUpdate = async () => {\n if (!confirm('Update Bizar packages? The dashboard will restart automatically.')) return;\n setStatus((s) => ({\n ...s,\n updating: true,\n requiresRestart: false,\n perPackage: {},\n error: undefined,\n }));\n try {\n await api.post('/updates/apply', { packages: ['bizar', 'bizar-dash', 'bizar-plugin'] });\n } catch (err) {\n setStatus((s) => ({ ...s, updating: false, error: (err as Error).message }));\n }\n };\n\n const restart = async () => {\n if (!confirm('Restart the dashboard? You will be disconnected briefly.')) return;\n try {\n await api.post('/restart');\n toast.info('Restarting…', 3000);\n setTimeout(() => window.location.reload(), 3000);\n } catch {\n toast.error('Restart failed');\n }\n };\n\n const isBusy = status.checking || status.updating;\n\n return (\n <Card id=\"settings-updates\" data-section=\"updates\">\n <CardTitle><Download size={14} /> Updates</CardTitle>\n <CardMeta>Check installed Bizar packages and apply dashboard updates.</CardMeta>\n\n {/* Current versions */}\n <div className=\"updates-current\">\n <h4>Installed versions</h4>\n <ul>\n {PACKAGES.map((p) => (\n <li key={p.id}>\n <span>{p.name}</span>\n <code className=\"mono\">{status.current[p.id] || '—'}</code>\n </li>\n ))}\n </ul>\n </div>\n\n {/* Latest + per-package status */}\n {status.latest && (\n <div className=\"updates-latest\">\n <h4>Latest available</h4>\n <ul>\n {PACKAGES.map((p) => {\n const cur = status.current[p.id];\n const lat = status.latest?.[p.id];\n const isOutdated = cur && lat && cur !== lat;\n return (\n <li key={p.id} className={isOutdated ? 'updates-outdated' : 'updates-current-version'}>\n <span>{p.name}</span>\n <code className=\"mono\">\n {lat || '—'}\n {isOutdated && <span className=\"updates-badge\">update available</span>}\n </code>\n </li>\n );\n })}\n </ul>\n </div>\n )}\n\n {/* Per-package progress rows */}\n {status.updating && (\n <div className=\"updates-progress-rows\">\n {PACKAGES.map((p) => {\n const pkgStatus = status.perPackage[p.id] || { status: 'idle', logs: [] as string[] };\n return (\n <div key={p.id} className=\"updates-pkg-row\">\n <div className=\"updates-pkg-row-header\">\n <span className=\"updates-pkg-name\">{p.name}</span>\n <div className=\"updates-pkg-status\">\n {pkgStatus.status === 'starting' && <span className=\"btn-spinner\" />}\n {pkgStatus.status === 'installing' && <span className=\"btn-spinner\" />}\n {pkgStatus.status === 'done' && <CheckCircle size={14} className=\"icon-success\" />}\n {pkgStatus.status === 'error' && <AlertCircle size={14} className=\"icon-error\" />}\n <span>{pkgStatus.status}</span>\n {pkgStatus.newVersion && (\n <code className=\"mono\" style={{ fontSize: 11 }}>→ {pkgStatus.newVersion}</code>\n )}\n </div>\n </div>\n {pkgStatus.logs.length > 0 && (\n <details className=\"updates-pkg-logs\">\n <summary>npm output ({pkgStatus.logs.length} lines)</summary>\n <pre>{pkgStatus.logs.join('\\n')}</pre>\n </details>\n )}\n {pkgStatus.status === 'error' && pkgStatus.error && (\n <div className=\"updates-pkg-error\">\n <AlertTriangle size={12} /> {pkgStatus.error}\n </div>\n )}\n </div>\n );\n })}\n </div>\n )}\n\n {/* Actions */}\n <div className=\"updates-actions\">\n <Button onClick={check} disabled={isBusy}>\n {status.checking ? <span className=\"btn-spinner\" /> : <RefreshCw size={14} />}\n Check for updates\n </Button>\n <Button\n variant=\"primary\"\n onClick={applyUpdate}\n disabled={!status.hasUpdates || status.updating}\n >\n {status.updating ? <span className=\"btn-spinner\" /> : <Download size={14} />}\n {status.updating ? 'Updating…' : (status.hasUpdates ? 'Update now' : 'Up to date')}\n </Button>\n {status.requiresRestart && (\n <Button variant=\"danger\" onClick={restart}>\n <RefreshCw size={14} /> Restart Dashboard\n </Button>\n )}\n </div>\n\n {status.error && (\n <div className=\"updates-error\">\n <AlertTriangle size={14} />\n <span>{status.error}</span>\n </div>\n )}\n </Card>\n );\n}\n","// src/web/hooks/useAutosave.ts\n// Debounced autosave hook for settings fields.\n\nimport { useState, useEffect, useRef, useCallback } from 'react';\n\nexport type AutosaveStatus = 'idle' | 'saving' | 'saved' | 'error';\n\nexport function useAutosave<T>(\n initialValue: T,\n saveFn: (value: T) => Promise<void>,\n {\n delay = 800,\n onSaved,\n onError,\n }: { delay?: number; onSaved?: () => void; onError?: (e: Error) => void } = {},\n) {\n const [value, setValue] = useState<T>(initialValue);\n const [status, setStatus] = useState<AutosaveStatus>('idle');\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const pendingValueRef = useRef<T | null>(null);\n const lastSavedValue = useRef<T>(initialValue);\n // Tracks whether a debounce timeout is currently pending (set when timeout is scheduled, cleared when it fires)\n const hasPendingDebounceRef = useRef(false);\n\n const save = useCallback(\n async (v: T) => {\n setStatus('saving');\n try {\n await saveFn(v);\n lastSavedValue.current = v;\n pendingValueRef.current = null;\n setStatus('saved');\n onSaved?.();\n setTimeout(() => setStatus('idle'), 2000);\n } catch (err) {\n setStatus('error');\n onError?.(err as Error);\n }\n },\n [saveFn, onSaved, onError],\n );\n\n const setValueAndSave = useCallback(\n (v: T) => {\n setValue(v);\n pendingValueRef.current = v;\n hasPendingDebounceRef.current = true;\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n timeoutRef.current = setTimeout(() => {\n timeoutRef.current = null;\n hasPendingDebounceRef.current = false;\n const toSave = pendingValueRef.current;\n pendingValueRef.current = null;\n if (toSave !== null) save(toSave);\n }, delay);\n },\n [delay, save],\n );\n\n /**\n * Clear any pending debounce without saving.\n * Use this when you want the debounce to handle the save naturally.\n */\n const clearDebounce = useCallback(() => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n hasPendingDebounceRef.current = false;\n }\n }, []);\n\n /**\n * Flush any pending save immediately (called on unmount or explicit flush).\n */\n const flush = useCallback(async () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n hasPendingDebounceRef.current = false;\n const toSave = pendingValueRef.current;\n pendingValueRef.current = null;\n if (toSave !== null) await save(toSave);\n }\n }, [save]);\n\n // Cleanup on unmount: fire pending save immediately\n useEffect(() => {\n return () => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n const toSave = pendingValueRef.current;\n pendingValueRef.current = null;\n // Fire-and-forget: call saveFn directly to persist without awaiting\n if (toSave !== null) {\n setStatus('saving');\n saveFn(toSave)\n .then(() => { lastSavedValue.current = toSave; setStatus('saved'); onSaved?.(); })\n .catch((err: Error) => { setStatus('error'); onError?.(err); })\n .finally(() => { setTimeout(() => setStatus('idle'), 2000); });\n }\n }\n };\n }, [saveFn, onSaved, onError]);\n\n return { value, setValue: setValueAndSave, status, save, flush, clearDebounce, hasPendingDebounceRef, pendingValueRef };\n}\n","// src/web/components/AutosaveField.tsx\n// Generic auto-saving field wrapper with inline save status animation.\n\nimport React from 'react';\nimport { Check, Loader, AlertCircle } from 'lucide-react';\nimport { useAutosave } from '../hooks/useAutosave';\n\nexport type { AutosaveStatus } from '../hooks/useAutosave';\n\nexport function AutosaveField<T = string>({\n initialValue,\n saveFn,\n delay = 800,\n render,\n className,\n}: {\n initialValue: T;\n saveFn: (value: T) => Promise<void>;\n delay?: number;\n render: (props: {\n value: T;\n onChange: (v: T) => void;\n onBlur: () => void;\n }) => React.ReactNode;\n className?: string;\n}) {\n const { value, setValue, status, save, clearDebounce, hasPendingDebounceRef, pendingValueRef } = useAutosave(initialValue, saveFn, { delay });\n\n const handleBlur = React.useCallback(() => {\n // If a debounce is pending (user was actively typing), clear the timeout\n // and save immediately — the user is done typing.\n if (hasPendingDebounceRef.current) {\n clearDebounce();\n // pendingValueRef.current holds the typed value not yet saved by debounce\n if (pendingValueRef.current !== null) save(pendingValueRef.current);\n }\n // If no debounce is pending: do nothing (value was already saved or unchanged).\n }, [clearDebounce, hasPendingDebounceRef, save, pendingValueRef]);\n\n return (\n <div className={`autosave-field ${className || ''} ${status}`}>\n {render({ value, onChange: setValue, onBlur: handleBlur })}\n <span className=\"autosave-status\" role=\"status\" aria-live=\"polite\">\n {status === 'saving' && <Loader className=\"spinning\" size={12} />}\n {status === 'saved' && <Check size={12} />}\n {status === 'error' && <AlertCircle size={12} />}\n </span>\n </div>\n );\n}\n","// src/web/views/settings/GeneralSection.tsx\nimport React from 'react';\nimport { Layout as LayoutIcon } from 'lucide-react';\nimport { Card, CardTitle, CardMeta } from '../../components/Card';\nimport { AutosaveField } from '../../components/AutosaveField';\nimport { cn } from '../../lib/utils';\nimport type { Settings } from '../../lib/types';\n\ntype Props = {\n settings: Settings;\n patchUi: (patch: Partial<Settings['ui']>) => void;\n patchTop: <K extends keyof Settings>(key: K, value: Settings[K]) => void;\n /** Called by AutosaveField after state is patched; the parent debounces the API save */\n autoSave?: (key: keyof Settings, value: Settings[keyof Settings]) => void;\n};\n\nconst LAYOUTS = [\n { id: 'topnav', label: 'Top nav' },\n { id: 'sidebar', label: 'Sidebar' },\n { id: 'both', label: 'Both' },\n] as const;\n\nexport function GeneralSection({ settings, patchUi, patchTop, autoSave }: Props) {\n return (\n <>\n {/* UI Layout */}\n <Card id=\"settings-layout\" data-section=\"layout\">\n <CardTitle><LayoutIcon size={14} /> UI layout</CardTitle>\n <CardMeta>Choose how the dashboard's navigation is presented.</CardMeta>\n <div className=\"layout-row\" data-setting-id=\"ui.layout\">\n {LAYOUTS.map((l) => (\n <button\n key={l.id}\n type=\"button\"\n className={cn('layout-card', settings.ui.layout === l.id && 'layout-card-active')}\n onClick={() => patchUi({ layout: l.id })}\n >\n <span className=\"layout-card-label\">{l.label}</span>\n </button>\n ))}\n </div>\n <label className=\"checkbox-row\" data-setting-id=\"ui.showHeader\">\n <input\n type=\"checkbox\"\n checked={settings.ui.showHeader}\n onChange={(e) => patchUi({ showHeader: e.target.checked })}\n />\n <span>Show header</span>\n </label>\n <label className=\"checkbox-row\" data-setting-id=\"ui.showStatusBar\">\n <input\n type=\"checkbox\"\n checked={settings.ui.showStatusBar}\n onChange={(e) => patchUi({ showStatusBar: e.target.checked })}\n />\n <span>Show status bar</span>\n </label>\n <div className=\"field\" data-setting-id=\"ui.defaultTab\">\n <label className=\"field-label\">Default tab</label>\n <select\n className=\"select\"\n value={settings.ui.defaultTab}\n onChange={(e) => patchUi({ defaultTab: e.target.value })}\n >\n <option value=\"overview\">Overview</option>\n <option value=\"chat\">Chat</option>\n <option value=\"agents\">Agents</option>\n <option value=\"artifacts\">Plans</option>\n <option value=\"projects\">Projects</option>\n <option value=\"tasks\">Tasks</option>\n <option value=\"config\">Config</option>\n <option value=\"settings\">Settings</option>\n <option value=\"mods\">Mods</option>\n <option value=\"schedules\">Schedules</option>\n </select>\n </div>\n </Card>\n\n {/* General */}\n <Card id=\"settings-general\" data-section=\"general\">\n <CardTitle>General</CardTitle>\n <CardMeta>Default agent + model override.</CardMeta>\n <div className=\"field\" data-setting-id=\"defaultAgent\">\n <label className=\"field-label\" htmlFor=\"set-default-agent\">Default agent</label>\n <AutosaveField\n initialValue={settings.defaultAgent || ''}\n saveFn={async (v) => {\n patchTop('defaultAgent', v);\n autoSave?.('defaultAgent', v);\n }}\n render={({ value, onChange, onBlur }) => (\n <input\n id=\"set-default-agent\"\n className=\"input\"\n type=\"text\"\n placeholder=\"e.g. odin\"\n value={value}\n onChange={e => onChange(e.target.value)}\n onBlur={onBlur}\n />\n )}\n />\n </div>\n <div className=\"field\" data-setting-id=\"defaultModel\">\n <label className=\"field-label\" htmlFor=\"set-default-model\">Model override</label>\n <AutosaveField\n initialValue={settings.defaultModel || ''}\n saveFn={async (v) => {\n patchTop('defaultModel', v);\n autoSave?.('defaultModel', v);\n }}\n render={({ value, onChange, onBlur }) => (\n <input\n id=\"set-default-model\"\n className=\"input\"\n type=\"text\"\n placeholder=\"(leave empty for provider default)\"\n value={value}\n onChange={e => onChange(e.target.value)}\n onBlur={onBlur}\n />\n )}\n />\n </div>\n </Card>\n </>\n );\n}\n","// src/web/components/TailscaleSettings.tsx\n//\n// v5.2 — Tailscale auth key integration UI.\n//\n// Allows users to authenticate with Tailscale using an auth key and\n// set up tailscale serve for the dashboard without using the CLI.\nimport React, { useEffect, useState } from 'react';\nimport { CheckCircle, Link as LinkIcon, Plug } from 'lucide-react';\nimport { Card, CardTitle, CardMeta } from './Card';\nimport { Button } from './Button';\nimport { api } from '../lib/api';\nimport type { TailscaleStatus } from '../lib/types';\n\ntype Props = {\n /** Existing tailscale status from Settings-level fetch. If provided, used as initial value. */\n initialStatus?: TailscaleStatus | null;\n};\n\nexport function TailscaleSettings({ initialStatus }: Props) {\n const [status, setStatus] = useState<TailscaleStatus | null>(initialStatus ?? null);\n const [authKey, setAuthKey] = useState('');\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n if (status === null) {\n api.get<TailscaleStatus>('/tailscale/status')\n .then((r) => setStatus(r))\n .catch(() => setStatus(null));\n }\n }, []);\n\n const loadStatus = async () => {\n try {\n const r = await api.get<TailscaleStatus>('/tailscale/status');\n setStatus(r);\n } catch {\n setError('Failed to load Tailscale status');\n }\n };\n\n const handleAuthenticate = async () => {\n if (!authKey.trim()) return;\n setLoading(true);\n setError(null);\n try {\n await api.post('/tailscale/setup', { authKey: authKey.trim() });\n await loadStatus();\n setAuthKey('');\n } catch (err) {\n setError((err as Error).message || 'Authentication failed');\n } finally {\n setLoading(false);\n }\n };\n\n const handleSetupServe = async () => {\n if (!status) return;\n setLoading(true);\n setError(null);\n try {\n await api.post('/tailscale/setup', {\n port: status.settings?.port || 4321,\n https: status.settings?.https !== false,\n hostname: status.settings?.hostname || '',\n });\n await loadStatus();\n } catch (err) {\n setError((err as Error).message || 'Setup failed');\n } finally {\n setLoading(false);\n }\n };\n\n const handleRemoveServe = async () => {\n setLoading(true);\n setError(null);\n try {\n await api.post('/tailscale/unserve');\n await loadStatus();\n } catch (err) {\n setError((err as Error).message || 'Remove failed');\n } finally {\n setLoading(false);\n }\n };\n\n const isInstalled = status?.installed ?? false;\n const isAuthenticated = status?.authenticated ?? false;\n const isServeEnabled = status?.settings?.enabled ?? false;\n const serveUrl = isServeEnabled ? `https://${status?.hostname || 'bizar-dash'}` : null;\n\n if (!isInstalled) {\n return (\n <Card id=\"settings-tailscale-auth\" data-section=\"tailscale\">\n <CardTitle><Plug size={14} /> Tailscale Integration</CardTitle>\n <CardMeta>Tailscale is not installed on this machine.</CardMeta>\n <p className=\"muted\">\n Install from{' '}\n <a href=\"https://tailscale.com/download\" target=\"_blank\" rel=\"noreferrer\">\n tailscale.com/download\n </a>\n </p>\n </Card>\n );\n }\n\n return (\n <Card id=\"settings-tailscale-auth\" data-section=\"tailscale\">\n <CardTitle><Plug size={14} /> Tailscale Integration</CardTitle>\n <CardMeta>Expose the dashboard over your Tailscale network using an auth key.</CardMeta>\n\n {error && (\n <p style={{ color: 'var(--color-error, #f85149)', fontSize: '0.85rem' }}>{error}</p>\n )}\n\n {isAuthenticated ? (\n <>\n <div className=\"status-row\" style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', marginBottom: '0.75rem' }}>\n <CheckCircle size={16} style={{ color: 'var(--color-success, #3fb950)' }} />\n <span>Authenticated as <strong>{status?.hostname || 'unknown'}</strong></span>\n </div>\n\n {isServeEnabled && serveUrl ? (\n <>\n <div className=\"status-row\" style={{ display: 'flex', alignItems: 'center', gap: '0.5rem', marginBottom: '0.75rem' }}>\n <LinkIcon size={16} />\n <a href={serveUrl} target=\"_blank\" rel=\"noreferrer\">{serveUrl}</a>\n </div>\n <Button variant=\"secondary\" size=\"sm\" onClick={handleRemoveServe} disabled={loading}>\n Remove serve\n </Button>\n </>\n ) : (\n <Button variant=\"primary\" size=\"sm\" onClick={handleSetupServe} disabled={loading}>\n Set up Tailscale serve\n </Button>\n )}\n </>\n ) : (\n <>\n <p className=\"muted\" style={{ marginBottom: '0.75rem' }}>Not authenticated with Tailscale.</p>\n <div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}>\n <input\n type=\"text\"\n className=\"input\"\n placeholder=\"tskey-...\"\n value={authKey}\n onChange={(e) => setAuthKey(e.target.value)}\n style={{ flex: 1, minWidth: '200px' }}\n disabled={loading}\n />\n <Button variant=\"primary\" size=\"sm\" onClick={handleAuthenticate} disabled={loading || !authKey.trim()}>\n Authenticate\n </Button>\n </div>\n </>\n )}\n </Card>\n );\n}\n","// src/web/views/settings/SystemLlmSection.tsx\nimport React from 'react';\nimport { Sparkles } from 'lucide-react';\nimport { Card, CardTitle, CardMeta } from '../../components/Card';\nimport type { Settings } from '../../lib/types';\n\ntype Props = {\n settings: Settings;\n patchTop: <K extends keyof Settings>(key: K, value: Settings[K]) => void;\n};\n\nexport function SystemLlmSection({ settings, patchTop }: Props) {\n return (\n <Card id=\"settings-system-llm\" data-section=\"system-llm\">\n <CardTitle><Sparkles size={14} /> System LLM API</CardTitle>\n <CardMeta>\n Configures the LLM used for automatic title generation, prompt enhancement,\n summarization, and other system-level calls.\n </CardMeta>\n\n <label className=\"checkbox-row\" data-setting-id=\"systemLlm.enabled\">\n <input\n type=\"checkbox\"\n checked={!!settings.systemLlm?.enabled}\n onChange={(e) => {\n const cur = settings.systemLlm || { enabled: true, provider: 'cline', model: 'cline/deepseek-v4-flash-free' };\n patchTop('systemLlm', { ...cur, enabled: e.target.checked });\n }}\n />\n <span>Enable system LLM calls</span>\n </label>\n\n <div className=\"field\" data-setting-id=\"systemLlm.provider\" style={{ marginTop: 'var(--space-3)' }}>\n <label className=\"field-label\" htmlFor=\"set-system-llm-provider\">Provider</label>\n <select\n id=\"set-system-llm-provider\"\n className=\"select\"\n value={settings.systemLlm?.provider || 'cline'}\n onChange={(e) => {\n const cur = settings.systemLlm || { enabled: true, provider: 'cline', model: 'cline/deepseek-v4-flash-free' };\n patchTop('systemLlm', { ...cur, provider: e.target.value });\n }}\n >\n <option value=\"cline\">Cline</option>\n <option value=\"openrouter\">OpenRouter</option>\n <option value=\"minimax\">MiniMax</option>\n </select>\n </div>\n\n <div className=\"field\" data-setting-id=\"systemLlm.model\">\n <label className=\"field-label\" htmlFor=\"set-system-llm-model\">Model</label>\n <input\n id=\"set-system-llm-model\"\n className=\"input mono\"\n type=\"text\"\n placeholder=\"cline/deepseek-v4-flash-free\"\n value={settings.systemLlm?.model || 'cline/deepseek-v4-flash-free'}\n onChange={(e) => {\n const cur = settings.systemLlm || { enabled: true, provider: 'cline', model: 'cline/deepseek-v4-flash-free' };\n patchTop('systemLlm', { ...cur, model: e.target.value });\n }}\n />\n <p className=\"field-help\">\n The API key is read from <code>auth.json</code> for the selected provider.\n Leave the default model for best results.\n </p>\n </div>\n\n <div style={{ marginTop: 'var(--space-4)' }}>\n <h4 style={{ margin: '0 0 var(--space-2)' }}>Features using this API</h4>\n <ul className=\"settings-feature-list\" style={{ margin: 0, paddingLeft: 'var(--space-4)', lineHeight: 1.8 }}>\n <li>Auto-title generation for new chat sessions</li>\n <li>Auto-title generation for new tasks</li>\n <li>&quot;Enhance prompt&quot; button in the task input</li>\n <li>&quot;Enhance prompt&quot; button in the chat composer</li>\n <li className=\"muted\" style={{ fontSize: 12 }}>Future: summarization, name generation, and more</li>\n </ul>\n </div>\n </Card>\n );\n}\n","// src/web/components/HeadroomSettings.tsx\n//\n// v1.0.0 — Headroom settings card for the Settings view.\n// Displays live status and provides controls for install, wrap, proxy, and configuration.\nimport React, { useEffect, useState, useCallback } from 'react';\nimport {\n Activity,\n CheckCircle,\n XCircle,\n Download,\n Play,\n Square,\n RefreshCw,\n ExternalLink,\n Zap,\n} from 'lucide-react';\nimport { Card, CardTitle, CardMeta } from '../components/Card';\nimport { Button } from '../components/Button';\nimport { useToast } from '../components/Toast';\nimport { api } from '../lib/api';\nimport { type HeadroomSettings } from '../lib/types';\n\ntype HeadroomStatus = {\n installed: boolean;\n version: string | null;\n proxyRunning: boolean;\n proxyPort: number | null;\n proxyPid: number | null;\n wrapped: boolean;\n healthy: 'ok' | 'warn' | 'fail';\n messages: string[];\n};\n\ntype HeadroomStats = {\n tokensSaved?: number;\n compressionRatio?: number;\n cacheHits?: number;\n transforms?: number;\n error?: string;\n};\n\nconst BACKEND_OPTIONS = [\n { value: 'anthropic', label: 'Anthropic' },\n { value: 'anyllm', label: 'AnyLLM' },\n { value: 'litellm-anthropic', label: 'LiteLLM (Anthropic)' },\n { value: 'litellm-openai', label: 'LiteLLM (OpenAI)' },\n];\n\ntype Props = {\n settings: HeadroomSettings;\n onPatch: (patch: Partial<HeadroomSettings>) => void;\n};\n\nexport function HeadroomSettingsCard({ settings, onPatch }: Props) {\n const toast = useToast();\n const [status, setStatus] = useState<HeadroomStatus | null>(null);\n const [stats, setStats] = useState<HeadroomStats | null>(null);\n const [loadingStatus, setLoadingStatus] = useState(false);\n const [installing, setInstalling] = useState(false);\n const [wrapping, setWrapping] = useState(false);\n const [unwrapping, setUnwrapping] = useState(false);\n const [startingProxy, setStartingProxy] = useState(false);\n const [stoppingProxy, setStoppingProxy] = useState(false);\n\n const loadStatus = useCallback(async () => {\n setLoadingStatus(true);\n try {\n const [s, st] = await Promise.all([\n api.get<HeadroomStatus>('/headroom/status'),\n api.get<HeadroomStats>('/headroom/stats?hours=24').catch(() => ({ error: 'no stats' })),\n ]);\n setStatus(s);\n if (!st.error) setStats(st);\n } catch (err) {\n toast.error(`Failed to load Headroom status: ${(err as Error).message}`);\n } finally {\n setLoadingStatus(false);\n }\n }, [toast]);\n\n useEffect(() => {\n loadStatus();\n }, [loadStatus]);\n\n const handleInstall = async () => {\n setInstalling(true);\n try {\n const r = await api.post<{ ok: boolean; installed: boolean; method: string }>('/headroom/install', { force: true });\n if (r.installed) {\n toast.success(`Headroom installed via ${r.method}.`);\n await loadStatus();\n } else {\n toast.error('Headroom install failed. Try: pip install \"headroom-ai[all]\"');\n }\n } catch (err) {\n toast.error(`Install failed: ${(err as Error).message}`);\n } finally {\n setInstalling(false);\n }\n };\n\n const handleWrap = async () => {\n setWrapping(true);\n try {\n const r = await api.post<{ ok: boolean }>('/headroom/wrap', { port: settings.port });\n if (r.ok) {\n toast.success('cline wrapped with Headroom.');\n await loadStatus();\n } else {\n toast.error('Wrap failed.');\n }\n } catch (err) {\n toast.error(`Wrap failed: ${(err as Error).message}`);\n } finally {\n setWrapping(false);\n }\n };\n\n const handleUnwrap = async () => {\n setUnwrapping(true);\n try {\n const r = await api.post<{ ok: boolean }>('/headroom/unwrap');\n if (r.ok) {\n toast.success('cline unwrapped from Headroom.');\n await loadStatus();\n } else {\n toast.error('Unwrap failed.');\n }\n } catch (err) {\n toast.error(`Unwrap failed: ${(err as Error).message}`);\n } finally {\n setUnwrapping(false);\n }\n };\n\n const handleStartProxy = async () => {\n setStartingProxy(true);\n try {\n const r = await api.post<{ ok: boolean }>('/headroom/proxy/start', {\n port: settings.port,\n host: settings.host,\n });\n if (r.ok) {\n toast.success(`Headroom proxy started on ${settings.host}:${settings.port}.`);\n await loadStatus();\n } else {\n toast.error('Proxy start failed.');\n }\n } catch (err) {\n toast.error(`Proxy start failed: ${(err as Error).message}`);\n } finally {\n setStartingProxy(false);\n }\n };\n\n const handleStopProxy = async () => {\n setStoppingProxy(true);\n try {\n const r = await api.post<{ ok: boolean }>('/headroom/proxy/stop');\n if (r.ok) {\n toast.success('Headroom proxy stopped.');\n await loadStatus();\n } else {\n toast.error('Proxy stop failed.');\n }\n } catch (err) {\n toast.error(`Proxy stop failed: ${(err as Error).message}`);\n } finally {\n setStoppingProxy(false);\n }\n };\n\n const handleOpenDashboard = async () => {\n try {\n const { spawn } = await import('node:child_process');\n spawn('headroom', ['dashboard'], { detached: true, stdio: 'ignore' });\n } catch {\n toast.error('Could not open Headroom dashboard. Run `headroom dashboard` manually.');\n }\n };\n\n const { installed, version, proxyRunning, proxyPort, wrapped } = status || {};\n const ratio = stats?.compressionRatio ?? 0;\n const saved = stats?.tokensSaved ?? 0;\n\n return (\n <div id=\"settings-headroom\" data-section=\"headroom\">\n <Card>\n <CardTitle>\n <Activity size={14} style={{ color: proxyRunning ? 'var(--success)' : 'var(--text-dim)' }} />\n Headroom\n {installed && version && (\n <span style={{ fontSize: 11, color: 'var(--text-dim)', marginLeft: 6 }}>\n v{version} {proxyRunning ? '✓' : '✗'}\n </span>\n )}\n {!installed && (\n <span style={{ fontSize: 11, color: 'var(--error)', marginLeft: 6 }}>not installed</span>\n )}\n </CardTitle>\n <CardMeta>\n Context compression for token efficiency. Compresses tool outputs, logs, and conversation\n history by 60–95% before they reach the model.\n </CardMeta>\n\n {/* Live status row */}\n <div style={{ display: 'flex', gap: 16, flexWrap: 'wrap', marginTop: 12, marginBottom: 12 }}>\n <div className=\"headroom-status-item\">\n {installed ? (\n <CheckCircle size={12} style={{ color: 'var(--success)' }} />\n ) : (\n <XCircle size={12} style={{ color: 'var(--error)' }} />\n )}\n <span>{installed ? 'Installed' : 'Not installed'}</span>\n </div>\n <div className=\"headroom-status-item\">\n {proxyRunning ? (\n <CheckCircle size={12} style={{ color: 'var(--success)' }} />\n ) : (\n <XCircle size={12} style={{ color: 'var(--text-dim)' }} />\n )}\n <span>Proxy {proxyRunning ? `${proxyPort} ✓` : 'stopped'}</span>\n </div>\n <div className=\"headroom-status-item\">\n {wrapped ? (\n <CheckCircle size={12} style={{ color: 'var(--success)' }} />\n ) : (\n <XCircle size={12} style={{ color: 'var(--text-dim)' }} />\n )}\n <span>cline {wrapped ? 'wrapped' : 'not wrapped'}</span>\n </div>\n </div>\n\n {/* Stats */}\n {(saved > 0 || ratio > 0) && (\n <div style={{ display: 'flex', gap: 24, marginBottom: 12 }}>\n <div>\n <div style={{ fontSize: 20, fontWeight: 700, color: 'var(--success)' }}>\n {ratio > 0 ? `${Math.round(ratio * 100)}%` : '—'}\n </div>\n <div className=\"muted\" style={{ fontSize: 11 }}>Compression ratio</div>\n </div>\n <div>\n <div style={{ fontSize: 20, fontWeight: 700, color: 'var(--accent)' }}>\n {saved > 0 ? saved.toLocaleString() : '—'}\n </div>\n <div className=\"muted\" style={{ fontSize: 11 }}>Tokens saved (24h)</div>\n </div>\n </div>\n )}\n\n {/* Action buttons */}\n <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginBottom: 8 }}>\n {!installed ? (\n <Button variant=\"primary\" size=\"sm\" onClick={handleInstall} disabled={installing}>\n {installing ? <span className=\"btn-spinner\" /> : <Download size={13} />}\n {installing ? 'Installing…' : 'Install Headroom'}\n </Button>\n ) : (\n <>\n <Button\n variant=\"secondary\"\n size=\"sm\"\n onClick={handleWrap}\n disabled={wrapping || wrapped}\n title={wrapped ? 'cline is already wrapped' : 'Wrap cline to route through Headroom proxy'}\n >\n {wrapping ? <span className=\"btn-spinner\" /> : <Zap size={13} />}\n {wrapping ? 'Wrapping…' : wrapped ? 'Wrapped ✓' : 'Wrap cline'}\n </Button>\n {wrapped && (\n <Button variant=\"ghost\" size=\"sm\" onClick={handleUnwrap} disabled={unwrapping}>\n {unwrapping ? <span className=\"btn-spinner\" /> : <RefreshCw size={13} />}\n {unwrapping ? 'Unwrapping…' : 'Unwrap'}\n </Button>\n )}\n </>\n )}\n\n {installed && (\n <>\n {proxyRunning ? (\n <Button variant=\"secondary\" size=\"sm\" onClick={handleStopProxy} disabled={stoppingProxy}>\n {stoppingProxy ? <span className=\"btn-spinner\" /> : <Square size={13} />}\n {stoppingProxy ? 'Stopping…' : 'Stop proxy'}\n </Button>\n ) : (\n <Button variant=\"secondary\" size=\"sm\" onClick={handleStartProxy} disabled={startingProxy}>\n {startingProxy ? <span className=\"btn-spinner\" /> : <Play size={13} />}\n {startingProxy ? 'Starting…' : 'Start proxy'}\n </Button>\n )}\n <Button variant=\"ghost\" size=\"sm\" onClick={handleOpenDashboard}>\n <ExternalLink size={13} />\n Dashboard\n </Button>\n </>\n )}\n\n <Button variant=\"ghost\" size=\"sm\" onClick={loadStatus} disabled={loadingStatus}>\n <RefreshCw size={13} className={loadingStatus ? 'spin' : ''} />\n </Button>\n </div>\n\n {/* Settings */}\n <div style={{ marginTop: 16 }}>\n <label className=\"checkbox-row\">\n <input\n type=\"checkbox\"\n checked={settings.enabled}\n onChange={(e) => onPatch({ enabled: e.target.checked })}\n />\n <span>Enable Headroom</span>\n </label>\n <label className=\"checkbox-row\">\n <input\n type=\"checkbox\"\n checked={settings.autoInstall}\n onChange={(e) => onPatch({ autoInstall: e.target.checked })}\n />\n <span>Auto-install if missing on dashboard startup</span>\n </label>\n <label className=\"checkbox-row\">\n <input\n type=\"checkbox\"\n checked={settings.autoStart}\n onChange={(e) => onPatch({ autoStart: e.target.checked })}\n />\n <span>Auto-start proxy on dashboard startup</span>\n </label>\n <label className=\"checkbox-row\">\n <input\n type=\"checkbox\"\n checked={settings.autoWrap}\n onChange={(e) => onPatch({ autoWrap: e.target.checked })}\n />\n <span>Auto-wrap cline on dashboard startup</span>\n </label>\n <label className=\"checkbox-row\">\n <input\n type=\"checkbox\"\n checked={settings.routeAllProviders}\n onChange={(e) => onPatch({ routeAllProviders: e.target.checked })}\n />\n <span>Route all providers through Headroom proxy</span>\n </label>\n\n <div className=\"task-form-row\" style={{ marginTop: 12 }}>\n <div className=\"task-form-field\">\n <label className=\"field-label\">Proxy port</label>\n <input\n type=\"number\"\n className=\"input\"\n value={settings.port}\n min={1}\n max={65535}\n onChange={(e) => onPatch({ port: Math.max(1, Math.min(65535, parseInt(e.target.value, 10) || 8787)) })}\n />\n </div>\n <div className=\"task-form-field\">\n <label className=\"field-label\">Proxy host</label>\n <input\n type=\"text\"\n className=\"input\"\n value={settings.host}\n onChange={(e) => onPatch({ host: e.target.value })}\n />\n </div>\n </div>\n\n <div className=\"task-form-row\">\n <div className=\"task-form-field\">\n <label className=\"field-label\">Backend</label>\n <select\n className=\"select\"\n value={settings.backend}\n onChange={(e) => onPatch({ backend: e.target.value })}\n >\n {BACKEND_OPTIONS.map((o) => (\n <option key={o.value} value={o.value}>{o.label}</option>\n ))}\n </select>\n </div>\n <div className=\"task-form-field\">\n <label className=\"field-label\">Monthly budget (USD)</label>\n <input\n type=\"number\"\n className=\"input\"\n value={settings.budget}\n min={0}\n step={1}\n onChange={(e) => onPatch({ budget: Math.max(0, parseFloat(e.target.value) || 0) })}\n />\n <span className=\"field-help\">0 = unlimited</span>\n </div>\n </div>\n\n <label className=\"checkbox-row\">\n <input\n type=\"checkbox\"\n checked={settings.outputShaper}\n onChange={(e) => onPatch({ outputShaper: e.target.checked })}\n />\n <span>Enable output shaper</span>\n </label>\n <label className=\"checkbox-row\">\n <input\n type=\"checkbox\"\n checked={settings.telemetry}\n onChange={(e) => onPatch({ telemetry: e.target.checked })}\n />\n <span>Enable telemetry</span>\n </label>\n </div>\n </Card>\n </div>\n );\n}\n","// src/web/views/settings/HeadroomSection.tsx\nimport React from 'react';\nimport { HeadroomSettingsCard } from '../../components/HeadroomSettings';\nimport { type HeadroomSettings, type Settings } from '../../lib/types';\n\nconst DEFAULT_HEADROOM_SETTINGS: HeadroomSettings = {\n enabled: true,\n autoInstall: true,\n port: 8787,\n host: '127.0.0.1',\n outputShaper: false,\n telemetry: false,\n budget: 0,\n backend: 'anthropic',\n autoStart: true,\n autoWrap: true,\n routeAllProviders: true,\n};\n\ntype Props = {\n settings: Settings;\n setSettings: React.Dispatch<React.SetStateAction<Settings>>;\n setDirty: React.Dispatch<React.SetStateAction<boolean>>;\n};\n\nexport function HeadroomSection({ settings, setSettings, setDirty }: Props) {\n return (\n <HeadroomSettingsCard\n settings={settings.headroom || DEFAULT_HEADROOM_SETTINGS}\n onPatch={(patch) => {\n setSettings((cur) => ({\n ...cur,\n headroom: { ...(cur.headroom || DEFAULT_HEADROOM_SETTINGS), ...patch },\n }));\n setDirty(true);\n }}\n />\n );\n}\n","// src/web/views/settings/ActivitySection.tsx\nimport React, { useEffect, useState, useCallback } from 'react';\nimport { Activity, Search, Eye, RefreshCw, Info } from 'lucide-react';\nimport { Card, CardTitle, CardMeta } from '../../components/Card';\nimport { Button } from '../../components/Button';\nimport { useToast } from '../../components/Toast';\nimport { api } from '../../lib/api';\nimport { cn } from '../../lib/utils';\nimport type { Settings } from '../../lib/types';\n\ntype Props = {\n about: Settings['about'];\n};\n\nfunction ActivityLogCard() {\n const toast = useToast();\n const [items, setItems] = useState<Array<{ kind?: string; ts?: string; slug?: string; message?: string }>>([]);\n const [hidden, setHidden] = useState<Set<string>>(new Set());\n const [loading, setLoading] = useState(true);\n const [filter, setFilter] = useState('');\n const [showHidden, setShowHidden] = useState(true);\n\n const refresh = useCallback(async () => {\n setLoading(true);\n try {\n const [log, hid] = await Promise.all([\n api.get<{ items: Array<{ kind?: string; ts?: string; slug?: string; message?: string }> }>('/activity'),\n api.get<{ hidden: string[] }>('/activity/hidden'),\n ]);\n setItems(Array.isArray(log.items) ? log.items : []);\n setHidden(new Set(hid.hidden || []));\n } catch (err) {\n toast.error(`Load failed: ${(err as Error).message}`);\n } finally {\n setLoading(false);\n }\n }, [toast]);\n\n useEffect(() => { refresh(); }, [refresh]);\n\n const keyOf = (it: { kind?: string; ts?: string; slug?: string }, idx: number) => {\n const k = `${it.kind || ''}|${it.ts || ''}|${it.slug || ''}|${idx}`;\n let h = 0;\n for (let i = 0; i < k.length; i++) h = ((h << 5) - h + k.charCodeAt(i)) | 0;\n return Math.abs(h).toString(16).padStart(8, '0').slice(0, 16);\n };\n\n const onRestoreAll = async () => {\n try {\n await api.del('/activity/hide');\n setHidden(new Set());\n toast.success('All hidden activity restored.');\n } catch (err) {\n toast.error(`Restore failed: ${(err as Error).message}`);\n }\n };\n\n const onRestoreOne = async (key: string) => {\n try {\n await api.del(`/activity/hide/${encodeURIComponent(key)}`);\n const next = new Set(hidden);\n next.delete(key);\n setHidden(next);\n } catch (err) {\n toast.error(`Restore failed: ${(err as Error).message}`);\n }\n };\n\n const filtered = items.filter((it, idx) => {\n const k = keyOf(it, idx);\n if (!showHidden && hidden.has(k)) return false;\n if (!filter) return true;\n const q = filter.toLowerCase();\n return [it.kind, it.slug, it.message].some((v) => typeof v === 'string' && v.toLowerCase().includes(q));\n });\n\n return (\n <Card id=\"settings-activity-log\" data-section=\"activity-log\">\n <CardTitle>\n <Activity size={14} /> Activity log\n <span className=\"muted\" style={{ fontWeight: 400, marginLeft: 8, fontSize: 12 }}>\n {items.length} total · {hidden.size} hidden\n </span>\n <Button variant=\"ghost\" size=\"sm\" style={{ marginLeft: 'auto' }} onClick={refresh} title=\"Reload\">\n <RefreshCw size={12} />\n </Button>\n </CardTitle>\n <CardMeta>\n Full history from <code>~/.bizar/activity.log</code>. Hiding an item in the Overview only hides it there — the entry stays here.\n </CardMeta>\n\n <div className=\"activity-log-toolbar\">\n <div className=\"activity-log-search\">\n <Search size={12} />\n <input\n type=\"text\"\n className=\"input\"\n placeholder=\"Filter by kind, slug, or message…\"\n value={filter}\n onChange={(e) => setFilter(e.target.value)}\n />\n </div>\n <label className=\"activity-log-toggle\">\n <input\n type=\"checkbox\"\n checked={showHidden}\n onChange={(e) => setShowHidden(e.target.checked)}\n />\n Show hidden\n </label>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n disabled={hidden.size === 0}\n onClick={onRestoreAll}\n title=\"Restore all hidden items to the Overview\"\n >\n <Eye size={12} /> Restore all\n </Button>\n </div>\n\n {loading ? (\n <div className=\"muted\" style={{ padding: '12px 0', fontSize: 12 }}>Loading…</div>\n ) : filtered.length === 0 ? (\n <div className=\"muted\" style={{ padding: '12px 0', fontSize: 12 }}>\n {items.length === 0 ? 'No activity yet.' : 'No items match the current filter.'}\n </div>\n ) : (\n <div className=\"activity-log-table-wrap\">\n <table className=\"activity-log-table\">\n <thead>\n <tr>\n <th>Kind</th>\n <th>Detail</th>\n <th>Time</th>\n <th></th>\n </tr>\n </thead>\n <tbody>\n {filtered.slice(0, 200).map((it, idx) => {\n const k = keyOf(it, idx);\n const isHidden = hidden.has(k);\n return (\n <tr key={`${it.ts}-${idx}`} className={cn(isHidden && 'activity-log-row-hidden')}>\n <td className=\"activity-log-kind\">{it.kind || 'activity'}</td>\n <td className=\"activity-log-detail\">\n {it.message || it.slug || '—'}\n </td>\n <td className=\"activity-log-time mono\">\n {it.ts ? new Date(it.ts).toLocaleString() : '—'}\n </td>\n <td>\n {isHidden ? (\n <Button variant=\"ghost\" size=\"sm\" onClick={() => onRestoreOne(k)} title=\"Restore to Overview\">\n <Eye size={12} />\n </Button>\n ) : (\n <span className=\"activity-log-state-tag\">shown</span>\n )}\n </td>\n </tr>\n );\n })}\n </tbody>\n </table>\n {filtered.length > 200 && (\n <div className=\"muted\" style={{ fontSize: 11, padding: '8px 0' }}>\n Showing first 200 of {filtered.length}.\n </div>\n )}\n </div>\n )}\n </Card>\n );\n}\n\nfunction AboutCard({ about }: Props) {\n return (\n <Card id=\"settings-about\" data-section=\"about\">\n <CardTitle><Info size={14} /> About</CardTitle>\n <CardMeta>Build metadata.</CardMeta>\n <dl className=\"about-table\">\n <dt>Version</dt>\n <dd className=\"mono\">{about?.version || '—'}</dd>\n <dt>Homepage</dt>\n <dd>\n <a href={about?.homepage || 'https://github.com/DrB0rk/BizarHarness'} target=\"_blank\" rel=\"noopener noreferrer\">\n {about?.homepage || 'https://github.com/DrB0rk/BizarHarness'}\n </a>\n </dd>\n <dt>License</dt>\n <dd>{about?.license || 'MIT'}</dd>\n </dl>\n </Card>\n );\n}\n\nexport function ActivitySection({ about }: Props) {\n return (\n <>\n <ActivityLogCard />\n <AboutCard about={about} />\n </>\n );\n}\n","// src/web/components/EnvVarManager.tsx — manage BIZAR_* env vars from ~/.config/bizar/env.json\nimport { useCallback, useEffect, useMemo, useState } from 'react';\nimport { Plus, Pencil, Trash2, Eye, EyeOff, RefreshCw, KeyRound, CheckCircle, X, Search, Download, Upload, ChevronDown, ChevronRight, AlertCircle } from 'lucide-react';\nimport { Button } from './Button';\nimport { useToast } from './Toast';\nimport { api } from '../lib/api';\nimport { cn } from '../lib/utils';\n\nexport type EnvVarEntry = {\n name: string;\n value: string; // masked on GET, full on edit\n createdAt: string;\n source: string;\n};\n\nexport type EnvVarGrouped = Record<string, EnvVarEntry[]>;\n\ntype Props = {\n onError?: (msg: string) => void;\n};\n\nexport function EnvVarManager({ onError }: Props) {\n const toast = useToast();\n const [vars, setVars] = useState<EnvVarEntry[]>([]);\n const [loading, setLoading] = useState(false);\n const [saving, setSaving] = useState(false);\n // Which rows have the value revealed (edit mode shows real value)\n const [revealed, setRevealed] = useState<Set<string>>(new Set());\n // Currently editing name (null = none)\n const [editing, setEditing] = useState<string | null>(null);\n const [editValue, setEditValue] = useState('');\n // New var form\n const [showAdd, setShowAdd] = useState(false);\n const [newName, setNewName] = useState('');\n const [newValue, setNewValue] = useState('');\n // Bulk operations\n const [showBulk, setShowBulk] = useState(false);\n const [bulkText, setBulkText] = useState('');\n const [bulkImporting, setBulkImporting] = useState(false);\n // Search / filter\n const [searchQ, setSearchQ] = useState('');\n // Group by prefix (collapsed state)\n const [collapsed, setCollapsed] = useState<Set<string>>(new Set());\n\n const load = useCallback(async () => {\n setLoading(true);\n try {\n const r = await api.get<EnvVarEntry[]>('/env-vars');\n setVars(r);\n } catch (err) {\n toast.error(`Load failed: ${(err as Error).message}`);\n onError?.((err as Error).message);\n } finally {\n setLoading(false);\n }\n }, [toast, onError]);\n\n useEffect(() => { load(); }, [load]);\n\n const toggleReveal = (name: string) => {\n setRevealed((prev) => {\n const next = new Set(prev);\n if (next.has(name)) next.delete(name);\n else next.add(name);\n return next;\n });\n };\n\n const handleAdd = async () => {\n const name = newName.trim().toUpperCase();\n const value = newValue.trim();\n if (!name || !/^BIZAR_[A-Z0-9_]+$/.test(name)) {\n toast.warning('Name must match BIZAR_<NAME> (e.g. BIZAR_MINIMAX_KEY)');\n return;\n }\n if (!value) {\n toast.warning('Value is required.');\n return;\n }\n if (vars.some((v) => v.name === name)) {\n toast.warning(`${name} already exists.`);\n return;\n }\n setSaving(true);\n try {\n await api.post('/env-vars', { name, value });\n toast.success(`${name} created.`);\n setShowAdd(false);\n setNewName('');\n setNewValue('');\n await load();\n } catch (err) {\n toast.error(`Create failed: ${(err as Error).message}`);\n } finally {\n setSaving(false);\n }\n };\n\n const startEdit = (v: EnvVarEntry) => {\n setEditing(v.name);\n setEditValue(v.value === v.value.replace(/\\*/g, '') ? v.value : '');\n setRevealed((prev) => { const next = new Set(prev); next.add(v.name); return next; });\n };\n\n const handleUpdate = async (name: string) => {\n if (!editValue.trim()) {\n toast.warning('Value cannot be empty.');\n return;\n }\n setSaving(true);\n try {\n await api.put(`/env-vars/${encodeURIComponent(name)}`, { value: editValue.trim() });\n toast.success(`${name} updated.`);\n setEditing(null);\n await load();\n } catch (err) {\n toast.error(`Update failed: ${(err as Error).message}`);\n } finally {\n setSaving(false);\n }\n };\n\n const handleDelete = async (name: string) => {\n if (!confirm(`Delete ${name}? This cannot be undone.`)) return;\n try {\n await api.del(`/env-vars/${encodeURIComponent(name)}`);\n toast.success(`${name} deleted.`);\n await load();\n } catch (err) {\n toast.error(`Delete failed: ${(err as Error).message}`);\n }\n };\n\n // ─── Bulk import ───────────────────────────────────────────────────────────\n const handleBulkImport = async () => {\n if (!bulkText.trim()) { toast.warning('Nothing to import.'); return; }\n setBulkImporting(true);\n try {\n const r = await api.post<{ ok: boolean; imported: number; skipped: number; errors: string[] }>('/env-vars/bulk-import', { envContent: bulkText });\n toast.success(`Imported ${r.imported}, skipped ${r.skipped}.`);\n if (r.errors.length > 0) {\n toast.warning(`${r.errors.length} lines had errors: ${r.errors.slice(0, 3).join('; ')}${r.errors.length > 3 ? '…' : ''}`);\n }\n setBulkText('');\n setShowBulk(false);\n await load();\n } catch (err) {\n toast.error(`Bulk import failed: ${(err as Error).message}`);\n } finally {\n setBulkImporting(false);\n }\n };\n\n // ─── Bulk export ───────────────────────────────────────────────────────────\n const handleExport = async () => {\n try {\n const r = await api.get<string>('/env-vars/export');\n // r is the raw text content\n const blob = new Blob([r as unknown as string], { type: 'text/plain' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = '.env';\n a.click();\n URL.revokeObjectURL(url);\n toast.success('Exported .env file.');\n } catch (err) {\n toast.error(`Export failed: ${(err as Error).message}`);\n }\n };\n\n // ─── Group by prefix ───────────────────────────────────────────────────────\n const filtered = useMemo(() => {\n if (!searchQ.trim()) return vars;\n const q = searchQ.toLowerCase();\n return vars.filter((v) => v.name.toLowerCase().includes(q));\n }, [vars, searchQ]);\n\n const grouped = useMemo(() => {\n const groups: Record<string, EnvVarEntry[]> = {};\n for (const v of filtered) {\n const prefix = v.name.split('_')[0] + '_';\n (groups[prefix] ??= []).push(v);\n }\n return groups;\n }, [filtered]);\n\n const toggleGroup = (prefix: string) => {\n setCollapsed((prev) => {\n const next = new Set(prev);\n if (next.has(prefix)) next.delete(prefix);\n else next.add(prefix);\n return next;\n });\n };\n\n const sortedGroupKeys = useMemo(() => {\n const known = ['BIZAR_', 'PROVIDER_', 'MODEL_', 'API_'];\n const keys = Object.keys(grouped).sort((a, b) => {\n const ai = known.indexOf(a); const bi = known.indexOf(b);\n if (ai === -1 && bi === -1) return a.localeCompare(b);\n if (ai === -1) return 1;\n if (bi === -1) return -1;\n return ai - bi;\n });\n return keys;\n }, [grouped]);\n\n if (loading) {\n return <div className=\"muted\" style={{ padding: '16px 0', fontSize: 13 }}>Loading env vars…</div>;\n }\n\n const hasGroups = sortedGroupKeys.length > 1;\n const groupMode = hasGroups;\n\n return (\n <div className=\"env-var-manager\">\n <div className=\"env-var-toolbar\">\n <Button variant=\"primary\" size=\"sm\" onClick={() => setShowAdd(true)}>\n <Plus size={12} /> New variable\n </Button>\n <Button variant=\"secondary\" size=\"sm\" onClick={() => setShowBulk((v) => !v)} className={showBulk ? 'active' : ''}>\n <Upload size={12} /> Bulk import\n </Button>\n <Button variant=\"ghost\" size=\"sm\" onClick={handleExport} title=\"Export as .env\">\n <Download size={12} /> Export\n </Button>\n <div className=\"env-var-search-wrap\">\n <Search size={12} className=\"env-var-search-icon\" />\n <input\n className=\"env-var-search input\"\n placeholder=\"Filter…\"\n value={searchQ}\n onChange={(e) => setSearchQ(e.target.value)}\n />\n </div>\n <Button variant=\"ghost\" size=\"sm\" onClick={load} title=\"Reload\">\n <RefreshCw size={12} />\n </Button>\n </div>\n\n {/* Bulk import panel */}\n {showBulk && (\n <div className=\"env-var-bulk-panel\">\n <div className=\"env-var-bulk-header\">\n <span>Paste <code>KEY=value</code> lines (one per line):</span>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => setShowBulk(false)}><X size={12} /></Button>\n </div>\n <textarea\n className=\"input mono env-var-bulk-textarea\"\n placeholder={\"BIZAR_API_KEY=your-key-here\\nPROVIDER_OPENAI_KEY=sk-...\\nMODEL_KEY=gem-...\"}\n value={bulkText}\n onChange={(e) => setBulkText(e.target.value)}\n rows={6}\n />\n <div className=\"env-var-bulk-footer\">\n <Button variant=\"primary\" size=\"sm\" onClick={handleBulkImport} disabled={bulkImporting || !bulkText.trim()}>\n {bulkImporting ? 'Importing…' : 'Import'}\n </Button>\n <span className=\"muted\" style={{ fontSize: 11 }}>\n Existing keys are skipped unless re-created individually. Invalid lines are reported.\n </span>\n </div>\n </div>\n )}\n\n {vars.length === 0 && !showAdd && (\n <div className=\"env-var-empty\">\n <KeyRound size={20} />\n <span>No BIZAR_* env vars yet.</span>\n <span style={{ fontSize: 11 }}>Create one to store API keys securely.</span>\n </div>\n )}\n\n {/* New var form */}\n {showAdd && (\n <div className=\"env-var-add-form\">\n <div className=\"env-var-add-row\">\n <input\n className=\"input mono\"\n style={{ width: 240 }}\n placeholder=\"BIZAR_NAME\"\n value={newName}\n onChange={(e) => setNewName(e.target.value.toUpperCase())}\n onKeyDown={(e) => { if (e.key === 'Enter') handleAdd(); if (e.key === 'Escape') setShowAdd(false); }}\n autoFocus\n />\n <input\n className=\"input mono\"\n type=\"password\"\n placeholder=\"value (API key, token, etc.)\"\n value={newValue}\n onChange={(e) => setNewValue(e.target.value)}\n onKeyDown={(e) => { if (e.key === 'Enter') handleAdd(); if (e.key === 'Escape') setShowAdd(false); }}\n />\n <Button variant=\"primary\" size=\"sm\" onClick={handleAdd} disabled={saving}>\n <CheckCircle size={12} /> Create\n </Button>\n <Button variant=\"ghost\" size=\"sm\" onClick={() => { setShowAdd(false); setNewName(''); setNewValue(''); }}>\n <X size={12} />\n </Button>\n </div>\n <p style={{ fontSize: 11, color: 'var(--text-dim)', margin: 0 }}>\n Names must match <code>BIZAR_<var>NAME</var></code> (uppercase letters, digits, underscores).\n </p>\n </div>\n )}\n\n {/* Var list — grouped by prefix */}\n {filtered.length > 0 && (\n groupMode ? (\n <div className=\"env-var-groups\">\n {sortedGroupKeys.map((prefix) => {\n const items = grouped[prefix];\n const isCollapsed = collapsed.has(prefix);\n return (\n <div key={prefix} className=\"env-var-group\">\n <button\n type=\"button\"\n className=\"env-var-group-header\"\n onClick={() => toggleGroup(prefix)}\n >\n {isCollapsed ? <ChevronRight size={12} /> : <ChevronDown size={12} />}\n <span className=\"env-var-group-prefix mono\">{prefix}</span>\n <span className=\"env-var-group-count muted\">({items.length})</span>\n </button>\n {!isCollapsed && (\n <div className=\"env-var-list\">\n {items.map((v) => (\n <VarRow key={v.name} v={v} editing={editing} editValue={editValue}\n isRevealed={revealed.has(v.name)}\n onToggleReveal={toggleReveal} onStartEdit={startEdit}\n onEditValueChange={setEditValue} onHandleUpdate={handleUpdate}\n onCancelEdit={() => setEditing(null)} onDelete={handleDelete}\n saving={saving} />\n ))}\n </div>\n )}\n </div>\n );\n })}\n </div>\n ) : (\n <div className=\"env-var-list\">\n {filtered.map((v) => (\n <VarRow key={v.name} v={v} editing={editing} editValue={editValue}\n isRevealed={revealed.has(v.name)}\n onToggleReveal={toggleReveal} onStartEdit={startEdit}\n onEditValueChange={setEditValue} onHandleUpdate={handleUpdate}\n onCancelEdit={() => setEditing(null)} onDelete={handleDelete}\n saving={saving} />\n ))}\n </div>\n )\n )}\n\n {filtered.length === 0 && vars.length > 0 && (\n <div className=\"env-var-empty\">\n <Search size={16} />\n <span>No env vars match \"{searchQ}\"</span>\n </div>\n )}\n </div>\n );\n}\n\n// ─── Individual var row (extracted for reuse in group mode) ───────────────────\ntype VarRowProps = {\n v: EnvVarEntry;\n editing: string | null;\n editValue: string;\n isRevealed: boolean;\n onToggleReveal: (name: string) => void;\n onStartEdit: (v: EnvVarEntry) => void;\n onEditValueChange: (v: string) => void;\n onHandleUpdate: (name: string) => void;\n onCancelEdit: () => void;\n onDelete: (name: string) => void;\n saving: boolean;\n};\n\nfunction VarRow({ v, editing, editValue, isRevealed, onToggleReveal, onStartEdit, onEditValueChange, onHandleUpdate, onCancelEdit, onDelete, saving }: VarRowProps) {\n const isEditing = editing === v.name;\n return (\n <div className={cn('env-var-row', isEditing && 'env-var-row-editing')}>\n <span className=\"env-var-name mono\">{v.name}</span>\n {isEditing ? (\n <input\n className=\"input mono\"\n type=\"password\"\n style={{ flex: 1, minWidth: 200 }}\n value={editValue}\n onChange={(e) => onEditValueChange(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') onHandleUpdate(v.name);\n if (e.key === 'Escape') onCancelEdit();\n }}\n autoFocus\n />\n ) : (\n <span className={cn('env-var-value mono', !isRevealed && 'env-var-value-masked')}>\n {v.value}\n </span>\n )}\n <span className=\"env-var-source muted\">{v.source}</span>\n <div className=\"env-var-actions\">\n {!isEditing && (\n <button type=\"button\" className=\"icon-btn\" title={isRevealed ? 'Hide' : 'Reveal'} onClick={() => onToggleReveal(v.name)}>\n {isRevealed ? <EyeOff size={12} /> : <Eye size={12} />}\n </button>\n )}\n {isEditing ? (\n <>\n <Button variant=\"primary\" size=\"sm\" onClick={() => onHandleUpdate(v.name)} disabled={saving}>Save</Button>\n <Button variant=\"ghost\" size=\"sm\" onClick={onCancelEdit}>Cancel</Button>\n </>\n ) : (\n <>\n <button type=\"button\" className=\"icon-btn\" title=\"Edit\" onClick={() => onStartEdit(v)}>\n <Pencil size={12} />\n </button>\n <button type=\"button\" className=\"icon-btn icon-btn-danger\" title=\"Delete\" onClick={() => onDelete(v.name)}>\n <Trash2 size={12} />\n </button>\n </>\n )}\n </div>\n </div>\n );\n}\n","// src/web/views/settings/EnvVarsSection.tsx\nimport React from 'react';\nimport { KeyRound } from 'lucide-react';\nimport { Card, CardTitle, CardMeta } from '../../components/Card';\nimport { EnvVarManager } from '../../components/EnvVarManager';\n\nexport function EnvVarsSection() {\n return (\n <Card id=\"settings-env-vars\" data-section=\"env-vars\">\n <CardTitle><KeyRound size={14} /> Environment Variables</CardTitle>\n <CardMeta>Manage BIZAR_* env vars (API keys, secrets). Values are stored in <code>~/.config/bizar/env.json</code> (mode 0600).</CardMeta>\n <EnvVarManager />\n </Card>\n );\n}\n"],"file":"assets/EnvVarsSection-CW6fWKds.js"}
@@ -1 +0,0 @@
1
- import{r,j as e}from"./react-vendor-D3pra6w-.js";import{a as y,S as w,c as R}from"./mobile-layout-DSmtNPja.js";import{C as M,a as U,b as B,B as k,G,E as V,S as P,U as $,H as A,T as C,c as H}from"./EnvVarsSection-CW6fWKds.js";import{u as D}from"./Toast-WTxc3m2g.js";import{j as N,aX as I,aQ as T,$ as K,af as F,s as O,aC as J,y as E,R as Q,aE as W,J as X,e as q}from"./icons-B0AKnYI3.js";import"./vendor-Di7Cp-rw.js";import"./flow-BpU0-gK2.js";function Y(){const s=D(),[i,t]=r.useState(!0),[c,a]=r.useState(!1),[o,d]=r.useState(!1),[p,g]=r.useState(""),[u,x]=r.useState(""),[f,h]=r.useState(!1),[j,n]=r.useState(0);r.useEffect(()=>{let l=!1;return(async()=>{var S,L;try{const[z,v]=await Promise.all([y.get("/memory/config/global").catch(()=>null),y.get("/memory/status").catch(()=>null)]);if(l)return;z&&g(((L=(S=z.config)==null?void 0:S.git)==null?void 0:L.remoteUrl)||""),v&&(x(v.vaultRoot||""),h(!!v.initialized))}finally{l||t(!1)}})(),()=>{l=!0}},[j]);const b=async()=>{a(!0);try{await y.put("/memory/config/global",{git:{remoteUrl:p}}),s.success("Memory config saved."),n(l=>l+1)}catch(l){s.error(`Save failed: ${l.message}`)}finally{a(!1)}},m=async()=>{d(!0);try{await y.post("/memory/init",{}),s.success("Vault initialised."),n(l=>l+1)}catch(l){s.error(`Initialise failed: ${l.message}`)}finally{d(!1)}};return e.jsxs(M,{id:"settings-memory","data-section":"memory",children:[e.jsxs(U,{children:[e.jsx(N,{size:14})," Memory vault"]}),e.jsx(B,{children:"The vault lives at a default location on disk. Only the git remote URL needs to be configured to enable sync across machines."}),i?e.jsx("div",{className:"flex-center",style:{padding:24},children:e.jsx(w,{size:"sm"})}):e.jsxs("div",{className:"settings-fields",children:[e.jsxs("div",{className:"field",children:[e.jsxs("label",{className:"field-label",children:[e.jsx(I,{size:12,style:{display:"inline",marginRight:4}}),"Vault path"]}),e.jsxs("div",{className:"field-readonly mono","data-testid":"settings-memory-vault-path",style:{fontSize:12,padding:"6px 0"},children:[u||e.jsx("span",{className:"muted",children:"not initialised"}),f?null:e.jsxs(k,{variant:"ghost",size:"sm",onClick:m,disabled:o,style:{marginLeft:12},children:[o?e.jsx(T,{size:12,className:"memory-spin"}):e.jsx(I,{size:12}),"Initialise"]})]})]}),e.jsxs("div",{className:"field",children:[e.jsxs("label",{className:"field-label",htmlFor:"set-memory-git-remote",children:[e.jsx(K,{size:12,style:{display:"inline",marginRight:4}}),"Git remote URL"]}),e.jsx("input",{id:"set-memory-git-remote",className:"input mono",type:"text",value:p,onChange:l=>g(l.target.value),placeholder:"git@github.com:org/repo.git"}),e.jsx("span",{className:"field-hint",children:"Leave empty to use local-only mode (no git sync)."})]}),e.jsx("div",{style:{marginTop:12},children:e.jsxs(k,{variant:"primary",size:"sm",onClick:b,disabled:c,children:[c?e.jsx(T,{size:12,className:"memory-spin"}):e.jsx(F,{size:12}),"Save memory config"]})})]})]})}function Z({settings:s,onSettingsChange:i,autoSave:t}){const c=(o,d)=>{i==null||i(o,d),t==null||t(o,d)},a=o=>{i==null||i("ui",{...s.ui,...o}),t==null||t("ui",{...s.ui,...o})};return e.jsx(G,{settings:s,patchUi:a,patchTop:c,autoSave:t})}function _({settings:s,onSettingsChange:i,autoSave:t}){return e.jsxs("div",{style:{fontSize:13,color:"var(--text-dim)",padding:"8px 0"},children:[e.jsx("p",{style:{marginBottom:8},children:"Provider management is available on desktop."}),e.jsx("a",{href:"/?desktop=1#settings-providers",className:"mobile-btn mobile-btn-secondary",style:{display:"inline-block",fontSize:12,padding:"6px 12px"},children:"Open desktop to manage providers"})]})}function ee(){return e.jsx(V,{})}function se(){return e.jsx(Y,{})}function ie({settings:s,onSettingsChange:i,autoSave:t}){const c=(a,o)=>{i==null||i(a,o),t==null||t(a,o)};return e.jsx(P,{settings:s,patchTop:c})}function te(){return e.jsx($,{})}function ne({settings:s}){const[i,t]=r.useState(s),[c,a]=r.useState(!1);return e.jsx(A,{settings:i,setSettings:t,setDirty:a})}function ae(){return e.jsx(C,{})}function oe({settings:s}){const i=s.about||{version:"—",homepage:"https://github.com/DrB0rk/BizarHarness",license:"MIT"};return e.jsx(H,{about:i})}function fe({settings:s,onSettingsChange:i,autoSave:t,snapshot:c}){var x,f,h,j;const[a,o]=r.useState(""),[d,p]=r.useState(null),u=[{id:"general",title:"General",icon:O,description:"Theme, accent, layout",component:e.jsx(Z,{settings:s,onSettingsChange:i,autoSave:t})},{id:"providers",title:"AI Providers",icon:N,description:`${((x=c==null?void 0:c.providers)==null?void 0:x.length)??0} configured`,component:e.jsx(_,{settings:s,onSettingsChange:i,autoSave:t})},{id:"env-vars",title:"Environment Variables",icon:J,description:"API keys, secrets",component:e.jsx(ee,{})},{id:"memory",title:"Memory Vault",icon:N,description:"Git remote, LightRAG config",component:e.jsx(se,{})},{id:"system-llm",title:"System LLM",icon:E,description:((f=s.systemLlm)==null?void 0:f.model)||"not set",component:e.jsx(ie,{settings:s,onSettingsChange:i,autoSave:t})},{id:"updates",title:"Updates",icon:Q,description:"Check for updates",component:e.jsx(te,{})},{id:"headroom",title:"Headroom",icon:E,description:(h=s.headroom)!=null&&h.enabled?"Enabled":"Disabled",component:e.jsx(ne,{settings:s})},{id:"tailscale",title:"Tailscale",icon:W,description:"Tailscale serve & auth",component:e.jsx(ae,{})},{id:"about",title:"About",icon:X,description:`Bizar v${((j=s.about)==null?void 0:j.version)||"—"}`,component:e.jsx(oe,{settings:s})}].filter(n=>!a||n.title.toLowerCase().includes(a.toLowerCase())||n.description.toLowerCase().includes(a.toLowerCase()));return e.jsxs("div",{className:"mobile-settings",children:[e.jsxs("div",{className:"mobile-settings-search",children:[e.jsx(le,{}),e.jsx("input",{value:a,onChange:n=>o(n.target.value),placeholder:"Search settings...","aria-label":"Search settings"})]}),e.jsxs("div",{className:"mobile-settings-list",children:[u.map(n=>{const b=n.icon,m=d===n.id;return e.jsxs("div",{className:R("mobile-settings-item",m&&"is-expanded"),children:[e.jsxs("button",{className:"mobile-settings-item-head",onClick:()=>p(m?null:n.id),"aria-expanded":m,"aria-controls":`mobile-settings-body-${n.id}`,children:[e.jsx("div",{className:"mobile-settings-item-icon",children:e.jsx(b,{size:18})}),e.jsxs("div",{className:"mobile-settings-item-text",children:[e.jsx("div",{className:"mobile-settings-item-title",children:n.title}),e.jsx("div",{className:"mobile-settings-item-desc",children:n.description})]}),e.jsx(q,{size:16,className:R("mobile-settings-item-chevron",m&&"is-rotated")})]}),m&&e.jsx("div",{id:`mobile-settings-body-${n.id}`,className:"mobile-settings-item-body",children:n.component})]},n.id)}),u.length===0&&e.jsxs("div",{className:"mobile-settings-empty",children:['No settings match "',a,'"']})]})]})}function le(){return e.jsxs("svg",{width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[e.jsx("circle",{cx:"11",cy:"11",r:"8"}),e.jsx("path",{d:"m21 21-4.35-4.35"})]})}export{fe as MobileSettings};