@arbidocs/blocks 0.3.119 → 0.3.121

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.d.ts CHANGED
@@ -1,12 +1,14 @@
1
- import { UseBoundStore, StoreApi } from 'zustand';
1
+ import { ThemeBundle, ThemeTokens, ThemeState } from './site/index.js';
2
+ export { Accordion, AccordionItem, AccordionProps, ApplyThemeOptions, AvatarBlock, AvatarBlockProps, Banner, BannerProps, BannerTone, BlockImage, BlockImageProps, BlockLink, BlockLinkComponent, BlockLinkProps, CTABanner, CTABannerProps, Callout, CalloutProps, CalloutVariant, ColorToken, Columns, ColumnsProps, ContactField, ContactForm, ContactFormProps, Container, ContainerProps, FAQ, FAQProps, FONT_PAIRINGS, FaqItem, FeatureGrid, FeatureGridProps, FeatureItem, FontPairing, Footer, FooterColumn, FooterLink, FooterProps, Gallery, GalleryImage, GalleryProps, Hero, HeroCta, HeroProps, ListBlock, ListBlockProps, ListItem, LogoCloud, LogoCloudProps, LogoItem, MediaText, MediaTextProps, NavLink, Navbar, NavbarProps, PricingPlan, PricingTable, PricingTableProps, RichTextBlock, RichTextBlockProps, SectionHeading, Spacer, SpacerProps, StepItem, Steps, StepsProps, THEME_STYLE_ID, TabItem, Tabs, TabsProps, TeamGrid, TeamGridProps, TeamMember, Testimonial, TestimonialGrid, TestimonialGridProps, TestimonialItem, TestimonialProps, ThemeEditor, ThemeEditorProps, ThemeEmitMode, ThemeFonts, ThemePreset, ThemeStoreConfig, VideoAspect, VideoEmbed, VideoEmbedProps, applyFontVars, applyStoredTheme, applyThemeVars, clearFontVars, clearThemeVars, createThemeStore, daysUntil, fmtDate, fmtDateTime, fontPackages, fromNow, gbp, getFontPairing, hrs, initials, matchFontPairing, pct, tid, toEmbedUrl, toHslTriplet } from './site/index.js';
2
3
  import * as react from 'react';
3
- import react__default, { ReactNode } from 'react';
4
+ import react__default, { ReactNode, ComponentType } from 'react';
4
5
  import * as _arbidocs_react from '@arbidocs/react';
5
6
  import { AiRunOptions, UserMenuItem, NotifyVariant, useArbi, AiTaskStatus } from '@arbidocs/react';
6
- export { AiRunOptions, AiTaskStatus, ArtifactEvent, SearchChunk, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, redlineStats, textToArtifact, toRedlineMarkdown } from '@arbidocs/react';
7
+ export { AiRunOptions, AiTaskStatus, ArtifactEvent, DocumentCard, DocumentCardProps, SearchChunk, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, redlineStats, textToArtifact, toRedlineMarkdown } from '@arbidocs/react';
7
8
  import { ColDef, GridOptions, GridApi, CellValueChangedEvent, GridState } from 'ag-grid-community';
8
9
  import { LucideIcon } from 'lucide-react';
9
10
  import { CustomField, Field, ComponentConfig, Data, Config } from '@measured/puck';
11
+ import { UseBoundStore, StoreApi } from 'zustand';
10
12
 
11
13
  /**
12
14
  * The reusable AI-native law-firm prompt library.
@@ -126,166 +128,6 @@ declare function createPromptLibrary(config: PromptConfig): {
126
128
  }];
127
129
  };
128
130
 
129
- /**
130
- * The theme-token model. Firm-agnostic: the token *catalog*, default values and
131
- * presets are supplied by the consuming app (via {@link createThemeStore}); this
132
- * module owns the shared types and the logic for writing tokens onto the live
133
- * document.
134
- *
135
- * There are two emit models, selected per-app by {@link ThemeEmitMode}:
136
- *
137
- * - `'flat'` (default): each colour key is written as an inline `--color-<key>`
138
- * custom property on `<html>`, plus `--font-*` / `--radius`. Inline styles
139
- * override the compiled `@theme` defaults, so an edit previews live across the
140
- * whole app (and the @arbidocs AI kit) and reverts cleanly. Apps whose CSS sets
141
- * `--color-*` HEX directly rely on this.
142
- *
143
- * - `'cssVars'`: 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'`. */
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-*`). `'cssVars'` emits raw HSL-triplet vars into an injected
254
- * `<style>` so an app's own `hsl(var(--x))` indirection + `.dark` variant
255
- * survive. 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
131
  /**
290
132
  * Shared shapes for the ARBI adapter — identical in demo and live mode so UI
291
133
  * never needs to know which backend is serving it — plus the per-firm config a
@@ -358,7 +200,20 @@ interface ConnectionValue {
358
200
  /** One-click demo entry (no backend required). */
359
201
  enterDemo: () => void;
360
202
  logout: () => void;
361
- setWorkspace: (id: string) => void;
203
+ /**
204
+ * Affirmatively select a workspace: opens it on the server (via the SDK),
205
+ * updates the app-wide state, and persists the choice so it survives future
206
+ * logins. This is the single entry point for "the user is now in workspace X".
207
+ */
208
+ setWorkspace: (id: string) => Promise<void>;
209
+ /**
210
+ * Re-assert the selected workspace on the server session (idempotent `/open`).
211
+ * Call before a workspace-scoped write when the session may have lost its open
212
+ * workspace (token re-login, backend restart, session expiry) — the server
213
+ * resolves the workspace from the session, so a dropped `/open` yields
214
+ * "No active workspace" until it's re-opened.
215
+ */
216
+ ensureWorkspaceOpen: () => Promise<void>;
362
217
  }
363
218
  declare function ConnectionProvider({ config, children, }: {
364
219
  config: FirmArbiConfig;
@@ -468,39 +323,6 @@ declare function useSemanticSearch(): {
468
323
  isRunning: boolean;
469
324
  };
470
325
 
471
- /**
472
- * Compose a stable, kebab-case data-testid from parts.
473
- * Convention: {area}-{element}[-{qualifier}...]
474
- * tid("matters", "row", "CLM-2026-0042") -> "matters-row-clm-2026-0042"
475
- * Keeps Playwright selectors predictable across a firm app.
476
- */
477
- declare function tid(...parts: Array<string | number | undefined | false>): string;
478
-
479
- /**
480
- * Firm-app formatting helpers — currency, initials, dates, durations and
481
- * percentages. UK-oriented defaults (en-GB / GBP / "d MMM yyyy") suit an English
482
- * law firm; nothing here is firm-branded, so every firm app shares them.
483
- *
484
- * Dependency-free on purpose (uses the platform `Intl` APIs) so the package
485
- * stays light and needs no date library.
486
- */
487
- /** Format a number as GBP, e.g. £1,240,000 or £12.4k depending on `compact`. */
488
- declare function gbp(value: number, compact?: boolean): string;
489
- /** Initials from a person's name, e.g. "Amara N. Okafor" -> "AO". */
490
- declare function initials(name: string): string;
491
- /** "12 Mar 2026" */
492
- declare function fmtDate(iso: string): string;
493
- /** "12 Mar 2026, 14:30" */
494
- declare function fmtDateTime(iso: string): string;
495
- /** "3 days ago" / "in 2 hours" — largest whole unit, like date-fns strict. */
496
- declare function fromNow(iso: string, from?: Date): string;
497
- /** Signed day count to a date from a reference day. Negative = overdue. */
498
- declare function daysUntil(iso: string, from?: Date): number;
499
- /** Compact hours, e.g. 12.5h */
500
- declare function hrs(n: number): string;
501
- /** Percentage with no decimals, e.g. 82% */
502
- declare function pct(n: number): string;
503
-
504
326
  interface Column<T> {
505
327
  key: string;
506
328
  header: ReactNode;
@@ -616,6 +438,39 @@ declare function EmptyState({ icon: Icon, title, description, action, className,
616
438
  testId?: string;
617
439
  }): react.JSX.Element;
618
440
 
441
+ interface UploadDropzoneProps {
442
+ /** Called with the chosen/dropped files. */
443
+ onFiles: (files: File[]) => void;
444
+ /** `accept` attribute for the file picker (e.g. `.pdf,.docx`). */
445
+ accept?: string;
446
+ /** Allow selecting more than one file. Defaults to true. */
447
+ multiple?: boolean;
448
+ /** Disables picking/dropping (e.g. no active workspace yet). */
449
+ disabled?: boolean;
450
+ /** Shows a spinner + busy copy while an upload is in flight. */
451
+ uploading?: boolean;
452
+ /** Primary line, e.g. "Drag files here". */
453
+ title?: string;
454
+ /** Secondary line under the title. */
455
+ description?: string;
456
+ /** Small muted hint, e.g. "PDF, DOCX, images — up to 50MB". */
457
+ hint?: string;
458
+ /** Copy shown next to the spinner while uploading. */
459
+ uploadingLabel?: string;
460
+ /** Glyph in the tile. Defaults to an upload-cloud icon. */
461
+ icon?: LucideIcon;
462
+ testId?: string;
463
+ className?: string;
464
+ }
465
+ /**
466
+ * A themeable drag-and-drop upload target. Source-agnostic: it just surfaces
467
+ * chosen files via `onFiles` — the host owns the actual upload. Handles its own
468
+ * drag-over state, click-to-browse, and a busy state, styled with the kit's
469
+ * SEMANTIC tokens only (border, muted, primary, foreground) so it renders in
470
+ * each firm's colours.
471
+ */
472
+ declare function UploadDropzone({ onFiles, accept, multiple, disabled, uploading, title, description, hint, uploadingLabel, icon: Icon, testId, className, }: UploadDropzoneProps): react.JSX.Element;
473
+
619
474
  /** Standard back-office page title block: eyebrow + title + description + actions. */
620
475
  declare function PageHeader({ title, eyebrow, description, actions, className, testId, }: {
621
476
  title: string;
@@ -626,18 +481,6 @@ declare function PageHeader({ title, eyebrow, description, actions, className, t
626
481
  testId?: string;
627
482
  }): react.JSX.Element;
628
483
 
629
- /** Editorial heading block for marketing sections: eyebrow + title + lead. */
630
- declare function SectionHeading({ eyebrow, title, lead, align, className, invert, eyebrowClassName, }: {
631
- eyebrow?: string;
632
- title: string;
633
- lead?: string;
634
- align?: 'left' | 'center';
635
- className?: string;
636
- invert?: boolean;
637
- /** Override the eyebrow styling (e.g. a firm brand accent). Default: token classes. */
638
- eyebrowClassName?: string;
639
- }): react.JSX.Element;
640
-
641
484
  /** Search + filter bar used above every list view. */
642
485
  declare function Toolbar({ search, onSearch, searchPlaceholder, filters, actions, className, testId, }: {
643
486
  search?: string;
@@ -670,70 +513,53 @@ interface MetricCardProps {
670
513
  /** The one true KPI tile. Used across every dashboard and module header. */
671
514
  declare function MetricCard({ label, value, icon: Icon, hint, delta, accent, testId, }: MetricCardProps): react.JSX.Element;
672
515
 
673
- type BlockImageAspect = '16:9' | '4:3' | '1:1' | '21:9' | '4:5';
674
- type BlockImageRounded = 'none' | 'md' | 'lg' | 'full';
675
- interface BlockImageProps {
676
- src?: string;
677
- alt?: string;
678
- aspect?: BlockImageAspect;
679
- rounded?: BlockImageRounded;
680
- /** Editorial "photo frame" treatment: a matte border ring + soft shadow. */
681
- framed?: boolean;
682
- className?: string;
683
- testId?: string;
684
- }
685
- /**
686
- * Image with a graceful, self-contained fallback: when `src` is empty it renders a
687
- * token-clean gradient placeholder (no network needed) so the palette always looks
688
- * good in the editor and in demos. Token-only classes so it themes per firm.
689
- */
690
- declare function BlockImage({ src, alt, aspect, rounded, framed, className, testId, }: BlockImageProps): react.JSX.Element;
691
-
692
- type HeroTone = 'default' | 'muted' | 'ink';
693
- type HeroAlign = 'left' | 'center';
694
- interface HeroCta {
695
- label?: string;
696
- href?: string;
697
- }
698
- interface HeroProps {
699
- eyebrow?: string;
700
- title: string;
701
- /** A word/phrase within the title to accent (rendered in the primary colour). */
702
- emphasis?: string;
703
- subtitle?: string;
704
- primaryCta?: HeroCta;
705
- secondaryCta?: HeroCta;
706
- align?: HeroAlign;
707
- tone?: HeroTone;
708
- /** Side visual (ignored when `backgroundImage` is set). */
709
- imageUrl?: string;
710
- /** Full-bleed background image URL/data-URI. Turns the hero into a cover band. */
711
- backgroundImage?: string;
712
- /** Darkening scrim over the background image, for legible copy. Default true. */
713
- overlay?: boolean;
714
- /** Override the eyebrow styling (e.g. a firm brand accent). Default: token classes. */
715
- eyebrowClassName?: string;
716
- testId?: string;
516
+ /** One member shown in the avatar stack — source-agnostic, not the raw SDK shape. */
517
+ interface WorkspaceCardMember {
518
+ name: string;
519
+ picture?: string | null;
717
520
  }
718
- /** Editorial marketing hero: eyebrow, headline, subtitle, two CTAs and a visual. */
719
- declare function Hero({ eyebrow, title, emphasis, subtitle, primaryCta, secondaryCta, align, tone, imageUrl, backgroundImage, overlay, eyebrowClassName, testId, }: HeroProps): react.JSX.Element;
720
-
721
- interface FeatureItem {
722
- icon?: LucideIcon;
723
- title: string;
521
+ interface WorkspaceCardProps {
522
+ name: string;
523
+ /** Uppercase eyebrow above the description, e.g. the matter/project name. */
524
+ matterRef?: string;
724
525
  description?: string;
725
- }
726
- interface FeatureGridProps {
727
- columns?: '2' | '3';
728
- items: FeatureItem[];
526
+ docCount: number;
527
+ /** Defaults to `members.length` when omitted. */
528
+ memberCount?: number;
529
+ /** Drives the overlapping avatar stack (shows up to 4, then a `+N` overflow). */
530
+ members?: WorkspaceCardMember[];
531
+ /** ISO timestamp — renders "Updated {relative} ago" when present. */
532
+ updatedAt?: string;
533
+ /** Marks this as the current workspace: a done-badge + disabled footer button. */
534
+ active?: boolean;
535
+ /** Shows a spinner in the footer button while a switch is in flight. */
536
+ busy?: boolean;
537
+ onOpen?: () => void;
538
+ /** Icon in the header tile. Defaults to a lock/folder glyph. */
539
+ icon?: LucideIcon;
540
+ /** Stable id for the card root; the footer button gets `${testId}-open`. */
729
541
  testId?: string;
542
+ /** Overrides the footer button's test id when a caller needs a specific one. */
543
+ buttonTestId?: string;
544
+ className?: string;
730
545
  }
731
- /** Responsive grid of icon + title + description feature cards. */
732
- declare function FeatureGrid({ columns, items, testId }: FeatureGridProps): react.JSX.Element;
546
+ /**
547
+ * The one true workspace card. A themeable, source-agnostic tile — icon,
548
+ * Active/Encrypted badge, title + uppercase matter eyebrow, a two-line
549
+ * description, a doc/member stat row, an overlapping member avatar stack, an
550
+ * "Updated … ago" line and a full-width Open / Current-workspace footer button.
551
+ *
552
+ * Styled with the kit's SEMANTIC tokens only (foreground, muted-foreground,
553
+ * primary, card, border, accent), so every firm gets it in its own colours
554
+ * without any firm-branded classes leaking in.
555
+ */
556
+ declare function WorkspaceCard({ name, matterRef, description, docCount, memberCount, members, updatedAt, active, busy, onOpen, icon: Icon, testId, buttonTestId, className, }: WorkspaceCardProps): react.JSX.Element;
733
557
 
734
558
  interface StatItem {
735
559
  value: string;
736
560
  label: string;
561
+ /** Optional caption under the label (e.g. a qualifier or unit). */
562
+ sub?: string;
737
563
  }
738
564
  interface StatGroupProps {
739
565
  items: StatItem[];
@@ -742,330 +568,6 @@ interface StatGroupProps {
742
568
  /** Big-number stat group — headline metrics with captions. */
743
569
  declare function StatGroup({ items, testId }: StatGroupProps): react.JSX.Element;
744
570
 
745
- interface TestimonialProps {
746
- quote: string;
747
- author?: string;
748
- role?: string;
749
- avatarUrl?: string;
750
- testId?: string;
751
- }
752
- /** A single pull-quote testimonial with an author and optional avatar. */
753
- declare function Testimonial({ quote, author, role, avatarUrl, testId }: TestimonialProps): react.JSX.Element;
754
-
755
- type CTABannerTone = 'default' | 'muted' | 'ink';
756
- interface CTABannerProps {
757
- title: string;
758
- subtitle?: string;
759
- cta?: {
760
- label?: string;
761
- href?: string;
762
- };
763
- tone?: CTABannerTone;
764
- /** Full-bleed background image URL/data-URI. Overrides the tone surface. */
765
- backgroundImage?: string;
766
- /** Darkening scrim over the background image. Default true. */
767
- overlay?: boolean;
768
- testId?: string;
769
- }
770
- /** Full-width call-to-action banner. */
771
- declare function CTABanner({ title, subtitle, cta, tone, backgroundImage, overlay, testId, }: CTABannerProps): react.JSX.Element;
772
-
773
- interface LogoItem {
774
- label: string;
775
- }
776
- interface LogoCloudProps {
777
- items: LogoItem[];
778
- testId?: string;
779
- }
780
- /**
781
- * Text-based "logo" wall — brand names rendered as muted pill marks. Self-contained
782
- * (no external images) so the palette looks complete offline.
783
- */
784
- declare function LogoCloud({ items, testId }: LogoCloudProps): react.JSX.Element;
785
-
786
- interface FaqItem {
787
- question: string;
788
- answer?: string;
789
- }
790
- interface FAQProps {
791
- items: FaqItem[];
792
- testId?: string;
793
- }
794
- /**
795
- * Accessible disclosure list. Built on native `<details>`/`<summary>` so it works
796
- * without client state (correct in the Puck preview and static renders) and stays
797
- * keyboard-accessible. Token-only styling.
798
- */
799
- declare function FAQ({ items, testId }: FAQProps): react.JSX.Element;
800
-
801
- interface RichTextBlockProps {
802
- content: string;
803
- testId?: string;
804
- }
805
- /**
806
- * Prose block. Reuses the shared markdown renderer (`AiMarkdown`) so long-form copy
807
- * gets the one house style for headings, lists, tables and emphasis — token-clean
808
- * and themeable per firm.
809
- */
810
- declare function RichTextBlock({ content, testId }: RichTextBlockProps): react.JSX.Element;
811
-
812
- type CalloutVariant = 'info' | 'success' | 'warning' | 'danger';
813
- interface CalloutProps {
814
- variant?: CalloutVariant;
815
- title?: string;
816
- body?: string;
817
- testId?: string;
818
- }
819
- /** Inline notice box in one of four semantic tones. */
820
- declare function Callout({ variant, title, body, testId }: CalloutProps): react.JSX.Element;
821
-
822
- type AvatarBlockSize = 'sm' | 'md' | 'lg' | 'xl';
823
- interface AvatarBlockProps {
824
- src?: string;
825
- name?: string;
826
- size?: AvatarBlockSize;
827
- testId?: string;
828
- }
829
- /** Avatar with an initials fallback when no image is supplied. */
830
- declare function AvatarBlock({ src, name, size, testId }: AvatarBlockProps): react.JSX.Element;
831
-
832
- interface NavLink {
833
- label: string;
834
- href?: string;
835
- }
836
- interface NavbarProps {
837
- brand: string;
838
- links: NavLink[];
839
- cta?: {
840
- label?: string;
841
- href?: string;
842
- };
843
- testId?: string;
844
- }
845
- /** Marketing top navigation: brand, inline links and an optional CTA button. */
846
- declare function Navbar({ brand, links, cta, testId }: NavbarProps): react.JSX.Element;
847
-
848
- interface FooterLink {
849
- label: string;
850
- href?: string;
851
- }
852
- interface FooterColumn {
853
- heading: string;
854
- links: FooterLink[];
855
- }
856
- interface FooterProps {
857
- columns: FooterColumn[];
858
- copyright?: string;
859
- testId?: string;
860
- }
861
- /** Site footer: a row of link columns plus a copyright line. */
862
- declare function Footer({ columns, copyright, testId }: FooterProps): react.JSX.Element;
863
-
864
- interface ListItem {
865
- text: string;
866
- }
867
- interface ListBlockProps {
868
- ordered?: boolean;
869
- items: ListItem[];
870
- testId?: string;
871
- }
872
- /** Ordered or unordered prose list. Token-clean. */
873
- declare function ListBlock({ ordered, items, testId }: ListBlockProps): react.JSX.Element;
874
-
875
- type ColumnsCount = 2 | 3 | 4;
876
- type ColumnsGap = 'sm' | 'md' | 'lg';
877
- interface ColumnsProps {
878
- count?: ColumnsCount;
879
- gap?: ColumnsGap;
880
- columns: ReactNode[];
881
- testId?: string;
882
- }
883
- /** Responsive N-column row. Each column hosts a nested Puck slot. */
884
- declare function Columns({ count, gap, columns, testId }: ColumnsProps): react.JSX.Element;
885
-
886
- type SpacerSize = 'sm' | 'md' | 'lg' | 'xl';
887
- interface SpacerProps {
888
- size?: SpacerSize;
889
- testId?: string;
890
- }
891
- /** Vertical whitespace. */
892
- declare function Spacer({ size, testId }: SpacerProps): react.JSX.Element;
893
-
894
- type ContainerWidth = 'narrow' | 'default' | 'wide' | 'full';
895
- type ContainerPadding = 'none' | 'sm' | 'md' | 'lg';
896
- interface ContainerProps {
897
- width?: ContainerWidth;
898
- padding?: ContainerPadding;
899
- children: ReactNode;
900
- testId?: string;
901
- }
902
- /** Max-width wrapper that centres and pads a nested Puck slot. */
903
- declare function Container({ width, padding, children, testId }: ContainerProps): react.JSX.Element;
904
-
905
- interface PricingPlan {
906
- name: string;
907
- price: string;
908
- period?: string;
909
- description?: string;
910
- features?: string[];
911
- cta?: {
912
- label?: string;
913
- href?: string;
914
- };
915
- /** Highlight this plan as the recommended one. */
916
- featured?: boolean;
917
- }
918
- interface PricingTableProps {
919
- plans: PricingPlan[];
920
- testId?: string;
921
- }
922
- /** A row of pricing plan cards with feature lists and a CTA per plan. */
923
- declare function PricingTable({ plans, testId }: PricingTableProps): react.JSX.Element;
924
-
925
- interface TabItem {
926
- label: string;
927
- content: string;
928
- }
929
- interface TabsProps {
930
- items: TabItem[];
931
- testId?: string;
932
- }
933
- /** A simple tabbed panel: a row of tab buttons over the active tab's content. */
934
- declare function Tabs({ items, testId }: TabsProps): react.JSX.Element;
935
-
936
- interface AccordionItem {
937
- title: string;
938
- content: string;
939
- }
940
- interface AccordionProps {
941
- items: AccordionItem[];
942
- /** Allow multiple panels open at once. Default false (single-open). */
943
- allowMultiple?: boolean;
944
- testId?: string;
945
- }
946
- /** A stack of collapsible title/content panels. */
947
- declare function Accordion({ items, allowMultiple, testId }: AccordionProps): react.JSX.Element;
948
-
949
- interface GalleryImage {
950
- src?: string;
951
- alt?: string;
952
- caption?: string;
953
- }
954
- interface GalleryProps {
955
- images: GalleryImage[];
956
- columns?: '2' | '3' | '4';
957
- aspect?: BlockImageProps['aspect'];
958
- rounded?: BlockImageProps['rounded'];
959
- testId?: string;
960
- }
961
- /** A responsive image grid. Each tile previews via BlockImage with a caption. */
962
- declare function Gallery({ images, columns, aspect, rounded, testId, }: GalleryProps): react.JSX.Element;
963
-
964
- type VideoAspect = '16:9' | '4:3' | '1:1';
965
- interface VideoEmbedProps {
966
- /** A YouTube / Vimeo / Loom page URL, or a direct video file URL. */
967
- url: string;
968
- title?: string;
969
- aspect?: VideoAspect;
970
- rounded?: boolean;
971
- testId?: string;
972
- }
973
- /**
974
- * Resolve a shareable video URL to an embeddable iframe src. Supports YouTube,
975
- * Vimeo and Loom; returns null for anything else (rendered as a `<video>`).
976
- */
977
- declare function toEmbedUrl(url: string): string | null;
978
- /** Embed a video from a YouTube/Vimeo/Loom link or a direct file, in a set aspect. */
979
- declare function VideoEmbed({ url, title, aspect, rounded, testId, }: VideoEmbedProps): react.JSX.Element;
980
-
981
- interface ContactField {
982
- name: string;
983
- label: string;
984
- type?: 'text' | 'email' | 'tel' | 'textarea';
985
- required?: boolean;
986
- }
987
- interface ContactFormProps {
988
- fields: ContactField[];
989
- submitLabel?: string;
990
- /** Optional POST endpoint. When set, the form submits natively to it. */
991
- action?: string;
992
- /** Message shown after a client-side (no-action) submit. */
993
- successMessage?: string;
994
- testId?: string;
995
- }
996
- /** A configurable lead/contact form. Posts to `action`, or confirms client-side. */
997
- declare function ContactForm({ fields, submitLabel, action, successMessage, testId, }: ContactFormProps): react.JSX.Element;
998
-
999
- type BannerTone = 'primary' | 'muted' | 'ink';
1000
- interface BannerProps {
1001
- text: string;
1002
- cta?: {
1003
- label?: string;
1004
- href?: string;
1005
- };
1006
- tone?: BannerTone;
1007
- testId?: string;
1008
- }
1009
- /** A slim full-width announcement strip with optional inline CTA. */
1010
- declare function Banner({ text, cta, tone, testId }: BannerProps): react.JSX.Element;
1011
-
1012
- interface MediaTextProps {
1013
- eyebrow?: string;
1014
- title: string;
1015
- body?: string;
1016
- imageUrl?: string;
1017
- /** Which side the media sits on (desktop). */
1018
- mediaSide?: 'left' | 'right';
1019
- cta?: {
1020
- label?: string;
1021
- href?: string;
1022
- };
1023
- testId?: string;
1024
- }
1025
- /** A split media-and-copy row: an image on one side, prose + CTA on the other. */
1026
- declare function MediaText({ eyebrow, title, body, imageUrl, mediaSide, cta, testId, }: MediaTextProps): react.JSX.Element;
1027
-
1028
- interface TeamMember {
1029
- name: string;
1030
- role?: string;
1031
- bio?: string;
1032
- avatarUrl?: string;
1033
- }
1034
- interface TeamGridProps {
1035
- members: TeamMember[];
1036
- columns?: '2' | '3' | '4';
1037
- testId?: string;
1038
- }
1039
- /** A grid of people cards: avatar, name, role and an optional short bio. */
1040
- declare function TeamGrid({ members, columns, testId }: TeamGridProps): react.JSX.Element;
1041
-
1042
- interface StepItem {
1043
- title: string;
1044
- description?: string;
1045
- }
1046
- interface StepsProps {
1047
- steps: StepItem[];
1048
- /** `timeline` draws a connected vertical rail; `numbered` is a horizontal grid. */
1049
- variant?: 'numbered' | 'timeline';
1050
- testId?: string;
1051
- }
1052
- /** A process / how-it-works sequence, as numbered cards or a vertical timeline. */
1053
- declare function Steps({ steps, variant, testId }: StepsProps): react.JSX.Element;
1054
-
1055
- interface TestimonialItem {
1056
- quote: string;
1057
- author?: string;
1058
- role?: string;
1059
- avatarUrl?: string;
1060
- }
1061
- interface TestimonialGridProps {
1062
- items: TestimonialItem[];
1063
- columns?: '2' | '3';
1064
- testId?: string;
1065
- }
1066
- /** A grid of multiple pull-quote testimonials. */
1067
- declare function TestimonialGrid({ items, columns, testId }: TestimonialGridProps): react.JSX.Element;
1068
-
1069
571
  /**
1070
572
  * Navigation shapes shared by firm app shells. The concrete nav manifest (which
1071
573
  * modules, in what order) is per-firm data the app supplies; these interfaces
@@ -1422,10 +924,13 @@ interface UseImageGen {
1422
924
  /** True while a generation is in flight. */
1423
925
  isGenerating: boolean;
1424
926
  /**
1425
- * Generate an image from a prompt. Resolves with the new image document's
1426
- * ext_id, or null when the prompt is empty or no image was produced.
927
+ * Generate an image from a prompt, optionally editing / drawing on one or
928
+ * more reference images already in the workspace. Pass the reference images'
929
+ * document ext_ids in `referenceDocIds` (omit or `[]` for plain text→image).
930
+ * Resolves with the new image document's ext_id, or null when the prompt is
931
+ * empty or no image was produced.
1427
932
  */
1428
- generate: (prompt: string) => Promise<string | null>;
933
+ generate: (prompt: string, referenceDocIds?: string[]) => Promise<string | null>;
1429
934
  /** Clear the current result and error. */
1430
935
  reset: () => void;
1431
936
  }
@@ -1437,9 +942,23 @@ declare const ASSET_REF_PREFIX = "asset:";
1437
942
  declare function asAssetRef(docId: string): string;
1438
943
  /** The workspace document ext_id behind an asset ref (`asset:doc-1` → `doc-1`). */
1439
944
  declare function assetRefId(src: string): string;
1440
- declare function AssetResolverProvider({ children }: {
945
+ /** What a resolver component receives. `AssetThumb` in `./assetImage` implements it. */
946
+ interface AssetRenderProps {
947
+ docId: string;
948
+ alt?: string;
949
+ className?: string;
950
+ testId?: string;
951
+ }
952
+ /** Renders a workspace asset ext_id as an image. */
953
+ type AssetRenderer = ComponentType<AssetRenderProps>;
954
+ declare function AssetResolverProvider({ render, children, }: {
955
+ /** Typically `AssetThumb` from `./assetImage`. */
956
+ render: AssetRenderer;
1441
957
  children: ReactNode;
1442
958
  }): react.JSX.Element;
959
+ /** The active resolver, or `null` when asset refs must render verbatim. */
960
+ declare function useAssetRenderer(): AssetRenderer | null;
961
+ /** True only where a bare asset ext_id can be resolved (the authenticated Studio). */
1443
962
  declare function useAssetResolverActive(): boolean;
1444
963
  /**
1445
964
  * Is `src` a workspace asset ref (`asset:<docId>`) rather than an already-
@@ -1447,6 +966,22 @@ declare function useAssetResolverActive(): boolean;
1447
966
  */
1448
967
  declare function isAssetRef(src: string | undefined): src is string;
1449
968
 
969
+ /**
970
+ * Resolve an asset ext_id to a displayable image src.
971
+ *
972
+ * Prefer the cheap thumbnail endpoint, but fall back to downloading the actual
973
+ * bytes as an object URL when there's no thumbnail — AI-generated (and freshly
974
+ * uploaded) images have no thumbnail yet, so the thumbnail-only path renders a
975
+ * broken `<img>`. The bytes always exist (it's a stored document), so the editor
976
+ * shows the real image immediately instead of waiting on async thumbnailing.
977
+ */
978
+ declare function useAssetImage(docId: string): string | undefined;
979
+ /**
980
+ * Resolve a single asset ext_id and render it as an `<img>`. This is the
981
+ * `AssetRenderer` the Studio injects; it satisfies `AssetRenderProps`.
982
+ */
983
+ declare function AssetThumb({ docId, alt, className, testId }: AssetRenderProps): react.JSX.Element;
984
+
1450
985
  type SectionTone = 'default' | 'muted' | 'ink';
1451
986
  type SectionPadding = 'none' | 'sm' | 'md' | 'lg';
1452
987
  type SectionWidth = 'narrow' | 'default' | 'wide' | 'full';
@@ -1690,4 +1225,4 @@ interface AgentsPanelProps {
1690
1225
  }
1691
1226
  declare function AgentsPanel({ store, testIdPrefix }: AgentsPanelProps): react.JSX.Element;
1692
1227
 
1693
- export { ASSET_REF_PREFIX, Accordion, type AccordionItem, type AccordionProps, type AgentSelectionStore, AgentsPanel, type AgentsPanelProps, type AiFields, type AiFieldsConfig, AppHeader, type AppHeaderProps, type AppHeaderUser, type AppNavItem, type AppNavSection, AppShell, type AppShellProps, type ApplyThemeOptions, type ArbiBlocksConfigOptions, type ArbiMode, type ArbiUser, type AssetMaterializer, AssetResolverProvider, type AssistantCitation, type AssistantMessage, type AssistantStep, AvatarBlock, type AvatarBlockProps, Banner, type BannerProps, type BannerTone, 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, type ContactField, ContactForm, type ContactFormProps, 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, Gallery, type GalleryImage, type GalleryProps, GridView, type GridViewEmptyState, type GridViewProps, type GridViewSelection, Hero, type HeroCta, type HeroProps, type ImageFieldConfig, type ImageFieldFactory, LegalArbiProvider, ListBlock, type ListBlockProps, type ListItem, LiveDataProvider, type LoadResult, LogoCloud, type LogoCloudProps, type LogoItem, type MaterializeOptions, type MatterContext, MediaText, type MediaTextProps, MetricCard, type MetricCardProps, ModuleManager, type ModuleManagerProps, type ModuleRegistry, type ModuleStoreBundle, type NavLink, Navbar, type NavbarProps, PageHeader, PageRenderer, type PageRendererProps, type PersistVia, type PricingPlan, PricingTable, type PricingTableProps, type PromptConfig, type PromptLibrary, type Prompts, type ResolvedModule, RichTextBlock, type RichTextBlockProps, Section, SectionHeading, type SectionProps, Spacer, type SpacerProps, StatGroup, type StatGroupProps, type StatItem, type StepItem, Steps, type StepsProps, StudioEditor, type StudioEditorProps, type StudioPageMeta, type StudioPersistence, type StudioPersistenceConfig, THEME_STYLE_ID, type TabItem, Tabs, type TabsProps, TeamGrid, type TeamGridProps, type TeamMember, Testimonial, TestimonialGrid, type TestimonialGridProps, type TestimonialItem, type TestimonialProps, type ThemeBundle, ThemeEditor, type ThemeEditorProps, type ThemeEmitMode, type ThemeFonts, type ThemePreset, type ThemeState, type ThemeStoreConfig, type ThemeTokens, Toolbar, type UseImageGen, VerticalBuilder, type VerticalBuilderProps, type VerticalConfig, type VerticalExport, type VerticalExportConfig, type VideoAspect, VideoEmbed, type VideoEmbedProps, WidgetPlaceholder, applyStoredTheme, applyThemeVars, asAssetRef, assetRefId, clearThemeVars, createAgentSelectionStore, createAiFields, createArbiBlocksConfig, createArbiMaterializer, createBlockKit, createImageField, createLiveWidgets, createModuleRegistry, createModuleStore, createPromptLibrary, createSiteBlocks, createStudioPersistence, createThemeStore, createVerticalExport, createWebsiteBlocks, daysUntil, fmtDate, fmtDateTime, fromNow, gbp, hrs, iconMap, initials, isAssetRef, isAuthenticated, materializePageData, matterToContext, pct, tid, toEmbedUrl, toHslTriplet, useAssetResolverActive, useConnection, useFirmAiTask, useImageGen, useLiveDataActive, useSemanticSearch, useWorkspaceDocs };
1228
+ export { ASSET_REF_PREFIX, type AgentSelectionStore, AgentsPanel, type AgentsPanelProps, type AiFields, type AiFieldsConfig, AppHeader, type AppHeaderProps, type AppHeaderUser, type AppNavItem, type AppNavSection, AppShell, type AppShellProps, type ArbiBlocksConfigOptions, type ArbiMode, type ArbiUser, type AssetMaterializer, type AssetRenderProps, type AssetRenderer, AssetResolverProvider, AssetThumb, type AssistantCitation, type AssistantMessage, type AssistantStep, type BlockKit, type BuildPrompt, type BuilderModule, type Column, ConnectionProvider, type ConnectionStatus, DataTable, DataTableBlock, type DataTableBlockColumn, type DataTableBlockProps, EmptyState, type ExportedModule, type FirmArbiConfig, GridView, type GridViewEmptyState, type GridViewProps, type GridViewSelection, type ImageFieldConfig, type ImageFieldFactory, LegalArbiProvider, LiveDataProvider, type LoadResult, type MaterializeOptions, type MatterContext, MetricCard, type MetricCardProps, ModuleManager, type ModuleManagerProps, type ModuleRegistry, type ModuleStoreBundle, PageHeader, PageRenderer, type PageRendererProps, type PersistVia, type PromptConfig, type PromptLibrary, type Prompts, type ResolvedModule, Section, type SectionProps, StatGroup, type StatGroupProps, type StatItem, StudioEditor, type StudioEditorProps, type StudioPageMeta, type StudioPersistence, type StudioPersistenceConfig, ThemeBundle, ThemeState, ThemeTokens, Toolbar, UploadDropzone, type UploadDropzoneProps, type UseImageGen, VerticalBuilder, type VerticalBuilderProps, type VerticalConfig, type VerticalExport, type VerticalExportConfig, WidgetPlaceholder, WorkspaceCard, type WorkspaceCardMember, type WorkspaceCardProps, asAssetRef, assetRefId, createAgentSelectionStore, createAiFields, createArbiBlocksConfig, createArbiMaterializer, createBlockKit, createImageField, createLiveWidgets, createModuleRegistry, createModuleStore, createPromptLibrary, createSiteBlocks, createStudioPersistence, createVerticalExport, createWebsiteBlocks, iconMap, isAssetRef, isAuthenticated, materializePageData, matterToContext, useAssetImage, useAssetRenderer, useAssetResolverActive, useConnection, useFirmAiTask, useImageGen, useLiveDataActive, useSemanticSearch, useWorkspaceDocs };