@ohhwells/bridge 0.1.128 → 0.1.130
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 +13909 -12255
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +147 -14
- package/dist/index.d.ts +147 -14
- package/dist/index.js +13861 -12222
- package/dist/index.js.map +1 -1
- package/dist/styles.css +2 -0
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -20,14 +20,7 @@ declare function OhhwellsBridge(): react_jsx_runtime.JSX.Element;
|
|
|
20
20
|
*/
|
|
21
21
|
declare const AI_TREE_SCHEMA_VERSIONS: readonly [1];
|
|
22
22
|
type SectionScaffold = 'stacked' | 'split' | 'overlay';
|
|
23
|
-
type AiBlockType = 'text' | 'button' | 'media' | 'media-list' | 'rating' | 'divider' | 'input' | 'section-header' | 'button-row' | 'card' | 'collection' | 'accordion' | 'media-strip' | 'form' | 'embed' | 'schedule-widget' | 'group' | 'video';
|
|
24
|
-
/** media-strip's `items[]` entry shape (rn-mobile-backend's `itemFields` for that block). */
|
|
25
|
-
interface MediaStripItem {
|
|
26
|
-
assetId: string;
|
|
27
|
-
alt: string;
|
|
28
|
-
href?: string;
|
|
29
|
-
linkTarget?: 'new-tab' | 'same-tab';
|
|
30
|
-
}
|
|
23
|
+
type AiBlockType = 'text' | 'button' | 'media' | 'media-list' | 'rating' | 'divider' | 'input' | 'section-header' | 'button-row' | 'card' | 'collection' | 'accordion' | 'media-strip' | 'form' | 'embed' | 'schedule-widget' | 'group' | 'link-list' | 'hours' | 'logo' | 'social-row' | 'video';
|
|
31
24
|
interface AiBlockNode {
|
|
32
25
|
type: AiBlockType;
|
|
33
26
|
span: number;
|
|
@@ -36,11 +29,8 @@ interface AiBlockNode {
|
|
|
36
29
|
slots?: Record<string, unknown>;
|
|
37
30
|
children?: AiBlockNode[];
|
|
38
31
|
/** Shape is per-block-type, defined by the backend's `BlockDef.itemFields` (accordion's
|
|
39
|
-
* question/answer, media-strip's assetId/alt/href/linkTarget). */
|
|
40
|
-
items?: Array<
|
|
41
|
-
question: string;
|
|
42
|
-
answer: string;
|
|
43
|
-
} | MediaStripItem>;
|
|
32
|
+
* question/answer, media-strip's assetId/alt/href/linkTarget, footer link-list/hours/social-row). */
|
|
33
|
+
items?: Array<Record<string, unknown>>;
|
|
44
34
|
animation?: {
|
|
45
35
|
effect: 'fade-in' | 'fade-in-up' | 'slide-in-left' | 'slide-in-right' | 'zoom-in';
|
|
46
36
|
trigger: 'on-scroll' | 'on-load' | 'on-hover';
|
|
@@ -232,6 +222,37 @@ declare const AI_TREE_TOKENS: {
|
|
|
232
222
|
};
|
|
233
223
|
declare function isRenderableTree(value: unknown): value is AiLayoutTree;
|
|
234
224
|
|
|
225
|
+
/**
|
|
226
|
+
* Footer layout-tree contract — bridge-side mirror of
|
|
227
|
+
* `rn-mobile-backend/app/helpers/ai-footer-tree.ts`.
|
|
228
|
+
*
|
|
229
|
+
* The server validates every footer tree fail-closed before it reaches the bridge.
|
|
230
|
+
* Legal links are never on this tree; the renderer injects them from site legal config.
|
|
231
|
+
*/
|
|
232
|
+
|
|
233
|
+
declare const FOOTER_COLUMN_SPANS: readonly [12, 6, 4, 3];
|
|
234
|
+
type FooterColumnSpan = (typeof FOOTER_COLUMN_SPANS)[number];
|
|
235
|
+
declare const FOOTER_COLUMN_BLOCKS: readonly ["link-list", "text", "hours", "logo", "social-row", "media-strip"];
|
|
236
|
+
declare const FOOTER_SOCIAL_PLATFORMS: readonly ["facebook", "instagram", "youtube", "linkedin", "twitter", "tiktok", "threads", "pinterest", "whatsapp"];
|
|
237
|
+
interface AiFooterColumn {
|
|
238
|
+
span: FooterColumnSpan;
|
|
239
|
+
blocks: AiBlockNode[];
|
|
240
|
+
}
|
|
241
|
+
/** Protected bottom bar — copyright only; legal links come from site config at render. */
|
|
242
|
+
interface AiFooterBottomBar {
|
|
243
|
+
/** May include `{year}` — resolved at render time. */
|
|
244
|
+
copyright: string;
|
|
245
|
+
}
|
|
246
|
+
interface AiFooterLayoutTree {
|
|
247
|
+
schemaVersion: number;
|
|
248
|
+
type: 'footer';
|
|
249
|
+
columns: AiFooterColumn[];
|
|
250
|
+
bottomBar: AiFooterBottomBar;
|
|
251
|
+
}
|
|
252
|
+
/** Resolve `{year}` in copyright copy — never ship a stale year. */
|
|
253
|
+
declare function resolveFooterCopyright(copyright: string, year?: number): string;
|
|
254
|
+
declare function isRenderableFooterTree(value: unknown): value is AiFooterLayoutTree;
|
|
255
|
+
|
|
235
256
|
interface AiTreeRendererProps {
|
|
236
257
|
tree: AiLayoutTree;
|
|
237
258
|
brand?: AiBrandKit;
|
|
@@ -247,6 +268,118 @@ interface AiTreeRendererProps {
|
|
|
247
268
|
editKeyPrefix?: string;
|
|
248
269
|
}
|
|
249
270
|
declare function AiTreeRenderer({ tree, brand, buttonStyle, resolveMedia, editKeyPrefix, }: AiTreeRendererProps): react_jsx_runtime.JSX.Element | null;
|
|
271
|
+
/** Tier 1 legal links — always rendered from site config, never from the footer tree. */
|
|
272
|
+
interface SiteLegalLinks {
|
|
273
|
+
privacy: {
|
|
274
|
+
label: string;
|
|
275
|
+
href: string;
|
|
276
|
+
};
|
|
277
|
+
terms: {
|
|
278
|
+
label: string;
|
|
279
|
+
href: string;
|
|
280
|
+
};
|
|
281
|
+
/** Cookie-settings control; omit to hide. Trigger only — no editable href. */
|
|
282
|
+
cookieSettings?: {
|
|
283
|
+
label: string;
|
|
284
|
+
onClick?: () => void;
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
interface AiFooterRendererProps {
|
|
288
|
+
tree: AiFooterLayoutTree;
|
|
289
|
+
brand?: AiBrandKit;
|
|
290
|
+
resolveMedia?: (ref: string) => string | null;
|
|
291
|
+
editKeyPrefix?: string;
|
|
292
|
+
/** Brand logo URL for `logo` blocks. */
|
|
293
|
+
logoUrl?: string | null;
|
|
294
|
+
/** Map page/anchor ids → navigable hrefs for link-list internals. */
|
|
295
|
+
resolvePageHref?: (pageOrAnchorId: string) => string | null;
|
|
296
|
+
/** Always painted in the bottom bar (Tier 1). */
|
|
297
|
+
legal: SiteLegalLinks;
|
|
298
|
+
/** Template footer's own surface — retrofit paints on the real band, not the brand's dark. */
|
|
299
|
+
surface?: {
|
|
300
|
+
background: string;
|
|
301
|
+
text: string;
|
|
302
|
+
} | null;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Site-wide footer from a validated footer layout tree .
|
|
306
|
+
* Bottom bar always includes copyright (`{year}` resolved) + legal links from `legal`.
|
|
307
|
+
*/
|
|
308
|
+
declare function AiFooterRenderer({ tree, brand, resolveMedia, editKeyPrefix, logoUrl, resolvePageHref, legal, surface, }: AiFooterRendererProps): react_jsx_runtime.JSX.Element | null;
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Site-wide AI footer persistence .
|
|
312
|
+
*
|
|
313
|
+
* One tree per site under `__ohw_ai_footer` — same Tier 1 guarded namespace as
|
|
314
|
+
* `__ohw_ai_sections`. Absent / empty = keep the template React footer
|
|
315
|
+
* (pixel-identical retrofit). Present = mount AiFooterRenderer and hide the
|
|
316
|
+
* template footer chrome.
|
|
317
|
+
*/
|
|
318
|
+
|
|
319
|
+
declare const AI_FOOTER_KEY = "__ohw_ai_footer";
|
|
320
|
+
interface AiFooterState {
|
|
321
|
+
v: 1;
|
|
322
|
+
tree: AiFooterLayoutTree;
|
|
323
|
+
media?: Record<string, string>;
|
|
324
|
+
brand?: AiBrandKit;
|
|
325
|
+
}
|
|
326
|
+
/** Parse a persisted footer value — malformed state means "no custom footer". */
|
|
327
|
+
declare function parseAiFooterState(raw: string | null | undefined): AiFooterState | null;
|
|
328
|
+
declare function serializeAiFooterState(state: AiFooterState): string;
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Default footer layout tree — bridge mirror of
|
|
332
|
+
* `rn-mobile-backend/app/helpers/ai-footer-default.ts`.
|
|
333
|
+
*
|
|
334
|
+
* Used when an AI edit first materialises a footer tree from the house default,
|
|
335
|
+
* and as the seed for prompt structured edits. Template footers stay until then.
|
|
336
|
+
*/
|
|
337
|
+
|
|
338
|
+
declare function buildDefaultFooterTree(opts?: {
|
|
339
|
+
businessName?: string;
|
|
340
|
+
about?: string;
|
|
341
|
+
pageLinks?: Array<{
|
|
342
|
+
label: string;
|
|
343
|
+
href: string;
|
|
344
|
+
}>;
|
|
345
|
+
}): AiFooterLayoutTree;
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Pure structured edits for the site-wide footer tree .
|
|
349
|
+
* Prompt intents map here; the bridge/persist layer validates via the backend
|
|
350
|
+
* (or client-side shape checks) before applying.
|
|
351
|
+
*/
|
|
352
|
+
|
|
353
|
+
/** Ensure a footer tree exists — seed from the house default when first editing. */
|
|
354
|
+
declare function ensureFooterTree(tree: AiFooterLayoutTree | null | undefined, opts?: {
|
|
355
|
+
businessName?: string;
|
|
356
|
+
}): AiFooterLayoutTree;
|
|
357
|
+
/**
|
|
358
|
+
* Add or update a social platform link. Same platform → update href (no duplicate icons);
|
|
359
|
+
* missing row → create `social-row` in the emptiest column.
|
|
360
|
+
*/
|
|
361
|
+
declare function appendFooterSocial(tree: AiFooterLayoutTree, entry: {
|
|
362
|
+
platform: string;
|
|
363
|
+
href: string;
|
|
364
|
+
}): AiFooterLayoutTree;
|
|
365
|
+
/** Insert an hours block into the emptiest/last column. */
|
|
366
|
+
declare function insertFooterHours(tree: AiFooterLayoutTree, hours: Array<{
|
|
367
|
+
day: string;
|
|
368
|
+
hours: string;
|
|
369
|
+
}>, title?: string): AiFooterLayoutTree;
|
|
370
|
+
/**
|
|
371
|
+
* Add a link-list column (≤4 link columns). Packs brand first so it does not
|
|
372
|
+
* burn a slot. Rejects when already at 4 link-lists (returns original).
|
|
373
|
+
*/
|
|
374
|
+
declare function addFooterLinkColumn(tree: AiFooterLayoutTree, column: {
|
|
375
|
+
title?: string;
|
|
376
|
+
links: Array<{
|
|
377
|
+
label: string;
|
|
378
|
+
href: string;
|
|
379
|
+
}>;
|
|
380
|
+
}): AiFooterLayoutTree;
|
|
381
|
+
/** Remove the first block matching `predicate` (e.g. about text). */
|
|
382
|
+
declare function removeFooterBlock(tree: AiFooterLayoutTree, predicate: (block: AiBlockNode, columnIndex: number, blockIndex: number) => boolean): AiFooterLayoutTree;
|
|
250
383
|
|
|
251
384
|
type LinkPage = {
|
|
252
385
|
path: string;
|
|
@@ -559,4 +692,4 @@ declare function useOhwCarousel(key: string, initial: CarouselSlide[]): {
|
|
|
559
692
|
bind: Record<string, string>;
|
|
560
693
|
};
|
|
561
694
|
|
|
562
|
-
export { AI_DEFAULT_BRAND, AI_TREE_SCHEMA_VERSIONS, AI_TREE_TOKENS, type AiBlockNode, type AiBrandKit, type AiLayoutTree, AiTreeRenderer, type AiTreeRendererProps, type CarouselSlide, CustomToolbar, CustomToolbarButton, CustomToolbarDivider, DragHandle, DropIndicator, type DropIndicatorProps, DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, EmptySection, type EmptySectionProps, ItemActionToolbar, type ItemActionToolbarProps, ItemInteractionLayer, type ItemInteractionLayerProps, type ItemInteractionState, LinkEditorPanel, type LinkModalMode, type LinkModalProps, type LinkPage, LinkPopover, type LinkSection, OhhwellsBridge, SchedulingWidget, type SectionScaffold, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, buildTarget, dropIndicatorVariants, filterAvailablePages, getEditModeInitialState, isEditSessionActive, isRenderableTree, isValidUrl, parseTarget, toggleVariants, useOhwCarousel, validateUrlInput };
|
|
695
|
+
export { AI_DEFAULT_BRAND, AI_FOOTER_KEY, AI_TREE_SCHEMA_VERSIONS, AI_TREE_TOKENS, type AiBlockNode, type AiBrandKit, type AiFooterBottomBar, type AiFooterColumn, type AiFooterLayoutTree, AiFooterRenderer, type AiFooterRendererProps, type AiLayoutTree, AiTreeRenderer, type AiTreeRendererProps, type CarouselSlide, CustomToolbar, CustomToolbarButton, CustomToolbarDivider, DragHandle, DropIndicator, type DropIndicatorProps, DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, EmptySection, type EmptySectionProps, FOOTER_COLUMN_BLOCKS, FOOTER_COLUMN_SPANS, FOOTER_SOCIAL_PLATFORMS, ItemActionToolbar, type ItemActionToolbarProps, ItemInteractionLayer, type ItemInteractionLayerProps, type ItemInteractionState, LinkEditorPanel, type LinkModalMode, type LinkModalProps, type LinkPage, LinkPopover, type LinkSection, OhhwellsBridge, SchedulingWidget, type SectionScaffold, type SiteLegalLinks, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, addFooterLinkColumn, appendFooterSocial, buildDefaultFooterTree, buildTarget, dropIndicatorVariants, ensureFooterTree, filterAvailablePages, getEditModeInitialState, insertFooterHours, isEditSessionActive, isRenderableFooterTree, isRenderableTree, isValidUrl, parseAiFooterState, parseTarget, removeFooterBlock, resolveFooterCopyright, serializeAiFooterState, toggleVariants, useOhwCarousel, validateUrlInput };
|
package/dist/index.d.ts
CHANGED
|
@@ -20,14 +20,7 @@ declare function OhhwellsBridge(): react_jsx_runtime.JSX.Element;
|
|
|
20
20
|
*/
|
|
21
21
|
declare const AI_TREE_SCHEMA_VERSIONS: readonly [1];
|
|
22
22
|
type SectionScaffold = 'stacked' | 'split' | 'overlay';
|
|
23
|
-
type AiBlockType = 'text' | 'button' | 'media' | 'media-list' | 'rating' | 'divider' | 'input' | 'section-header' | 'button-row' | 'card' | 'collection' | 'accordion' | 'media-strip' | 'form' | 'embed' | 'schedule-widget' | 'group' | 'video';
|
|
24
|
-
/** media-strip's `items[]` entry shape (rn-mobile-backend's `itemFields` for that block). */
|
|
25
|
-
interface MediaStripItem {
|
|
26
|
-
assetId: string;
|
|
27
|
-
alt: string;
|
|
28
|
-
href?: string;
|
|
29
|
-
linkTarget?: 'new-tab' | 'same-tab';
|
|
30
|
-
}
|
|
23
|
+
type AiBlockType = 'text' | 'button' | 'media' | 'media-list' | 'rating' | 'divider' | 'input' | 'section-header' | 'button-row' | 'card' | 'collection' | 'accordion' | 'media-strip' | 'form' | 'embed' | 'schedule-widget' | 'group' | 'link-list' | 'hours' | 'logo' | 'social-row' | 'video';
|
|
31
24
|
interface AiBlockNode {
|
|
32
25
|
type: AiBlockType;
|
|
33
26
|
span: number;
|
|
@@ -36,11 +29,8 @@ interface AiBlockNode {
|
|
|
36
29
|
slots?: Record<string, unknown>;
|
|
37
30
|
children?: AiBlockNode[];
|
|
38
31
|
/** Shape is per-block-type, defined by the backend's `BlockDef.itemFields` (accordion's
|
|
39
|
-
* question/answer, media-strip's assetId/alt/href/linkTarget). */
|
|
40
|
-
items?: Array<
|
|
41
|
-
question: string;
|
|
42
|
-
answer: string;
|
|
43
|
-
} | MediaStripItem>;
|
|
32
|
+
* question/answer, media-strip's assetId/alt/href/linkTarget, footer link-list/hours/social-row). */
|
|
33
|
+
items?: Array<Record<string, unknown>>;
|
|
44
34
|
animation?: {
|
|
45
35
|
effect: 'fade-in' | 'fade-in-up' | 'slide-in-left' | 'slide-in-right' | 'zoom-in';
|
|
46
36
|
trigger: 'on-scroll' | 'on-load' | 'on-hover';
|
|
@@ -232,6 +222,37 @@ declare const AI_TREE_TOKENS: {
|
|
|
232
222
|
};
|
|
233
223
|
declare function isRenderableTree(value: unknown): value is AiLayoutTree;
|
|
234
224
|
|
|
225
|
+
/**
|
|
226
|
+
* Footer layout-tree contract — bridge-side mirror of
|
|
227
|
+
* `rn-mobile-backend/app/helpers/ai-footer-tree.ts`.
|
|
228
|
+
*
|
|
229
|
+
* The server validates every footer tree fail-closed before it reaches the bridge.
|
|
230
|
+
* Legal links are never on this tree; the renderer injects them from site legal config.
|
|
231
|
+
*/
|
|
232
|
+
|
|
233
|
+
declare const FOOTER_COLUMN_SPANS: readonly [12, 6, 4, 3];
|
|
234
|
+
type FooterColumnSpan = (typeof FOOTER_COLUMN_SPANS)[number];
|
|
235
|
+
declare const FOOTER_COLUMN_BLOCKS: readonly ["link-list", "text", "hours", "logo", "social-row", "media-strip"];
|
|
236
|
+
declare const FOOTER_SOCIAL_PLATFORMS: readonly ["facebook", "instagram", "youtube", "linkedin", "twitter", "tiktok", "threads", "pinterest", "whatsapp"];
|
|
237
|
+
interface AiFooterColumn {
|
|
238
|
+
span: FooterColumnSpan;
|
|
239
|
+
blocks: AiBlockNode[];
|
|
240
|
+
}
|
|
241
|
+
/** Protected bottom bar — copyright only; legal links come from site config at render. */
|
|
242
|
+
interface AiFooterBottomBar {
|
|
243
|
+
/** May include `{year}` — resolved at render time. */
|
|
244
|
+
copyright: string;
|
|
245
|
+
}
|
|
246
|
+
interface AiFooterLayoutTree {
|
|
247
|
+
schemaVersion: number;
|
|
248
|
+
type: 'footer';
|
|
249
|
+
columns: AiFooterColumn[];
|
|
250
|
+
bottomBar: AiFooterBottomBar;
|
|
251
|
+
}
|
|
252
|
+
/** Resolve `{year}` in copyright copy — never ship a stale year. */
|
|
253
|
+
declare function resolveFooterCopyright(copyright: string, year?: number): string;
|
|
254
|
+
declare function isRenderableFooterTree(value: unknown): value is AiFooterLayoutTree;
|
|
255
|
+
|
|
235
256
|
interface AiTreeRendererProps {
|
|
236
257
|
tree: AiLayoutTree;
|
|
237
258
|
brand?: AiBrandKit;
|
|
@@ -247,6 +268,118 @@ interface AiTreeRendererProps {
|
|
|
247
268
|
editKeyPrefix?: string;
|
|
248
269
|
}
|
|
249
270
|
declare function AiTreeRenderer({ tree, brand, buttonStyle, resolveMedia, editKeyPrefix, }: AiTreeRendererProps): react_jsx_runtime.JSX.Element | null;
|
|
271
|
+
/** Tier 1 legal links — always rendered from site config, never from the footer tree. */
|
|
272
|
+
interface SiteLegalLinks {
|
|
273
|
+
privacy: {
|
|
274
|
+
label: string;
|
|
275
|
+
href: string;
|
|
276
|
+
};
|
|
277
|
+
terms: {
|
|
278
|
+
label: string;
|
|
279
|
+
href: string;
|
|
280
|
+
};
|
|
281
|
+
/** Cookie-settings control; omit to hide. Trigger only — no editable href. */
|
|
282
|
+
cookieSettings?: {
|
|
283
|
+
label: string;
|
|
284
|
+
onClick?: () => void;
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
interface AiFooterRendererProps {
|
|
288
|
+
tree: AiFooterLayoutTree;
|
|
289
|
+
brand?: AiBrandKit;
|
|
290
|
+
resolveMedia?: (ref: string) => string | null;
|
|
291
|
+
editKeyPrefix?: string;
|
|
292
|
+
/** Brand logo URL for `logo` blocks. */
|
|
293
|
+
logoUrl?: string | null;
|
|
294
|
+
/** Map page/anchor ids → navigable hrefs for link-list internals. */
|
|
295
|
+
resolvePageHref?: (pageOrAnchorId: string) => string | null;
|
|
296
|
+
/** Always painted in the bottom bar (Tier 1). */
|
|
297
|
+
legal: SiteLegalLinks;
|
|
298
|
+
/** Template footer's own surface — retrofit paints on the real band, not the brand's dark. */
|
|
299
|
+
surface?: {
|
|
300
|
+
background: string;
|
|
301
|
+
text: string;
|
|
302
|
+
} | null;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Site-wide footer from a validated footer layout tree .
|
|
306
|
+
* Bottom bar always includes copyright (`{year}` resolved) + legal links from `legal`.
|
|
307
|
+
*/
|
|
308
|
+
declare function AiFooterRenderer({ tree, brand, resolveMedia, editKeyPrefix, logoUrl, resolvePageHref, legal, surface, }: AiFooterRendererProps): react_jsx_runtime.JSX.Element | null;
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Site-wide AI footer persistence .
|
|
312
|
+
*
|
|
313
|
+
* One tree per site under `__ohw_ai_footer` — same Tier 1 guarded namespace as
|
|
314
|
+
* `__ohw_ai_sections`. Absent / empty = keep the template React footer
|
|
315
|
+
* (pixel-identical retrofit). Present = mount AiFooterRenderer and hide the
|
|
316
|
+
* template footer chrome.
|
|
317
|
+
*/
|
|
318
|
+
|
|
319
|
+
declare const AI_FOOTER_KEY = "__ohw_ai_footer";
|
|
320
|
+
interface AiFooterState {
|
|
321
|
+
v: 1;
|
|
322
|
+
tree: AiFooterLayoutTree;
|
|
323
|
+
media?: Record<string, string>;
|
|
324
|
+
brand?: AiBrandKit;
|
|
325
|
+
}
|
|
326
|
+
/** Parse a persisted footer value — malformed state means "no custom footer". */
|
|
327
|
+
declare function parseAiFooterState(raw: string | null | undefined): AiFooterState | null;
|
|
328
|
+
declare function serializeAiFooterState(state: AiFooterState): string;
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Default footer layout tree — bridge mirror of
|
|
332
|
+
* `rn-mobile-backend/app/helpers/ai-footer-default.ts`.
|
|
333
|
+
*
|
|
334
|
+
* Used when an AI edit first materialises a footer tree from the house default,
|
|
335
|
+
* and as the seed for prompt structured edits. Template footers stay until then.
|
|
336
|
+
*/
|
|
337
|
+
|
|
338
|
+
declare function buildDefaultFooterTree(opts?: {
|
|
339
|
+
businessName?: string;
|
|
340
|
+
about?: string;
|
|
341
|
+
pageLinks?: Array<{
|
|
342
|
+
label: string;
|
|
343
|
+
href: string;
|
|
344
|
+
}>;
|
|
345
|
+
}): AiFooterLayoutTree;
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Pure structured edits for the site-wide footer tree .
|
|
349
|
+
* Prompt intents map here; the bridge/persist layer validates via the backend
|
|
350
|
+
* (or client-side shape checks) before applying.
|
|
351
|
+
*/
|
|
352
|
+
|
|
353
|
+
/** Ensure a footer tree exists — seed from the house default when first editing. */
|
|
354
|
+
declare function ensureFooterTree(tree: AiFooterLayoutTree | null | undefined, opts?: {
|
|
355
|
+
businessName?: string;
|
|
356
|
+
}): AiFooterLayoutTree;
|
|
357
|
+
/**
|
|
358
|
+
* Add or update a social platform link. Same platform → update href (no duplicate icons);
|
|
359
|
+
* missing row → create `social-row` in the emptiest column.
|
|
360
|
+
*/
|
|
361
|
+
declare function appendFooterSocial(tree: AiFooterLayoutTree, entry: {
|
|
362
|
+
platform: string;
|
|
363
|
+
href: string;
|
|
364
|
+
}): AiFooterLayoutTree;
|
|
365
|
+
/** Insert an hours block into the emptiest/last column. */
|
|
366
|
+
declare function insertFooterHours(tree: AiFooterLayoutTree, hours: Array<{
|
|
367
|
+
day: string;
|
|
368
|
+
hours: string;
|
|
369
|
+
}>, title?: string): AiFooterLayoutTree;
|
|
370
|
+
/**
|
|
371
|
+
* Add a link-list column (≤4 link columns). Packs brand first so it does not
|
|
372
|
+
* burn a slot. Rejects when already at 4 link-lists (returns original).
|
|
373
|
+
*/
|
|
374
|
+
declare function addFooterLinkColumn(tree: AiFooterLayoutTree, column: {
|
|
375
|
+
title?: string;
|
|
376
|
+
links: Array<{
|
|
377
|
+
label: string;
|
|
378
|
+
href: string;
|
|
379
|
+
}>;
|
|
380
|
+
}): AiFooterLayoutTree;
|
|
381
|
+
/** Remove the first block matching `predicate` (e.g. about text). */
|
|
382
|
+
declare function removeFooterBlock(tree: AiFooterLayoutTree, predicate: (block: AiBlockNode, columnIndex: number, blockIndex: number) => boolean): AiFooterLayoutTree;
|
|
250
383
|
|
|
251
384
|
type LinkPage = {
|
|
252
385
|
path: string;
|
|
@@ -559,4 +692,4 @@ declare function useOhwCarousel(key: string, initial: CarouselSlide[]): {
|
|
|
559
692
|
bind: Record<string, string>;
|
|
560
693
|
};
|
|
561
694
|
|
|
562
|
-
export { AI_DEFAULT_BRAND, AI_TREE_SCHEMA_VERSIONS, AI_TREE_TOKENS, type AiBlockNode, type AiBrandKit, type AiLayoutTree, AiTreeRenderer, type AiTreeRendererProps, type CarouselSlide, CustomToolbar, CustomToolbarButton, CustomToolbarDivider, DragHandle, DropIndicator, type DropIndicatorProps, DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, EmptySection, type EmptySectionProps, ItemActionToolbar, type ItemActionToolbarProps, ItemInteractionLayer, type ItemInteractionLayerProps, type ItemInteractionState, LinkEditorPanel, type LinkModalMode, type LinkModalProps, type LinkPage, LinkPopover, type LinkSection, OhhwellsBridge, SchedulingWidget, type SectionScaffold, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, buildTarget, dropIndicatorVariants, filterAvailablePages, getEditModeInitialState, isEditSessionActive, isRenderableTree, isValidUrl, parseTarget, toggleVariants, useOhwCarousel, validateUrlInput };
|
|
695
|
+
export { AI_DEFAULT_BRAND, AI_FOOTER_KEY, AI_TREE_SCHEMA_VERSIONS, AI_TREE_TOKENS, type AiBlockNode, type AiBrandKit, type AiFooterBottomBar, type AiFooterColumn, type AiFooterLayoutTree, AiFooterRenderer, type AiFooterRendererProps, type AiLayoutTree, AiTreeRenderer, type AiTreeRendererProps, type CarouselSlide, CustomToolbar, CustomToolbarButton, CustomToolbarDivider, DragHandle, DropIndicator, type DropIndicatorProps, DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, EmptySection, type EmptySectionProps, FOOTER_COLUMN_BLOCKS, FOOTER_COLUMN_SPANS, FOOTER_SOCIAL_PLATFORMS, ItemActionToolbar, type ItemActionToolbarProps, ItemInteractionLayer, type ItemInteractionLayerProps, type ItemInteractionState, LinkEditorPanel, type LinkModalMode, type LinkModalProps, type LinkPage, LinkPopover, type LinkSection, OhhwellsBridge, SchedulingWidget, type SectionScaffold, type SiteLegalLinks, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, addFooterLinkColumn, appendFooterSocial, buildDefaultFooterTree, buildTarget, dropIndicatorVariants, ensureFooterTree, filterAvailablePages, getEditModeInitialState, insertFooterHours, isEditSessionActive, isRenderableFooterTree, isRenderableTree, isValidUrl, parseAiFooterState, parseTarget, removeFooterBlock, resolveFooterCopyright, serializeAiFooterState, toggleVariants, useOhwCarousel, validateUrlInput };
|