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
|
|
@ -30,12 +30,20 @@ import { useEffect, useRef, useState } from 'preact/hooks';
|
|||
import type { ComponentChildren } from 'preact';
|
||||
import { Link, useLocation } from 'wouter-preact';
|
||||
import { LinksMenu } from './LinksMenu.js';
|
||||
import { SettingsMenu } from './SettingsMenu.js';
|
||||
import { UserMenu } from './UserMenu.js';
|
||||
import { useApplyThemeOverride } from '../lib/theme-apply.js';
|
||||
import { useApplyMotionOverride } from '../lib/motion-apply.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 './Shell.css';
|
||||
|
||||
// Storage keys this app owns for `@hive/shared`'s settings mechanism —
|
||||
// kept here, not derived, so the two mount points below (the
|
||||
// `useApply*` effects and `<SettingsMenu>`) always agree on which
|
||||
// browser-local key they're reading/writing. Theme defaults to `'dark'`,
|
||||
// not `'system'` — see `@hive/shared/theme-apply.js`'s file comment.
|
||||
const THEME_KEY = 'swarm-ui:theme-override';
|
||||
const MOTION_KEY = 'swarm-ui:motion-override';
|
||||
|
||||
const NAV_ITEMS: { href: string; label: string; accent: string }[] = [
|
||||
{ href: '/', label: 'hives', accent: 'var(--purple)' },
|
||||
{ href: '/agents', label: 'agents', accent: 'var(--green)' },
|
||||
|
|
@ -68,7 +76,7 @@ const HOP_MS = 140;
|
|||
// multi-step `setTimeout` sequence has no CSS equivalent to hook, so it
|
||||
// checks the same two sources directly. `data-motion` takes precedence
|
||||
// over the OS preference either direction (reduce *or* allow), same
|
||||
// override semantics `lib/motion-apply.ts` establishes.
|
||||
// override semantics `@hive/shared/motion-apply.js` establishes.
|
||||
function prefersReducedMotion(): boolean {
|
||||
const override = document.documentElement.dataset.motion;
|
||||
if (override === 'reduce') return true;
|
||||
|
|
@ -127,11 +135,12 @@ export function Shell({ children }: { children: ComponentChildren }) {
|
|||
// Applied once here, not inside `SettingsMenu` — every route mounts
|
||||
// through this one `<Shell>`, so the override takes effect regardless
|
||||
// of which page is showing or whether the menu's ever been opened,
|
||||
// and `useLocalSetting`'s same-tab subscription (settings-storage.ts)
|
||||
// means `SettingsMenu` changing the stored value re-runs these
|
||||
// effects without either component needing a reference to the other.
|
||||
useApplyThemeOverride();
|
||||
useApplyMotionOverride();
|
||||
// and `useLocalSetting`'s same-tab subscription
|
||||
// (`@hive/shared/settings-storage.js`) means `SettingsMenu` changing
|
||||
// the stored value re-runs these effects without either component
|
||||
// needing a reference to the other.
|
||||
useApplyThemeOverride(THEME_KEY);
|
||||
useApplyMotionOverride(MOTION_KEY);
|
||||
|
||||
// Measures nav item `index`'s rect relative to `.shell-nav`.
|
||||
// `getBoundingClientRect()` on both elements at the same tick keeps
|
||||
|
|
@ -282,7 +291,7 @@ export function Shell({ children }: { children: ComponentChildren }) {
|
|||
`margin-left: auto` split the available space between them
|
||||
instead of sitting flush together at the right edge. */}
|
||||
<div class="shell-header-actions">
|
||||
<SettingsMenu />
|
||||
<SettingsMenu themeKey={THEME_KEY} motionKey={MOTION_KEY} />
|
||||
<LinksMenu />
|
||||
<UserMenu />
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue