@syntrologie/adapt-search 2.8.0-canary.568

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.
@@ -0,0 +1,32 @@
1
+ /**
2
+ * adaptive-search — product URL scheme guard
3
+ *
4
+ * Same rule and same reasoning as `isSafeNavigationHref` in
5
+ * `packages/adaptives/adaptive-overlays/src/cta-navigation.ts`: parse with the
6
+ * URL parser rather than matching string prefixes, because browsers strip
7
+ * embedded control characters from a scheme before resolving it, so
8
+ * `"java\tscript:alert(1)"` slips straight past a
9
+ * `.trim().toLowerCase().startsWith('javascript:')` check.
10
+ *
11
+ * It is copied rather than imported because that helper is internal to
12
+ * adaptive-overlays (its package `exports` publish only `./runtime`,
13
+ * `./schema` and `./cdn`), and every adaptive in this repo keeps its own
14
+ * sanitizer for the same reason — see `adaptive-product/src/sanitizer.ts`,
15
+ * `adaptive-overlays/src/sanitizer.ts`, `adaptive-content/src/sanitizer.ts`.
16
+ */
17
+ /** Where an unsafe or unparseable product URL points instead: nowhere. */
18
+ export declare const UNSAFE_HREF_FALLBACK = "#";
19
+ /** True when `href` resolves to a scheme it is safe to navigate to. */
20
+ export declare function isSafeProductHref(href: unknown): href is string;
21
+ /**
22
+ * The href to render for a product tile: the authored URL when its scheme is
23
+ * safe, otherwise `'#'`.
24
+ *
25
+ * The tile keeps its anchor either way. A shopper who can reach a tile with
26
+ * the keyboard must still be able to focus it and hear its name, and a tile
27
+ * whose link we silently dropped is a tile that looks tappable and is not.
28
+ * Callers mark the fallback case with `data-unsafe-url` so it is visible in
29
+ * the DOM rather than looking like an ordinary link.
30
+ */
31
+ export declare function safeProductHref(href: unknown): string;
32
+ //# sourceMappingURL=hrefSafety.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hrefSafety.d.ts","sourceRoot":"","sources":["../src/hrefSafety.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,0EAA0E;AAC1E,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC,uEAAuE;AACvE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,MAAM,CAW/D;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAErD"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * adaptive-search — region modules
3
+ *
4
+ * One function per module the plan can put in a region. Everything here is
5
+ * rendered with Lit `${}` interpolation only: never `unsafeHTML`, because
6
+ * every string on this surface is either shopper-authored (the query, the
7
+ * chips) or merchant catalogue text arriving over the wire.
8
+ *
9
+ * A module this build does not know renders nothing and throws nothing. The
10
+ * plan service ships new modules on its own cadence; an older SDK on a
11
+ * merchant's page must degrade to a quieter surface, never to a broken one.
12
+ */
13
+ import { nothing, type TemplateResult } from 'lit';
14
+ import type { SurfaceRegion } from './variants';
15
+ /** A product as the plan service and the store search API both describe one. */
16
+ export interface SearchProduct {
17
+ handle: string;
18
+ title: string;
19
+ url: string;
20
+ price?: string;
21
+ imageUrl?: string;
22
+ }
23
+ /** A constraint the surface understood, shown as a removable chip. */
24
+ export interface SearchChip {
25
+ key: string;
26
+ label: string;
27
+ }
28
+ /** A follow-up the plan suggests, shown as a tappable button. */
29
+ export interface SearchSuggestion {
30
+ value: string;
31
+ label: string;
32
+ }
33
+ /** Follow-up bar payload: the shopper's words, what we understood, what next. */
34
+ export interface FollowupBarPayload {
35
+ query?: string;
36
+ chips?: SearchChip[];
37
+ suggestions?: SearchSuggestion[];
38
+ /**
39
+ * A short line the plan wants under the chips, e.g. "Nothing under $5. Here
40
+ * are the closest matches." Merchant-neutral server prose, interpolated like
41
+ * every other string here and never `unsafeHTML`.
42
+ */
43
+ notice?: string;
44
+ }
45
+ /** How a tile reports itself when it is tapped. */
46
+ export interface ResultTapDetail {
47
+ handle: string;
48
+ regionId: string;
49
+ module: string;
50
+ position: number;
51
+ }
52
+ /** The three shapes a follow-up takes. All patch the same server session. */
53
+ export type FollowupDetail = {
54
+ kind: 'refine';
55
+ value: string;
56
+ } | {
57
+ kind: 'chip_removed';
58
+ value: string;
59
+ } | {
60
+ kind: 'item_dismissed';
61
+ value: string;
62
+ };
63
+ export declare const RESULT_TAP_EVENT = "syntro:search:result-tap";
64
+ export declare const FOLLOWUP_EVENT = "syntro:search:followup";
65
+ export declare const SEE_STORE_RESULTS_EVENT = "syntro:search:see-store-results";
66
+ /** Read the follow-up bar's payload. Exported so the surface can merge the query in. */
67
+ export declare function readFollowupBarPayload(payload: unknown): Required<FollowupBarPayload>;
68
+ /**
69
+ * Handles the shopper has hidden. A dismissal is optimistic — the tile goes as
70
+ * soon as it is tapped, before any server round trip — and it OUTLIVES the
71
+ * payload it was tapped in: a later repaint of the same region, or a whole new
72
+ * plan for a surface with no server session, would otherwise hand the shopper
73
+ * back the product they just rejected.
74
+ *
75
+ * `visible` / `visibleOne` below apply this to what is RENDERED, which is what
76
+ * hides a tile in a region that has already painted and is not repainted. The
77
+ * runtime keeps the same rule over incoming payloads — `withoutDismissed` in
78
+ * `runtime-sdk/src/search/SearchSurfaceController.ts` — so a dismissed product
79
+ * is also out of `result_count`. Both are needed; neither replaces the other.
80
+ */
81
+ export type DismissedHandles = ReadonlySet<string>;
82
+ /**
83
+ * How many products a filled region put on screen. The surface announces the
84
+ * running total through its live region, so this has to agree with what the
85
+ * module above actually rendered — dismissals included, because a hidden tile
86
+ * is not a result the shopper was shown.
87
+ */
88
+ export declare function countRegionProducts(region: SurfaceRegion, dismissed?: DismissedHandles): number;
89
+ /** Use the same payload readers as rendering to decide whether there is an answer. */
90
+ export declare function regionHasAnswer(region: SurfaceRegion, dismissed?: DismissedHandles): boolean;
91
+ /** Render a region's module. An unknown module renders nothing and throws nothing. */
92
+ export declare function renderRegionModule(region: SurfaceRegion, dismissed?: DismissedHandles): TemplateResult | typeof nothing;
93
+ //# sourceMappingURL=regions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"regions.d.ts","sourceRoot":"","sources":["../src/regions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAQ,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AAGzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAMhD,gFAAgF;AAChF,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,sEAAsE;AACtE,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,iEAAiE;AACjE,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,iFAAiF;AACjF,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IACrB,WAAW,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACjC;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,mDAAmD;AACnD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,6EAA6E;AAC7E,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACjC;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAM9C,eAAO,MAAM,gBAAgB,6BAA6B,CAAC;AAC3D,eAAO,MAAM,cAAc,2BAA2B,CAAC;AACvD,eAAO,MAAM,uBAAuB,oCAAoC,CAAC;AA8DzE,wFAAwF;AACxF,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,QAAQ,CAAC,kBAAkB,CAAC,CAQrF;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,gBAAgB,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;AAiBnD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,aAAa,EACrB,SAAS,GAAE,gBAAiC,GAC3C,MAAM,CAqBR;AAED,sFAAsF;AACtF,wBAAgB,eAAe,CAC7B,MAAM,EAAE,aAAa,EACrB,SAAS,GAAE,gBAAiC,GAC3C,OAAO,CAKT;AA+ND,sFAAsF;AACtF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,aAAa,EACrB,SAAS,GAAE,gBAAiC,GAC3C,cAAc,GAAG,OAAO,OAAO,CA0BjC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * adaptive-search — Runtime manifest
3
+ *
4
+ * Registers `<syntro-search-surface>` as a side effect of importing this
5
+ * module, and exposes the mountable the runtime's WidgetRegistry uses.
6
+ */
7
+ import { type MountPlumbing } from '@syntrologie/sdk-contracts';
8
+ import type { SearchSurfaceConfig } from './schema';
9
+ /**
10
+ * Mounts the surface and, when the interceptor already chose a variant,
11
+ * paints its skeleton in the same frame. Later tasks keep the element handle
12
+ * and stream regions into it with `fillRegion`.
13
+ */
14
+ export declare const SearchSurfaceMountable: {
15
+ mount(container: HTMLElement, config?: (SearchSurfaceConfig & MountPlumbing) | null): () => void;
16
+ };
17
+ export declare const runtime: {
18
+ id: string;
19
+ version: string;
20
+ name: string;
21
+ description: string;
22
+ /** No DOM-mutation executors: this surface renders only. */
23
+ executors: never[];
24
+ widgets: {
25
+ id: string;
26
+ component: {
27
+ mount(container: HTMLElement, config?: (SearchSurfaceConfig & MountPlumbing) | null): () => void;
28
+ };
29
+ metadata: {
30
+ name: string;
31
+ description: string;
32
+ icon: string;
33
+ /**
34
+ * The surface replaces a whole results page, so it needs the
35
+ * full-viewport slot. It is not self-sufficient anywhere else: the
36
+ * scrolling and scroll containment it relies on come from
37
+ * `overlay_full`'s own slot styles (`overflow: auto` plus
38
+ * `overscroll-behavior: contain`), not from this element.
39
+ */
40
+ slots: string[];
41
+ };
42
+ }[];
43
+ };
44
+ export default runtime;
45
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,KAAK,aAAa,EAAsB,MAAM,4BAA4B,CAAC;AAGpF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAIpD;;;;GAIG;AACH,eAAO,MAAM,sBAAsB;qBAChB,WAAW,WAAW,CAAC,mBAAmB,GAAG,aAAa,CAAC,GAAG,IAAI;CASpF,CAAC;AAEF,eAAO,MAAM,OAAO;;;;;IAMlB,4DAA4D;;;;;6BAjB3C,WAAW,WAAW,CAAC,mBAAmB,GAAG,aAAa,CAAC,GAAG,IAAI;;;;;;YA4B7E;;;;;;eAMG;;;;CAKV,CAAC;AAEF,eAAe,OAAO,CAAC"}
@@ -0,0 +1,12 @@
1
+ import {
2
+ SearchSurfaceMountable,
3
+ runtime,
4
+ runtime_default
5
+ } from "./chunk-3KPQ5AOG.js";
6
+ import "./chunk-NKOTQPVZ.js";
7
+ export {
8
+ SearchSurfaceMountable,
9
+ runtime_default as default,
10
+ runtime
11
+ };
12
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * adaptive-search — Zod schema and capabilities documentation
3
+ *
4
+ * The surface is driven by the plan service at runtime, not authored as a
5
+ * canvas tile, so the only config it accepts is the opening state the search
6
+ * interceptor hands it at mount: which variant the rules chose and what the
7
+ * shopper typed. Everything after that arrives through `fillRegion`.
8
+ */
9
+ import { z } from 'zod';
10
+ export declare const searchSurfaceSchema: z.ZodObject<{
11
+ variant: z.ZodOptional<z.ZodEnum<["grid", "stacked", "rail", "hero", "compare"]>>;
12
+ query: z.ZodOptional<z.ZodString>;
13
+ }, "strict", z.ZodTypeAny, {
14
+ query?: string | undefined;
15
+ variant?: "grid" | "stacked" | "rail" | "hero" | "compare" | undefined;
16
+ }, {
17
+ query?: string | undefined;
18
+ variant?: "grid" | "stacked" | "rail" | "hero" | "compare" | undefined;
19
+ }>;
20
+ export type SearchSurfaceConfig = z.infer<typeof searchSurfaceSchema>;
21
+ /**
22
+ * No tile widgets: this surface is not something a config author places on a
23
+ * page. It is mounted by the search interceptor into the full-viewport
24
+ * overlay slot when a shopper searches.
25
+ */
26
+ export declare const tileWidgets: {
27
+ widget: string;
28
+ defName: string;
29
+ propsSchema: z.ZodTypeAny;
30
+ }[];
31
+ export declare const CAPABILITIES_DOCUMENTATION: {
32
+ packageId: string;
33
+ plannerSummary: string;
34
+ description: string;
35
+ whenToUse: {
36
+ goal: string;
37
+ action: string;
38
+ }[];
39
+ conventions: {
40
+ name: string;
41
+ description: string;
42
+ }[];
43
+ events: {
44
+ name: string;
45
+ when: string;
46
+ props: string;
47
+ }[];
48
+ };
49
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,eAAO,MAAM,mBAAmB;;;;;;;;;EAcrB,CAAC;AAEZ,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEtE;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,CAAC,CAAC,UAAU,CAAC;CAC3B,EAAO,CAAC;AAMT,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;CAoDtC,CAAC"}
package/dist/schema.js ADDED
@@ -0,0 +1,66 @@
1
+ import {
2
+ SURFACE_VARIANTS,
3
+ external_exports
4
+ } from "./chunk-NKOTQPVZ.js";
5
+
6
+ // src/schema.ts
7
+ var MAX_QUERY_LENGTH = 512;
8
+ var searchSurfaceSchema = external_exports.object({
9
+ variant: external_exports.enum(SURFACE_VARIANTS).optional().describe(
10
+ "Layout to paint immediately: grid, stacked, rail, hero or compare. Omit to mount an empty surface and wait for the plan."
11
+ ),
12
+ query: external_exports.string().max(MAX_QUERY_LENGTH).optional().describe("The shopper's own words, shown in the follow-up bar and still editable.")
13
+ }).strict();
14
+ var tileWidgets = [];
15
+ var CAPABILITIES_DOCUMENTATION = {
16
+ packageId: "adaptive-search",
17
+ plannerSummary: "The search results surface. Reach for this when the strategy is about what a shopper sees after they search, not about placing a tile on a page.",
18
+ description: "Draws a search results page as a Syntro surface: a layout skeleton painted per variant, then filled region by region as content arrives.",
19
+ whenToUse: [
20
+ {
21
+ goal: "Turn on the search takeover for a workspace",
22
+ action: "Set search.enabled to true in the config. The surface mounts itself when a shopper searches."
23
+ },
24
+ {
25
+ goal: "Watch searches without changing what a shopper sees",
26
+ action: "Set search.observe to true instead. Telemetry is emitted and the store\u2019s own search still runs."
27
+ }
28
+ ],
29
+ conventions: [
30
+ {
31
+ name: "The plan chooses contents, never shape",
32
+ description: "Each variant owns a fixed set of named regions. The plan service fills them by region id; it cannot invent a region or move one."
33
+ },
34
+ {
35
+ name: "Theme tokens",
36
+ description: "The surface reads CSS custom properties from the canvas theme: --sc-color-primary, --sc-font-family, --sc-tile-background, --sc-tile-text-color, --sc-chip-background, --sc-border-radius. It hard-codes no brand colors."
37
+ },
38
+ {
39
+ name: "The way back to the store",
40
+ description: 'Every variant carries a "See store results" button in its sticky query region, so a shopper can always reach the store\u2019s own results.'
41
+ }
42
+ ],
43
+ events: [
44
+ {
45
+ name: "syntro:search:followup",
46
+ when: "Shopper refines the search: types a new query, removes a chip, or dismisses a product",
47
+ props: "{ kind: 'refine' | 'chip_removed' | 'item_dismissed', value }"
48
+ },
49
+ {
50
+ name: "syntro:search:result-tap",
51
+ when: "Shopper taps a product tile",
52
+ props: "{ handle, regionId, module, position }"
53
+ },
54
+ {
55
+ name: "syntro:search:see-store-results",
56
+ when: "Shopper asks for the store's own search results instead",
57
+ props: "{}"
58
+ }
59
+ ]
60
+ };
61
+ export {
62
+ CAPABILITIES_DOCUMENTATION,
63
+ searchSurfaceSchema,
64
+ tileWidgets
65
+ };
66
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/schema.ts"],
4
+ "sourcesContent": ["/**\n * adaptive-search \u2014 Zod schema and capabilities documentation\n *\n * The surface is driven by the plan service at runtime, not authored as a\n * canvas tile, so the only config it accepts is the opening state the search\n * interceptor hands it at mount: which variant the rules chose and what the\n * shopper typed. Everything after that arrives through `fillRegion`.\n */\n\nimport { z } from 'zod';\n\nimport { SURFACE_VARIANTS } from './variants';\n\n/** The classifier stops reading a query here, so nothing longer can be real. */\nconst MAX_QUERY_LENGTH = 512;\n\nexport const searchSurfaceSchema = z\n .object({\n variant: z\n .enum(SURFACE_VARIANTS)\n .optional()\n .describe(\n 'Layout to paint immediately: grid, stacked, rail, hero or compare. Omit to mount an empty surface and wait for the plan.'\n ),\n query: z\n .string()\n .max(MAX_QUERY_LENGTH)\n .optional()\n .describe(\"The shopper's own words, shown in the follow-up bar and still editable.\"),\n })\n .strict();\n\nexport type SearchSurfaceConfig = z.infer<typeof searchSurfaceSchema>;\n\n/**\n * No tile widgets: this surface is not something a config author places on a\n * page. It is mounted by the search interceptor into the full-viewport\n * overlay slot when a shopper searches.\n */\nexport const tileWidgets: {\n widget: string;\n defName: string;\n propsSchema: z.ZodTypeAny;\n}[] = [];\n\n// ============================================================================\n// Capabilities Documentation (injected into JSON Schema for LLM prompts)\n// ============================================================================\n\nexport const CAPABILITIES_DOCUMENTATION = {\n packageId: 'adaptive-search',\n plannerSummary:\n 'The search results surface. Reach for this when the strategy is about what a shopper sees after they search, not about placing a tile on a page.',\n description:\n 'Draws a search results page as a Syntro surface: a layout skeleton painted per variant, then filled region by region as content arrives.',\n whenToUse: [\n {\n goal: 'Turn on the search takeover for a workspace',\n action:\n 'Set search.enabled to true in the config. The surface mounts itself when a shopper searches.',\n },\n {\n goal: 'Watch searches without changing what a shopper sees',\n action:\n 'Set search.observe to true instead. Telemetry is emitted and the store\u2019s own search still runs.',\n },\n ],\n conventions: [\n {\n name: 'The plan chooses contents, never shape',\n description:\n 'Each variant owns a fixed set of named regions. The plan service fills them by region id; it cannot invent a region or move one.',\n },\n {\n name: 'Theme tokens',\n description:\n 'The surface reads CSS custom properties from the canvas theme: --sc-color-primary, --sc-font-family, --sc-tile-background, --sc-tile-text-color, --sc-chip-background, --sc-border-radius. It hard-codes no brand colors.',\n },\n {\n name: 'The way back to the store',\n description:\n 'Every variant carries a \"See store results\" button in its sticky query region, so a shopper can always reach the store\u2019s own results.',\n },\n ],\n events: [\n {\n name: 'syntro:search:followup',\n when: 'Shopper refines the search: types a new query, removes a chip, or dismisses a product',\n props: \"{ kind: 'refine' | 'chip_removed' | 'item_dismissed', value }\",\n },\n {\n name: 'syntro:search:result-tap',\n when: 'Shopper taps a product tile',\n props: '{ handle, regionId, module, position }',\n },\n {\n name: 'syntro:search:see-store-results',\n when: \"Shopper asks for the store's own search results instead\",\n props: '{}',\n },\n ],\n};\n"],
5
+ "mappings": ";;;;;;AAcA,IAAM,mBAAmB;AAElB,IAAM,sBAAsB,iBAChC,OAAO;AAAA,EACN,SAAS,iBACN,KAAK,gBAAgB,EACrB,SAAS,EACT;AAAA,IACC;AAAA,EACF;AAAA,EACF,OAAO,iBACJ,OAAO,EACP,IAAI,gBAAgB,EACpB,SAAS,EACT,SAAS,yEAAyE;AACvF,CAAC,EACA,OAAO;AASH,IAAM,cAIP,CAAC;AAMA,IAAM,6BAA6B;AAAA,EACxC,WAAW;AAAA,EACX,gBACE;AAAA,EACF,aACE;AAAA,EACF,WAAW;AAAA,IACT;AAAA,MACE,MAAM;AAAA,MACN,QACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,QACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,aAAa;AAAA,IACX;AAAA,MACE,MAAM;AAAA,MACN,aACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,aACE;AAAA,IACJ;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,aACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,QAAQ;AAAA,IACN;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,MACN,OAAO;AAAA,IACT;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,MACN,OAAO;AAAA,IACT;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,MAAM;AAAA,MACN,OAAO;AAAA,IACT;AAAA,EACF;AACF;",
6
+ "names": []
7
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * adaptive-search — layout variants
3
+ *
4
+ * A variant declares the fixed set of named regions the surface paints. The
5
+ * plan chooses what goes IN each region; it never chooses the shape. That
6
+ * split is what lets the surface paint a skeleton the instant the shopper
7
+ * presses Enter, before a single byte of content has arrived.
8
+ *
9
+ * `src/__fixtures__/search-variant-roles.json` is the wire contract: the
10
+ * plan service keeps a byte-equal copy and asserts against this path, so the
11
+ * region ids the server emits always resolve to a region this surface owns.
12
+ */
13
+ /** The five layouts. Same values as the SDK classifier's `SearchVariantHint`. */
14
+ export declare const SURFACE_VARIANTS: readonly ["grid", "stacked", "rail", "hero", "compare"];
15
+ export type SurfaceVariant = (typeof SURFACE_VARIANTS)[number];
16
+ /** What a region is FOR. A module fills it; the role decides where it sits. */
17
+ export type RegionRole = 'query' | 'primary' | 'secondary' | 'support' | 'converse';
18
+ /** One painted region: an id the plan addresses, a role, and the module inside. */
19
+ export interface SurfaceRegion {
20
+ id: string;
21
+ role: RegionRole;
22
+ module: string;
23
+ payload?: unknown;
24
+ }
25
+ /** A server plan: the variant to paint and the regions to fill. */
26
+ export interface SurfacePlan {
27
+ variant: SurfaceVariant;
28
+ regions: SurfaceRegion[];
29
+ suppress?: string[];
30
+ }
31
+ /** The fixed slots each variant owns. The plan chooses contents, never shape. */
32
+ export declare const VARIANT_REGION_ROLES: Record<SurfaceVariant, readonly RegionRole[]>;
33
+ /** A region slot in the painted skeleton: `<role>-<n>`, n restarting per role. */
34
+ export interface SkeletonRegion {
35
+ id: string;
36
+ role: RegionRole;
37
+ }
38
+ /**
39
+ * The region slots a variant paints, in paint order, with deterministic ids.
40
+ *
41
+ * Deterministic because the plan service numbers its regions the same way and
42
+ * addresses them by id alone. Derived from `VARIANT_REGION_ROLES` rather than
43
+ * listed a second time, so the two can never disagree.
44
+ */
45
+ export declare function skeletonRegions(variant: SurfaceVariant): SkeletonRegion[];
46
+ /**
47
+ * True when every region in the plan has a slot of its role left in the
48
+ * variant. A plan that does not fit is a plan whose extra regions would paint
49
+ * nowhere, which is worse than painting the variant the plan asked for.
50
+ */
51
+ export declare function planRegionsFit(plan: SurfacePlan): boolean;
52
+ //# sourceMappingURL=variants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variants.d.ts","sourceRoot":"","sources":["../src/variants.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,iFAAiF;AACjF,eAAO,MAAM,gBAAgB,yDAA0D,CAAC;AAExF,MAAM,MAAM,cAAc,GAAG,CAAC,OAAO,gBAAgB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/D,+EAA+E;AAC/E,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,UAAU,CAAC;AAEpF,mFAAmF;AACnF,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,mEAAmE;AACnE,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,cAAc,CAAC;IACxB,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,iFAAiF;AACjF,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,cAAc,EAAE,SAAS,UAAU,EAAE,CAM9E,CAAC;AAEF,kFAAkF;AAClF,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,cAAc,EAAE,CAOzE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAQzD"}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@syntrologie/adapt-search",
3
+ "version": "2.8.0-canary.568",
4
+ "description": "Adaptive Search: the search results surface, a layout skeleton painted per variant and then filled region by region as content arrives.",
5
+ "license": "Proprietary",
6
+ "private": false,
7
+ "author": "Syntrologie <eng@syntrologie.com>",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/SyntropyForge/amazing-demos.git",
11
+ "directory": "packages/adaptives/adaptive-search"
12
+ },
13
+ "publishConfig": {
14
+ "access": "public"
15
+ },
16
+ "syntrologie": {
17
+ "bundlingMode": "cdn-only"
18
+ },
19
+ "type": "module",
20
+ "exports": {
21
+ "./runtime": {
22
+ "types": "./dist/runtime.d.ts",
23
+ "import": "./dist/runtime.js"
24
+ },
25
+ "./schema": {
26
+ "types": "./dist/schema.d.ts",
27
+ "import": "./dist/schema.js"
28
+ },
29
+ "./cdn": {
30
+ "types": "./dist/cdn.d.ts",
31
+ "import": "./dist/cdn.js"
32
+ }
33
+ },
34
+ "files": [
35
+ "dist"
36
+ ],
37
+ "scripts": {
38
+ "build": "tsc --emitDeclarationOnly && node ../scripts/build-lib.mjs",
39
+ "typecheck": "tsc --noEmit",
40
+ "clean": "rm -rf dist",
41
+ "test": "vitest run",
42
+ "test:watch": "vitest",
43
+ "lint": "biome check ."
44
+ },
45
+ "peerDependencies": {
46
+ "lit": "3.3.2"
47
+ },
48
+ "devDependencies": {
49
+ "@open-wc/testing": "4.0.0",
50
+ "@open-wc/testing-helpers": "3.0.1",
51
+ "@syntrologie/sdk-contracts": "*",
52
+ "jsdom": "26.1.0",
53
+ "lit": "3.3.2",
54
+ "typescript": "5.9.3",
55
+ "vitest": "4.0.18",
56
+ "zod": "3.25.76"
57
+ }
58
+ }