@exegia/corpora-ui 0.22.0 → 0.23.0

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 (40) hide show
  1. package/dist-lib/components/blocks/auth/__tests__/auth-flow-block.test.d.ts +1 -0
  2. package/dist-lib/components/blocks/auth/auth-flow-block.d.ts +123 -0
  3. package/dist-lib/components/blocks/scaffold/__tests__/scaffold-atom.test.d.ts +1 -0
  4. package/dist-lib/components/blocks/scaffold/index.d.ts +2 -0
  5. package/dist-lib/components/blocks/scaffold/scaffold-atom.d.ts +97 -0
  6. package/dist-lib/components/blocks/scaffold/scaffold-context.d.ts +3 -1
  7. package/dist-lib/components/blocks/scaffold/scaffold-root.d.ts +12 -3
  8. package/dist-lib/components/blocks/scaffold/type.d.ts +58 -18
  9. package/dist-lib/components/blocks/scaffold/use-scaffold-state.d.ts +26 -0
  10. package/dist-lib/components/blocks/scaffold/use-scaffold.d.ts +7 -1
  11. package/dist-lib/components/blocks/scaffold/utils.d.ts +11 -1
  12. package/dist-lib/components/ui/switch.d.ts +4 -0
  13. package/dist-lib/index.d.ts +1 -0
  14. package/dist-lib/index.js +1383 -1196
  15. package/dist-lib/index.js.map +1 -1
  16. package/package.json +1 -1
  17. package/src/components/beste/piece/browser-frame.tsx +1 -1
  18. package/src/components/beste/piece/utils.ts +4 -4
  19. package/src/components/blocks/auth/__tests__/auth-flow-block.test.tsx +183 -0
  20. package/src/components/blocks/auth/auth-flow-block.tsx +296 -0
  21. package/src/components/blocks/scaffold/__tests__/scaffold-atom.test.tsx +210 -0
  22. package/src/components/blocks/scaffold/index.ts +15 -0
  23. package/src/components/blocks/scaffold/scaffold-actions.tsx +5 -1
  24. package/src/components/blocks/scaffold/scaffold-atom.ts +383 -0
  25. package/src/components/blocks/scaffold/scaffold-canvas.tsx +12 -4
  26. package/src/components/blocks/scaffold/scaffold-context.ts +3 -1
  27. package/src/components/blocks/scaffold/scaffold-inspector.tsx +9 -2
  28. package/src/components/blocks/scaffold/scaffold-panel.tsx +9 -5
  29. package/src/components/blocks/scaffold/scaffold-root.tsx +64 -32
  30. package/src/components/blocks/scaffold/scaffold-tab.tsx +13 -9
  31. package/src/components/blocks/scaffold/type.ts +64 -18
  32. package/src/components/blocks/scaffold/use-scaffold-state.ts +58 -0
  33. package/src/components/blocks/scaffold/use-scaffold.ts +30 -19
  34. package/src/components/blocks/scaffold/utils.ts +45 -1
  35. package/src/components/docs/component-preview.tsx +7 -2
  36. package/src/components/docs/demo-controls.tsx +32 -23
  37. package/src/components/ui/switch.tsx +30 -0
  38. package/src/index.ts +1 -0
  39. package/dist-lib/components/blocks/scaffold/use-panel-visibility.d.ts +0 -19
  40. package/src/components/blocks/scaffold/use-panel-visibility.ts +0 -180
@@ -0,0 +1,123 @@
1
+ import { SocialProvider } from '../../composed/social-providers';
2
+ import { AuthAccent } from '../../../lib/auth-accent';
3
+ import { CodeAuthBlockProps } from './code-auth-block';
4
+ import { ForgotPasswordBlockProps } from './forgot-password-block';
5
+ import { LoginBlockProps } from './login-block';
6
+ import { OnboardingBlockProps, OnboardingStepConfig, OnboardingValue } from './onboarding-block';
7
+ import { SignupBlockProps } from './signup-block';
8
+ import { UpdatePasswordBlockProps } from './update-password-block';
9
+ import { AuthFlowId, AuthFlowState, AuthFlowStep, AuthUser, BeginAuthVerificationOptions } from './auth-state-type';
10
+ import * as React from "react";
11
+ /**
12
+ * Where the flow goes after a step callback resolves. Returned from every
13
+ * `AuthFlowBlock` handler:
14
+ *
15
+ * - `{ user }` — the attempt signed someone in: mark the flow complete AND
16
+ * sign the session in (one atomic write, `completeAuthFlowAtom`).
17
+ * - `{ verify }` — a code went out: record the identifier and move to the
18
+ * verification step (`beginAuthVerificationAtom`).
19
+ * - `{ step }` — plain navigation with a clean slate (`goToAuthStepAtom`).
20
+ * - `void` — stay put; the block renders its own success state.
21
+ *
22
+ * Rejections are NOT handled here — they propagate back into the block,
23
+ * which owns its transient error/shake state (see `react/CLAUDE.md`,
24
+ * "Third implementation: auth").
25
+ */
26
+ export type AuthFlowDirective = {
27
+ user: AuthUser;
28
+ } | {
29
+ verify: BeginAuthVerificationOptions;
30
+ } | {
31
+ step: AuthFlowStep;
32
+ } | void;
33
+ export type AuthFlowHandler<Data = void> = (data: Data) => Promise<AuthFlowDirective> | AuthFlowDirective;
34
+ /** Per-step prop overrides, merged over the orchestrator's wiring — spread
35
+ * last, so an app can restyle a block or unhook a default navigation link
36
+ * (`{ login: { onSignup: undefined } }` removes the sign-up hand-off). */
37
+ export interface AuthFlowStepOverrides {
38
+ login?: Partial<LoginBlockProps>;
39
+ signup?: Partial<SignupBlockProps>;
40
+ "verify-code"?: Partial<CodeAuthBlockProps>;
41
+ "forgot-password"?: Partial<ForgotPasswordBlockProps>;
42
+ "update-password"?: Partial<UpdatePasswordBlockProps>;
43
+ onboarding?: Partial<OnboardingBlockProps>;
44
+ }
45
+ export interface AuthFlowBlockProps {
46
+ /** Which flow instance to orchestrate. The default flow unless a re-auth
47
+ * modal or a second surface needs its own. */
48
+ flowId?: AuthFlowId;
49
+ /** Brand mark handed to every step's card. */
50
+ logo?: React.ReactNode;
51
+ /** Brand accent handed to every step's card. */
52
+ accent?: AuthAccent;
53
+ /** Social providers offered on the login and signup steps. */
54
+ providers?: SocialProvider[];
55
+ /** The login attempt. Resolve with a directive; reject to show the error
56
+ * in the block. */
57
+ onLogin?: AuthFlowHandler<{
58
+ email: string;
59
+ password: string;
60
+ remember: boolean;
61
+ }>;
62
+ /** The signup attempt. */
63
+ onSignup?: AuthFlowHandler<{
64
+ name: string;
65
+ email: string;
66
+ password: string;
67
+ }>;
68
+ /** A social provider chosen on the login or signup step. */
69
+ onProviderSelect?: AuthFlowHandler<SocialProvider>;
70
+ /** The forgot-password request. Resolving without a directive stays on the
71
+ * step (the block shows its own "link sent" state). */
72
+ onRequestReset?: AuthFlowHandler<{
73
+ email: string;
74
+ }>;
75
+ /** The code entered on the verification step. */
76
+ onVerifyCode?: AuthFlowHandler<string>;
77
+ /** "Resend code" on the verification step. */
78
+ onResendCode?: AuthFlowHandler;
79
+ /** The update-password submit. */
80
+ onUpdatePassword?: AuthFlowHandler<{
81
+ password: string;
82
+ }>;
83
+ /** Onboarding finished, with the merged profile. */
84
+ onOnboardingComplete?: AuthFlowHandler<Record<string, OnboardingValue>>;
85
+ /** Declared onboarding steps, handed to `OnboardingBlock`. */
86
+ onboardingSteps?: OnboardingStepConfig[];
87
+ /** Per-step prop overrides, merged over the orchestrator's wiring. */
88
+ steps?: AuthFlowStepOverrides;
89
+ /** Replace any step's UI entirely; return `undefined` to keep the default
90
+ * for that step. Receives the flow state for destination copy etc. */
91
+ renderStep?: (step: AuthFlowStep, flow: AuthFlowState) => React.ReactNode | undefined;
92
+ /** Replaces the whole default success card. */
93
+ success?: React.ReactNode;
94
+ /** Card title of the default success step. */
95
+ successTitle?: string;
96
+ /** Body under the default success step's "You're signed in" check. */
97
+ successDescription?: string;
98
+ className?: string;
99
+ }
100
+ /**
101
+ * Renders the right auth block for the flow's current step with the store
102
+ * wiring built in — the switchboard every host app was hand-rolling around
103
+ * `useAuthFlow`. Navigation links between steps (login ↔ signup, forgot
104
+ * password, back from verification) are pre-wired to `goToStep`; each
105
+ * submit-shaped prop awaits your handler and applies the returned
106
+ * {@link AuthFlowDirective}.
107
+ *
108
+ * The blocks themselves stay untouched: passwords, codes and field drafts
109
+ * live in their local state, and a rejected handler renders as the block's
110
+ * own error — the orchestrator never mirrors transients into the store.
111
+ * Needs `ExegiaProvider` above it, like every stateful block.
112
+ *
113
+ * ```tsx
114
+ * <AuthFlowBlock
115
+ * onLogin={async ({ email, password }) => {
116
+ * const outcome = await api.signIn(email, password)
117
+ * return outcome.mfa ? { verify: { identifier: email } } : { user: outcome.user }
118
+ * }}
119
+ * onVerifyCode={async (code) => ({ user: await api.verify(code) })}
120
+ * />
121
+ * ```
122
+ */
123
+ export declare function AuthFlowBlock({ flowId, logo, accent, providers, onLogin, onSignup, onProviderSelect, onRequestReset, onVerifyCode, onResendCode, onUpdatePassword, onOnboardingComplete, onboardingSteps, steps, renderStep, success, successTitle, successDescription, className, }: AuthFlowBlockProps): React.ReactElement;
@@ -11,6 +11,8 @@ import { ScaffoldTab } from './scaffold-tab';
11
11
  import { ScaffoldSubPanel } from './scaffold-sub-panel.tsx';
12
12
  export { useScaffoldContext } from './scaffold-context';
13
13
  export { useScaffold } from './use-scaffold';
14
+ export { useScaffoldActions, useScaffoldState } from './use-scaffold-state';
15
+ export { removeScaffoldInstance, resetScaffoldAtom, scaffoldHiddenPanelIdsAtom, scaffoldHoveredPanelIdAtom, scaffoldInspectorOpenAtom, scaffoldPanelCapacityAtom, scaffoldPanelDimmedAtom, scaffoldPanelHiddenAtom, scaffoldStateAtom, setScaffoldInspectorOpenAtom, toggleScaffoldInspectorAtom, toggleScaffoldPanelAtom, } from './scaffold-atom';
14
16
  export type * from './type';
15
17
  export * from './constants';
16
18
  export { PanelCloseButton, PanelMenuButton, ScaffoldActions, ScaffoldCanvas, ScaffoldInspector, ScaffoldMain, ScaffoldPanel, ScaffoldRoot, ScaffoldSidebar, ScaffoldTab, };
@@ -0,0 +1,97 @@
1
+ import { Atom } from 'jotai';
2
+ import { ScaffoldConfig, ScaffoldHandlers, ScaffoldInstanceId, ScaffoldPanelVisibility, ScaffoldState } from './type';
3
+ type Family<AtomType> = ((id: ScaffoldInstanceId) => AtomType) & {
4
+ remove: (id: ScaffoldInstanceId) => void;
5
+ };
6
+ export declare const scaffoldInspectorOpenAtom: Family<import('jotai').PrimitiveAtom<boolean> & {
7
+ init: boolean;
8
+ }>;
9
+ /** @internal The canvas's ordered panel ids, as registered each render. */
10
+ export declare const scaffoldPanelIdsAtom: Family<import('jotai').PrimitiveAtom<readonly string[]> & {
11
+ init: readonly string[];
12
+ }>;
13
+ /** @internal The capacity derived from the canvas's measured width — the
14
+ * derived number is stored, not the raw width, so resize storms only touch
15
+ * the store when a panel actually gains or loses room. */
16
+ export declare const scaffoldMeasuredCapacityAtom: Family<import('jotai').PrimitiveAtom<number | null> & {
17
+ init: number | null;
18
+ }>;
19
+ /** @internal The three visibility lists, settled as one value. */
20
+ export declare const scaffoldVisibilityAtom: Family<import('jotai').PrimitiveAtom<ScaffoldPanelVisibility> & {
21
+ init: ScaffoldPanelVisibility;
22
+ }>;
23
+ export declare const scaffoldHoveredPanelIdAtom: Family<import('jotai').PrimitiveAtom<string | null> & {
24
+ init: string | null;
25
+ }>;
26
+ /** @internal */
27
+ export declare const scaffoldConfigAtom: Family<import('jotai').PrimitiveAtom<ScaffoldConfig> & {
28
+ init: ScaffoldConfig;
29
+ }>;
30
+ /** @internal */
31
+ export declare const scaffoldHandlersAtom: Family<import('jotai').PrimitiveAtom<ScaffoldHandlers> & {
32
+ init: ScaffoldHandlers;
33
+ }>;
34
+ /** How many panels fit side by side; the cap while the canvas is unmeasured. */
35
+ export declare const scaffoldPanelCapacityAtom: Family<Atom<number>>;
36
+ /** Ids of panels currently hidden (auto + user), for tabs to reflect. */
37
+ export declare const scaffoldHiddenPanelIdsAtom: Family<Atom<readonly string[]>>;
38
+ /** Whether one panel is hidden. A tab subscribes here and sits still while
39
+ * its siblings toggle. Empty `panelId` (a tab without one) reads false. */
40
+ export declare const scaffoldPanelHiddenAtom: (id: ScaffoldInstanceId, panelId: string) => Atom<boolean>;
41
+ /** Whether one panel fades back while another panel's tab holds the
42
+ * spotlight. Moving the hover between two tabs re-renders those panels
43
+ * only — everyone else's boolean holds. */
44
+ export declare const scaffoldPanelDimmedAtom: (id: ScaffoldInstanceId, panelId: string) => Atom<boolean>;
45
+ /** The whole state of one scaffold. This changes on every hover move, so a
46
+ * component that reads one field should subscribe to that field's atom
47
+ * instead: `useAtomValue(scaffoldInspectorOpenAtom("workspace"))`. */
48
+ export declare const scaffoldStateAtom: Family<Atom<ScaffoldState>>;
49
+ /** Reports through `onInspectorOpenChange` always; writes the store only
50
+ * when no `inspectorOpen` prop controls the root — the prop is truth then. */
51
+ export declare const setScaffoldInspectorOpenAtom: Family<import('jotai').WritableAtom<null, [open: boolean], void> & {
52
+ init: null;
53
+ }>;
54
+ export declare const toggleScaffoldInspectorAtom: Family<import('jotai').WritableAtom<null, [], void> & {
55
+ init: null;
56
+ }>;
57
+ /** Show/hide an id'd panel. Showing past capacity auto-hides the
58
+ * least-recently-activated visible panel; the last visible one never hides. */
59
+ export declare const toggleScaffoldPanelAtom: Family<import('jotai').WritableAtom<null, [panelId: string], void> & {
60
+ init: null;
61
+ }>;
62
+ /** @internal Tabs report pointer enter/leave on their label. Leaving clears
63
+ * only its own id — a tab that unmounts mid-hover must not wipe out a hover
64
+ * the pointer has already moved on to. */
65
+ export declare const setScaffoldPanelHoveredAtom: Family<import('jotai').WritableAtom<null, [panelId: string, hovered: boolean], void> & {
66
+ init: null;
67
+ }>;
68
+ /** @internal Canvas reports its ordered panel ids each render. Inert when
69
+ * the ids did not move; otherwise the visibility settles in the same write. */
70
+ export declare const registerScaffoldPanelIdsAtom: Family<import('jotai').WritableAtom<null, [ids: readonly string[]], void> & {
71
+ init: null;
72
+ }>;
73
+ /** @internal Canvas reports its measured width. Only the derived capacity is
74
+ * stored, so resize storms settle to at most one visibility change. */
75
+ export declare const measureScaffoldCanvasAtom: Family<import('jotai').WritableAtom<null, [width: number], void> & {
76
+ init: null;
77
+ }>;
78
+ /** @internal Projection of the mounted root's controlled props. */
79
+ export declare const projectScaffoldPropsAtom: Family<import('jotai').WritableAtom<null, [config: ScaffoldConfig, inspectorOpen: boolean | undefined], void> & {
80
+ init: null;
81
+ }>;
82
+ /** @internal */
83
+ export declare const setScaffoldHandlersAtom: Family<import('jotai').WritableAtom<null, [handlers: ScaffoldHandlers], void> & {
84
+ init: null;
85
+ }>;
86
+ /** @internal Seed the uncontrolled inspector on mount without firing
87
+ * `onInspectorOpenChange`. */
88
+ export declare const seedScaffoldInspectorAtom: Family<import('jotai').WritableAtom<null, [open: boolean], void> & {
89
+ init: null;
90
+ }>;
91
+ export declare const resetScaffoldAtom: Family<import('jotai').WritableAtom<null, [], void> & {
92
+ init: null;
93
+ }>;
94
+ /** Forget a scaffold entirely — every family drops the key so the store can
95
+ * release its state. Call on teardown of a named instance. */
96
+ export declare function removeScaffoldInstance(id: ScaffoldInstanceId): void;
97
+ export {};
@@ -1,5 +1,7 @@
1
1
  import { ScaffoldContextValue } from './type';
2
2
  import * as React from "react";
3
3
  export declare const ScaffoldContext: React.Context<ScaffoldContextValue | null>;
4
- /** Read the scaffold's shared state; must run under `Scaffold.Root`. */
4
+ /** Read the scaffold's identity (`scaffoldId`, `inspectorWidth`); must run
5
+ * under `Scaffold.Root`. State itself lives in the store — subscribe to the
6
+ * atoms, or reach them by id through `useScaffoldState` / `useScaffoldActions`. */
5
7
  export declare function useScaffoldContext(): ScaffoldContextValue;
@@ -2,7 +2,16 @@ import { ScaffoldRootProps } from './type';
2
2
  import * as React from "react";
3
3
  /**
4
4
  * The scaffold's viewport: desktop backdrop + horizontal row of rail and
5
- * main region, and the provider for the shared inspector state. Fills its
6
- * container — size it from the outside (e.g. `h-svh` for a full page).
5
+ * main region, and the binding between this instance's props and its slice
6
+ * of the store. Fills its container — size it from the outside (e.g.
7
+ * `h-svh` for a full page).
8
+ *
9
+ * State lives in the store under `scaffoldId`; the context carries only the
10
+ * id and the drawer width, so its value never changes while the scaffold is
11
+ * mounted and parts subscribe to exactly the atoms they render from. A
12
+ * controlled `inspectorOpen` stays the source of truth: it is projected
13
+ * one-way into the store (so remote readers see current data), and the
14
+ * inspector actions report through `onInspectorOpenChange` instead of
15
+ * writing.
7
16
  */
8
- export declare function ScaffoldRoot({ inspectorOpen: inspectorOpenProp, defaultInspectorOpen, onInspectorOpenChange, inspectorWidth, className, children, ...rest }: ScaffoldRootProps): React.ReactElement;
17
+ export declare function ScaffoldRoot({ scaffoldId: scaffoldIdProp, inspectorOpen: inspectorOpenProp, defaultInspectorOpen, onInspectorOpenChange, inspectorWidth, className, children, ...rest }: ScaffoldRootProps): React.ReactElement;
@@ -1,35 +1,69 @@
1
1
  import { default as React, ComponentProps, ReactElement, ReactNode } from 'react';
2
- /** Inspector state shared by every scaffold part through ScaffoldContext. */
2
+ /** Key of one scaffold's slice of the store. */
3
+ export type ScaffoldInstanceId = string;
4
+ /** The identity every scaffold part shares through ScaffoldContext. State
5
+ * itself lives in the store — parts subscribe to the atoms they render from,
6
+ * so this value never changes identity while the scaffold is mounted. */
3
7
  export interface ScaffoldContextValue {
4
- /** Whether the inspector drawer is currently shown. */
5
- inspectorOpen: boolean;
8
+ scaffoldId: ScaffoldInstanceId;
6
9
  /** Drawer width in px — Actions reads it to slide out of the drawer's way. */
7
10
  inspectorWidth: number;
8
- setInspectorOpen: (open: boolean) => void;
9
- toggleInspector: () => void;
11
+ }
12
+ /** One scaffold's whole state, as `useScaffoldState` returns it. A component
13
+ * that watches one field should subscribe to that field's atom instead. */
14
+ export interface ScaffoldState {
15
+ /** Whether the inspector drawer is currently shown. */
16
+ inspectorOpen: boolean;
10
17
  /** How many panels currently fit side by side, from the measured canvas
11
18
  * width at `SCAFFOLD_PANEL_MIN_WIDTH` per panel (1..`SCAFFOLD_PANEL_CAPACITY`). */
12
19
  panelCapacity: number;
13
20
  /** Ids of id'd panels the scaffold is currently hiding — auto-hidden by
14
21
  * capacity pressure or toggled away from a tab. */
15
22
  hiddenPanelIds: readonly string[];
16
- isPanelHidden: (id: string) => boolean;
17
- /** Show/hide an id'd panel. Showing past capacity auto-hides the
18
- * least-recently-activated visible panel; the last visible one never hides. */
19
- togglePanelVisibility: (id: string) => void;
20
23
  /** Id of the panel whose tab the pointer is resting on — the canvas
21
24
  * spotlights it by fading every other id'd panel to 0.35 opacity. */
22
25
  hoveredPanelId: string | null;
23
- /** @internal Tabs report pointer enter/leave on their label. */
24
- setPanelHovered: (id: string, hovered: boolean) => void;
25
- /** @internal Canvas reports its ordered panel ids. */
26
- registerPanelIds: (ids: readonly string[]) => void;
27
- /** @internal Canvas reports its measured width. */
28
- setCanvasWidth: (width: number) => void;
26
+ }
27
+ /** Drive a scaffold by id from anywhere, as `useScaffoldActions` returns it. */
28
+ export interface ScaffoldStateActions {
29
+ setInspectorOpen: (open: boolean) => void;
30
+ toggleInspector: () => void;
31
+ /** Show/hide an id'd panel. Showing past capacity auto-hides the
32
+ * least-recently-activated visible panel; the last visible one never hides. */
33
+ togglePanel: (panelId: string) => void;
34
+ reset: () => void;
35
+ }
36
+ /** @internal Which props currently control the scaffold — write gates for
37
+ * the action atoms, so the store never overwrites a prop. */
38
+ export interface ScaffoldConfig {
39
+ controlsInspector: boolean;
40
+ }
41
+ /** @internal The mounted root's callbacks, published into the store so
42
+ * action atoms can report changes wherever they were fired from. */
43
+ export interface ScaffoldHandlers {
44
+ onInspectorOpenChange?: (open: boolean) => void;
45
+ }
46
+ /** @internal Bookkeeping for responsive panel hiding.
47
+ * - `visibleOrder`: visible panel ids, least-recently-activated first — the
48
+ * front is what capacity pressure evicts next.
49
+ * - `autoHidden`: panels the scaffold hid because the canvas shrank, most
50
+ * recent last. They return on their own when room comes back.
51
+ * - `userHidden`: panels hidden by a tab press. They stay hidden until the
52
+ * tab is pressed again, however wide the canvas grows. */
53
+ export interface ScaffoldPanelVisibility {
54
+ visibleOrder: string[];
55
+ autoHidden: string[];
56
+ userHidden: string[];
29
57
  }
30
58
  export interface ScaffoldRootProps extends ComponentProps<"div"> {
31
- /** Controlled inspector state — pair with `onInspectorOpenChange`, or
32
- * spread `useScaffold().providerProps` instead of wiring by hand. */
59
+ /** Names this scaffold's slice of the store, so `useScaffoldState` /
60
+ * `useScaffoldActions` can drive it by id and its state outlives the
61
+ * component. Omitted, the root keys off `useId` and drops its state on
62
+ * unmount. `useScaffold().providerProps` carries one. */
63
+ scaffoldId?: ScaffoldInstanceId;
64
+ /** Controlled inspector state — pair with `onInspectorOpenChange`. The
65
+ * prop stays the source of truth: store writes are gated off, and actions
66
+ * report through the callback instead. */
33
67
  inspectorOpen?: boolean;
34
68
  /** Initial inspector state when uncontrolled. Closed by default. */
35
69
  defaultInspectorOpen?: boolean;
@@ -148,16 +182,22 @@ export interface PanelMenuButtonProps extends PanelFloatingButtonProps {
148
182
  swapped?: boolean;
149
183
  }
150
184
  export interface UseScaffoldOptions {
185
+ /** Names the scaffold's slice of the store. Omitted, the hook generates
186
+ * one and drops its state on unmount. */
187
+ scaffoldId?: ScaffoldInstanceId;
151
188
  /** Initial inspector state. Closed by default. */
152
189
  defaultInspectorOpen?: boolean;
153
190
  /** Fires on every inspector open/close. */
154
191
  onInspectorChange?: (open: boolean) => void;
155
192
  }
156
193
  export interface ScaffoldControls {
194
+ /** The id the hook and the root share — hand it to `useScaffoldState` /
195
+ * `useScaffoldActions` to drive the scaffold from elsewhere. */
196
+ scaffoldId: ScaffoldInstanceId;
157
197
  inspectorOpen: boolean;
158
198
  setInspectorOpen: (open: boolean) => void;
159
199
  toggleInspector: () => void;
160
200
  /** Spread onto Scaffold.Root. */
161
- providerProps: Pick<ScaffoldRootProps, "inspectorOpen" | "onInspectorOpenChange">;
201
+ providerProps: Pick<ScaffoldRootProps, "scaffoldId" | "defaultInspectorOpen" | "onInspectorOpenChange">;
162
202
  }
163
203
  export {};
@@ -0,0 +1,26 @@
1
+ import { ScaffoldInstanceId, ScaffoldState, ScaffoldStateActions } from './type';
2
+ /**
3
+ * Read the scaffold registered under `scaffoldId` from anywhere below
4
+ * `ExegiaProvider` — no controller, no props, no provider of its own.
5
+ *
6
+ * ```tsx
7
+ * const { inspectorOpen, hiddenPanelIds } = useScaffoldState("workspace")
8
+ * ```
9
+ *
10
+ * This returns the whole state object, so the caller re-renders on every
11
+ * change, hovers included. A component that reads one field should subscribe
12
+ * to that field's atom instead:
13
+ * `useAtomValue(scaffoldInspectorOpenAtom("workspace"))`.
14
+ */
15
+ export declare function useScaffoldState(scaffoldId: ScaffoldInstanceId): ScaffoldState;
16
+ /**
17
+ * Drive the scaffold registered under `scaffoldId` from anywhere. Writes
18
+ * only — the caller never re-renders when the scaffold changes, so this is
19
+ * what a command palette or a keyboard shortcut should reach for.
20
+ *
21
+ * ```tsx
22
+ * const scaffold = useScaffoldActions("workspace")
23
+ * <Button onClick={scaffold.toggleInspector}>Inspect</Button>
24
+ * ```
25
+ */
26
+ export declare function useScaffoldActions(scaffoldId: ScaffoldInstanceId): ScaffoldStateActions;
@@ -4,5 +4,11 @@ import { ScaffoldControls, UseScaffoldOptions } from './type';
4
4
  * (title-bar buttons, command palette, shortcuts). Spread the returned
5
5
  * `providerProps` onto `Scaffold.Root`; without this hook the root manages
6
6
  * the same state internally via `defaultInspectorOpen`.
7
+ *
8
+ * The hook and the root meet in the store: `providerProps` carries a
9
+ * `scaffoldId`, both sides read and write that instance's atoms, and
10
+ * `useScaffoldState` / `useScaffoldActions` reach the same slice from
11
+ * anywhere else by id. Unnamed scaffolds key off `useId` and are dropped on
12
+ * unmount; an explicit `scaffoldId` outlives its component.
7
13
  */
8
- export declare function useScaffold({ defaultInspectorOpen, onInspectorChange, }?: UseScaffoldOptions): ScaffoldControls;
14
+ export declare function useScaffold({ scaffoldId: scaffoldIdProp, defaultInspectorOpen, onInspectorChange, }?: UseScaffoldOptions): ScaffoldControls;
@@ -1,12 +1,22 @@
1
1
  import { Variants } from 'motion/react';
2
2
  import { ClassNameValue } from 'tailwind-merge';
3
- import { TSubPanelVariant } from './type.ts';
3
+ import { ScaffoldPanelVisibility, TSubPanelVariant } from './type.ts';
4
4
  /**
5
5
  * How many panels fit on a canvas of `width` px, granting each
6
6
  * `SCAFFOLD_PANEL_MIN_WIDTH` plus the gaps between them. Clamped to
7
7
  * [1, SCAFFOLD_PANEL_CAPACITY]; an unmeasured canvas (`null`) fits the cap.
8
8
  */
9
9
  export declare function getPanelCapacity(width: number | null): number;
10
+ /** @internal An untracked scaffold: nothing registered, nothing hidden. */
11
+ export declare const EMPTY_SCAFFOLD_VISIBILITY: ScaffoldPanelVisibility;
12
+ /**
13
+ * @internal Fold the registered ids and current capacity into a settled
14
+ * visibility: prune departed panels, admit new ones, evict the
15
+ * least-recently-activated visible panels past capacity, and restore
16
+ * auto-hidden ones when room returns. Returns `prev` untouched when nothing
17
+ * changes, so callers can compare identities instead of contents.
18
+ */
19
+ export declare function reconcileVisibility(prev: ScaffoldPanelVisibility, ids: readonly string[], capacity: number): ScaffoldPanelVisibility;
10
20
  /** Desktop backdrop the whole scaffold sits on — a soft warm-gray wash. */
11
21
  export declare const scaffoldBackgroundClass: ClassNameValue;
12
22
  /** Card surface shared by a panel's primary area and secondary strip. */
@@ -0,0 +1,4 @@
1
+ import { Switch as SwitchPrimitive } from '@base-ui/react/switch';
2
+ import { default as React } from 'react';
3
+ export declare function Switch({ className, ...props }: SwitchPrimitive.Root.Props): React.ReactElement;
4
+ export { SwitchPrimitive };
@@ -27,6 +27,7 @@ export * from './components/composed/tree';
27
27
  export { DEFAULT_BEZEL_ANGLE, PresenceBadge, UserAvatar, initialsFrom, removeUserAvatarInstance, resetUserAvatarAtom, setUserAvatarBezelAngleAtom, setUserAvatarPresenceAtom, toggleUserAvatarPresenceAtom, useUserAvatar, useUserAvatarActions, useUserAvatarState, userAvatarBezelAngleAtom, userAvatarIsOnlineAtom, userAvatarPresenceAtom, userAvatarStateAtom, } from './components/user-avatar';
28
28
  export type { UseUserAvatarOptions, UserAvatarActions, UserAvatarBinding, UserAvatarInstanceId, UserAvatarProps, UserAvatarState, UserPresence, } from './components/user-avatar';
29
29
  export * from './components/blocks/auth/auth-shell';
30
+ export * from './components/blocks/auth/auth-flow-block';
30
31
  export * from './components/blocks/auth/auth-state';
31
32
  export * from './components/blocks/auth/code-auth-block';
32
33
  export * from './components/blocks/auth/forgot-password-block';