@arbidocs/blocks 0.3.110
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.
- package/dist/index.cjs +3736 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1213 -0
- package/dist/index.d.ts +1213 -0
- package/dist/index.js +3634 -0
- package/dist/index.js.map +1 -0
- package/package.json +75 -0
- package/src/components/grid.css +365 -0
- package/src/studio/studio.css +70 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,1213 @@
|
|
|
1
|
+
import { UseBoundStore, StoreApi } from 'zustand';
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
import react__default, { ReactNode } from 'react';
|
|
4
|
+
import * as _arbidocs_react from '@arbidocs/react';
|
|
5
|
+
import { AiRunOptions, useArbi } from '@arbidocs/react';
|
|
6
|
+
export { AiRunOptions, AiTaskStatus, ArtifactEvent, SearchChunk, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, redlineStats, textToArtifact, toRedlineMarkdown } from '@arbidocs/react';
|
|
7
|
+
import { ColDef, GridOptions, GridApi, CellValueChangedEvent, GridState } from 'ag-grid-community';
|
|
8
|
+
import { LucideIcon } from 'lucide-react';
|
|
9
|
+
import { Config, CustomField, Field, ComponentConfig } from '@measured/puck';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The reusable AI-native law-firm prompt library.
|
|
13
|
+
*
|
|
14
|
+
* Deliberately opinionated, litigation-grade instructions: they demand citations
|
|
15
|
+
* to the source documents, hedge appropriately, and always frame the output as a
|
|
16
|
+
* draft for a regulated lawyer to review ("AI proposes, the lawyer disposes").
|
|
17
|
+
* Firm-specific register (name, jurisdiction, house language) is injected via
|
|
18
|
+
* {@link PromptConfig} so a second firm reuses the same library with its own
|
|
19
|
+
* voice — call {@link createPromptLibrary} once and share the result app-wide.
|
|
20
|
+
*/
|
|
21
|
+
interface MatterContext {
|
|
22
|
+
title: string;
|
|
23
|
+
client?: string;
|
|
24
|
+
court?: string;
|
|
25
|
+
claimNumber?: string;
|
|
26
|
+
practice?: string;
|
|
27
|
+
summary?: string;
|
|
28
|
+
}
|
|
29
|
+
/** Build a {@link MatterContext} from any matter-shaped record. */
|
|
30
|
+
declare function matterToContext(m: {
|
|
31
|
+
title: string;
|
|
32
|
+
clientName?: string;
|
|
33
|
+
court?: string | null;
|
|
34
|
+
claimNumber?: string | null;
|
|
35
|
+
practice?: string;
|
|
36
|
+
summary?: string;
|
|
37
|
+
}): MatterContext;
|
|
38
|
+
/** Firm voice for the prompt library — everything firm-specific lives here. */
|
|
39
|
+
interface PromptConfig {
|
|
40
|
+
/** The firm's name, e.g. "Clarum". */
|
|
41
|
+
firmName: string;
|
|
42
|
+
/** The legal system adjective used throughout, e.g. "English". */
|
|
43
|
+
jurisdiction?: string;
|
|
44
|
+
/** House writing language, e.g. "British English". */
|
|
45
|
+
language?: string;
|
|
46
|
+
/** How the reviewing professional is addressed, e.g. "qualified solicitor". */
|
|
47
|
+
reviewerTitle?: string;
|
|
48
|
+
/** Who signs off compliance/onboarding, e.g. "MLRO". */
|
|
49
|
+
complianceOfficer?: string;
|
|
50
|
+
}
|
|
51
|
+
type PromptLibrary = ReturnType<typeof createPromptLibrary>;
|
|
52
|
+
/** The map of prompt builders (`prompts.brief(ctx)`, `prompts.revise(...)`, …). */
|
|
53
|
+
type Prompts = PromptLibrary['prompts'];
|
|
54
|
+
/**
|
|
55
|
+
* Build a firm-branded prompt library. Every prompt grounds on the supplied
|
|
56
|
+
* documents, cites sources, and is framed as a draft for a lawyer to review.
|
|
57
|
+
*/
|
|
58
|
+
declare function createPromptLibrary(config: PromptConfig): {
|
|
59
|
+
prompts: {
|
|
60
|
+
/** Executive brief of the matter, from the live bundle. */
|
|
61
|
+
brief: (m?: MatterContext) => string;
|
|
62
|
+
/** Sourced chronology of events. */
|
|
63
|
+
chronology: (m?: MatterContext) => string;
|
|
64
|
+
/** Disclosure / relevance + privilege review. */
|
|
65
|
+
disclosure: (m?: MatterContext) => string;
|
|
66
|
+
/** Merits / risk assessment — the real "AI outlook". */
|
|
67
|
+
risk: (m?: MatterContext) => string;
|
|
68
|
+
/** Extract deadlines / key dates with rule references. */
|
|
69
|
+
deadlines: (m?: MatterContext) => string;
|
|
70
|
+
/** Draft a document (letter / pleading / note). */
|
|
71
|
+
draft: (kind: string, m?: MatterContext) => string;
|
|
72
|
+
/** Free-form question about the matter. */
|
|
73
|
+
ask: (question: string, m?: MatterContext) => string;
|
|
74
|
+
/**
|
|
75
|
+
* Draft a clean document artifact (output IS the document — no preamble).
|
|
76
|
+
*
|
|
77
|
+
* Pure single-pass generation from the matter context: it deliberately does
|
|
78
|
+
* NOT tell the agent to look up or cite the document bundle. Asking a drafting
|
|
79
|
+
* agent to "cite the source behind each fact" sends it into a retrieval loop
|
|
80
|
+
* that can spin for minutes before emitting a token; a lawyer wants a fast
|
|
81
|
+
* first draft to refine, so we draft from the known matter facts and mark
|
|
82
|
+
* genuinely-missing specifics as [PLACEHOLDER]. (Grounded, cited analysis is
|
|
83
|
+
* what {@link brief}/{@link risk} are for.)
|
|
84
|
+
*/
|
|
85
|
+
draftArtifact: (kind: string, instructions: string, m?: MatterContext) => string;
|
|
86
|
+
/**
|
|
87
|
+
* Revise a supplied passage. Returns ONLY the full revised text (no commentary),
|
|
88
|
+
* so the app can diff it against the original into a redline.
|
|
89
|
+
*/
|
|
90
|
+
revise: (original: string, instruction: string, m?: MatterContext) => string;
|
|
91
|
+
/** Morning digest across the matter. */
|
|
92
|
+
digest: (m?: MatterContext) => string;
|
|
93
|
+
/** UTBMS-coded billing narrative from time context. */
|
|
94
|
+
billing: (activity: string, m?: MatterContext) => string;
|
|
95
|
+
/** Closing / lessons-learned memo. */
|
|
96
|
+
closing: (m?: MatterContext) => string;
|
|
97
|
+
/** Thought-leadership / insight article from the matter's themes. */
|
|
98
|
+
insight: (m?: MatterContext) => string;
|
|
99
|
+
/** Auto-tagging / issue classification of documents. */
|
|
100
|
+
autoTag: (m?: MatterContext) => string;
|
|
101
|
+
/** Verify uploaded onboarding documents against a KYC/CDD checklist. */
|
|
102
|
+
kyc: (clientName: string, m?: MatterContext) => string;
|
|
103
|
+
/** Triage a prospective enquiry. */
|
|
104
|
+
triage: (enquiry: string, m?: MatterContext) => string;
|
|
105
|
+
/** Conflict-of-interest style check against the workspace. */
|
|
106
|
+
conflict: (parties: string, m?: MatterContext) => string;
|
|
107
|
+
/** Capability statement / pitch letter. */
|
|
108
|
+
pitch: (opportunity: string, m?: MatterContext) => string;
|
|
109
|
+
};
|
|
110
|
+
WORKFLOW_ACTIONS: readonly [{
|
|
111
|
+
readonly id: "brief";
|
|
112
|
+
readonly title: "Matter brief";
|
|
113
|
+
readonly prompt: (m?: MatterContext) => string;
|
|
114
|
+
}, {
|
|
115
|
+
readonly id: "chronology";
|
|
116
|
+
readonly title: "Build a chronology";
|
|
117
|
+
readonly prompt: (m?: MatterContext) => string;
|
|
118
|
+
}, {
|
|
119
|
+
readonly id: "disclosure";
|
|
120
|
+
readonly title: "Review for disclosure";
|
|
121
|
+
readonly prompt: (m?: MatterContext) => string;
|
|
122
|
+
}, {
|
|
123
|
+
readonly id: "risk";
|
|
124
|
+
readonly title: "Merits & risk";
|
|
125
|
+
readonly prompt: (m?: MatterContext) => string;
|
|
126
|
+
}];
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* The theme-token model for the legal vertical. Firm-agnostic: the token
|
|
131
|
+
* *catalog*, default values and presets are supplied by the firm (via
|
|
132
|
+
* {@link createThemeStore}); this module owns the shared types and the logic for
|
|
133
|
+
* writing tokens onto the live document.
|
|
134
|
+
*
|
|
135
|
+
* There are two emit models, selected per-firm by {@link ThemeEmitMode}:
|
|
136
|
+
*
|
|
137
|
+
* - `'flat'` (default — Clarum): each colour key is written as an inline
|
|
138
|
+
* `--color-<key>` custom property on `<html>`, plus `--font-*` / `--radius`.
|
|
139
|
+
* Inline styles override the compiled `@theme` defaults, so an edit previews
|
|
140
|
+
* live across the whole app (and the @arbidocs AI kit) and reverts cleanly.
|
|
141
|
+
* Firms whose CSS sets `--color-*` HEX directly (Clarum) rely on this.
|
|
142
|
+
*
|
|
143
|
+
* - `'cssVars'` (the ARBI monolith): each colour key is written as a raw
|
|
144
|
+
* HSL-triplet custom property (`--<key>: <h s l>`) into an injected
|
|
145
|
+
* `<style id="arbi-theme-vars">` appended to `<head>`. The monolith's source
|
|
146
|
+
* of truth is HSL triplets wrapped as `hsl(var(--x))`, with a real `.dark`
|
|
147
|
+
* variant; writing resolved hex inline would pin the colour and defeat both
|
|
148
|
+
* the indirection and the dark-class swap. The injected sheet is *unlayered*,
|
|
149
|
+
* so it wins over the monolith's `@layer base` `colors.css` at equal
|
|
150
|
+
* specificity, and a `:root { light } / :root.dark { dark }` structure keeps
|
|
151
|
+
* the dark cascade alive.
|
|
152
|
+
*/
|
|
153
|
+
interface ColorToken {
|
|
154
|
+
/** Key without the `--color-` prefix, e.g. "primary" → `--color-primary`. */
|
|
155
|
+
key: string;
|
|
156
|
+
label: string;
|
|
157
|
+
/** Grouping id for the panel layout (e.g. "shadcn" | "brand" | "ai"). */
|
|
158
|
+
group: string;
|
|
159
|
+
}
|
|
160
|
+
interface ThemeFonts {
|
|
161
|
+
display: string;
|
|
162
|
+
sans: string;
|
|
163
|
+
}
|
|
164
|
+
interface ThemeTokens {
|
|
165
|
+
/** Colour values keyed by token key (hex). */
|
|
166
|
+
colors: Record<string, string>;
|
|
167
|
+
fonts: ThemeFonts;
|
|
168
|
+
/** Base radius in rem. */
|
|
169
|
+
radius: number;
|
|
170
|
+
}
|
|
171
|
+
interface ThemePreset {
|
|
172
|
+
id: string;
|
|
173
|
+
label: string;
|
|
174
|
+
tokens: ThemeTokens;
|
|
175
|
+
}
|
|
176
|
+
/** How a theme is written onto the document. See the module doc for the models. */
|
|
177
|
+
type ThemeEmitMode = 'flat' | 'cssVars';
|
|
178
|
+
interface ApplyThemeOptions {
|
|
179
|
+
/** Emit model. Defaults to `'flat'` (Clarum's behaviour). */
|
|
180
|
+
mode?: ThemeEmitMode;
|
|
181
|
+
/**
|
|
182
|
+
* `cssVars` mode only: dark-variant colour values keyed by token key. Emitted
|
|
183
|
+
* under `:root.dark`. When omitted, only `:root` is emitted — note that an
|
|
184
|
+
* overridden light token then also applies in dark (the injected `:root` is
|
|
185
|
+
* unlayered and beats the app's own `.dark` rules), so a firm with a dark mode
|
|
186
|
+
* MUST supply this for every editable token to keep dark mode intact.
|
|
187
|
+
*/
|
|
188
|
+
dark?: Record<string, string>;
|
|
189
|
+
/** `flat` mode only: target element. Defaults to `document.documentElement`. */
|
|
190
|
+
root?: HTMLElement;
|
|
191
|
+
}
|
|
192
|
+
/** Id of the injected `<style>` that carries the `cssVars`-mode custom properties. */
|
|
193
|
+
declare const THEME_STYLE_ID = "arbi-theme-vars";
|
|
194
|
+
/**
|
|
195
|
+
* Convert a colour to the monolith's `H S% L%` HSL-triplet format.
|
|
196
|
+
*
|
|
197
|
+
* Handles `#rgb` / `#rrggbb`, `rgb()` / `rgba()`, and passes an existing triplet
|
|
198
|
+
* through untouched (so a firm can seed exact `colors.css` triplets for a
|
|
199
|
+
* byte-identical no-op). Values it cannot parse — notably `oklch(...)`, which the
|
|
200
|
+
* status tokens use — are returned unchanged so they remain usable as a raw
|
|
201
|
+
* custom-property value; converting those cleanly is out of scope.
|
|
202
|
+
*/
|
|
203
|
+
declare function toHslTriplet(color: string): string;
|
|
204
|
+
/**
|
|
205
|
+
* Write a theme onto the live document.
|
|
206
|
+
*
|
|
207
|
+
* In `flat` mode (default) writes inline `--color-<key>` / `--font-*` / `--radius`
|
|
208
|
+
* on `document.documentElement` (or a supplied root). In `cssVars` mode writes
|
|
209
|
+
* raw HSL-triplet vars into an injected `<style#arbi-theme-vars>` (see the module
|
|
210
|
+
* doc). The legacy `applyThemeVars(theme, rootElement)` call form still works.
|
|
211
|
+
*/
|
|
212
|
+
declare function applyThemeVars(theme: ThemeTokens, optionsOrRoot?: ApplyThemeOptions | HTMLElement): void;
|
|
213
|
+
/**
|
|
214
|
+
* Remove a theme's overrides, reverting to the stylesheet defaults.
|
|
215
|
+
*
|
|
216
|
+
* In `flat` mode removes the inline overrides for the theme's keys. In `cssVars`
|
|
217
|
+
* mode removes the injected `<style#arbi-theme-vars>` in full (restoring the
|
|
218
|
+
* app's own `colors.css` exactly).
|
|
219
|
+
*/
|
|
220
|
+
declare function clearThemeVars(theme: ThemeTokens, optionsOrRoot?: ApplyThemeOptions | HTMLElement): void;
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* The shared theme store factory. A firm calls {@link createThemeStore} once
|
|
224
|
+
* with its token catalog, default values and presets; the returned bundle (a
|
|
225
|
+
* zustand hook + metadata) is the single source of truth consumed by every
|
|
226
|
+
* theme surface (the back-office builder panel and the studio drawer), so the
|
|
227
|
+
* token model, presets and apply logic live in exactly one place.
|
|
228
|
+
*/
|
|
229
|
+
|
|
230
|
+
interface ThemeState extends ThemeTokens {
|
|
231
|
+
activePresetId: string | null;
|
|
232
|
+
setColor: (key: string, value: string) => void;
|
|
233
|
+
setFont: (which: keyof ThemeFonts, value: string) => void;
|
|
234
|
+
setRadius: (radius: number) => void;
|
|
235
|
+
applyPreset: (presetId: string) => void;
|
|
236
|
+
reset: () => void;
|
|
237
|
+
/** Snapshot of the editable tokens (for export / live apply). */
|
|
238
|
+
snapshot: () => ThemeTokens;
|
|
239
|
+
}
|
|
240
|
+
interface ThemeStoreConfig {
|
|
241
|
+
/** The editable token catalog (keys + labels + groups). */
|
|
242
|
+
tokens: ColorToken[];
|
|
243
|
+
/** Group id → heading, for the grouped panel layout. */
|
|
244
|
+
groupLabels: Record<string, string>;
|
|
245
|
+
/** Shipped defaults (the firm's brand values). */
|
|
246
|
+
defaultTheme: ThemeTokens;
|
|
247
|
+
/** Starting points the user can apply then fine-tune. */
|
|
248
|
+
presets: ThemePreset[];
|
|
249
|
+
/** localStorage key for persistence (namespaced per firm). */
|
|
250
|
+
storageKey: string;
|
|
251
|
+
/**
|
|
252
|
+
* How edits are written onto the document. Defaults to `'flat'` (inline
|
|
253
|
+
* `--color-*`, Clarum). `'cssVars'` emits raw HSL-triplet vars into an injected
|
|
254
|
+
* `<style>` so the app's own `hsl(var(--x))` indirection + `.dark` variant
|
|
255
|
+
* survive (the ARBI monolith). See {@link applyThemeVars}.
|
|
256
|
+
*/
|
|
257
|
+
mode?: ThemeEmitMode;
|
|
258
|
+
/**
|
|
259
|
+
* `cssVars` mode only: the dark-variant colour values keyed by token key,
|
|
260
|
+
* emitted under `:root.dark`. Required (per editable token) for any firm with a
|
|
261
|
+
* dark mode, otherwise a light-token override leaks into dark.
|
|
262
|
+
*/
|
|
263
|
+
darkColors?: Record<string, string>;
|
|
264
|
+
}
|
|
265
|
+
interface ThemeBundle extends ThemeStoreConfig {
|
|
266
|
+
useStore: UseBoundStore<StoreApi<ThemeState>>;
|
|
267
|
+
}
|
|
268
|
+
/** Build a firm-branded theme store bundle. */
|
|
269
|
+
declare function createThemeStore(config: ThemeStoreConfig): ThemeBundle;
|
|
270
|
+
/** Apply a bundle's current store to the live document, honouring its emit mode. */
|
|
271
|
+
declare function applyStoredTheme(bundle: ThemeBundle): void;
|
|
272
|
+
|
|
273
|
+
interface ThemeEditorProps {
|
|
274
|
+
/** The firm's theme store bundle (from createThemeStore). */
|
|
275
|
+
bundle: ThemeBundle;
|
|
276
|
+
variant?: 'panel' | 'drawer';
|
|
277
|
+
/** Firm identity shown in the panel's live-preview header. */
|
|
278
|
+
preview?: {
|
|
279
|
+
title: string;
|
|
280
|
+
subtitle?: string;
|
|
281
|
+
};
|
|
282
|
+
/** Optional persistence side-channel (e.g. save theme to ARBI); returns a "via" label. */
|
|
283
|
+
onSave?: () => Promise<string | void>;
|
|
284
|
+
/** Stable testid namespace, e.g. "builder-theme" | "studio-theme". */
|
|
285
|
+
testIdPrefix?: string;
|
|
286
|
+
}
|
|
287
|
+
declare function ThemeEditor({ bundle, variant, preview, onSave, testIdPrefix, }: ThemeEditorProps): react.JSX.Element;
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Shared shapes for the ARBI adapter — identical in demo and live mode so UI
|
|
291
|
+
* never needs to know which backend is serving it — plus the per-firm config a
|
|
292
|
+
* firm app supplies once to wire the connection + AI layer.
|
|
293
|
+
*/
|
|
294
|
+
type ArbiMode = 'demo' | 'live';
|
|
295
|
+
type ConnectionStatus = 'demo' | 'disconnected' | 'connecting' | 'authenticated' | 'error';
|
|
296
|
+
interface ArbiUser {
|
|
297
|
+
name: string;
|
|
298
|
+
email: string;
|
|
299
|
+
}
|
|
300
|
+
/** A retrieval-augmented answer step surfaced from the agent loop. */
|
|
301
|
+
interface AssistantStep {
|
|
302
|
+
label: string;
|
|
303
|
+
detail?: string;
|
|
304
|
+
status: 'done' | 'running' | 'queued';
|
|
305
|
+
}
|
|
306
|
+
interface AssistantCitation {
|
|
307
|
+
docId: string;
|
|
308
|
+
docName: string;
|
|
309
|
+
page?: number;
|
|
310
|
+
quote: string;
|
|
311
|
+
}
|
|
312
|
+
interface AssistantMessage {
|
|
313
|
+
id: string;
|
|
314
|
+
role: 'user' | 'assistant';
|
|
315
|
+
content: string;
|
|
316
|
+
steps?: AssistantStep[];
|
|
317
|
+
citations?: AssistantCitation[];
|
|
318
|
+
streaming?: boolean;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Everything firm-specific about the ARBI wiring, injected once at the app root.
|
|
322
|
+
* Framework-agnostic on purpose: the app reads its own env (Vite, Next, …) and
|
|
323
|
+
* passes the resolved values in, so the package never touches `import.meta.env`.
|
|
324
|
+
*/
|
|
325
|
+
interface FirmArbiConfig {
|
|
326
|
+
/** Live backend URL. Empty string ⇒ run entirely in seeded demo mode. */
|
|
327
|
+
apiUrl: string;
|
|
328
|
+
/** Inert URL used to mount the SDK provider when there's no live backend. */
|
|
329
|
+
demoApiUrl?: string;
|
|
330
|
+
/** Preferred live workspace ext_id (else the first workspace is used). */
|
|
331
|
+
workspaceId?: string;
|
|
332
|
+
/** Default assistant *configuration* ext_id every agentic run is grounded on. */
|
|
333
|
+
assistantConfig?: string;
|
|
334
|
+
/** The seeded demo identity shown when running without a backend. */
|
|
335
|
+
demoUser: ArbiUser;
|
|
336
|
+
/** Workspace id used in demo mode (default "demo-firm"). */
|
|
337
|
+
demoWorkspaceId?: string;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
interface ConnectionValue {
|
|
341
|
+
mode: ArbiMode;
|
|
342
|
+
status: ConnectionStatus;
|
|
343
|
+
user: ArbiUser | null;
|
|
344
|
+
workspaceId: string | null;
|
|
345
|
+
apiConfigured: boolean;
|
|
346
|
+
apiUrl: string;
|
|
347
|
+
error: string | null;
|
|
348
|
+
/** Default assistant config ext_id (for the AI hooks). */
|
|
349
|
+
assistantConfig?: string;
|
|
350
|
+
/** Fallback workspace ext_id when none is actively selected. */
|
|
351
|
+
fallbackWorkspaceId?: string;
|
|
352
|
+
/** Authenticate. Live login when a backend is configured, otherwise the demo firm. */
|
|
353
|
+
login: (email: string, password: string) => Promise<void>;
|
|
354
|
+
/** One-click demo entry (no backend required). */
|
|
355
|
+
enterDemo: () => void;
|
|
356
|
+
logout: () => void;
|
|
357
|
+
setWorkspace: (id: string) => void;
|
|
358
|
+
}
|
|
359
|
+
declare function ConnectionProvider({ config, children, }: {
|
|
360
|
+
config: FirmArbiConfig;
|
|
361
|
+
children: ReactNode;
|
|
362
|
+
}): react.JSX.Element;
|
|
363
|
+
declare function useConnection(): ConnectionValue;
|
|
364
|
+
declare const isAuthenticated: (s: ConnectionStatus) => s is "demo" | "authenticated";
|
|
365
|
+
|
|
366
|
+
declare function LegalArbiProvider({ config, children, }: {
|
|
367
|
+
config: FirmArbiConfig;
|
|
368
|
+
children: ReactNode;
|
|
369
|
+
}): react.JSX.Element;
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* The kit's `useAiTask` with the firm's default assistant configuration
|
|
373
|
+
* injected, so call sites needn't know the config ext_id.
|
|
374
|
+
*/
|
|
375
|
+
declare function useFirmAiTask(): {
|
|
376
|
+
run: (question: string, docIds: string[], opts?: AiRunOptions) => Promise<_arbidocs_react.SSEStreamResult | null>;
|
|
377
|
+
status: _arbidocs_react.AiTaskStatus;
|
|
378
|
+
text: string;
|
|
379
|
+
stepCount: number;
|
|
380
|
+
error: string | null;
|
|
381
|
+
responseId: string | null;
|
|
382
|
+
artifacts: {
|
|
383
|
+
t?: number | null;
|
|
384
|
+
type: "arbi.artifact";
|
|
385
|
+
doc_ext_id: string;
|
|
386
|
+
title: string;
|
|
387
|
+
mode: "markdown" | "html" | "text" | "image";
|
|
388
|
+
content: string;
|
|
389
|
+
file_type: string;
|
|
390
|
+
is_complete: boolean;
|
|
391
|
+
version: number;
|
|
392
|
+
message_ext_id?: string | null;
|
|
393
|
+
}[];
|
|
394
|
+
reset: () => void;
|
|
395
|
+
isRunning: boolean;
|
|
396
|
+
isDone: boolean;
|
|
397
|
+
};
|
|
398
|
+
/** Resolve the active workspace + its document ids (what we ground the agent on). */
|
|
399
|
+
declare function useWorkspaceDocs(): {
|
|
400
|
+
wsId: string | undefined;
|
|
401
|
+
docs: {
|
|
402
|
+
external_id: string;
|
|
403
|
+
workspace_ext_id: string;
|
|
404
|
+
file_name?: string | null | undefined;
|
|
405
|
+
status?: string | null | undefined;
|
|
406
|
+
error_message?: string | null | undefined;
|
|
407
|
+
failed_stage?: string | null | undefined;
|
|
408
|
+
n_pages?: number | null | undefined;
|
|
409
|
+
n_chunks?: number | null | undefined;
|
|
410
|
+
tokens?: number | null | undefined;
|
|
411
|
+
file_type?: string | null | undefined;
|
|
412
|
+
file_size?: number | null | undefined;
|
|
413
|
+
storage_type?: string | null | undefined;
|
|
414
|
+
storage_uri?: string | null | undefined;
|
|
415
|
+
content_hash?: string | null | undefined;
|
|
416
|
+
shared?: boolean | null | undefined;
|
|
417
|
+
re_ocred?: boolean | null | undefined;
|
|
418
|
+
config_ext_id?: string | null | undefined;
|
|
419
|
+
parent_ext_id?: string | null | undefined;
|
|
420
|
+
created_by_ext_id: string;
|
|
421
|
+
updated_by_ext_id?: string | null | undefined;
|
|
422
|
+
created_at: string;
|
|
423
|
+
updated_at: string;
|
|
424
|
+
wp_type?: string | null | undefined;
|
|
425
|
+
folder?: string | null | undefined;
|
|
426
|
+
doctags: {
|
|
427
|
+
doc_ext_id: string;
|
|
428
|
+
tag_ext_id: string;
|
|
429
|
+
note?: string | null | undefined;
|
|
430
|
+
citations?: {
|
|
431
|
+
[x: string]: {
|
|
432
|
+
chunk_ids: string[];
|
|
433
|
+
scores: number[];
|
|
434
|
+
statement: string;
|
|
435
|
+
offset_start: number;
|
|
436
|
+
offset_end: number;
|
|
437
|
+
};
|
|
438
|
+
} | null | undefined;
|
|
439
|
+
created_by_ext_id: string;
|
|
440
|
+
updated_by_ext_id?: string | null | undefined;
|
|
441
|
+
created_at: string;
|
|
442
|
+
updated_at: string;
|
|
443
|
+
}[];
|
|
444
|
+
doc_metadata?: {
|
|
445
|
+
doc_nature?: string | null | undefined;
|
|
446
|
+
doc_author?: string | null | undefined;
|
|
447
|
+
doc_subject?: string | null | undefined;
|
|
448
|
+
doc_date?: string | null | undefined;
|
|
449
|
+
title?: string | null | undefined;
|
|
450
|
+
} | null | undefined;
|
|
451
|
+
}[];
|
|
452
|
+
docIds: string[];
|
|
453
|
+
isLoading: boolean;
|
|
454
|
+
isError: boolean;
|
|
455
|
+
ready: boolean;
|
|
456
|
+
};
|
|
457
|
+
/** Search the active workspace's documents and return the top matching passages. */
|
|
458
|
+
declare function useSemanticSearch(): {
|
|
459
|
+
query: string;
|
|
460
|
+
results: _arbidocs_react.SearchChunk[];
|
|
461
|
+
status: _arbidocs_react.AiTaskStatus;
|
|
462
|
+
error: string | null;
|
|
463
|
+
search: (q: string, mode?: "semantic" | "keyword" | "hybrid") => Promise<void>;
|
|
464
|
+
isRunning: boolean;
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Compose a stable, kebab-case data-testid from parts.
|
|
469
|
+
* Convention: {area}-{element}[-{qualifier}...]
|
|
470
|
+
* tid("matters", "row", "CLM-2026-0042") -> "matters-row-clm-2026-0042"
|
|
471
|
+
* Keeps Playwright selectors predictable across a firm app.
|
|
472
|
+
*/
|
|
473
|
+
declare function tid(...parts: Array<string | number | undefined | false>): string;
|
|
474
|
+
|
|
475
|
+
/**
|
|
476
|
+
* Firm-app formatting helpers — currency, initials, dates, durations and
|
|
477
|
+
* percentages. UK-oriented defaults (en-GB / GBP / "d MMM yyyy") suit an English
|
|
478
|
+
* law firm; nothing here is firm-branded, so every firm app shares them.
|
|
479
|
+
*
|
|
480
|
+
* Dependency-free on purpose (uses the platform `Intl` APIs) so the package
|
|
481
|
+
* stays light and needs no date library.
|
|
482
|
+
*/
|
|
483
|
+
/** Format a number as GBP, e.g. £1,240,000 or £12.4k depending on `compact`. */
|
|
484
|
+
declare function gbp(value: number, compact?: boolean): string;
|
|
485
|
+
/** Initials from a person's name, e.g. "Amara N. Okafor" -> "AO". */
|
|
486
|
+
declare function initials(name: string): string;
|
|
487
|
+
/** "12 Mar 2026" */
|
|
488
|
+
declare function fmtDate(iso: string): string;
|
|
489
|
+
/** "12 Mar 2026, 14:30" */
|
|
490
|
+
declare function fmtDateTime(iso: string): string;
|
|
491
|
+
/** "3 days ago" / "in 2 hours" — largest whole unit, like date-fns strict. */
|
|
492
|
+
declare function fromNow(iso: string, from?: Date): string;
|
|
493
|
+
/** Signed day count to a date from a reference day. Negative = overdue. */
|
|
494
|
+
declare function daysUntil(iso: string, from?: Date): number;
|
|
495
|
+
/** Compact hours, e.g. 12.5h */
|
|
496
|
+
declare function hrs(n: number): string;
|
|
497
|
+
/** Percentage with no decimals, e.g. 82% */
|
|
498
|
+
declare function pct(n: number): string;
|
|
499
|
+
|
|
500
|
+
interface Column<T> {
|
|
501
|
+
key: string;
|
|
502
|
+
header: ReactNode;
|
|
503
|
+
render: (row: T) => ReactNode;
|
|
504
|
+
align?: 'left' | 'right' | 'center';
|
|
505
|
+
className?: string;
|
|
506
|
+
headClassName?: string;
|
|
507
|
+
}
|
|
508
|
+
interface DataTableProps<T> {
|
|
509
|
+
columns: Column<T>[];
|
|
510
|
+
rows: T[];
|
|
511
|
+
getRowId: (row: T) => string;
|
|
512
|
+
onRowClick?: (row: T) => void;
|
|
513
|
+
/** testid area prefix, e.g. "matters" -> rows get "matters-row-{id}" */
|
|
514
|
+
area: string;
|
|
515
|
+
emptyTitle?: string;
|
|
516
|
+
emptyDescription?: string;
|
|
517
|
+
className?: string;
|
|
518
|
+
}
|
|
519
|
+
/** The one true table. Column-config driven, fully test-id'd, optional row navigation. */
|
|
520
|
+
declare function DataTable<T>({ columns, rows, getRowId, onRowClick, area, emptyTitle, emptyDescription, className, }: DataTableProps<T>): react.JSX.Element;
|
|
521
|
+
|
|
522
|
+
interface DataTableBlockColumn {
|
|
523
|
+
/** Column heading text. */
|
|
524
|
+
header: string;
|
|
525
|
+
/** Key used to look a cell up in each row record. */
|
|
526
|
+
field: string;
|
|
527
|
+
/** Cell/heading alignment. */
|
|
528
|
+
align?: 'left' | 'right' | 'center';
|
|
529
|
+
}
|
|
530
|
+
interface DataTableBlockProps {
|
|
531
|
+
/** Serializable column schema. */
|
|
532
|
+
columns: DataTableBlockColumn[];
|
|
533
|
+
/** One record per row; cells are looked up by each column's `field`. */
|
|
534
|
+
rows: Array<Record<string, string>>;
|
|
535
|
+
/** Optional caption rendered above the table. */
|
|
536
|
+
caption?: string;
|
|
537
|
+
/** Empty-state title shown when there are no rows. */
|
|
538
|
+
emptyText?: string;
|
|
539
|
+
/** testid area prefix; also set on the wrapper element. */
|
|
540
|
+
testId?: string;
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Renders a {@link DataTable} from a flat, serializable schema. Columns are mapped
|
|
544
|
+
* to `Column<TableRowData>[]` (header string, positional-stable key, alignment) and
|
|
545
|
+
* each cell is resolved by `field`; rows carry a synthetic id for React keys/testids.
|
|
546
|
+
*/
|
|
547
|
+
declare function DataTableBlock({ columns, rows, caption, emptyText, testId }: DataTableBlockProps): react.JSX.Element;
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* GridView — a flexible AG Grid presentational core.
|
|
551
|
+
*
|
|
552
|
+
* Pure and app-agnostic: it owns no global state and pulls in no store. Column
|
|
553
|
+
* state persistence (order / width / visibility / sort / filter) is exposed as a
|
|
554
|
+
* controlled/uncontrolled prop pair — `gridState` (initial state, read once at
|
|
555
|
+
* mount) and `onGridStateChange` (called whenever AG Grid's state changes). An
|
|
556
|
+
* app wires those to wherever it keeps the state (Zustand, localStorage, a URL,
|
|
557
|
+
* a server) via a thin adapter; the core stays decoupled.
|
|
558
|
+
*
|
|
559
|
+
* Themed entirely via CSS variables (see the sibling `grid.css`, which a
|
|
560
|
+
* consuming app imports globally), so it inherits the host's light/dark theme.
|
|
561
|
+
*/
|
|
562
|
+
|
|
563
|
+
interface GridViewSelection<T> {
|
|
564
|
+
mode: 'single' | 'multiple';
|
|
565
|
+
selected: T[];
|
|
566
|
+
onSelectionChange: (items: T[]) => void;
|
|
567
|
+
}
|
|
568
|
+
interface GridViewEmptyState {
|
|
569
|
+
icon?: react__default.ReactNode;
|
|
570
|
+
title?: string;
|
|
571
|
+
message?: string;
|
|
572
|
+
action?: react__default.ReactNode;
|
|
573
|
+
}
|
|
574
|
+
interface GridViewProps<T> {
|
|
575
|
+
items: T[];
|
|
576
|
+
columnDefs: ColDef<T>[];
|
|
577
|
+
getItemId: (item: T) => string;
|
|
578
|
+
selection?: GridViewSelection<T>;
|
|
579
|
+
emptyState?: GridViewEmptyState;
|
|
580
|
+
isLoading?: boolean;
|
|
581
|
+
loadingMessage?: string;
|
|
582
|
+
gridOptions?: Partial<GridOptions<T>>;
|
|
583
|
+
onGridReady?: (params: {
|
|
584
|
+
api: GridApi<T>;
|
|
585
|
+
}) => void;
|
|
586
|
+
onCellValueChanged?: (event: CellValueChangedEvent<T>) => void;
|
|
587
|
+
onRowClick?: (item: T) => void;
|
|
588
|
+
persistState?: boolean;
|
|
589
|
+
gridId?: string;
|
|
590
|
+
gridState?: GridState;
|
|
591
|
+
onGridStateChange?: (state: GridState) => void;
|
|
592
|
+
initialVisibleColumns?: string[];
|
|
593
|
+
className?: string;
|
|
594
|
+
height?: string | number;
|
|
595
|
+
rowHeight?: number;
|
|
596
|
+
testId?: string;
|
|
597
|
+
pinnedBottomRowData?: T[];
|
|
598
|
+
pinnedTopRowData?: T[];
|
|
599
|
+
pagination?: boolean;
|
|
600
|
+
paginationPageSizeSelector?: number[];
|
|
601
|
+
showEmpty?: boolean;
|
|
602
|
+
}
|
|
603
|
+
declare function GridView<T>({ items, columnDefs, getItemId, selection, emptyState, isLoading, loadingMessage, gridOptions, onGridReady, onCellValueChanged, onRowClick, persistState, gridState, onGridStateChange, initialVisibleColumns, className, height, rowHeight, testId, pinnedBottomRowData, pinnedTopRowData, pagination, paginationPageSizeSelector, showEmpty, }: GridViewProps<T>): react__default.JSX.Element;
|
|
604
|
+
|
|
605
|
+
/** Centered placeholder for empty lists/panels: icon + title + description + action. */
|
|
606
|
+
declare function EmptyState({ icon: Icon, title, description, action, className, testId, }: {
|
|
607
|
+
icon?: LucideIcon;
|
|
608
|
+
title: string;
|
|
609
|
+
description?: string;
|
|
610
|
+
action?: ReactNode;
|
|
611
|
+
className?: string;
|
|
612
|
+
testId?: string;
|
|
613
|
+
}): react.JSX.Element;
|
|
614
|
+
|
|
615
|
+
/** Standard back-office page title block: eyebrow + title + description + actions. */
|
|
616
|
+
declare function PageHeader({ title, eyebrow, description, actions, className, testId, }: {
|
|
617
|
+
title: string;
|
|
618
|
+
eyebrow?: string;
|
|
619
|
+
description?: string;
|
|
620
|
+
actions?: ReactNode;
|
|
621
|
+
className?: string;
|
|
622
|
+
testId?: string;
|
|
623
|
+
}): react.JSX.Element;
|
|
624
|
+
|
|
625
|
+
/** Editorial heading block for marketing sections: eyebrow + title + lead. */
|
|
626
|
+
declare function SectionHeading({ eyebrow, title, lead, align, className, invert, }: {
|
|
627
|
+
eyebrow?: string;
|
|
628
|
+
title: string;
|
|
629
|
+
lead?: string;
|
|
630
|
+
align?: 'left' | 'center';
|
|
631
|
+
className?: string;
|
|
632
|
+
invert?: boolean;
|
|
633
|
+
}): react.JSX.Element;
|
|
634
|
+
|
|
635
|
+
/** Search + filter bar used above every list view. */
|
|
636
|
+
declare function Toolbar({ search, onSearch, searchPlaceholder, filters, actions, className, testId, }: {
|
|
637
|
+
search?: string;
|
|
638
|
+
onSearch?: (v: string) => void;
|
|
639
|
+
searchPlaceholder?: string;
|
|
640
|
+
filters?: ReactNode;
|
|
641
|
+
actions?: ReactNode;
|
|
642
|
+
className?: string;
|
|
643
|
+
testId?: string;
|
|
644
|
+
}): react.JSX.Element;
|
|
645
|
+
|
|
646
|
+
interface MetricCardProps {
|
|
647
|
+
label: string;
|
|
648
|
+
value: string | number;
|
|
649
|
+
icon?: LucideIcon;
|
|
650
|
+
hint?: string;
|
|
651
|
+
delta?: {
|
|
652
|
+
value: string;
|
|
653
|
+
direction: 'up' | 'down';
|
|
654
|
+
good?: boolean;
|
|
655
|
+
};
|
|
656
|
+
/** Icon accent, keyed to the firm's brand token contract. */
|
|
657
|
+
accent?: 'ink' | 'emerald' | 'brass';
|
|
658
|
+
testId?: string;
|
|
659
|
+
}
|
|
660
|
+
/** The one true KPI tile. Used across every dashboard and module header. */
|
|
661
|
+
declare function MetricCard({ label, value, icon: Icon, hint, delta, accent, testId, }: MetricCardProps): react.JSX.Element;
|
|
662
|
+
|
|
663
|
+
type BlockImageAspect = '16:9' | '4:3' | '1:1' | '21:9' | '4:5';
|
|
664
|
+
type BlockImageRounded = 'none' | 'md' | 'lg' | 'full';
|
|
665
|
+
interface BlockImageProps {
|
|
666
|
+
src?: string;
|
|
667
|
+
alt?: string;
|
|
668
|
+
aspect?: BlockImageAspect;
|
|
669
|
+
rounded?: BlockImageRounded;
|
|
670
|
+
className?: string;
|
|
671
|
+
testId?: string;
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Image with a graceful, self-contained fallback: when `src` is empty it renders a
|
|
675
|
+
* token-clean gradient placeholder (no network needed) so the palette always looks
|
|
676
|
+
* good in the editor and in demos. Token-only classes so it themes per firm.
|
|
677
|
+
*/
|
|
678
|
+
declare function BlockImage({ src, alt, aspect, rounded, className, testId, }: BlockImageProps): react.JSX.Element;
|
|
679
|
+
|
|
680
|
+
type HeroTone = 'default' | 'muted' | 'ink';
|
|
681
|
+
type HeroAlign = 'left' | 'center';
|
|
682
|
+
interface HeroCta {
|
|
683
|
+
label?: string;
|
|
684
|
+
href?: string;
|
|
685
|
+
}
|
|
686
|
+
interface HeroProps {
|
|
687
|
+
eyebrow?: string;
|
|
688
|
+
title: string;
|
|
689
|
+
subtitle?: string;
|
|
690
|
+
primaryCta?: HeroCta;
|
|
691
|
+
secondaryCta?: HeroCta;
|
|
692
|
+
align?: HeroAlign;
|
|
693
|
+
tone?: HeroTone;
|
|
694
|
+
imageUrl?: string;
|
|
695
|
+
testId?: string;
|
|
696
|
+
}
|
|
697
|
+
/** Editorial marketing hero: eyebrow, headline, subtitle, two CTAs and a visual. */
|
|
698
|
+
declare function Hero({ eyebrow, title, subtitle, primaryCta, secondaryCta, align, tone, imageUrl, testId, }: HeroProps): react.JSX.Element;
|
|
699
|
+
|
|
700
|
+
interface FeatureItem {
|
|
701
|
+
icon?: LucideIcon;
|
|
702
|
+
title: string;
|
|
703
|
+
description?: string;
|
|
704
|
+
}
|
|
705
|
+
interface FeatureGridProps {
|
|
706
|
+
columns?: '2' | '3';
|
|
707
|
+
items: FeatureItem[];
|
|
708
|
+
testId?: string;
|
|
709
|
+
}
|
|
710
|
+
/** Responsive grid of icon + title + description feature cards. */
|
|
711
|
+
declare function FeatureGrid({ columns, items, testId }: FeatureGridProps): react.JSX.Element;
|
|
712
|
+
|
|
713
|
+
interface StatItem {
|
|
714
|
+
value: string;
|
|
715
|
+
label: string;
|
|
716
|
+
}
|
|
717
|
+
interface StatGroupProps {
|
|
718
|
+
items: StatItem[];
|
|
719
|
+
testId?: string;
|
|
720
|
+
}
|
|
721
|
+
/** Big-number stat group — headline metrics with captions. */
|
|
722
|
+
declare function StatGroup({ items, testId }: StatGroupProps): react.JSX.Element;
|
|
723
|
+
|
|
724
|
+
interface TestimonialProps {
|
|
725
|
+
quote: string;
|
|
726
|
+
author?: string;
|
|
727
|
+
role?: string;
|
|
728
|
+
avatarUrl?: string;
|
|
729
|
+
testId?: string;
|
|
730
|
+
}
|
|
731
|
+
/** A single pull-quote testimonial with an author and optional avatar. */
|
|
732
|
+
declare function Testimonial({ quote, author, role, avatarUrl, testId }: TestimonialProps): react.JSX.Element;
|
|
733
|
+
|
|
734
|
+
type CTABannerTone = 'default' | 'muted' | 'ink';
|
|
735
|
+
interface CTABannerProps {
|
|
736
|
+
title: string;
|
|
737
|
+
subtitle?: string;
|
|
738
|
+
cta?: {
|
|
739
|
+
label?: string;
|
|
740
|
+
href?: string;
|
|
741
|
+
};
|
|
742
|
+
tone?: CTABannerTone;
|
|
743
|
+
testId?: string;
|
|
744
|
+
}
|
|
745
|
+
/** Full-width call-to-action banner. */
|
|
746
|
+
declare function CTABanner({ title, subtitle, cta, tone, testId }: CTABannerProps): react.JSX.Element;
|
|
747
|
+
|
|
748
|
+
interface LogoItem {
|
|
749
|
+
label: string;
|
|
750
|
+
}
|
|
751
|
+
interface LogoCloudProps {
|
|
752
|
+
items: LogoItem[];
|
|
753
|
+
testId?: string;
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Text-based "logo" wall — brand names rendered as muted pill marks. Self-contained
|
|
757
|
+
* (no external images) so the palette looks complete offline.
|
|
758
|
+
*/
|
|
759
|
+
declare function LogoCloud({ items, testId }: LogoCloudProps): react.JSX.Element;
|
|
760
|
+
|
|
761
|
+
interface FaqItem {
|
|
762
|
+
question: string;
|
|
763
|
+
answer?: string;
|
|
764
|
+
}
|
|
765
|
+
interface FAQProps {
|
|
766
|
+
items: FaqItem[];
|
|
767
|
+
testId?: string;
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* Accessible disclosure list. Built on native `<details>`/`<summary>` so it works
|
|
771
|
+
* without client state (correct in the Puck preview and static renders) and stays
|
|
772
|
+
* keyboard-accessible. Token-only styling.
|
|
773
|
+
*/
|
|
774
|
+
declare function FAQ({ items, testId }: FAQProps): react.JSX.Element;
|
|
775
|
+
|
|
776
|
+
interface RichTextBlockProps {
|
|
777
|
+
content: string;
|
|
778
|
+
testId?: string;
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* Prose block. Reuses the shared markdown renderer (`AiMarkdown`) so long-form copy
|
|
782
|
+
* gets the one house style for headings, lists, tables and emphasis — token-clean
|
|
783
|
+
* and themeable per firm.
|
|
784
|
+
*/
|
|
785
|
+
declare function RichTextBlock({ content, testId }: RichTextBlockProps): react.JSX.Element;
|
|
786
|
+
|
|
787
|
+
type CalloutVariant = 'info' | 'success' | 'warning' | 'danger';
|
|
788
|
+
interface CalloutProps {
|
|
789
|
+
variant?: CalloutVariant;
|
|
790
|
+
title?: string;
|
|
791
|
+
body?: string;
|
|
792
|
+
testId?: string;
|
|
793
|
+
}
|
|
794
|
+
/** Inline notice box in one of four semantic tones. */
|
|
795
|
+
declare function Callout({ variant, title, body, testId }: CalloutProps): react.JSX.Element;
|
|
796
|
+
|
|
797
|
+
type AvatarBlockSize = 'sm' | 'md' | 'lg' | 'xl';
|
|
798
|
+
interface AvatarBlockProps {
|
|
799
|
+
src?: string;
|
|
800
|
+
name?: string;
|
|
801
|
+
size?: AvatarBlockSize;
|
|
802
|
+
testId?: string;
|
|
803
|
+
}
|
|
804
|
+
/** Avatar with an initials fallback when no image is supplied. */
|
|
805
|
+
declare function AvatarBlock({ src, name, size, testId }: AvatarBlockProps): react.JSX.Element;
|
|
806
|
+
|
|
807
|
+
interface NavLink {
|
|
808
|
+
label: string;
|
|
809
|
+
href?: string;
|
|
810
|
+
}
|
|
811
|
+
interface NavbarProps {
|
|
812
|
+
brand: string;
|
|
813
|
+
links: NavLink[];
|
|
814
|
+
cta?: {
|
|
815
|
+
label?: string;
|
|
816
|
+
href?: string;
|
|
817
|
+
};
|
|
818
|
+
testId?: string;
|
|
819
|
+
}
|
|
820
|
+
/** Marketing top navigation: brand, inline links and an optional CTA button. */
|
|
821
|
+
declare function Navbar({ brand, links, cta, testId }: NavbarProps): react.JSX.Element;
|
|
822
|
+
|
|
823
|
+
interface FooterLink {
|
|
824
|
+
label: string;
|
|
825
|
+
href?: string;
|
|
826
|
+
}
|
|
827
|
+
interface FooterColumn {
|
|
828
|
+
heading: string;
|
|
829
|
+
links: FooterLink[];
|
|
830
|
+
}
|
|
831
|
+
interface FooterProps {
|
|
832
|
+
columns: FooterColumn[];
|
|
833
|
+
copyright?: string;
|
|
834
|
+
testId?: string;
|
|
835
|
+
}
|
|
836
|
+
/** Site footer: a row of link columns plus a copyright line. */
|
|
837
|
+
declare function Footer({ columns, copyright, testId }: FooterProps): react.JSX.Element;
|
|
838
|
+
|
|
839
|
+
interface ListItem {
|
|
840
|
+
text: string;
|
|
841
|
+
}
|
|
842
|
+
interface ListBlockProps {
|
|
843
|
+
ordered?: boolean;
|
|
844
|
+
items: ListItem[];
|
|
845
|
+
testId?: string;
|
|
846
|
+
}
|
|
847
|
+
/** Ordered or unordered prose list. Token-clean. */
|
|
848
|
+
declare function ListBlock({ ordered, items, testId }: ListBlockProps): react.JSX.Element;
|
|
849
|
+
|
|
850
|
+
type ColumnsCount = 2 | 3 | 4;
|
|
851
|
+
type ColumnsGap = 'sm' | 'md' | 'lg';
|
|
852
|
+
interface ColumnsProps {
|
|
853
|
+
count?: ColumnsCount;
|
|
854
|
+
gap?: ColumnsGap;
|
|
855
|
+
columns: ReactNode[];
|
|
856
|
+
testId?: string;
|
|
857
|
+
}
|
|
858
|
+
/** Responsive N-column row. Each column hosts a nested Puck slot. */
|
|
859
|
+
declare function Columns({ count, gap, columns, testId }: ColumnsProps): react.JSX.Element;
|
|
860
|
+
|
|
861
|
+
type SpacerSize = 'sm' | 'md' | 'lg' | 'xl';
|
|
862
|
+
interface SpacerProps {
|
|
863
|
+
size?: SpacerSize;
|
|
864
|
+
testId?: string;
|
|
865
|
+
}
|
|
866
|
+
/** Vertical whitespace. */
|
|
867
|
+
declare function Spacer({ size, testId }: SpacerProps): react.JSX.Element;
|
|
868
|
+
|
|
869
|
+
type ContainerWidth = 'narrow' | 'default' | 'wide' | 'full';
|
|
870
|
+
type ContainerPadding = 'none' | 'sm' | 'md' | 'lg';
|
|
871
|
+
interface ContainerProps {
|
|
872
|
+
width?: ContainerWidth;
|
|
873
|
+
padding?: ContainerPadding;
|
|
874
|
+
children: ReactNode;
|
|
875
|
+
testId?: string;
|
|
876
|
+
}
|
|
877
|
+
/** Max-width wrapper that centres and pads a nested Puck slot. */
|
|
878
|
+
declare function Container({ width, padding, children, testId }: ContainerProps): react.JSX.Element;
|
|
879
|
+
|
|
880
|
+
/**
|
|
881
|
+
* Navigation shapes shared by firm app shells. The concrete nav manifest (which
|
|
882
|
+
* modules, in what order) is per-firm data the app supplies; these interfaces
|
|
883
|
+
* give it a typed contract the shared shell can render.
|
|
884
|
+
*/
|
|
885
|
+
|
|
886
|
+
interface AppNavItem {
|
|
887
|
+
to: string;
|
|
888
|
+
label: string;
|
|
889
|
+
icon: LucideIcon;
|
|
890
|
+
id: string;
|
|
891
|
+
/** true for ARBI-powered modules (document management + agentic AI). */
|
|
892
|
+
arbi?: boolean;
|
|
893
|
+
end?: boolean;
|
|
894
|
+
}
|
|
895
|
+
interface AppNavSection {
|
|
896
|
+
heading: string;
|
|
897
|
+
items: AppNavItem[];
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* Shared studio types — the persistence contract, page metadata and the AI
|
|
902
|
+
* prompt-builder signature the firm supplies. Firm-agnostic: a firm app wires
|
|
903
|
+
* these to its own marketing workspace and voice.
|
|
904
|
+
*/
|
|
905
|
+
|
|
906
|
+
/** The Arbi instance type, borrowed from the hook so we don't import internals. */
|
|
907
|
+
type Arbi = ReturnType<typeof useArbi>;
|
|
908
|
+
/** A page the studio can author (slug → friendly label). */
|
|
909
|
+
interface StudioPageMeta {
|
|
910
|
+
slug: string;
|
|
911
|
+
label: string;
|
|
912
|
+
}
|
|
913
|
+
/** Where a save/load ultimately resolved. */
|
|
914
|
+
type PersistVia = 'arbi' | 'local';
|
|
915
|
+
interface LoadResult<T> {
|
|
916
|
+
data: T | null;
|
|
917
|
+
via: PersistVia;
|
|
918
|
+
}
|
|
919
|
+
/** The save/load surface the studio + renderer consume. */
|
|
920
|
+
interface StudioPersistence {
|
|
921
|
+
savePage: (arbi: Arbi | null, live: boolean, slug: string, data: unknown) => Promise<PersistVia>;
|
|
922
|
+
loadPage: <T>(arbi: Arbi | null, live: boolean, slug: string) => Promise<LoadResult<T>>;
|
|
923
|
+
saveTheme: (arbi: Arbi | null, live: boolean, theme: unknown) => Promise<PersistVia>;
|
|
924
|
+
loadTheme: <T>(arbi: Arbi | null, live: boolean) => Promise<LoadResult<T>>;
|
|
925
|
+
}
|
|
926
|
+
/** Build an AI instruction for a field, given its kind, label and current text. */
|
|
927
|
+
type BuildPrompt = (a: {
|
|
928
|
+
kind: string;
|
|
929
|
+
label: string;
|
|
930
|
+
current: string;
|
|
931
|
+
}) => string;
|
|
932
|
+
|
|
933
|
+
interface StudioEditorProps {
|
|
934
|
+
config: Config;
|
|
935
|
+
pages: readonly StudioPageMeta[];
|
|
936
|
+
persistence: StudioPersistence;
|
|
937
|
+
themeBundle: ThemeBundle;
|
|
938
|
+
defaultTheme: ThemeTokens;
|
|
939
|
+
brandLabel: string;
|
|
940
|
+
backHref?: string;
|
|
941
|
+
backLabel?: string;
|
|
942
|
+
live: boolean;
|
|
943
|
+
initialSlug?: string;
|
|
944
|
+
testIdPrefix?: string;
|
|
945
|
+
}
|
|
946
|
+
declare function StudioEditor({ config, pages, persistence, themeBundle, defaultTheme, brandLabel, backHref, backLabel, live, initialSlug, testIdPrefix, }: StudioEditorProps): react.JSX.Element;
|
|
947
|
+
|
|
948
|
+
interface PageRendererProps {
|
|
949
|
+
config: Config;
|
|
950
|
+
persistence: StudioPersistence;
|
|
951
|
+
defaultTheme: ThemeTokens;
|
|
952
|
+
live: boolean;
|
|
953
|
+
slug?: string;
|
|
954
|
+
emptyState?: {
|
|
955
|
+
title: string;
|
|
956
|
+
body: string;
|
|
957
|
+
};
|
|
958
|
+
testIdPrefix?: string;
|
|
959
|
+
}
|
|
960
|
+
declare function PageRenderer({ config, persistence, defaultTheme, live, slug: slugProp, emptyState, testIdPrefix, }: PageRendererProps): react.JSX.Element;
|
|
961
|
+
|
|
962
|
+
interface StudioPersistenceConfig {
|
|
963
|
+
/** The public marketing workspace that backs the site builder. */
|
|
964
|
+
workspaceId: string;
|
|
965
|
+
/** Folder inside the workspace where studio artefacts live. */
|
|
966
|
+
folder?: string;
|
|
967
|
+
/** Prefix for the localStorage mirror keys (e.g. "clarum-studio-"). */
|
|
968
|
+
storagePrefix: string;
|
|
969
|
+
/** localStorage key for the theme mirror (default `${storagePrefix}theme`). */
|
|
970
|
+
themeStorageKey?: string;
|
|
971
|
+
/** File name for the persisted theme document (default "theme.json"). */
|
|
972
|
+
themeFileName?: string;
|
|
973
|
+
/** File name for a page's persisted document (default `${slug}.json`). */
|
|
974
|
+
pageFileName?: (slug: string) => string;
|
|
975
|
+
}
|
|
976
|
+
declare function createStudioPersistence(cfg: StudioPersistenceConfig): StudioPersistence;
|
|
977
|
+
|
|
978
|
+
interface AiFieldsConfig {
|
|
979
|
+
/** The marketing workspace the AI grounds on. */
|
|
980
|
+
workspaceId: string;
|
|
981
|
+
/** Turns a field's kind/label/current text into an instruction. */
|
|
982
|
+
buildPrompt: BuildPrompt;
|
|
983
|
+
/** Default prompt kind when a field does not specify one. */
|
|
984
|
+
defaultKind?: string;
|
|
985
|
+
/** Testid namespace for the field + write button. */
|
|
986
|
+
testIdPrefix?: string;
|
|
987
|
+
}
|
|
988
|
+
interface AiFields {
|
|
989
|
+
aiText: (label: string, placeholder?: string) => CustomField<string>;
|
|
990
|
+
aiTextarea: (label: string, placeholder?: string, kind?: string) => CustomField<string>;
|
|
991
|
+
}
|
|
992
|
+
declare function createAiFields(cfg: AiFieldsConfig): AiFields;
|
|
993
|
+
|
|
994
|
+
interface SectionProps {
|
|
995
|
+
children: ReactNode;
|
|
996
|
+
tone?: 'default' | 'muted' | 'ink';
|
|
997
|
+
className?: string;
|
|
998
|
+
}
|
|
999
|
+
declare function Section({ children, tone, className }: SectionProps): react.JSX.Element;
|
|
1000
|
+
|
|
1001
|
+
/**
|
|
1002
|
+
* Shared field helpers for the block registry. Every block config — the built-ins
|
|
1003
|
+
* in {@link createArbiBlocksConfig} and the website blocks in
|
|
1004
|
+
* {@link file://./websiteBlocks.tsx} — builds its Puck fields from this one kit so
|
|
1005
|
+
* the AI-or-plain copy fallback, the icon `select`, the tone/bool radios and the
|
|
1006
|
+
* stable render-time testid are declared once.
|
|
1007
|
+
*/
|
|
1008
|
+
|
|
1009
|
+
declare const iconMap: Record<string, LucideIcon>;
|
|
1010
|
+
/** The bundle of field helpers a block config needs. */
|
|
1011
|
+
interface BlockKit {
|
|
1012
|
+
/** Single-line copy: AI-authorable when a firm supplies aiFields, else plain text. */
|
|
1013
|
+
textField: (label: string, placeholder?: string) => Field;
|
|
1014
|
+
/** Multi-line copy: AI-authorable when a firm supplies aiFields, else plain textarea. */
|
|
1015
|
+
textareaField: (label: string, placeholder?: string, kind?: string) => Field;
|
|
1016
|
+
/** A Yes/No radio. */
|
|
1017
|
+
boolRadio: (label: string) => Field;
|
|
1018
|
+
/** A `select` over {@link iconMap} names. */
|
|
1019
|
+
iconField: (label?: string) => Field;
|
|
1020
|
+
/** Resolve an icon name to its Lucide component. */
|
|
1021
|
+
resolveIcon: (name?: string) => LucideIcon | undefined;
|
|
1022
|
+
/** Stable, e2e-locatable testid derived from a block's Puck id. */
|
|
1023
|
+
blockTestId: (id: string) => string;
|
|
1024
|
+
}
|
|
1025
|
+
/** Build the shared field helpers, closing over the firm's optional AI fields. */
|
|
1026
|
+
declare function createBlockKit(ai?: AiFields): BlockKit;
|
|
1027
|
+
|
|
1028
|
+
interface ArbiBlocksConfigOptions {
|
|
1029
|
+
/**
|
|
1030
|
+
* Firm AI fields from {@link createAiFields}. When present, every string copy
|
|
1031
|
+
* prop becomes an AI-authorable field; when absent, plain Puck text inputs.
|
|
1032
|
+
*/
|
|
1033
|
+
aiFields?: AiFields;
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
declare function createArbiBlocksConfig(opts?: ArbiBlocksConfigOptions): Config;
|
|
1037
|
+
|
|
1038
|
+
/** Build every website block, closing over the shared field {@link BlockKit}. */
|
|
1039
|
+
declare function createWebsiteBlocks(kit: BlockKit): Record<string, ComponentConfig>;
|
|
1040
|
+
|
|
1041
|
+
/**
|
|
1042
|
+
* Module registry types — a typed model of a firm's back-office modules,
|
|
1043
|
+
* derived from its canonical nav. The builder toggles/reorders these; the
|
|
1044
|
+
* sidebar consumes the resolved result. Firm-agnostic: the nav manifest is
|
|
1045
|
+
* supplied to {@link createModuleRegistry}.
|
|
1046
|
+
*/
|
|
1047
|
+
|
|
1048
|
+
interface BuilderModule {
|
|
1049
|
+
id: string;
|
|
1050
|
+
label: string;
|
|
1051
|
+
/** Section heading it belongs to (Practice / Knowledge & AI / …). */
|
|
1052
|
+
section: string;
|
|
1053
|
+
icon: LucideIcon;
|
|
1054
|
+
to: string;
|
|
1055
|
+
arbi: boolean;
|
|
1056
|
+
end: boolean;
|
|
1057
|
+
/** Original position — global index across all sections. */
|
|
1058
|
+
defaultOrder: number;
|
|
1059
|
+
}
|
|
1060
|
+
interface ResolvedModule extends BuilderModule {
|
|
1061
|
+
enabled: boolean;
|
|
1062
|
+
order: number;
|
|
1063
|
+
}
|
|
1064
|
+
interface ModuleRegistry {
|
|
1065
|
+
DEFAULT_MODULES: BuilderModule[];
|
|
1066
|
+
SECTION_ORDER: string[];
|
|
1067
|
+
toNavItem: (m: BuilderModule) => AppNavItem;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
/**
|
|
1071
|
+
* Module registry — a typed model of the back-office modules derived from a
|
|
1072
|
+
* firm's canonical nav. We read the nav rather than re-declaring it so the
|
|
1073
|
+
* registry can never drift from the shipped nav (DRY). The builder
|
|
1074
|
+
* toggles/reorders these; the sidebar can consume the result via the module
|
|
1075
|
+
* store's `useEnabledNav()` without any nav data being duplicated here.
|
|
1076
|
+
*/
|
|
1077
|
+
|
|
1078
|
+
/** Build a registry from a firm's nav manifest. */
|
|
1079
|
+
declare function createModuleRegistry(appNav: AppNavSection[]): ModuleRegistry;
|
|
1080
|
+
|
|
1081
|
+
interface ModuleOverride {
|
|
1082
|
+
enabled: boolean;
|
|
1083
|
+
/** Effective order within the section. */
|
|
1084
|
+
order: number;
|
|
1085
|
+
}
|
|
1086
|
+
interface ModuleStore {
|
|
1087
|
+
/** Sparse overrides keyed by module id; absent → registry default. */
|
|
1088
|
+
overrides: Record<string, ModuleOverride>;
|
|
1089
|
+
toggle: (id: string) => void;
|
|
1090
|
+
/** Move a module up/down among its enabled+disabled section siblings. */
|
|
1091
|
+
move: (id: string, dir: 'up' | 'down') => void;
|
|
1092
|
+
reset: () => void;
|
|
1093
|
+
}
|
|
1094
|
+
interface ModuleStoreBundle {
|
|
1095
|
+
useModuleStore: UseBoundStore<StoreApi<ModuleStore>>;
|
|
1096
|
+
useResolvedModules: () => {
|
|
1097
|
+
section: string;
|
|
1098
|
+
modules: ResolvedModule[];
|
|
1099
|
+
}[];
|
|
1100
|
+
useEnabledNav: () => AppNavSection[];
|
|
1101
|
+
}
|
|
1102
|
+
declare function createModuleStore({ registry, storageKey, }: {
|
|
1103
|
+
registry: ModuleRegistry;
|
|
1104
|
+
storageKey: string;
|
|
1105
|
+
}): ModuleStoreBundle;
|
|
1106
|
+
|
|
1107
|
+
/**
|
|
1108
|
+
* Agent-selection store — which ARBI behaviour configurations and agents the
|
|
1109
|
+
* firm marks "active" for this vertical. Pure selection state (the configs and
|
|
1110
|
+
* agents themselves come live from ARBI via React Query), persisted so the
|
|
1111
|
+
* choice seeds the exported vertical config.
|
|
1112
|
+
*
|
|
1113
|
+
* Firm-agnostic: {@link createAgentSelectionStore} parameterises the persist key.
|
|
1114
|
+
*/
|
|
1115
|
+
|
|
1116
|
+
interface AgentSelectionStore {
|
|
1117
|
+
/** Configuration ext_ids marked active. */
|
|
1118
|
+
configIds: string[];
|
|
1119
|
+
/** Agent ext_ids marked active. */
|
|
1120
|
+
agentIds: string[];
|
|
1121
|
+
toggleConfig: (id: string) => void;
|
|
1122
|
+
toggleAgent: (id: string) => void;
|
|
1123
|
+
clear: () => void;
|
|
1124
|
+
}
|
|
1125
|
+
declare function createAgentSelectionStore({ storageKey, }: {
|
|
1126
|
+
storageKey: string;
|
|
1127
|
+
}): UseBoundStore<StoreApi<AgentSelectionStore>>;
|
|
1128
|
+
|
|
1129
|
+
/**
|
|
1130
|
+
* Vertical-config export shapes — the JSON seed a generator consumes to stamp
|
|
1131
|
+
* out a new vertical app (theme + enabled/ordered modules + selected agents).
|
|
1132
|
+
*/
|
|
1133
|
+
|
|
1134
|
+
interface ExportedModule {
|
|
1135
|
+
id: string;
|
|
1136
|
+
label: string;
|
|
1137
|
+
section: string;
|
|
1138
|
+
to: string;
|
|
1139
|
+
arbi: boolean;
|
|
1140
|
+
enabled: boolean;
|
|
1141
|
+
order: number;
|
|
1142
|
+
}
|
|
1143
|
+
interface VerticalConfig {
|
|
1144
|
+
version: 1;
|
|
1145
|
+
generatedAt: string;
|
|
1146
|
+
vertical: {
|
|
1147
|
+
name: string;
|
|
1148
|
+
key: string;
|
|
1149
|
+
};
|
|
1150
|
+
theme: ThemeTokens;
|
|
1151
|
+
modules: ExportedModule[];
|
|
1152
|
+
agents: {
|
|
1153
|
+
configIds: string[];
|
|
1154
|
+
agentIds: string[];
|
|
1155
|
+
};
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
/**
|
|
1159
|
+
* Vertical-config export — combine the configured theme, enabled/ordered
|
|
1160
|
+
* modules and selected ARBI agent configs into one JSON document. This is the
|
|
1161
|
+
* seed a generator would consume to stamp out a new vertical app.
|
|
1162
|
+
*
|
|
1163
|
+
* Firm-agnostic: {@link createVerticalExport} closes over the theme/module/agent
|
|
1164
|
+
* stores and the module registry.
|
|
1165
|
+
*/
|
|
1166
|
+
|
|
1167
|
+
interface VerticalExportConfig {
|
|
1168
|
+
vertical: {
|
|
1169
|
+
name: string;
|
|
1170
|
+
key: string;
|
|
1171
|
+
};
|
|
1172
|
+
themeStore: UseBoundStore<StoreApi<ThemeState>>;
|
|
1173
|
+
moduleStore: ModuleStoreBundle;
|
|
1174
|
+
agentStore: UseBoundStore<StoreApi<AgentSelectionStore>>;
|
|
1175
|
+
registry: ModuleRegistry;
|
|
1176
|
+
}
|
|
1177
|
+
interface VerticalExport {
|
|
1178
|
+
buildVerticalConfig: () => VerticalConfig;
|
|
1179
|
+
serialize: (cfg?: VerticalConfig) => string;
|
|
1180
|
+
download: (cfg?: VerticalConfig) => void;
|
|
1181
|
+
}
|
|
1182
|
+
declare function createVerticalExport(cfg: VerticalExportConfig): VerticalExport;
|
|
1183
|
+
|
|
1184
|
+
type AgentStore$1 = UseBoundStore<StoreApi<AgentSelectionStore>>;
|
|
1185
|
+
interface VerticalBuilderProps {
|
|
1186
|
+
themeBundle: ThemeBundle;
|
|
1187
|
+
moduleStore: ModuleStoreBundle;
|
|
1188
|
+
agentStore: AgentStore$1;
|
|
1189
|
+
exporter: VerticalExport;
|
|
1190
|
+
themePreview: {
|
|
1191
|
+
title: string;
|
|
1192
|
+
subtitle?: string;
|
|
1193
|
+
};
|
|
1194
|
+
header?: ReactNode;
|
|
1195
|
+
studioHref?: string;
|
|
1196
|
+
testIdPrefix?: string;
|
|
1197
|
+
}
|
|
1198
|
+
declare function VerticalBuilder({ themeBundle, moduleStore, agentStore, exporter, themePreview, header, testIdPrefix, }: VerticalBuilderProps): react.JSX.Element;
|
|
1199
|
+
|
|
1200
|
+
interface ModuleManagerProps {
|
|
1201
|
+
store: ModuleStoreBundle;
|
|
1202
|
+
testIdPrefix?: string;
|
|
1203
|
+
}
|
|
1204
|
+
declare function ModuleManager({ store, testIdPrefix }: ModuleManagerProps): react.JSX.Element;
|
|
1205
|
+
|
|
1206
|
+
type AgentStore = UseBoundStore<StoreApi<AgentSelectionStore>>;
|
|
1207
|
+
interface AgentsPanelProps {
|
|
1208
|
+
store: AgentStore;
|
|
1209
|
+
testIdPrefix?: string;
|
|
1210
|
+
}
|
|
1211
|
+
declare function AgentsPanel({ store, testIdPrefix }: AgentsPanelProps): react.JSX.Element;
|
|
1212
|
+
|
|
1213
|
+
export { type AgentSelectionStore, AgentsPanel, type AgentsPanelProps, type AiFields, type AiFieldsConfig, type AppNavItem, type AppNavSection, type ApplyThemeOptions, type ArbiBlocksConfigOptions, type ArbiMode, type ArbiUser, type AssistantCitation, type AssistantMessage, type AssistantStep, AvatarBlock, type AvatarBlockProps, BlockImage, type BlockImageProps, type BlockKit, type BuildPrompt, type BuilderModule, CTABanner, type CTABannerProps, Callout, type CalloutProps, type CalloutVariant, type ColorToken, type Column, Columns, type ColumnsProps, ConnectionProvider, type ConnectionStatus, Container, type ContainerProps, DataTable, DataTableBlock, type DataTableBlockColumn, type DataTableBlockProps, EmptyState, type ExportedModule, FAQ, type FAQProps, type FaqItem, FeatureGrid, type FeatureGridProps, type FeatureItem, type FirmArbiConfig, Footer, type FooterColumn, type FooterLink, type FooterProps, GridView, type GridViewEmptyState, type GridViewProps, type GridViewSelection, Hero, type HeroCta, type HeroProps, LegalArbiProvider, ListBlock, type ListBlockProps, type ListItem, type LoadResult, LogoCloud, type LogoCloudProps, type LogoItem, type MatterContext, MetricCard, type MetricCardProps, ModuleManager, type ModuleManagerProps, type ModuleRegistry, type ModuleStoreBundle, type NavLink, Navbar, type NavbarProps, PageHeader, PageRenderer, type PageRendererProps, type PersistVia, type PromptConfig, type PromptLibrary, type Prompts, type ResolvedModule, RichTextBlock, type RichTextBlockProps, Section, SectionHeading, type SectionProps, Spacer, type SpacerProps, StatGroup, type StatGroupProps, type StatItem, StudioEditor, type StudioEditorProps, type StudioPageMeta, type StudioPersistence, type StudioPersistenceConfig, THEME_STYLE_ID, Testimonial, type TestimonialProps, type ThemeBundle, ThemeEditor, type ThemeEditorProps, type ThemeEmitMode, type ThemeFonts, type ThemePreset, type ThemeState, type ThemeStoreConfig, type ThemeTokens, Toolbar, VerticalBuilder, type VerticalBuilderProps, type VerticalConfig, type VerticalExport, type VerticalExportConfig, applyStoredTheme, applyThemeVars, clearThemeVars, createAgentSelectionStore, createAiFields, createArbiBlocksConfig, createBlockKit, createModuleRegistry, createModuleStore, createPromptLibrary, createStudioPersistence, createThemeStore, createVerticalExport, createWebsiteBlocks, daysUntil, fmtDate, fmtDateTime, fromNow, gbp, hrs, iconMap, initials, isAuthenticated, matterToContext, pct, tid, toHslTriplet, useConnection, useFirmAiTask, useSemanticSearch, useWorkspaceDocs };
|