settings: move the theme/motion panel into @hive/shared, add motion to agent
Review feedback on this PR (mara): "i think the component should be shared. motion setting is missing." Both addressed: - `settings-storage.ts` (generic localStorage hook), `theme-apply.ts`, `motion-apply.ts`, and `SettingsMenu.tsx`/`.css` all move from swarm-ui's `lib/`/`shell/` into `@hive/shared/src/settings/` — agent's previous local copies are deleted outright rather than kept as a second implementation. One component, `Badge` trigger everywhere (already used elsewhere in swarm-ui, so not a new visual language there either) — storage keys stay caller-owned (`themeKey`/ `motionKey` props + matching `useApplyThemeOverride`/ `useApplyMotionOverride` calls at each package's single mount point) so agent and swarm-ui keep fully independent, non-colliding persisted settings. - Agent's settings menu now includes the motion row, matching swarm-ui's. No animation in the agent package is gated behind `data-motion` yet — same as when swarm-ui first built this plumbing ahead of having a consumer — so it's currently inert there, ready for whenever agent grows a motion-guarded animation. - swarm-ui's own theme default flips to `'dark'` as part of this move (`theme-apply.ts`'s new default), superseding PR #3715 — that PR becomes redundant once this lands and will be closed rather than merged, to avoid the two colliding on the same file. Verified end-to-end with real screenshots on both pages: shared component renders identically (Badge trigger, theme+motion rows, dark default) on agent's mock server and a static rebuild of swarm-ui's dist.
This commit is contained in:
parent
b28e8f1c4e
commit
9720bdfad0
16 changed files with 230 additions and 487 deletions
|
|
@ -6,9 +6,11 @@
|
|||
// optional polish.
|
||||
import { useEffect, useRef, useState } from 'preact/hooks';
|
||||
import type { BadgeTone } from '@hive/shared/badge.js';
|
||||
import { SettingsMenu } from '@hive/shared/settings-menu.js';
|
||||
import { useApplyThemeOverride } from '@hive/shared/theme-apply.js';
|
||||
import { useApplyMotionOverride } from '@hive/shared/motion-apply.js';
|
||||
import { Header } from './components/Header.js';
|
||||
import { MetaNav } from './components/MetaNav.js';
|
||||
import { SettingsMenu } from './components/SettingsMenu.js';
|
||||
import { StatusChips } from './components/StatusChips.js';
|
||||
import { LiveStream, type LiveStreamHandle } from './components/LiveStream.js';
|
||||
import { HeaderPill } from './components/HeaderPill.js';
|
||||
|
|
@ -23,11 +25,17 @@ import { fmtAge, fmtTokens } from './lib/format.js';
|
|||
import { resolveDashboardBase } from './lib/dashboardBase.js';
|
||||
import { submitPauseResume } from './lib/pauseAction.js';
|
||||
import { postModel, postEffort } from './lib/modelEffort.js';
|
||||
import { useApplyThemeOverride } from './lib/theme-apply.js';
|
||||
import type { TokenUsage } from './types.js';
|
||||
|
||||
type OpenPanel = 'inbox' | 'todos' | null;
|
||||
|
||||
// Storage keys this page owns for `@hive/shared`'s settings mechanism —
|
||||
// kept here, not derived, so `useApplyThemeOverride`/`useApplyMotionOverride`
|
||||
// (mounted once below) and `<SettingsMenu>` (in `pills`, further down)
|
||||
// always agree on which browser-local key they're reading/writing.
|
||||
const THEME_KEY = 'agent:theme-override';
|
||||
const MOTION_KEY = 'agent:motion-override';
|
||||
|
||||
const ALIVE_LABELS: Record<string, { glyph: string; text: string; tone: BadgeTone }> = {
|
||||
online: { glyph: '●', text: 'alive', tone: 'positive' },
|
||||
rate_limited: { glyph: '⊘', text: 'rate limited', tone: 'warning' },
|
||||
|
|
@ -55,7 +63,8 @@ function tokenTotal(u: TokenUsage | null): number | null {
|
|||
}
|
||||
|
||||
export function Root() {
|
||||
useApplyThemeOverride();
|
||||
useApplyThemeOverride(THEME_KEY);
|
||||
useApplyMotionOverride(MOTION_KEY);
|
||||
const { state, refresh } = useAgentState();
|
||||
const { todos, refresh: refreshTodos } = useTodos();
|
||||
const [openPanel, setOpenPanel] = useState<OpenPanel>(null);
|
||||
|
|
@ -103,7 +112,7 @@ export function Root() {
|
|||
dashboardBase={resolveDashboardBase(state.dashboard_port)}
|
||||
/>
|
||||
) : null}
|
||||
<SettingsMenu />
|
||||
<SettingsMenu themeKey={THEME_KEY} motionKey={MOTION_KEY} />
|
||||
</>
|
||||
);
|
||||
// Kept mounted regardless of `openPanel` (open/closed toggles just the
|
||||
|
|
|
|||
Loading…
Reference in a new issue