@ai-matrx/content-ir-react 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/dist/db-component.d.cts +8 -8
  3. package/dist/db-component.d.ts +8 -8
  4. package/dist/directive-host-DAb7Keg8.d.cts +63 -0
  5. package/dist/directive-host-DAb7Keg8.d.ts +63 -0
  6. package/dist/directives.cjs +382 -0
  7. package/dist/directives.cjs.map +1 -0
  8. package/dist/directives.d.cts +70 -0
  9. package/dist/directives.d.ts +70 -0
  10. package/dist/directives.js +369 -0
  11. package/dist/directives.js.map +1 -0
  12. package/dist/{host-types-CDcLW0TD.d.cts → host-types-C8jR31Wk.d.cts} +9 -0
  13. package/dist/{host-types-CpItjcYh.d.ts → host-types-IUEDmso7.d.ts} +9 -0
  14. package/dist/host.cjs +14 -4
  15. package/dist/host.cjs.map +1 -1
  16. package/dist/host.d.cts +5 -4
  17. package/dist/host.d.ts +5 -4
  18. package/dist/host.js +14 -4
  19. package/dist/host.js.map +1 -1
  20. package/dist/index.cjs +437 -46
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +85 -21
  23. package/dist/index.d.ts +85 -21
  24. package/dist/index.js +435 -30
  25. package/dist/index.js.map +1 -1
  26. package/dist/react.cjs +401 -46
  27. package/dist/react.cjs.map +1 -1
  28. package/dist/react.d.cts +11 -10
  29. package/dist/react.d.ts +11 -10
  30. package/dist/react.js +398 -29
  31. package/dist/react.js.map +1 -1
  32. package/dist/{route-QL2Vqf2e.d.cts → route-Bu85A41G.d.cts} +83 -3
  33. package/dist/{route-PUuEuEug.d.ts → route-DoFoDKBn.d.ts} +83 -3
  34. package/dist/route.cjs +14 -0
  35. package/dist/route.cjs.map +1 -1
  36. package/dist/route.d.cts +1 -1
  37. package/dist/route.d.ts +1 -1
  38. package/dist/route.js +15 -0
  39. package/dist/route.js.map +1 -1
  40. package/package.json +15 -2
@@ -0,0 +1,369 @@
1
+ "use client";
2
+
3
+ // directives/directive-host.tsx
4
+ import { createContext, useContext } from "react";
5
+ import { jsx } from "react/jsx-runtime";
6
+ var DirectiveHostContext = createContext(null);
7
+ function DirectiveHostProvider({ host, children }) {
8
+ return /* @__PURE__ */ jsx(DirectiveHostContext.Provider, { value: host, children });
9
+ }
10
+ var EMPTY_HOST = {};
11
+ function useDirectiveHost() {
12
+ return useContext(DirectiveHostContext) ?? EMPTY_HOST;
13
+ }
14
+
15
+ // directives/registry.ts
16
+ import {
17
+ buildDirectiveSlug
18
+ } from "@ai-matrx/content-ir";
19
+ var REGISTRY_SLOT = /* @__PURE__ */ Symbol.for("ai-matrx.content-ir-react.directive-renderers");
20
+ var registry = globalThis[REGISTRY_SLOT] ??= {
21
+ bySlug: /* @__PURE__ */ new Map(),
22
+ byClass: /* @__PURE__ */ new Map()
23
+ };
24
+ function registerDirectiveRenderer(directiveClass, renderer, noun) {
25
+ if (noun) registry.bySlug.set(buildDirectiveSlug(directiveClass, noun), renderer);
26
+ else registry.byClass.set(directiveClass, renderer);
27
+ }
28
+ function getDirectiveRenderer(directive) {
29
+ return registry.bySlug.get(directive.slug) ?? registry.byClass.get(directive.directiveClass) ?? null;
30
+ }
31
+ function resetDirectiveRenderers() {
32
+ registry.bySlug.clear();
33
+ registry.byClass.clear();
34
+ }
35
+
36
+ // directives/ApplyDirectiveButton.tsx
37
+ import { useEffect, useRef, useState } from "react";
38
+
39
+ // directives/icons.tsx
40
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
41
+ function Svg({ children, ...props }) {
42
+ return /* @__PURE__ */ jsx2(
43
+ "svg",
44
+ {
45
+ xmlns: "http://www.w3.org/2000/svg",
46
+ viewBox: "0 0 24 24",
47
+ fill: "none",
48
+ stroke: "currentColor",
49
+ strokeWidth: 2,
50
+ strokeLinecap: "round",
51
+ strokeLinejoin: "round",
52
+ "aria-hidden": "true",
53
+ ...props,
54
+ children
55
+ }
56
+ );
57
+ }
58
+ var FilePlusIcon = (p) => /* @__PURE__ */ jsxs(Svg, { ...p, children: [
59
+ /* @__PURE__ */ jsx2("path", { d: "M4 22h14a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v4" }),
60
+ /* @__PURE__ */ jsx2("path", { d: "M14 2v4a2 2 0 0 0 2 2h4" }),
61
+ /* @__PURE__ */ jsx2("path", { d: "M3 15h6" }),
62
+ /* @__PURE__ */ jsx2("path", { d: "M6 12v6" })
63
+ ] });
64
+ var PencilIcon = (p) => /* @__PURE__ */ jsxs(Svg, { ...p, children: [
65
+ /* @__PURE__ */ jsx2("path", { d: "M12 20h9" }),
66
+ /* @__PURE__ */ jsx2("path", { d: "M16.376 3.622a1 1 0 0 1 3.002 3.002L7.368 18.635a2 2 0 0 1-.855.506l-2.872.838a.5.5 0 0 1-.62-.62l.838-2.872a2 2 0 0 1 .506-.854z" })
67
+ ] });
68
+ var TrashIcon = (p) => /* @__PURE__ */ jsxs(Svg, { ...p, children: [
69
+ /* @__PURE__ */ jsx2("path", { d: "M3 6h18" }),
70
+ /* @__PURE__ */ jsx2("path", { d: "M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" }),
71
+ /* @__PURE__ */ jsx2("path", { d: "M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" }),
72
+ /* @__PURE__ */ jsx2("line", { x1: "10", x2: "10", y1: "11", y2: "17" }),
73
+ /* @__PURE__ */ jsx2("line", { x1: "14", x2: "14", y1: "11", y2: "17" })
74
+ ] });
75
+ var PlayIcon = (p) => /* @__PURE__ */ jsx2(Svg, { ...p, children: /* @__PURE__ */ jsx2("polygon", { points: "6 3 20 12 6 21 6 3" }) });
76
+ var WebhookIcon = (p) => /* @__PURE__ */ jsxs(Svg, { ...p, children: [
77
+ /* @__PURE__ */ jsx2("path", { d: "M18 16.98h-5.99c-1.1 0-1.95.94-2.48 1.9A4 4 0 0 1 2 17c.01-.7.2-1.4.57-2" }),
78
+ /* @__PURE__ */ jsx2("path", { d: "m6 17 3.13-5.78c.53-.97.1-2.01-.4-2.96A4 4 0 0 1 14 5.5c.63.42 1.13 1 1.44 1.66" }),
79
+ /* @__PURE__ */ jsx2("path", { d: "m12 6 3.13 5.73C15.66 12.7 16.9 13 18 13a4 4 0 0 1 0 8" })
80
+ ] });
81
+ var BoxesIcon = (p) => /* @__PURE__ */ jsxs(Svg, { ...p, children: [
82
+ /* @__PURE__ */ jsx2("path", { d: "M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z" }),
83
+ /* @__PURE__ */ jsx2("path", { d: "m7 16.5-4.74-2.85" }),
84
+ /* @__PURE__ */ jsx2("path", { d: "m7 16.5 5-3" }),
85
+ /* @__PURE__ */ jsx2("path", { d: "M7 16.5v5.17" }),
86
+ /* @__PURE__ */ jsx2("path", { d: "M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z" }),
87
+ /* @__PURE__ */ jsx2("path", { d: "m17 16.5-5-3" }),
88
+ /* @__PURE__ */ jsx2("path", { d: "m17 16.5 4.74-2.85" }),
89
+ /* @__PURE__ */ jsx2("path", { d: "M17 16.5v5.17" }),
90
+ /* @__PURE__ */ jsx2("path", { d: "M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z" }),
91
+ /* @__PURE__ */ jsx2("path", { d: "M12 8 7.26 5.15" }),
92
+ /* @__PURE__ */ jsx2("path", { d: "m12 8 4.74-2.85" }),
93
+ /* @__PURE__ */ jsx2("path", { d: "M12 13.5V8" })
94
+ ] });
95
+ var ChevronDownIcon = (p) => /* @__PURE__ */ jsx2(Svg, { ...p, children: /* @__PURE__ */ jsx2("path", { d: "m6 9 6 6 6-6" }) });
96
+ var ChevronRightIcon = (p) => /* @__PURE__ */ jsx2(Svg, { ...p, children: /* @__PURE__ */ jsx2("path", { d: "m9 18 6-6-6-6" }) });
97
+ var MaximizeIcon = (p) => /* @__PURE__ */ jsxs(Svg, { ...p, children: [
98
+ /* @__PURE__ */ jsx2("polyline", { points: "15 3 21 3 21 9" }),
99
+ /* @__PURE__ */ jsx2("polyline", { points: "9 21 3 21 3 15" }),
100
+ /* @__PURE__ */ jsx2("line", { x1: "21", x2: "14", y1: "3", y2: "10" }),
101
+ /* @__PURE__ */ jsx2("line", { x1: "3", x2: "10", y1: "21", y2: "14" })
102
+ ] });
103
+ var LoaderIcon = (p) => /* @__PURE__ */ jsx2(Svg, { ...p, children: /* @__PURE__ */ jsx2("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) });
104
+ var CheckCircleIcon = (p) => /* @__PURE__ */ jsxs(Svg, { ...p, children: [
105
+ /* @__PURE__ */ jsx2("circle", { cx: "12", cy: "12", r: "10" }),
106
+ /* @__PURE__ */ jsx2("path", { d: "m9 12 2 2 4-4" })
107
+ ] });
108
+ var AlertIcon = (p) => /* @__PURE__ */ jsxs(Svg, { ...p, children: [
109
+ /* @__PURE__ */ jsx2("path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" }),
110
+ /* @__PURE__ */ jsx2("path", { d: "M12 9v4" }),
111
+ /* @__PURE__ */ jsx2("path", { d: "M12 17h.01" })
112
+ ] });
113
+ var AGENT_NOUNS = /* @__PURE__ */ new Set(["create_agent", "create_agent_definition", "agent"]);
114
+ function classIcon(directiveClass, noun) {
115
+ if (directiveClass === "action" && AGENT_NOUNS.has(noun)) return WebhookIcon;
116
+ switch (directiveClass) {
117
+ case "create":
118
+ return FilePlusIcon;
119
+ case "update":
120
+ return PencilIcon;
121
+ case "delete":
122
+ return TrashIcon;
123
+ default:
124
+ return PlayIcon;
125
+ }
126
+ }
127
+
128
+ // directives/ApplyDirectiveButton.tsx
129
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
130
+ function ApplyDirectiveButton({
131
+ directive,
132
+ label = "Apply",
133
+ itemCount,
134
+ onApplied
135
+ }) {
136
+ const host = useDirectiveHost();
137
+ const [state, setState] = useState({ status: "idle" });
138
+ const [elapsed, setElapsed] = useState(0);
139
+ const startedAt = useRef(null);
140
+ useEffect(() => {
141
+ if (state.status !== "applying") return;
142
+ startedAt.current = Date.now();
143
+ const id = setInterval(
144
+ () => setElapsed(Math.round((Date.now() - (startedAt.current ?? 0)) / 1e3)),
145
+ 1e3
146
+ );
147
+ return () => clearInterval(id);
148
+ }, [state.status]);
149
+ if (!directive.parsed.executes) return null;
150
+ const confirm = host.confirm;
151
+ if (!confirm) return null;
152
+ const items = directive.items;
153
+ async function handleApply() {
154
+ if (!confirm) return;
155
+ setState({ status: "applying" });
156
+ try {
157
+ const result = await confirm({ __kind: directive.slug, items });
158
+ setState({ status: "applied", applied: result.applied, failed: result.failed });
159
+ onApplied?.();
160
+ } catch (error) {
161
+ setState({
162
+ status: "error",
163
+ message: error instanceof Error && error.message ? error.message : "That couldn't be applied just now. Please try again."
164
+ });
165
+ }
166
+ }
167
+ if (state.status === "applied") {
168
+ const bad = state.failed > 0;
169
+ return /* @__PURE__ */ jsxs2(
170
+ "span",
171
+ {
172
+ className: `inline-flex items-center gap-1 text-xs ${bad ? "text-destructive" : "text-primary"}`,
173
+ children: [
174
+ bad ? /* @__PURE__ */ jsx3(AlertIcon, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx3(CheckCircleIcon, { className: "h-3.5 w-3.5" }),
175
+ "Applied ",
176
+ state.applied,
177
+ bad ? ` \xB7 ${state.failed} failed` : ""
178
+ ]
179
+ }
180
+ );
181
+ }
182
+ return /* @__PURE__ */ jsxs2("span", { className: "inline-flex items-center gap-2", children: [
183
+ /* @__PURE__ */ jsxs2(
184
+ "button",
185
+ {
186
+ type: "button",
187
+ onClick: () => void handleApply(),
188
+ disabled: state.status === "applying" || items.length === 0,
189
+ className: "inline-flex items-center gap-1 rounded-md border border-border bg-background px-2 py-1 text-xs font-medium hover:bg-accent disabled:opacity-50",
190
+ children: [
191
+ state.status === "applying" ? /* @__PURE__ */ jsx3(LoaderIcon, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx3(PlayIcon, { className: "h-3.5 w-3.5" }),
192
+ state.status === "applying" ? `Applying${itemCount ? ` ${itemCount} ${itemCount === 1 ? "item" : "items"}` : ""}\u2026${elapsed ? ` ${elapsed}s` : ""}` : label
193
+ ]
194
+ }
195
+ ),
196
+ state.status === "error" ? /* @__PURE__ */ jsxs2("span", { className: "inline-flex max-w-sm items-start gap-1 text-xs text-destructive", children: [
197
+ /* @__PURE__ */ jsx3(AlertIcon, { className: "mt-0.5 h-3.5 w-3.5 shrink-0" }),
198
+ /* @__PURE__ */ jsx3("span", { children: state.message })
199
+ ] }) : null
200
+ ] });
201
+ }
202
+
203
+ // directives/SideEffectDirectiveCard.tsx
204
+ import { useState as useState2 } from "react";
205
+ import {
206
+ asKindInstance,
207
+ directiveDisplay,
208
+ itemFacts,
209
+ itemSubtitle,
210
+ itemTitle,
211
+ nounTitleColumn
212
+ } from "@ai-matrx/content-ir";
213
+ import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
214
+ var ROWS_BEFORE_FOLD = 3;
215
+ function ItemRow({
216
+ directive,
217
+ item,
218
+ index,
219
+ total
220
+ }) {
221
+ const host = useDirectiveHost();
222
+ const titleColumn = nounTitleColumn(directive.noun, host.nouns);
223
+ const title = itemTitle(item, titleColumn, index, total);
224
+ const subtitle = itemSubtitle(item);
225
+ const facts = itemFacts(item);
226
+ const kind = host.itemKind?.(directive.slug) ?? null;
227
+ const display = directiveDisplay(directive.directiveClass, directive.noun, host.nouns);
228
+ const openItem = host.openItem;
229
+ const body = /* @__PURE__ */ jsxs3(Fragment, { children: [
230
+ /* @__PURE__ */ jsx4("span", { className: "min-w-0 shrink-0 truncate font-medium text-foreground", children: title }),
231
+ /* @__PURE__ */ jsx4("span", { className: "hidden min-w-0 flex-1 items-baseline gap-2 overflow-hidden sm:flex", children: facts.map((fact) => /* @__PURE__ */ jsxs3("span", { className: "truncate text-[11px] text-muted-foreground", children: [
232
+ fact.value,
233
+ " ",
234
+ fact.label
235
+ ] }, fact.key)) }),
236
+ subtitle && facts.length === 0 ? /* @__PURE__ */ jsx4("span", { className: "hidden truncate text-[11px] text-muted-foreground sm:inline", children: subtitle }) : null,
237
+ openItem ? /* @__PURE__ */ jsx4(MaximizeIcon, { className: "ml-auto h-3 w-3 shrink-0 text-muted-foreground" }) : null
238
+ ] });
239
+ return /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 py-1 text-xs", children: [
240
+ openItem ? /* @__PURE__ */ jsx4(
241
+ "button",
242
+ {
243
+ type: "button",
244
+ onClick: () => openItem({
245
+ // Stamped so the panel's kind pipeline types it from its own first key.
246
+ item: asKindInstance(item, kind) ?? item,
247
+ itemKind: kind,
248
+ title,
249
+ subtitle: `${display.title} \xB7 item ${index + 1} of ${total}`,
250
+ directive,
251
+ index,
252
+ total
253
+ }),
254
+ className: "flex min-w-0 flex-1 items-center gap-2 rounded px-1 py-0.5 text-left hover:bg-accent hover:text-accent-foreground",
255
+ title: "Open to read it",
256
+ children: body
257
+ }
258
+ ) : /* @__PURE__ */ jsx4("span", { className: "flex min-w-0 flex-1 items-center gap-2 px-1 py-0.5", children: body }),
259
+ host.renderCopy?.({ label: title, value: item, kind, size: "xs" })
260
+ ] });
261
+ }
262
+ function SideEffectDirectiveCard({ directive }) {
263
+ const host = useDirectiveHost();
264
+ const [expanded, setExpanded] = useState2(false);
265
+ const items = directive.items;
266
+ const total = items.length;
267
+ const display = directiveDisplay(directive.directiveClass, directive.noun, host.nouns);
268
+ const Icon = classIcon(directive.directiveClass, directive.noun);
269
+ const shown = expanded ? items : items.slice(0, ROWS_BEFORE_FOLD);
270
+ const hidden = total - shown.length;
271
+ return /* @__PURE__ */ jsxs3("div", { className: "my-3 rounded-md border border-border bg-card px-2.5 py-1.5", "data-directive": directive.slug, children: [
272
+ /* @__PURE__ */ jsxs3("div", { className: "flex flex-wrap items-center gap-x-2 gap-y-1", children: [
273
+ /* @__PURE__ */ jsx4(Icon, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
274
+ /* @__PURE__ */ jsx4("span", { className: "text-sm font-medium text-foreground", children: display.title }),
275
+ display.family ? /* @__PURE__ */ jsx4("span", { className: "text-xs text-muted-foreground", children: display.family }) : null,
276
+ /* @__PURE__ */ jsxs3("span", { className: "text-xs tabular-nums text-muted-foreground", children: [
277
+ total,
278
+ " ",
279
+ total === 1 ? "item" : "items"
280
+ ] }),
281
+ /* @__PURE__ */ jsxs3("div", { className: "ml-auto flex items-center gap-1", children: [
282
+ host.renderCopy?.({ label: display.title, value: directive.shell, size: "icon" }),
283
+ /* @__PURE__ */ jsx4(ApplyDirectiveButton, { directive, itemCount: total })
284
+ ] })
285
+ ] }),
286
+ total === 0 ? (
287
+ // Stated, never silent: an empty batch is a real (and suspicious) state.
288
+ /* @__PURE__ */ jsx4("p", { className: "mt-1 text-xs text-muted-foreground", children: "No items \u2014 nothing would be written." })
289
+ ) : /* @__PURE__ */ jsx4("div", { className: "mt-0.5 divide-y divide-border/60", children: shown.map((item, i) => /* @__PURE__ */ jsx4(ItemRow, { directive, item, index: i, total }, i)) }),
290
+ hidden > 0 || expanded ? /* @__PURE__ */ jsxs3(
291
+ "button",
292
+ {
293
+ type: "button",
294
+ onClick: () => setExpanded((v) => !v),
295
+ className: "mt-0.5 inline-flex items-center gap-1 rounded px-1 py-0.5 text-[11px] text-muted-foreground hover:text-foreground",
296
+ children: [
297
+ expanded ? /* @__PURE__ */ jsx4(ChevronDownIcon, { className: "h-3 w-3" }) : /* @__PURE__ */ jsx4(ChevronRightIcon, { className: "h-3 w-3" }),
298
+ expanded ? "Show less" : `Show ${hidden} more`
299
+ ]
300
+ }
301
+ ) : null
302
+ ] });
303
+ }
304
+
305
+ // directives/DirectiveFallbackCard.tsx
306
+ import { directiveDisplay as directiveDisplay2 } from "@ai-matrx/content-ir";
307
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
308
+ function DirectiveFallbackCard({ directive, reason }) {
309
+ const host = useDirectiveHost();
310
+ const count = directive.items.length;
311
+ const display = directiveDisplay2(directive.directiveClass, directive.noun, host.nouns);
312
+ return /* @__PURE__ */ jsxs4("div", { className: "my-3 inline-flex flex-wrap items-center gap-2 rounded-md border border-border bg-muted px-2.5 py-1 text-sm", children: [
313
+ /* @__PURE__ */ jsx5(BoxesIcon, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground" }),
314
+ /* @__PURE__ */ jsx5("span", { className: "text-foreground", children: display.title }),
315
+ display.family ? /* @__PURE__ */ jsxs4("span", { className: "text-muted-foreground", children: [
316
+ "\xB7 ",
317
+ display.family
318
+ ] }) : null,
319
+ /* @__PURE__ */ jsxs4("span", { className: "text-muted-foreground", children: [
320
+ "\xB7 ",
321
+ count,
322
+ " ",
323
+ count === 1 ? "item" : "items"
324
+ ] }),
325
+ reason ? /* @__PURE__ */ jsxs4("span", { className: "text-muted-foreground", children: [
326
+ "\xB7 ",
327
+ reason
328
+ ] }) : null,
329
+ /* @__PURE__ */ jsx5(ApplyDirectiveButton, { directive })
330
+ ] });
331
+ }
332
+
333
+ // directives/DirectiveRender.tsx
334
+ import { tryDecodeDirectiveContent } from "@ai-matrx/content-ir";
335
+ import { jsx as jsx6 } from "react/jsx-runtime";
336
+ function DirectiveRender({ content, className }) {
337
+ const host = useDirectiveHost();
338
+ const directive = tryDecodeDirectiveContent(content, (message) => {
339
+ const line = `[kind-directives] ${message}`;
340
+ if (host.reportError) host.reportError(line);
341
+ else console.error(line);
342
+ });
343
+ if (!directive) {
344
+ const raw = typeof content === "string" ? content : JSON.stringify(content, null, 2);
345
+ return /* @__PURE__ */ jsx6(
346
+ "pre",
347
+ {
348
+ className: `my-3 overflow-x-auto rounded-md border border-border bg-muted px-3 py-2 text-xs text-muted-foreground${className ? ` ${className}` : ""}`,
349
+ children: raw
350
+ }
351
+ );
352
+ }
353
+ const Renderer = getDirectiveRenderer(directive);
354
+ if (Renderer) return /* @__PURE__ */ jsx6(Renderer, { directive });
355
+ if (directive.parsed.executes) return /* @__PURE__ */ jsx6(SideEffectDirectiveCard, { directive });
356
+ return /* @__PURE__ */ jsx6(DirectiveFallbackCard, { directive });
357
+ }
358
+ export {
359
+ ApplyDirectiveButton,
360
+ DirectiveFallbackCard,
361
+ DirectiveHostProvider,
362
+ DirectiveRender,
363
+ SideEffectDirectiveCard,
364
+ getDirectiveRenderer,
365
+ registerDirectiveRenderer,
366
+ resetDirectiveRenderers,
367
+ useDirectiveHost
368
+ };
369
+ //# sourceMappingURL=directives.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../directives/directive-host.tsx","../directives/registry.ts","../directives/ApplyDirectiveButton.tsx","../directives/icons.tsx","../directives/SideEffectDirectiveCard.tsx","../directives/DirectiveFallbackCard.tsx","../directives/DirectiveRender.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * THE DIRECTIVE HOST CONTRACT — what the directive tier refuses to decide.\n *\n * The package owns the DECISIONS (decode → renderer → card → floor, the\n * position law, never-null rendering) and the card itself. Four things stay\n * host property and enter through named seams:\n *\n * 1. APPLYING a side-effect directive — `POST /directives/confirm` as the\n * user, with the host's auth and base URL. Absent → the Apply control is\n * ABSENT (honest), never a dead button.\n * 2. OPENING an item to read it before approving — a window panel, a dialog,\n * a route. Absent → items are not clickable.\n * 3. COPY controls — host chrome (matrx-frontend's CopyButtons). Absent → none.\n * 4. The NOUN catalog + item-kind lookup — server-derived tables the host\n * mirrors (or reads from its kind catalog's `items` edges).\n *\n * A host that has adopted `ContentIrRenderProvider` passes `directives` on its\n * ContentIrHost; a host that has NOT (Workflow Studio, 2026-09-08) mounts\n * `DirectiveHostProvider` alone — the directive tier never requires the full\n * render host, because a chat bubble must be able to draw a directive card\n * identically everywhere, today.\n */\n\nimport { createContext, useContext, type ReactNode } from \"react\";\nimport type {\n DecodedDirective,\n DirectiveItemKindLookup,\n DirectiveNounCatalog,\n} from \"@ai-matrx/content-ir\";\n\nexport interface DirectiveApplyResult {\n applied: number;\n failed: number;\n}\n\nexport interface DirectiveOpenItemOptions {\n /** The item, `__kind`-stamped when the shape has an item kind. */\n item: Record<string, unknown>;\n /** The content-IR kind this item IS, or null when the shape has none. */\n itemKind: string | null;\n /** What the item is called — the same title the card shows. */\n title: string;\n /** \"Run Create agent definition · item 1 of 1\". */\n subtitle: string | null;\n directive: DecodedDirective;\n index: number;\n total: number;\n}\n\nexport interface DirectiveCopyProps {\n label: string;\n /** The value to copy — the whole shell or one item. */\n value: unknown;\n /** The item's kind, when copying an item. */\n kind?: string | null;\n size: \"icon\" | \"xs\";\n}\n\nexport interface DirectiveHost {\n /**\n * Apply a side-effect directive as the user (the server re-validates every\n * item against the registered model and runs the ONE handler; idempotent).\n * Reject with an Error whose message is safe to show.\n */\n confirm?: (shell: { __kind: string; items: Record<string, unknown>[] }) => Promise<DirectiveApplyResult>;\n /** Open ONE item to read it properly before approving the write. */\n openItem?: (options: DirectiveOpenItemOptions) => void;\n /** Host copy controls. */\n renderCopy?: (props: DirectiveCopyProps) => ReactNode;\n /** The mirrored noun catalog (label / family / title column). */\n nouns?: DirectiveNounCatalog;\n /** slug → the kind ONE item is (THE DIRECTIVE⇄KIND SEAM), or null. */\n itemKind?: DirectiveItemKindLookup;\n /** Where a decode failure or an unhonourable shell is reported. Default: console.error. */\n reportError?: (message: string) => void;\n}\n\nconst DirectiveHostContext = createContext<DirectiveHost | null>(null);\n\nexport interface DirectiveHostProviderProps {\n host: DirectiveHost;\n children: ReactNode;\n}\n\nexport function DirectiveHostProvider({ host, children }: DirectiveHostProviderProps) {\n return <DirectiveHostContext.Provider value={host}>{children}</DirectiveHostContext.Provider>;\n}\n\nconst EMPTY_HOST: DirectiveHost = {};\n\n/**\n * The wired directive host. Unlike the render host this does NOT throw when\n * unwired: a directive still renders (named, itemised, copy-less, apply-less)\n * — degrading the chrome is honest, hiding the content is not.\n */\nexport function useDirectiveHost(): DirectiveHost {\n return useContext(DirectiveHostContext) ?? EMPTY_HOST;\n}\n","/**\n * Kind Directives — the renderer registry, and THE PREFIX TIER.\n *\n * A directive IS a kind instance, so the renderer for one is resolved from its\n * SLUG, in the same shape the kind component resolver uses:\n *\n * getDirectiveRenderer(directive)\n * → an exact-slug renderer (directive_v1_action_plan_tree)\n * → else the CLASS prefix rule (every directive_v1_reference_*)\n * → else null (DirectiveRender's own tier: the\n * side-effect card, then the floor)\n *\n * THE PREFIX RULE is Arman's routing-language ruling made real: registering\n * `reference` once gives EVERY enrolled noun a renderer for free; a custom\n * renderer for one slug overrides it; a brand-new server noun renders with\n * ZERO client edits. Hosts register their bespoke renderers (reference chips,\n * plan trees) here; the package supplies the side-effect card and the floor.\n */\n\nimport type { ComponentType } from \"react\";\nimport {\n type DecodedDirective,\n type DirectiveClass,\n buildDirectiveSlug,\n} from \"@ai-matrx/content-ir\";\n\nexport interface DirectiveRendererProps {\n /** The decoded two-key shell — slug, class, noun, items, position law. */\n directive: DecodedDirective;\n}\n\nexport type DirectiveRenderer = ComponentType<DirectiveRendererProps>;\n\n// REGISTRY ON A globalThis SLOT — never module-level Maps. With `splitting:\n// false` dual ESM/CJS output this module is duplicated into the root bundle and\n// the `./directives` bundle, and a host registering a renderer through one\n// graph must be visible to a card rendering through the other.\nconst REGISTRY_SLOT = Symbol.for(\"ai-matrx.content-ir-react.directive-renderers\");\ninterface Registry {\n bySlug: Map<string, DirectiveRenderer>;\n byClass: Map<DirectiveClass, DirectiveRenderer>;\n}\ntype RegistryHolder = typeof globalThis & { [REGISTRY_SLOT]?: Registry };\nconst registry: Registry = ((globalThis as RegistryHolder)[REGISTRY_SLOT] ??= {\n bySlug: new Map(),\n byClass: new Map(),\n});\n\n/**\n * Register a renderer for a whole CLASS (the prefix rule), or for one exact\n * `(class, noun)` pair, which wins. `noun` is passed as a noun, never a\n * hand-typed slug: the slug is BUILT by the grammar, so a registration whose\n * slug could not be parsed back is unconstructable rather than unreachable.\n */\nexport function registerDirectiveRenderer(\n directiveClass: DirectiveClass,\n renderer: DirectiveRenderer,\n noun?: string,\n): void {\n if (noun) registry.bySlug.set(buildDirectiveSlug(directiveClass, noun), renderer);\n else registry.byClass.set(directiveClass, renderer);\n}\n\n/** The renderer for a slug: exact → class prefix rule → null. */\nexport function getDirectiveRenderer(\n directive: Pick<DecodedDirective, \"slug\" | \"directiveClass\">,\n): DirectiveRenderer | null {\n return (\n registry.bySlug.get(directive.slug) ?? registry.byClass.get(directive.directiveClass) ?? null\n );\n}\n\n/** Tests only. */\nexport function resetDirectiveRenderers(): void {\n registry.bySlug.clear();\n registry.byClass.clear();\n}\n","\"use client\";\n\n/**\n * \"Apply\" for a side-effect Kind Directive that arrived as CONTENT.\n *\n * THE POSITION RULE, honoured exactly: a directive inside content NEVER\n * auto-executes — that is the guard against untrusted content mutating things.\n * But \"does not auto-execute\" was never \"is inert\": the user seeing it must be\n * able to say yes. This button IS that yes, through the host's `confirm` seam\n * (`POST /directives/confirm`, re-validated server-side, run as the user).\n *\n * No `confirm` seam → NO button. A control that cannot do its job is absent,\n * never dead. Only a side-effect class is applicable at all (`parsed.executes`).\n */\n\nimport { useEffect, useRef, useState } from \"react\";\nimport type { DecodedDirective } from \"@ai-matrx/content-ir\";\nimport { useDirectiveHost } from \"./directive-host\";\nimport { AlertIcon, CheckCircleIcon, LoaderIcon, PlayIcon } from \"./icons\";\n\ntype ApplyState =\n | { status: \"idle\" }\n | { status: \"applying\" }\n | { status: \"applied\"; applied: number; failed: number }\n | { status: \"error\"; message: string };\n\nexport interface ApplyDirectiveButtonProps {\n directive: DecodedDirective;\n label?: string;\n /** How many things this will write — shown during the wait so a long apply reads as work, not a frozen spinner. */\n itemCount?: number;\n onApplied?: () => void;\n}\n\nexport function ApplyDirectiveButton({\n directive,\n label = \"Apply\",\n itemCount,\n onApplied,\n}: ApplyDirectiveButtonProps) {\n const host = useDirectiveHost();\n const [state, setState] = useState<ApplyState>({ status: \"idle\" });\n const [elapsed, setElapsed] = useState(0);\n const startedAt = useRef<number | null>(null);\n\n // An honest clock beats a fake progress bar.\n useEffect(() => {\n if (state.status !== \"applying\") return;\n startedAt.current = Date.now();\n const id = setInterval(\n () => setElapsed(Math.round((Date.now() - (startedAt.current ?? 0)) / 1000)),\n 1000,\n );\n return () => clearInterval(id);\n }, [state.status]);\n\n if (!directive.parsed.executes) return null;\n const confirm = host.confirm;\n if (!confirm) return null;\n\n const items = directive.items;\n\n async function handleApply() {\n if (!confirm) return;\n setState({ status: \"applying\" });\n try {\n const result = await confirm({ __kind: directive.slug, items });\n setState({ status: \"applied\", applied: result.applied, failed: result.failed });\n onApplied?.();\n } catch (error) {\n setState({\n status: \"error\",\n message:\n error instanceof Error && error.message\n ? error.message\n : \"That couldn't be applied just now. Please try again.\",\n });\n }\n }\n\n if (state.status === \"applied\") {\n const bad = state.failed > 0;\n return (\n <span\n className={`inline-flex items-center gap-1 text-xs ${bad ? \"text-destructive\" : \"text-primary\"}`}\n >\n {bad ? <AlertIcon className=\"h-3.5 w-3.5\" /> : <CheckCircleIcon className=\"h-3.5 w-3.5\" />}\n Applied {state.applied}\n {bad ? ` · ${state.failed} failed` : \"\"}\n </span>\n );\n }\n\n return (\n <span className=\"inline-flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => void handleApply()}\n disabled={state.status === \"applying\" || items.length === 0}\n className=\"inline-flex items-center gap-1 rounded-md border border-border bg-background px-2 py-1 text-xs font-medium hover:bg-accent disabled:opacity-50\"\n >\n {state.status === \"applying\" ? (\n <LoaderIcon className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <PlayIcon className=\"h-3.5 w-3.5\" />\n )}\n {state.status === \"applying\"\n ? `Applying${itemCount ? ` ${itemCount} ${itemCount === 1 ? \"item\" : \"items\"}` : \"\"}…${elapsed ? ` ${elapsed}s` : \"\"}`\n : label}\n </button>\n {state.status === \"error\" ? (\n <span className=\"inline-flex max-w-sm items-start gap-1 text-xs text-destructive\">\n <AlertIcon className=\"mt-0.5 h-3.5 w-3.5 shrink-0\" />\n <span>{state.message}</span>\n </span>\n ) : null}\n </span>\n );\n}\n\nexport default ApplyDirectiveButton;\n","/**\n * The handful of glyphs the directive cards need, as inline SVG. This package\n * depends on no icon library (see the FEATURE boundary); the paths are the\n * Lucide set's (ISC), so the cards look identical to a Lucide-using host.\n */\n\nimport type { ReactElement, SVGProps } from \"react\";\n\ntype IconProps = SVGProps<SVGSVGElement>;\n\nfunction Svg({ children, ...props }: IconProps) {\n return (\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={2}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n {...props}\n >\n {children}\n </svg>\n );\n}\n\nexport const FilePlusIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M4 22h14a2 2 0 0 0 2-2V7l-5-5H6a2 2 0 0 0-2 2v4\" />\n <path d=\"M14 2v4a2 2 0 0 0 2 2h4\" />\n <path d=\"M3 15h6\" />\n <path d=\"M6 12v6\" />\n </Svg>\n);\nexport const PencilIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M12 20h9\" />\n <path d=\"M16.376 3.622a1 1 0 0 1 3.002 3.002L7.368 18.635a2 2 0 0 1-.855.506l-2.872.838a.5.5 0 0 1-.62-.62l.838-2.872a2 2 0 0 1 .506-.854z\" />\n </Svg>\n);\nexport const TrashIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M3 6h18\" />\n <path d=\"M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6\" />\n <path d=\"M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2\" />\n <line x1=\"10\" x2=\"10\" y1=\"11\" y2=\"17\" />\n <line x1=\"14\" x2=\"14\" y1=\"11\" y2=\"17\" />\n </Svg>\n);\nexport const PlayIcon = (p: IconProps) => (\n <Svg {...p}>\n <polygon points=\"6 3 20 12 6 21 6 3\" />\n </Svg>\n);\nexport const WebhookIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M18 16.98h-5.99c-1.1 0-1.95.94-2.48 1.9A4 4 0 0 1 2 17c.01-.7.2-1.4.57-2\" />\n <path d=\"m6 17 3.13-5.78c.53-.97.1-2.01-.4-2.96A4 4 0 0 1 14 5.5c.63.42 1.13 1 1.44 1.66\" />\n <path d=\"m12 6 3.13 5.73C15.66 12.7 16.9 13 18 13a4 4 0 0 1 0 8\" />\n </Svg>\n);\nexport const BoxesIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M2.97 12.92A2 2 0 0 0 2 14.63v3.24a2 2 0 0 0 .97 1.71l3 1.8a2 2 0 0 0 2.06 0L12 19v-5.5l-5-3-4.03 2.42Z\" />\n <path d=\"m7 16.5-4.74-2.85\" />\n <path d=\"m7 16.5 5-3\" />\n <path d=\"M7 16.5v5.17\" />\n <path d=\"M12 13.5V19l3.97 2.38a2 2 0 0 0 2.06 0l3-1.8a2 2 0 0 0 .97-1.71v-3.24a2 2 0 0 0-.97-1.71L17 10.5l-5 3Z\" />\n <path d=\"m17 16.5-5-3\" />\n <path d=\"m17 16.5 4.74-2.85\" />\n <path d=\"M17 16.5v5.17\" />\n <path d=\"M7.97 4.42A2 2 0 0 0 7 6.13v4.37l5 3 5-3V6.13a2 2 0 0 0-.97-1.71l-3-1.8a2 2 0 0 0-2.06 0l-3 1.8Z\" />\n <path d=\"M12 8 7.26 5.15\" />\n <path d=\"m12 8 4.74-2.85\" />\n <path d=\"M12 13.5V8\" />\n </Svg>\n);\nexport const ChevronDownIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"m6 9 6 6 6-6\" />\n </Svg>\n);\nexport const ChevronRightIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"m9 18 6-6-6-6\" />\n </Svg>\n);\nexport const MaximizeIcon = (p: IconProps) => (\n <Svg {...p}>\n <polyline points=\"15 3 21 3 21 9\" />\n <polyline points=\"9 21 3 21 3 15\" />\n <line x1=\"21\" x2=\"14\" y1=\"3\" y2=\"10\" />\n <line x1=\"3\" x2=\"10\" y1=\"21\" y2=\"14\" />\n </Svg>\n);\nexport const LoaderIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"M21 12a9 9 0 1 1-6.219-8.56\" />\n </Svg>\n);\nexport const CheckCircleIcon = (p: IconProps) => (\n <Svg {...p}>\n <circle cx=\"12\" cy=\"12\" r=\"10\" />\n <path d=\"m9 12 2 2 4-4\" />\n </Svg>\n);\nexport const AlertIcon = (p: IconProps) => (\n <Svg {...p}>\n <path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\" />\n <path d=\"M12 9v4\" />\n <path d=\"M12 17h.01\" />\n </Svg>\n);\n\n/**\n * The icon for a side-effect directive card. THE CLASS LEADS: on a card whose\n * job is \"authorize this write\", the first thing to see is whether this\n * CREATES, CHANGES, or DELETES. A noun override exists only where the platform\n * has a settled visual identity for the thing — agents are a Webhook.\n */\nconst AGENT_NOUNS = new Set([\"create_agent\", \"create_agent_definition\", \"agent\"]);\n\nexport function classIcon(directiveClass: string, noun: string): (p: IconProps) => ReactElement {\n if (directiveClass === \"action\" && AGENT_NOUNS.has(noun)) return WebhookIcon;\n switch (directiveClass) {\n case \"create\":\n return FilePlusIcon;\n case \"update\":\n return PencilIcon;\n case \"delete\":\n return TrashIcon;\n default:\n return PlayIcon;\n }\n}\n","\"use client\";\n\n/**\n * The card for a SIDE-EFFECT directive in content — create / update / delete /\n * action. One card for every shape the server can register, so a person is\n * never asked to approve a write they cannot identify.\n *\n * WHY IT EXISTS (Arman, 2026-08-26): the floor it replaced gave \"a name and\n * then an apply button, which essentially tells the user to click apply and\n * conduct a potentially destructive action without any clue as to what this\n * thing is.\"\n *\n * THE DESIGN:\n * - Say what it IS, from authority — the catalog's own words for the title,\n * the noun's title column for each item, never a guess.\n * - Facts, not prose — a few scalar chips; nesting goes to the panel.\n * - Read before you approve — `View` opens the item through the host's seam,\n * which wraps the item's OWN kind component (THE DIRECTIVE⇄KIND SEAM).\n * - Copy is table stakes, at the batch and at the item (host chrome).\n * - Apply stays exactly where it was — the host's confirm seam.\n *\n * NEVER RETURNS NULL — a shape with no items degrades to a stated line.\n */\n\nimport { useState } from \"react\";\nimport {\n asKindInstance,\n directiveDisplay,\n itemFacts,\n itemSubtitle,\n itemTitle,\n nounTitleColumn,\n type DecodedDirective,\n} from \"@ai-matrx/content-ir\";\nimport { ApplyDirectiveButton } from \"./ApplyDirectiveButton\";\nimport { useDirectiveHost } from \"./directive-host\";\nimport { ChevronDownIcon, ChevronRightIcon, MaximizeIcon, classIcon } from \"./icons\";\n\nexport interface SideEffectDirectiveCardProps {\n directive: DecodedDirective;\n}\n\n/** How many items render as rows before the rest collapse behind a toggle. */\nconst ROWS_BEFORE_FOLD = 3;\n\nfunction ItemRow({\n directive,\n item,\n index,\n total,\n}: {\n directive: DecodedDirective;\n item: Record<string, unknown>;\n index: number;\n total: number;\n}) {\n const host = useDirectiveHost();\n const titleColumn = nounTitleColumn(directive.noun, host.nouns);\n const title = itemTitle(item, titleColumn, index, total);\n const subtitle = itemSubtitle(item);\n const facts = itemFacts(item);\n const kind = host.itemKind?.(directive.slug) ?? null;\n const display = directiveDisplay(directive.directiveClass, directive.noun, host.nouns);\n const openItem = host.openItem;\n\n const body = (\n <>\n {/* THE IDENTITY WINS THE SPACE: the title keeps its intrinsic width and the facts give way. */}\n <span className=\"min-w-0 shrink-0 truncate font-medium text-foreground\">{title}</span>\n <span className=\"hidden min-w-0 flex-1 items-baseline gap-2 overflow-hidden sm:flex\">\n {facts.map((fact) => (\n <span key={fact.key} className=\"truncate text-[11px] text-muted-foreground\">\n {fact.value} {fact.label}\n </span>\n ))}\n </span>\n {subtitle && facts.length === 0 ? (\n <span className=\"hidden truncate text-[11px] text-muted-foreground sm:inline\">{subtitle}</span>\n ) : null}\n {openItem ? (\n <MaximizeIcon className=\"ml-auto h-3 w-3 shrink-0 text-muted-foreground\" />\n ) : null}\n </>\n );\n\n return (\n <div className=\"flex items-center gap-2 py-1 text-xs\">\n {openItem ? (\n <button\n type=\"button\"\n onClick={() =>\n openItem({\n // Stamped so the panel's kind pipeline types it from its own first key.\n item: asKindInstance(item, kind) ?? item,\n itemKind: kind,\n title,\n subtitle: `${display.title} · item ${index + 1} of ${total}`,\n directive,\n index,\n total,\n })\n }\n className=\"flex min-w-0 flex-1 items-center gap-2 rounded px-1 py-0.5 text-left hover:bg-accent hover:text-accent-foreground\"\n title=\"Open to read it\"\n >\n {body}\n </button>\n ) : (\n <span className=\"flex min-w-0 flex-1 items-center gap-2 px-1 py-0.5\">{body}</span>\n )}\n {host.renderCopy?.({ label: title, value: item, kind, size: \"xs\" })}\n </div>\n );\n}\n\nexport function SideEffectDirectiveCard({ directive }: SideEffectDirectiveCardProps) {\n const host = useDirectiveHost();\n const [expanded, setExpanded] = useState(false);\n const items = directive.items;\n const total = items.length;\n const display = directiveDisplay(directive.directiveClass, directive.noun, host.nouns);\n const Icon = classIcon(directive.directiveClass, directive.noun);\n\n const shown = expanded ? items : items.slice(0, ROWS_BEFORE_FOLD);\n const hidden = total - shown.length;\n\n return (\n <div className=\"my-3 rounded-md border border-border bg-card px-2.5 py-1.5\" data-directive={directive.slug}>\n <div className=\"flex flex-wrap items-center gap-x-2 gap-y-1\">\n <Icon className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"text-sm font-medium text-foreground\">{display.title}</span>\n {display.family ? <span className=\"text-xs text-muted-foreground\">{display.family}</span> : null}\n <span className=\"text-xs tabular-nums text-muted-foreground\">\n {total} {total === 1 ? \"item\" : \"items\"}\n </span>\n <div className=\"ml-auto flex items-center gap-1\">\n {host.renderCopy?.({ label: display.title, value: directive.shell, size: \"icon\" })}\n <ApplyDirectiveButton directive={directive} itemCount={total} />\n </div>\n </div>\n\n {total === 0 ? (\n // Stated, never silent: an empty batch is a real (and suspicious) state.\n <p className=\"mt-1 text-xs text-muted-foreground\">No items — nothing would be written.</p>\n ) : (\n <div className=\"mt-0.5 divide-y divide-border/60\">\n {shown.map((item, i) => (\n <ItemRow key={i} directive={directive} item={item} index={i} total={total} />\n ))}\n </div>\n )}\n\n {hidden > 0 || expanded ? (\n <button\n type=\"button\"\n onClick={() => setExpanded((v) => !v)}\n className=\"mt-0.5 inline-flex items-center gap-1 rounded px-1 py-0.5 text-[11px] text-muted-foreground hover:text-foreground\"\n >\n {expanded ? <ChevronDownIcon className=\"h-3 w-3\" /> : <ChevronRightIcon className=\"h-3 w-3\" />}\n {expanded ? \"Show less\" : `Show ${hidden} more`}\n </button>\n ) : null}\n </div>\n );\n}\n\nexport default SideEffectDirectiveCard;\n","\"use client\";\n\n/**\n * The neutral \"we could not render this, but we will NEVER hide it\" card —\n * THE PREFIX FLOOR of the Kind Directives route.\n *\n * THE RULE THIS ENFORCES: a registered directive renderer must never return\n * `null`. `DirectiveRender` renders a renderer's output verbatim, so a `null`\n * deletes the assistant's whole message block (2026-07-26: a 70KB content plan\n * vanished exactly that way). When a renderer cannot draw its rich view, it\n * renders THIS instead: named by class and noun, itemised by count, with Apply\n * when the class is a side effect and the host can apply.\n */\n\nimport { directiveDisplay, type DecodedDirective } from \"@ai-matrx/content-ir\";\nimport { ApplyDirectiveButton } from \"./ApplyDirectiveButton\";\nimport { useDirectiveHost } from \"./directive-host\";\nimport { BoxesIcon } from \"./icons\";\n\nexport interface DirectiveFallbackCardProps {\n directive: DecodedDirective;\n /** Short, human explanation of why the rich renderer degraded. */\n reason?: string;\n}\n\nexport function DirectiveFallbackCard({ directive, reason }: DirectiveFallbackCardProps) {\n const host = useDirectiveHost();\n const count = directive.items.length;\n const display = directiveDisplay(directive.directiveClass, directive.noun, host.nouns);\n return (\n <div className=\"my-3 inline-flex flex-wrap items-center gap-2 rounded-md border border-border bg-muted px-2.5 py-1 text-sm\">\n <BoxesIcon className=\"h-3.5 w-3.5 shrink-0 text-muted-foreground\" />\n <span className=\"text-foreground\">{display.title}</span>\n {display.family ? <span className=\"text-muted-foreground\">· {display.family}</span> : null}\n <span className=\"text-muted-foreground\">\n · {count} {count === 1 ? \"item\" : \"items\"}\n </span>\n {reason ? <span className=\"text-muted-foreground\">· {reason}</span> : null}\n <ApplyDirectiveButton directive={directive} />\n </div>\n );\n}\n\nexport default DirectiveFallbackCard;\n","\"use client\";\n\n/**\n * DirectiveRender — THE renderer for anything whose `__kind` is a reserved\n * directive slug, on every AI Matrx UI.\n *\n * Pipeline (one pipeline, mirroring the backend):\n * 1. `tryDecodeDirectiveContent` → the two-key shell (a stored 4-key fence is\n * translated inside the decoder). Not a directive (bad JSON / no reserved\n * `__kind`) → raw <pre>, never throws. An unhonourable shell is REPORTED\n * through the host and falls to raw — a render seam must not take a whole\n * message block down over one fence, and must never go silent.\n * 2. `getDirectiveRenderer` → exact slug, else the CLASS prefix rule (the\n * host's registrations). Found → render it.\n * 3. THE SIDE-EFFECT TIER — every create / update / delete / action shape\n * renders the side-effect card, so a person is never asked to approve a\n * write they cannot identify.\n * 4. THE FLOOR — a class nothing registers still shows the fallback card.\n * Never null, never silent.\n *\n * Position decides capability: in content only `reference`/`secret` resolve;\n * a side-effect directive in prose is a card with an explicit Apply, never\n * executed on sight.\n */\n\nimport { tryDecodeDirectiveContent } from \"@ai-matrx/content-ir\";\nimport { useDirectiveHost } from \"./directive-host\";\nimport { DirectiveFallbackCard } from \"./DirectiveFallbackCard\";\nimport { getDirectiveRenderer } from \"./registry\";\nimport { SideEffectDirectiveCard } from \"./SideEffectDirectiveCard\";\n\nexport interface DirectiveRenderProps {\n /** The fence body (JSON string) or an already-parsed shell / value. */\n content: unknown;\n className?: string;\n}\n\nexport function DirectiveRender({ content, className }: DirectiveRenderProps) {\n const host = useDirectiveHost();\n const directive = tryDecodeDirectiveContent(content, (message) => {\n const line = `[kind-directives] ${message}`;\n if (host.reportError) host.reportError(line);\n else console.error(line);\n });\n\n if (!directive) {\n const raw = typeof content === \"string\" ? content : JSON.stringify(content, null, 2);\n return (\n <pre\n className={`my-3 overflow-x-auto rounded-md border border-border bg-muted px-3 py-2 text-xs text-muted-foreground${className ? ` ${className}` : \"\"}`}\n >\n {raw}\n </pre>\n );\n }\n\n const Renderer = getDirectiveRenderer(directive);\n if (Renderer) return <Renderer directive={directive} />;\n if (directive.parsed.executes) return <SideEffectDirectiveCard directive={directive} />;\n return <DirectiveFallbackCard directive={directive} />;\n}\n\nexport default DirectiveRender;\n"],"mappings":";;;AAyBA,SAAS,eAAe,kBAAkC;AA8DjD;AART,IAAM,uBAAuB,cAAoC,IAAI;AAO9D,SAAS,sBAAsB,EAAE,MAAM,SAAS,GAA+B;AACpF,SAAO,oBAAC,qBAAqB,UAArB,EAA8B,OAAO,MAAO,UAAS;AAC/D;AAEA,IAAM,aAA4B,CAAC;AAO5B,SAAS,mBAAkC;AAChD,SAAO,WAAW,oBAAoB,KAAK;AAC7C;;;AC/EA;AAAA,EAGE;AAAA,OACK;AAaP,IAAM,gBAAgB,uBAAO,IAAI,+CAA+C;AAMhF,IAAM,WAAuB,WAA8B,aAAa,MAAM;AAAA,EAC5E,QAAQ,oBAAI,IAAI;AAAA,EAChB,SAAS,oBAAI,IAAI;AACnB;AAQO,SAAS,0BACd,gBACA,UACA,MACM;AACN,MAAI,KAAM,UAAS,OAAO,IAAI,mBAAmB,gBAAgB,IAAI,GAAG,QAAQ;AAAA,MAC3E,UAAS,QAAQ,IAAI,gBAAgB,QAAQ;AACpD;AAGO,SAAS,qBACd,WAC0B;AAC1B,SACE,SAAS,OAAO,IAAI,UAAU,IAAI,KAAK,SAAS,QAAQ,IAAI,UAAU,cAAc,KAAK;AAE7F;AAGO,SAAS,0BAAgC;AAC9C,WAAS,OAAO,MAAM;AACtB,WAAS,QAAQ,MAAM;AACzB;;;AC7DA,SAAS,WAAW,QAAQ,gBAAgB;;;ACHxC,gBAAAA,MAiBF,YAjBE;AAFJ,SAAS,IAAI,EAAE,UAAU,GAAG,MAAM,GAAc;AAC9C,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,OAAM;AAAA,MACN,SAAQ;AAAA,MACR,MAAK;AAAA,MACL,QAAO;AAAA,MACP,aAAa;AAAA,MACb,eAAc;AAAA,MACd,gBAAe;AAAA,MACf,eAAY;AAAA,MACX,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ;AAEO,IAAM,eAAe,CAAC,MAC3B,qBAAC,OAAK,GAAG,GACP;AAAA,kBAAAA,KAAC,UAAK,GAAE,mDAAkD;AAAA,EAC1D,gBAAAA,KAAC,UAAK,GAAE,2BAA0B;AAAA,EAClC,gBAAAA,KAAC,UAAK,GAAE,WAAU;AAAA,EAClB,gBAAAA,KAAC,UAAK,GAAE,WAAU;AAAA,GACpB;AAEK,IAAM,aAAa,CAAC,MACzB,qBAAC,OAAK,GAAG,GACP;AAAA,kBAAAA,KAAC,UAAK,GAAE,YAAW;AAAA,EACnB,gBAAAA,KAAC,UAAK,GAAE,qIAAoI;AAAA,GAC9I;AAEK,IAAM,YAAY,CAAC,MACxB,qBAAC,OAAK,GAAG,GACP;AAAA,kBAAAA,KAAC,UAAK,GAAE,WAAU;AAAA,EAClB,gBAAAA,KAAC,UAAK,GAAE,yCAAwC;AAAA,EAChD,gBAAAA,KAAC,UAAK,GAAE,sCAAqC;AAAA,EAC7C,gBAAAA,KAAC,UAAK,IAAG,MAAK,IAAG,MAAK,IAAG,MAAK,IAAG,MAAK;AAAA,EACtC,gBAAAA,KAAC,UAAK,IAAG,MAAK,IAAG,MAAK,IAAG,MAAK,IAAG,MAAK;AAAA,GACxC;AAEK,IAAM,WAAW,CAAC,MACvB,gBAAAA,KAAC,OAAK,GAAG,GACP,0BAAAA,KAAC,aAAQ,QAAO,sBAAqB,GACvC;AAEK,IAAM,cAAc,CAAC,MAC1B,qBAAC,OAAK,GAAG,GACP;AAAA,kBAAAA,KAAC,UAAK,GAAE,4EAA2E;AAAA,EACnF,gBAAAA,KAAC,UAAK,GAAE,mFAAkF;AAAA,EAC1F,gBAAAA,KAAC,UAAK,GAAE,0DAAyD;AAAA,GACnE;AAEK,IAAM,YAAY,CAAC,MACxB,qBAAC,OAAK,GAAG,GACP;AAAA,kBAAAA,KAAC,UAAK,GAAE,2GAA0G;AAAA,EAClH,gBAAAA,KAAC,UAAK,GAAE,qBAAoB;AAAA,EAC5B,gBAAAA,KAAC,UAAK,GAAE,eAAc;AAAA,EACtB,gBAAAA,KAAC,UAAK,GAAE,gBAAe;AAAA,EACvB,gBAAAA,KAAC,UAAK,GAAE,0GAAyG;AAAA,EACjH,gBAAAA,KAAC,UAAK,GAAE,gBAAe;AAAA,EACvB,gBAAAA,KAAC,UAAK,GAAE,sBAAqB;AAAA,EAC7B,gBAAAA,KAAC,UAAK,GAAE,iBAAgB;AAAA,EACxB,gBAAAA,KAAC,UAAK,GAAE,oGAAmG;AAAA,EAC3G,gBAAAA,KAAC,UAAK,GAAE,mBAAkB;AAAA,EAC1B,gBAAAA,KAAC,UAAK,GAAE,mBAAkB;AAAA,EAC1B,gBAAAA,KAAC,UAAK,GAAE,cAAa;AAAA,GACvB;AAEK,IAAM,kBAAkB,CAAC,MAC9B,gBAAAA,KAAC,OAAK,GAAG,GACP,0BAAAA,KAAC,UAAK,GAAE,gBAAe,GACzB;AAEK,IAAM,mBAAmB,CAAC,MAC/B,gBAAAA,KAAC,OAAK,GAAG,GACP,0BAAAA,KAAC,UAAK,GAAE,iBAAgB,GAC1B;AAEK,IAAM,eAAe,CAAC,MAC3B,qBAAC,OAAK,GAAG,GACP;AAAA,kBAAAA,KAAC,cAAS,QAAO,kBAAiB;AAAA,EAClC,gBAAAA,KAAC,cAAS,QAAO,kBAAiB;AAAA,EAClC,gBAAAA,KAAC,UAAK,IAAG,MAAK,IAAG,MAAK,IAAG,KAAI,IAAG,MAAK;AAAA,EACrC,gBAAAA,KAAC,UAAK,IAAG,KAAI,IAAG,MAAK,IAAG,MAAK,IAAG,MAAK;AAAA,GACvC;AAEK,IAAM,aAAa,CAAC,MACzB,gBAAAA,KAAC,OAAK,GAAG,GACP,0BAAAA,KAAC,UAAK,GAAE,+BAA8B,GACxC;AAEK,IAAM,kBAAkB,CAAC,MAC9B,qBAAC,OAAK,GAAG,GACP;AAAA,kBAAAA,KAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,MAAK;AAAA,EAC/B,gBAAAA,KAAC,UAAK,GAAE,iBAAgB;AAAA,GAC1B;AAEK,IAAM,YAAY,CAAC,MACxB,qBAAC,OAAK,GAAG,GACP;AAAA,kBAAAA,KAAC,UAAK,GAAE,4EAA2E;AAAA,EACnF,gBAAAA,KAAC,UAAK,GAAE,WAAU;AAAA,EAClB,gBAAAA,KAAC,UAAK,GAAE,cAAa;AAAA,GACvB;AASF,IAAM,cAAc,oBAAI,IAAI,CAAC,gBAAgB,2BAA2B,OAAO,CAAC;AAEzE,SAAS,UAAU,gBAAwB,MAA8C;AAC9F,MAAI,mBAAmB,YAAY,YAAY,IAAI,IAAI,EAAG,QAAO;AACjE,UAAQ,gBAAgB;AAAA,IACtB,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO;AAAA,IACT;AACE,aAAO;AAAA,EACX;AACF;;;ADrDM,SAGS,OAAAC,MAHT,QAAAC,aAAA;AAjDC,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA;AACF,GAA8B;AAC5B,QAAM,OAAO,iBAAiB;AAC9B,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAqB,EAAE,QAAQ,OAAO,CAAC;AACjE,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,CAAC;AACxC,QAAM,YAAY,OAAsB,IAAI;AAG5C,YAAU,MAAM;AACd,QAAI,MAAM,WAAW,WAAY;AACjC,cAAU,UAAU,KAAK,IAAI;AAC7B,UAAM,KAAK;AAAA,MACT,MAAM,WAAW,KAAK,OAAO,KAAK,IAAI,KAAK,UAAU,WAAW,MAAM,GAAI,CAAC;AAAA,MAC3E;AAAA,IACF;AACA,WAAO,MAAM,cAAc,EAAE;AAAA,EAC/B,GAAG,CAAC,MAAM,MAAM,CAAC;AAEjB,MAAI,CAAC,UAAU,OAAO,SAAU,QAAO;AACvC,QAAM,UAAU,KAAK;AACrB,MAAI,CAAC,QAAS,QAAO;AAErB,QAAM,QAAQ,UAAU;AAExB,iBAAe,cAAc;AAC3B,QAAI,CAAC,QAAS;AACd,aAAS,EAAE,QAAQ,WAAW,CAAC;AAC/B,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,EAAE,QAAQ,UAAU,MAAM,MAAM,CAAC;AAC9D,eAAS,EAAE,QAAQ,WAAW,SAAS,OAAO,SAAS,QAAQ,OAAO,OAAO,CAAC;AAC9E,kBAAY;AAAA,IACd,SAAS,OAAO;AACd,eAAS;AAAA,QACP,QAAQ;AAAA,QACR,SACE,iBAAiB,SAAS,MAAM,UAC5B,MAAM,UACN;AAAA,MACR,CAAC;AAAA,IACH;AAAA,EACF;AAEA,MAAI,MAAM,WAAW,WAAW;AAC9B,UAAM,MAAM,MAAM,SAAS;AAC3B,WACE,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,0CAA0C,MAAM,qBAAqB,cAAc;AAAA,QAE7F;AAAA,gBAAM,gBAAAD,KAAC,aAAU,WAAU,eAAc,IAAK,gBAAAA,KAAC,mBAAgB,WAAU,eAAc;AAAA,UAAG;AAAA,UAClF,MAAM;AAAA,UACd,MAAM,SAAM,MAAM,MAAM,YAAY;AAAA;AAAA;AAAA,IACvC;AAAA,EAEJ;AAEA,SACE,gBAAAC,MAAC,UAAK,WAAU,kCACd;AAAA,oBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,MAAM,KAAK,YAAY;AAAA,QAChC,UAAU,MAAM,WAAW,cAAc,MAAM,WAAW;AAAA,QAC1D,WAAU;AAAA,QAET;AAAA,gBAAM,WAAW,aAChB,gBAAAD,KAAC,cAAW,WAAU,4BAA2B,IAEjD,gBAAAA,KAAC,YAAS,WAAU,eAAc;AAAA,UAEnC,MAAM,WAAW,aACd,WAAW,YAAY,IAAI,SAAS,IAAI,cAAc,IAAI,SAAS,OAAO,KAAK,EAAE,SAAI,UAAU,IAAI,OAAO,MAAM,EAAE,KAClH;AAAA;AAAA;AAAA,IACN;AAAA,IACC,MAAM,WAAW,UAChB,gBAAAC,MAAC,UAAK,WAAU,mEACd;AAAA,sBAAAD,KAAC,aAAU,WAAU,+BAA8B;AAAA,MACnD,gBAAAA,KAAC,UAAM,gBAAM,SAAQ;AAAA,OACvB,IACE;AAAA,KACN;AAEJ;;;AE9FA,SAAS,YAAAE,iBAAgB;AACzB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AAiCH,mBAEE,OAAAC,MAGI,QAAAC,aALN;AAvBJ,IAAM,mBAAmB;AAEzB,SAAS,QAAQ;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAKG;AACD,QAAM,OAAO,iBAAiB;AAC9B,QAAM,cAAc,gBAAgB,UAAU,MAAM,KAAK,KAAK;AAC9D,QAAM,QAAQ,UAAU,MAAM,aAAa,OAAO,KAAK;AACvD,QAAM,WAAW,aAAa,IAAI;AAClC,QAAM,QAAQ,UAAU,IAAI;AAC5B,QAAM,OAAO,KAAK,WAAW,UAAU,IAAI,KAAK;AAChD,QAAM,UAAU,iBAAiB,UAAU,gBAAgB,UAAU,MAAM,KAAK,KAAK;AACrF,QAAM,WAAW,KAAK;AAEtB,QAAM,OACJ,gBAAAA,MAAA,YAEE;AAAA,oBAAAD,KAAC,UAAK,WAAU,yDAAyD,iBAAM;AAAA,IAC/E,gBAAAA,KAAC,UAAK,WAAU,sEACb,gBAAM,IAAI,CAAC,SACV,gBAAAC,MAAC,UAAoB,WAAU,8CAC5B;AAAA,WAAK;AAAA,MAAM;AAAA,MAAE,KAAK;AAAA,SADV,KAAK,GAEhB,CACD,GACH;AAAA,IACC,YAAY,MAAM,WAAW,IAC5B,gBAAAD,KAAC,UAAK,WAAU,+DAA+D,oBAAS,IACtF;AAAA,IACH,WACC,gBAAAA,KAAC,gBAAa,WAAU,kDAAiD,IACvE;AAAA,KACN;AAGF,SACE,gBAAAC,MAAC,SAAI,WAAU,wCACZ;AAAA,eACC,gBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,MACP,SAAS;AAAA;AAAA,UAEP,MAAM,eAAe,MAAM,IAAI,KAAK;AAAA,UACpC,UAAU;AAAA,UACV;AAAA,UACA,UAAU,GAAG,QAAQ,KAAK,cAAW,QAAQ,CAAC,OAAO,KAAK;AAAA,UAC1D;AAAA,UACA;AAAA,UACA;AAAA,QACF,CAAC;AAAA,QAEH,WAAU;AAAA,QACV,OAAM;AAAA,QAEL;AAAA;AAAA,IACH,IAEA,gBAAAA,KAAC,UAAK,WAAU,sDAAsD,gBAAK;AAAA,IAE5E,KAAK,aAAa,EAAE,OAAO,OAAO,OAAO,MAAM,MAAM,MAAM,KAAK,CAAC;AAAA,KACpE;AAEJ;AAEO,SAAS,wBAAwB,EAAE,UAAU,GAAiC;AACnF,QAAM,OAAO,iBAAiB;AAC9B,QAAM,CAAC,UAAU,WAAW,IAAIE,UAAS,KAAK;AAC9C,QAAM,QAAQ,UAAU;AACxB,QAAM,QAAQ,MAAM;AACpB,QAAM,UAAU,iBAAiB,UAAU,gBAAgB,UAAU,MAAM,KAAK,KAAK;AACrF,QAAM,OAAO,UAAU,UAAU,gBAAgB,UAAU,IAAI;AAE/D,QAAM,QAAQ,WAAW,QAAQ,MAAM,MAAM,GAAG,gBAAgB;AAChE,QAAM,SAAS,QAAQ,MAAM;AAE7B,SACE,gBAAAD,MAAC,SAAI,WAAU,8DAA6D,kBAAgB,UAAU,MACpG;AAAA,oBAAAA,MAAC,SAAI,WAAU,+CACb;AAAA,sBAAAD,KAAC,QAAK,WAAU,8CAA6C;AAAA,MAC7D,gBAAAA,KAAC,UAAK,WAAU,uCAAuC,kBAAQ,OAAM;AAAA,MACpE,QAAQ,SAAS,gBAAAA,KAAC,UAAK,WAAU,iCAAiC,kBAAQ,QAAO,IAAU;AAAA,MAC5F,gBAAAC,MAAC,UAAK,WAAU,8CACb;AAAA;AAAA,QAAM;AAAA,QAAE,UAAU,IAAI,SAAS;AAAA,SAClC;AAAA,MACA,gBAAAA,MAAC,SAAI,WAAU,mCACZ;AAAA,aAAK,aAAa,EAAE,OAAO,QAAQ,OAAO,OAAO,UAAU,OAAO,MAAM,OAAO,CAAC;AAAA,QACjF,gBAAAD,KAAC,wBAAqB,WAAsB,WAAW,OAAO;AAAA,SAChE;AAAA,OACF;AAAA,IAEC,UAAU;AAAA;AAAA,MAET,gBAAAA,KAAC,OAAE,WAAU,sCAAqC,uDAAoC;AAAA,QAEtF,gBAAAA,KAAC,SAAI,WAAU,oCACZ,gBAAM,IAAI,CAAC,MAAM,MAChB,gBAAAA,KAAC,WAAgB,WAAsB,MAAY,OAAO,GAAG,SAA/C,CAA6D,CAC5E,GACH;AAAA,IAGD,SAAS,KAAK,WACb,gBAAAC;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;AAAA,QACpC,WAAU;AAAA,QAET;AAAA,qBAAW,gBAAAD,KAAC,mBAAgB,WAAU,WAAU,IAAK,gBAAAA,KAAC,oBAAiB,WAAU,WAAU;AAAA,UAC3F,WAAW,cAAc,QAAQ,MAAM;AAAA;AAAA;AAAA,IAC1C,IACE;AAAA,KACN;AAEJ;;;ACtJA,SAAS,oBAAAG,yBAA+C;AAiBlD,gBAAAC,MAEkB,QAAAC,aAFlB;AANC,SAAS,sBAAsB,EAAE,WAAW,OAAO,GAA+B;AACvF,QAAM,OAAO,iBAAiB;AAC9B,QAAM,QAAQ,UAAU,MAAM;AAC9B,QAAM,UAAUC,kBAAiB,UAAU,gBAAgB,UAAU,MAAM,KAAK,KAAK;AACrF,SACE,gBAAAD,MAAC,SAAI,WAAU,8GACb;AAAA,oBAAAD,KAAC,aAAU,WAAU,8CAA6C;AAAA,IAClE,gBAAAA,KAAC,UAAK,WAAU,mBAAmB,kBAAQ,OAAM;AAAA,IAChD,QAAQ,SAAS,gBAAAC,MAAC,UAAK,WAAU,yBAAwB;AAAA;AAAA,MAAG,QAAQ;AAAA,OAAO,IAAU;AAAA,IACtF,gBAAAA,MAAC,UAAK,WAAU,yBAAwB;AAAA;AAAA,MACnC;AAAA,MAAM;AAAA,MAAE,UAAU,IAAI,SAAS;AAAA,OACpC;AAAA,IACC,SAAS,gBAAAA,MAAC,UAAK,WAAU,yBAAwB;AAAA;AAAA,MAAG;AAAA,OAAO,IAAU;AAAA,IACtE,gBAAAD,KAAC,wBAAqB,WAAsB;AAAA,KAC9C;AAEJ;;;AChBA,SAAS,iCAAiC;AAuBpC,gBAAAG,YAAA;AAXC,SAAS,gBAAgB,EAAE,SAAS,UAAU,GAAyB;AAC5E,QAAM,OAAO,iBAAiB;AAC9B,QAAM,YAAY,0BAA0B,SAAS,CAAC,YAAY;AAChE,UAAM,OAAO,qBAAqB,OAAO;AACzC,QAAI,KAAK,YAAa,MAAK,YAAY,IAAI;AAAA,QACtC,SAAQ,MAAM,IAAI;AAAA,EACzB,CAAC;AAED,MAAI,CAAC,WAAW;AACd,UAAM,MAAM,OAAO,YAAY,WAAW,UAAU,KAAK,UAAU,SAAS,MAAM,CAAC;AACnF,WACE,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,wGAAwG,YAAY,IAAI,SAAS,KAAK,EAAE;AAAA,QAElJ;AAAA;AAAA,IACH;AAAA,EAEJ;AAEA,QAAM,WAAW,qBAAqB,SAAS;AAC/C,MAAI,SAAU,QAAO,gBAAAA,KAAC,YAAS,WAAsB;AACrD,MAAI,UAAU,OAAO,SAAU,QAAO,gBAAAA,KAAC,2BAAwB,WAAsB;AACrF,SAAO,gBAAAA,KAAC,yBAAsB,WAAsB;AACtD;","names":["jsx","jsx","jsxs","useState","jsx","jsxs","useState","directiveDisplay","jsx","jsxs","directiveDisplay","jsx"]}
@@ -1,6 +1,7 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { KindDefinition } from '@ai-matrx/content-ir';
3
3
  import { d as ComponentResolutionSource, a as ContentIrErrorReporter } from './component-resolution.types-C1MzCf2s.cjs';
4
+ import { b as DirectiveHost } from './directive-host-DAb7Keg8.cjs';
4
5
 
5
6
  /**
6
7
  * THE HOST CONTRACT — everything this package refuses to decide.
@@ -113,6 +114,14 @@ interface ContentIrHost extends ContentIrRenderSeams {
113
114
  * host that lies here renders the wrong component everywhere.
114
115
  */
115
116
  platform: string;
117
+ /**
118
+ * The Kind Directives seams (apply / open item / copy / noun catalog / item
119
+ * kind). Optional: a host without them still renders every directive card,
120
+ * apply-less and copy-less — honest chrome, never hidden content. Provided
121
+ * to the directive tier by `ContentIrRenderProvider`; a host that has not
122
+ * adopted the render provider mounts `DirectiveHostProvider` directly.
123
+ */
124
+ directives?: DirectiveHost;
116
125
  }
117
126
  /**
118
127
  * The pure route's dependencies — a strict subset of {@link ContentIrHost},
@@ -1,6 +1,7 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { KindDefinition } from '@ai-matrx/content-ir';
3
3
  import { d as ComponentResolutionSource, a as ContentIrErrorReporter } from './component-resolution.types-C1MzCf2s.js';
4
+ import { b as DirectiveHost } from './directive-host-DAb7Keg8.js';
4
5
 
5
6
  /**
6
7
  * THE HOST CONTRACT — everything this package refuses to decide.
@@ -113,6 +114,14 @@ interface ContentIrHost extends ContentIrRenderSeams {
113
114
  * host that lies here renders the wrong component everywhere.
114
115
  */
115
116
  platform: string;
117
+ /**
118
+ * The Kind Directives seams (apply / open item / copy / noun catalog / item
119
+ * kind). Optional: a host without them still renders every directive card,
120
+ * apply-less and copy-less — honest chrome, never hidden content. Provided
121
+ * to the directive tier by `ContentIrRenderProvider`; a host that has not
122
+ * adopted the render provider mounts `DirectiveHostProvider` directly.
123
+ */
124
+ directives?: DirectiveHost;
116
125
  }
117
126
  /**
118
127
  * The pure route's dependencies — a strict subset of {@link ContentIrHost},
package/dist/host.cjs CHANGED
@@ -46,17 +46,27 @@ function routeEnvOf(host) {
46
46
  }
47
47
 
48
48
  // host/ContentIrProvider.tsx
49
+ var import_react2 = require("react");
50
+
51
+ // directives/directive-host.tsx
49
52
  var import_react = require("react");
50
53
  var import_jsx_runtime = require("react/jsx-runtime");
51
- var ContentIrHostContext = (0, import_react.createContext)(null);
54
+ var DirectiveHostContext = (0, import_react.createContext)(null);
55
+ function DirectiveHostProvider({ host, children }) {
56
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DirectiveHostContext.Provider, { value: host, children });
57
+ }
58
+
59
+ // host/ContentIrProvider.tsx
60
+ var import_jsx_runtime2 = require("react/jsx-runtime");
61
+ var ContentIrHostContext = (0, import_react2.createContext)(null);
52
62
  function ContentIrRenderProvider({
53
63
  host,
54
64
  children
55
65
  }) {
56
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ContentIrHostContext.Provider, { value: host, children });
66
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ContentIrHostContext.Provider, { value: host, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DirectiveHostProvider, { host: host.directives ?? {}, children }) });
57
67
  }
58
68
  function useContentIrHostOrNull() {
59
- return (0, import_react.useContext)(ContentIrHostContext);
69
+ return (0, import_react2.useContext)(ContentIrHostContext);
60
70
  }
61
71
  function useContentIrHost() {
62
72
  const host = useContentIrHostOrNull();
@@ -69,6 +79,6 @@ function useContentIrHost() {
69
79
  }
70
80
  function useKindRouteEnv() {
71
81
  const host = useContentIrHost();
72
- return (0, import_react.useMemo)(() => routeEnvOf(host), [host]);
82
+ return (0, import_react2.useMemo)(() => routeEnvOf(host), [host]);
73
83
  }
74
84
  //# sourceMappingURL=host.cjs.map
package/dist/host.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../host.ts","../host/error-report.ts","../host/host-types.ts","../host/ContentIrProvider.tsx"],"sourcesContent":["/**\n * `@ai-matrx/content-ir-react/host` — the contract every consumer wires once:\n * the scream seam, the host/seam types, and the React provider.\n *\n * NOTE: this entry is stamped \"use client\" (it carries the provider). The\n * TYPES here are importable from anywhere; a server-side module that needs\n * only pure route logic imports `/route` instead.\n */\nexport * from \"./host/error-report\";\nexport * from \"./host/host-types\";\nexport * from \"./host/ContentIrProvider\";\n","/**\n * THE SCREAM SEAM.\n *\n * Every recovery path in this package is loud: a defective component row, a\n * failed resolver load, a kind whose component throws on a provisional value.\n * The package refuses to own where those screams land — matrx-frontend routes\n * them to the Error Inspector, the dashboard to its own log, the extension to\n * its background page. Each host binds one function.\n *\n * A host that binds a no-op has chosen silence, which is a defect in itself;\n * {@link consoleErrorReporter} is the honest default.\n */\n\nexport interface ContentIrErrorReport {\n /** Always `\"content-ir\"` — hosts key their capture stores on it. */\n source: \"content-ir\";\n message: string;\n name?: string;\n stack?: string;\n /** Free-form grouping key (the host's `relation`), e.g. `\"partial-kind\"`. */\n relation?: string;\n raw?: unknown;\n}\n\nexport type ContentIrErrorReporter = (report: ContentIrErrorReport) => void;\n\n/** The default when a host binds nothing: never silent. */\nexport const consoleErrorReporter: ContentIrErrorReporter = (report) => {\n console.error(`[content-ir] ${report.message}`, report.raw ?? \"\");\n};\n","/**\n * THE HOST CONTRACT — everything this package refuses to decide.\n *\n * The kernel (`@ai-matrx/content-ir`) proved the shape: extract the portable\n * part, let host capability enter through explicit named seams. The render\n * layer needs four things it must never own:\n *\n * 1. WHERE KIND DEFINITIONS COME FROM (a bundled table, `content_ir`, a REST\n * call). The route only asks \"is this kind registered, and does it carry a\n * legacy bridge / a partial-ready opt-in\".\n * 2. WHERE COMPONENT ROWS COME FROM — {@link ComponentResolutionSource}. The\n * package ships `ComponentResolver`, which any host can construct with its\n * own loaders; a host with its own resolver only has to satisfy the type.\n * 3. HOW A ROUTED BLOCK ACTUALLY DRAWS. `applyIrKindRoute` decides a block's\n * TYPE; turning that type into pixels is the host's dispatch table\n * (matrx-frontend's `BlockRenderer`, the dashboard's small map). The\n * package never imports a component library.\n * 4. WHERE SCREAMS LAND — {@link ContentIrErrorReporter}.\n *\n * No Next.js, no Redux, no Supabase, no router, no host error capture. If a\n * seam is missing, ADD A SEAM — never an import.\n */\n\nimport type { ReactNode } from \"react\";\nimport type { KindDefinition } from \"@ai-matrx/content-ir\";\nimport type { ContentIrErrorReporter } from \"./error-report\";\nimport type { ComponentResolutionSource } from \"../resolver/component-resolution.types\";\n\n/**\n * The minimum a render block must expose for the kind route to work. Hosts\n * pass their own richer block type through; the route is generic over `T` and\n * preserves every extra field.\n */\nexport interface IrRoutableBlock {\n type: string;\n serverData?: Record<string, unknown>;\n metadata?: Record<string, unknown>;\n}\n\n/** A routable block that also carries its source text (the raw region). */\nexport interface IrRenderBlock extends IrRoutableBlock {\n content: string;\n language?: string;\n}\n\n/**\n * Where registered kinds come from. `getDefinition` is the only method the\n * pure route needs; the rest drive the granular repaint and the warm-up tick\n * that keeps a cold registry from being mistaken for \"this kind has no\n * component\".\n */\nexport interface KindDefinitionSource {\n getDefinition(kind: string): KindDefinition | undefined;\n /** Monotonic per-kind version — the repaint snapshot key. */\n getKindVersion(kind: string): number;\n subscribeKind(kind: string, listener: () => void): () => void;\n /** One definition load per app session. */\n ensureWarm(): Promise<void>;\n}\n\n/**\n * The rendering seams. Everything here returns host UI; the package supplies\n * the DECISIONS and the chrome-free structure around them.\n */\nexport interface ContentIrRenderSeams {\n /**\n * Draw a block the kind route has already typed. This is the host's dispatch\n * table — the single place a component key becomes a component.\n */\n renderBlock(block: IrRenderBlock): ReactNode;\n /**\n * THE FLOOR: render ANY JSON value as a human document. Reached when a kind\n * has no render-trusted component, when the value is not an object at all,\n * and by the generic structured view.\n *\n * This is deliberately a seam and not a bundled component. Rendering a value\n * WELL means prose through the host's markdown renderer, media through the\n * host's file handler, and uniform arrays through the host's data table —\n * all of which are host property. A host that has none of those can pass a\n * `<pre>`; it will be honest, just plain.\n */\n renderValue(props: StructuredValueRenderProps): ReactNode;\n /**\n * The \"still arriving\" indicator used by the provisional frame. Optional —\n * hosts without a shimmer get plain text.\n */\n renderShimmer?(text: string): ReactNode;\n /**\n * The honest \"this shape has no custom component yet\" notice. Optional: the\n * package draws a plain amber line when a host supplies nothing, and a host\n * with an icon set or its own callout component supplies that instead. It is\n * a seam and not a bundled component because this package depends on no icon\n * library.\n */\n renderNotice?(text: string): ReactNode;\n}\n\nexport interface StructuredValueRenderProps {\n value: unknown;\n /** The kind slug this value claims, when known. Honesty line only — never a renderer choice. */\n kind?: string;\n /** Why this shape has no custom view, in human words. */\n note?: string;\n /** Show the \"what this is / raw data\" footer. Default true. */\n footer?: boolean;\n}\n\n/**\n * One host, wired once, read by every component in this package through\n * {@link ContentIrRenderProvider}.\n */\nexport interface ContentIrHost extends ContentIrRenderSeams {\n kinds: KindDefinitionSource;\n components: ComponentResolutionSource;\n reportError: ContentIrErrorReporter;\n /**\n * The `kind_component.platform` this host resolves as. \"web\" for every\n * browser UI; the column already models \"react-native\" and friends, and a\n * host that lies here renders the wrong component everywhere.\n */\n platform: string;\n}\n\n/**\n * The pure route's dependencies — a strict subset of {@link ContentIrHost},\n * because `applyIrKindRoute` is called OUTSIDE React (reducers, stream\n * accumulators, tests) where there is no provider to read.\n */\nexport interface KindRouteEnv {\n kinds: Pick<KindDefinitionSource, \"getDefinition\">;\n /**\n * `hasSettled` is optional and read ONLY to suppress a negative verdict from\n * a registry that has not loaded yet — see THE COLD-VERDICT RULE on\n * `ComponentResolutionSource`.\n */\n components: Pick<ComponentResolutionSource, \"resolve\"> &\n Partial<Pick<ComponentResolutionSource, \"hasSettled\">>;\n reportError: ContentIrErrorReporter;\n platform: string;\n}\n\n/** Narrow a host (or anything host-shaped) to what the pure route needs. */\nexport function routeEnvOf(host: ContentIrHost): KindRouteEnv {\n return {\n kinds: host.kinds,\n components: host.components,\n reportError: host.reportError,\n platform: host.platform,\n };\n}\n","\"use client\";\n\n/**\n * The one place a host is wired in. Everything React in this package reads its\n * capability from here — no module singleton, no import into an app.\n *\n * A host mounts this ONCE, high enough that every kind-rendering surface is\n * inside it:\n *\n * ```tsx\n * <ContentIrRenderProvider host={matrxContentIrHost}>\n * <App />\n * </ContentIrRenderProvider>\n * ```\n *\n * Reading it outside a provider THROWS rather than falling back to a default.\n * A silent default would render the wrong component for every kind in the app\n * and look like a data problem for weeks.\n */\n\nimport { createContext, useContext, useMemo, type ReactNode } from \"react\";\nimport type { ContentIrHost, KindRouteEnv } from \"./host-types\";\nimport { routeEnvOf } from \"./host-types\";\n\nconst ContentIrHostContext = createContext<ContentIrHost | null>(null);\n\nexport interface ContentIrRenderProviderProps {\n host: ContentIrHost;\n children: ReactNode;\n}\n\nexport function ContentIrRenderProvider({\n host,\n children,\n}: ContentIrRenderProviderProps) {\n return (\n <ContentIrHostContext.Provider value={host}>\n {children}\n </ContentIrHostContext.Provider>\n );\n}\n\n/**\n * The wired host, or null. For the rare consumer that can legitimately run\n * without a provider because it was handed its sources explicitly (see\n * `useContentIrKindVersion`). Everything else uses {@link useContentIrHost}.\n */\nexport function useContentIrHostOrNull(): ContentIrHost | null {\n return useContext(ContentIrHostContext);\n}\n\n/** The wired host. Throws outside a provider — see the module doc. */\nexport function useContentIrHost(): ContentIrHost {\n const host = useContentIrHostOrNull();\n if (!host) {\n throw new Error(\n \"[content-ir-react] No ContentIrRenderProvider above this component. \" +\n \"Mount one at the app root with your host adapter (kind definitions, \" +\n \"component resolver, renderBlock, renderValue, reportError).\",\n );\n }\n return host;\n}\n\n/** The wired host narrowed to what the pure route functions take. */\nexport function useKindRouteEnv(): KindRouteEnv {\n const host = useContentIrHost();\n return useMemo(() => routeEnvOf(host), [host]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC2BO,IAAM,uBAA+C,CAAC,WAAW;AACtE,UAAQ,MAAM,gBAAgB,OAAO,OAAO,IAAI,OAAO,OAAO,EAAE;AAClE;;;ACiHO,SAAS,WAAW,MAAmC;AAC5D,SAAO;AAAA,IACL,OAAO,KAAK;AAAA,IACZ,YAAY,KAAK;AAAA,IACjB,aAAa,KAAK;AAAA,IAClB,UAAU,KAAK;AAAA,EACjB;AACF;;;ACjIA,mBAAmE;AAgB/D;AAZJ,IAAM,2BAAuB,4BAAoC,IAAI;AAO9D,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AACF,GAAiC;AAC/B,SACE,4CAAC,qBAAqB,UAArB,EAA8B,OAAO,MACnC,UACH;AAEJ;AAOO,SAAS,yBAA+C;AAC7D,aAAO,yBAAW,oBAAoB;AACxC;AAGO,SAAS,mBAAkC;AAChD,QAAM,OAAO,uBAAuB;AACpC,MAAI,CAAC,MAAM;AACT,UAAM,IAAI;AAAA,MACR;AAAA,IAGF;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,kBAAgC;AAC9C,QAAM,OAAO,iBAAiB;AAC9B,aAAO,sBAAQ,MAAM,WAAW,IAAI,GAAG,CAAC,IAAI,CAAC;AAC/C;","names":[]}
1
+ {"version":3,"sources":["../host.ts","../host/error-report.ts","../host/host-types.ts","../host/ContentIrProvider.tsx","../directives/directive-host.tsx"],"sourcesContent":["/**\n * `@ai-matrx/content-ir-react/host` — the contract every consumer wires once:\n * the scream seam, the host/seam types, and the React provider.\n *\n * NOTE: this entry is stamped \"use client\" (it carries the provider). The\n * TYPES here are importable from anywhere; a server-side module that needs\n * only pure route logic imports `/route` instead.\n */\nexport * from \"./host/error-report\";\nexport * from \"./host/host-types\";\nexport * from \"./host/ContentIrProvider\";\n","/**\n * THE SCREAM SEAM.\n *\n * Every recovery path in this package is loud: a defective component row, a\n * failed resolver load, a kind whose component throws on a provisional value.\n * The package refuses to own where those screams land — matrx-frontend routes\n * them to the Error Inspector, the dashboard to its own log, the extension to\n * its background page. Each host binds one function.\n *\n * A host that binds a no-op has chosen silence, which is a defect in itself;\n * {@link consoleErrorReporter} is the honest default.\n */\n\nexport interface ContentIrErrorReport {\n /** Always `\"content-ir\"` — hosts key their capture stores on it. */\n source: \"content-ir\";\n message: string;\n name?: string;\n stack?: string;\n /** Free-form grouping key (the host's `relation`), e.g. `\"partial-kind\"`. */\n relation?: string;\n raw?: unknown;\n}\n\nexport type ContentIrErrorReporter = (report: ContentIrErrorReport) => void;\n\n/** The default when a host binds nothing: never silent. */\nexport const consoleErrorReporter: ContentIrErrorReporter = (report) => {\n console.error(`[content-ir] ${report.message}`, report.raw ?? \"\");\n};\n","/**\n * THE HOST CONTRACT — everything this package refuses to decide.\n *\n * The kernel (`@ai-matrx/content-ir`) proved the shape: extract the portable\n * part, let host capability enter through explicit named seams. The render\n * layer needs four things it must never own:\n *\n * 1. WHERE KIND DEFINITIONS COME FROM (a bundled table, `content_ir`, a REST\n * call). The route only asks \"is this kind registered, and does it carry a\n * legacy bridge / a partial-ready opt-in\".\n * 2. WHERE COMPONENT ROWS COME FROM — {@link ComponentResolutionSource}. The\n * package ships `ComponentResolver`, which any host can construct with its\n * own loaders; a host with its own resolver only has to satisfy the type.\n * 3. HOW A ROUTED BLOCK ACTUALLY DRAWS. `applyIrKindRoute` decides a block's\n * TYPE; turning that type into pixels is the host's dispatch table\n * (matrx-frontend's `BlockRenderer`, the dashboard's small map). The\n * package never imports a component library.\n * 4. WHERE SCREAMS LAND — {@link ContentIrErrorReporter}.\n *\n * No Next.js, no Redux, no Supabase, no router, no host error capture. If a\n * seam is missing, ADD A SEAM — never an import.\n */\n\nimport type { ReactNode } from \"react\";\nimport type { KindDefinition } from \"@ai-matrx/content-ir\";\nimport type { ContentIrErrorReporter } from \"./error-report\";\nimport type { ComponentResolutionSource } from \"../resolver/component-resolution.types\";\nimport type { DirectiveHost } from \"../directives/directive-host\";\n\n/**\n * The minimum a render block must expose for the kind route to work. Hosts\n * pass their own richer block type through; the route is generic over `T` and\n * preserves every extra field.\n */\nexport interface IrRoutableBlock {\n type: string;\n serverData?: Record<string, unknown>;\n metadata?: Record<string, unknown>;\n}\n\n/** A routable block that also carries its source text (the raw region). */\nexport interface IrRenderBlock extends IrRoutableBlock {\n content: string;\n language?: string;\n}\n\n/**\n * Where registered kinds come from. `getDefinition` is the only method the\n * pure route needs; the rest drive the granular repaint and the warm-up tick\n * that keeps a cold registry from being mistaken for \"this kind has no\n * component\".\n */\nexport interface KindDefinitionSource {\n getDefinition(kind: string): KindDefinition | undefined;\n /** Monotonic per-kind version — the repaint snapshot key. */\n getKindVersion(kind: string): number;\n subscribeKind(kind: string, listener: () => void): () => void;\n /** One definition load per app session. */\n ensureWarm(): Promise<void>;\n}\n\n/**\n * The rendering seams. Everything here returns host UI; the package supplies\n * the DECISIONS and the chrome-free structure around them.\n */\nexport interface ContentIrRenderSeams {\n /**\n * Draw a block the kind route has already typed. This is the host's dispatch\n * table — the single place a component key becomes a component.\n */\n renderBlock(block: IrRenderBlock): ReactNode;\n /**\n * THE FLOOR: render ANY JSON value as a human document. Reached when a kind\n * has no render-trusted component, when the value is not an object at all,\n * and by the generic structured view.\n *\n * This is deliberately a seam and not a bundled component. Rendering a value\n * WELL means prose through the host's markdown renderer, media through the\n * host's file handler, and uniform arrays through the host's data table —\n * all of which are host property. A host that has none of those can pass a\n * `<pre>`; it will be honest, just plain.\n */\n renderValue(props: StructuredValueRenderProps): ReactNode;\n /**\n * The \"still arriving\" indicator used by the provisional frame. Optional —\n * hosts without a shimmer get plain text.\n */\n renderShimmer?(text: string): ReactNode;\n /**\n * The honest \"this shape has no custom component yet\" notice. Optional: the\n * package draws a plain amber line when a host supplies nothing, and a host\n * with an icon set or its own callout component supplies that instead. It is\n * a seam and not a bundled component because this package depends on no icon\n * library.\n */\n renderNotice?(text: string): ReactNode;\n}\n\nexport interface StructuredValueRenderProps {\n value: unknown;\n /** The kind slug this value claims, when known. Honesty line only — never a renderer choice. */\n kind?: string;\n /** Why this shape has no custom view, in human words. */\n note?: string;\n /** Show the \"what this is / raw data\" footer. Default true. */\n footer?: boolean;\n}\n\n/**\n * One host, wired once, read by every component in this package through\n * {@link ContentIrRenderProvider}.\n */\nexport interface ContentIrHost extends ContentIrRenderSeams {\n kinds: KindDefinitionSource;\n components: ComponentResolutionSource;\n reportError: ContentIrErrorReporter;\n /**\n * The `kind_component.platform` this host resolves as. \"web\" for every\n * browser UI; the column already models \"react-native\" and friends, and a\n * host that lies here renders the wrong component everywhere.\n */\n platform: string;\n /**\n * The Kind Directives seams (apply / open item / copy / noun catalog / item\n * kind). Optional: a host without them still renders every directive card,\n * apply-less and copy-less — honest chrome, never hidden content. Provided\n * to the directive tier by `ContentIrRenderProvider`; a host that has not\n * adopted the render provider mounts `DirectiveHostProvider` directly.\n */\n directives?: DirectiveHost;\n}\n\n/**\n * The pure route's dependencies — a strict subset of {@link ContentIrHost},\n * because `applyIrKindRoute` is called OUTSIDE React (reducers, stream\n * accumulators, tests) where there is no provider to read.\n */\nexport interface KindRouteEnv {\n kinds: Pick<KindDefinitionSource, \"getDefinition\">;\n /**\n * `hasSettled` is optional and read ONLY to suppress a negative verdict from\n * a registry that has not loaded yet — see THE COLD-VERDICT RULE on\n * `ComponentResolutionSource`.\n */\n components: Pick<ComponentResolutionSource, \"resolve\"> &\n Partial<Pick<ComponentResolutionSource, \"hasSettled\">>;\n reportError: ContentIrErrorReporter;\n platform: string;\n}\n\n/** Narrow a host (or anything host-shaped) to what the pure route needs. */\nexport function routeEnvOf(host: ContentIrHost): KindRouteEnv {\n return {\n kinds: host.kinds,\n components: host.components,\n reportError: host.reportError,\n platform: host.platform,\n };\n}\n","\"use client\";\n\n/**\n * The one place a host is wired in. Everything React in this package reads its\n * capability from here — no module singleton, no import into an app.\n *\n * A host mounts this ONCE, high enough that every kind-rendering surface is\n * inside it:\n *\n * ```tsx\n * <ContentIrRenderProvider host={matrxContentIrHost}>\n * <App />\n * </ContentIrRenderProvider>\n * ```\n *\n * Reading it outside a provider THROWS rather than falling back to a default.\n * A silent default would render the wrong component for every kind in the app\n * and look like a data problem for weeks.\n */\n\nimport { createContext, useContext, useMemo, type ReactNode } from \"react\";\nimport type { ContentIrHost, KindRouteEnv } from \"./host-types\";\nimport { routeEnvOf } from \"./host-types\";\nimport { DirectiveHostProvider } from \"../directives/directive-host\";\n\nconst ContentIrHostContext = createContext<ContentIrHost | null>(null);\n\nexport interface ContentIrRenderProviderProps {\n host: ContentIrHost;\n children: ReactNode;\n}\n\nexport function ContentIrRenderProvider({\n host,\n children,\n}: ContentIrRenderProviderProps) {\n return (\n <ContentIrHostContext.Provider value={host}>\n <DirectiveHostProvider host={host.directives ?? {}}>{children}</DirectiveHostProvider>\n </ContentIrHostContext.Provider>\n );\n}\n\n/**\n * The wired host, or null. For the rare consumer that can legitimately run\n * without a provider because it was handed its sources explicitly (see\n * `useContentIrKindVersion`). Everything else uses {@link useContentIrHost}.\n */\nexport function useContentIrHostOrNull(): ContentIrHost | null {\n return useContext(ContentIrHostContext);\n}\n\n/** The wired host. Throws outside a provider — see the module doc. */\nexport function useContentIrHost(): ContentIrHost {\n const host = useContentIrHostOrNull();\n if (!host) {\n throw new Error(\n \"[content-ir-react] No ContentIrRenderProvider above this component. \" +\n \"Mount one at the app root with your host adapter (kind definitions, \" +\n \"component resolver, renderBlock, renderValue, reportError).\",\n );\n }\n return host;\n}\n\n/** The wired host narrowed to what the pure route functions take. */\nexport function useKindRouteEnv(): KindRouteEnv {\n const host = useContentIrHost();\n return useMemo(() => routeEnvOf(host), [host]);\n}\n","\"use client\";\n\n/**\n * THE DIRECTIVE HOST CONTRACT — what the directive tier refuses to decide.\n *\n * The package owns the DECISIONS (decode → renderer → card → floor, the\n * position law, never-null rendering) and the card itself. Four things stay\n * host property and enter through named seams:\n *\n * 1. APPLYING a side-effect directive — `POST /directives/confirm` as the\n * user, with the host's auth and base URL. Absent → the Apply control is\n * ABSENT (honest), never a dead button.\n * 2. OPENING an item to read it before approving — a window panel, a dialog,\n * a route. Absent → items are not clickable.\n * 3. COPY controls — host chrome (matrx-frontend's CopyButtons). Absent → none.\n * 4. The NOUN catalog + item-kind lookup — server-derived tables the host\n * mirrors (or reads from its kind catalog's `items` edges).\n *\n * A host that has adopted `ContentIrRenderProvider` passes `directives` on its\n * ContentIrHost; a host that has NOT (Workflow Studio, 2026-09-08) mounts\n * `DirectiveHostProvider` alone — the directive tier never requires the full\n * render host, because a chat bubble must be able to draw a directive card\n * identically everywhere, today.\n */\n\nimport { createContext, useContext, type ReactNode } from \"react\";\nimport type {\n DecodedDirective,\n DirectiveItemKindLookup,\n DirectiveNounCatalog,\n} from \"@ai-matrx/content-ir\";\n\nexport interface DirectiveApplyResult {\n applied: number;\n failed: number;\n}\n\nexport interface DirectiveOpenItemOptions {\n /** The item, `__kind`-stamped when the shape has an item kind. */\n item: Record<string, unknown>;\n /** The content-IR kind this item IS, or null when the shape has none. */\n itemKind: string | null;\n /** What the item is called — the same title the card shows. */\n title: string;\n /** \"Run Create agent definition · item 1 of 1\". */\n subtitle: string | null;\n directive: DecodedDirective;\n index: number;\n total: number;\n}\n\nexport interface DirectiveCopyProps {\n label: string;\n /** The value to copy — the whole shell or one item. */\n value: unknown;\n /** The item's kind, when copying an item. */\n kind?: string | null;\n size: \"icon\" | \"xs\";\n}\n\nexport interface DirectiveHost {\n /**\n * Apply a side-effect directive as the user (the server re-validates every\n * item against the registered model and runs the ONE handler; idempotent).\n * Reject with an Error whose message is safe to show.\n */\n confirm?: (shell: { __kind: string; items: Record<string, unknown>[] }) => Promise<DirectiveApplyResult>;\n /** Open ONE item to read it properly before approving the write. */\n openItem?: (options: DirectiveOpenItemOptions) => void;\n /** Host copy controls. */\n renderCopy?: (props: DirectiveCopyProps) => ReactNode;\n /** The mirrored noun catalog (label / family / title column). */\n nouns?: DirectiveNounCatalog;\n /** slug → the kind ONE item is (THE DIRECTIVE⇄KIND SEAM), or null. */\n itemKind?: DirectiveItemKindLookup;\n /** Where a decode failure or an unhonourable shell is reported. Default: console.error. */\n reportError?: (message: string) => void;\n}\n\nconst DirectiveHostContext = createContext<DirectiveHost | null>(null);\n\nexport interface DirectiveHostProviderProps {\n host: DirectiveHost;\n children: ReactNode;\n}\n\nexport function DirectiveHostProvider({ host, children }: DirectiveHostProviderProps) {\n return <DirectiveHostContext.Provider value={host}>{children}</DirectiveHostContext.Provider>;\n}\n\nconst EMPTY_HOST: DirectiveHost = {};\n\n/**\n * The wired directive host. Unlike the render host this does NOT throw when\n * unwired: a directive still renders (named, itemised, copy-less, apply-less)\n * — degrading the chrome is honest, hiding the content is not.\n */\nexport function useDirectiveHost(): DirectiveHost {\n return useContext(DirectiveHostContext) ?? EMPTY_HOST;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC2BO,IAAM,uBAA+C,CAAC,WAAW;AACtE,UAAQ,MAAM,gBAAgB,OAAO,OAAO,IAAI,OAAO,OAAO,EAAE;AAClE;;;AC0HO,SAAS,WAAW,MAAmC;AAC5D,SAAO;AAAA,IACL,OAAO,KAAK;AAAA,IACZ,YAAY,KAAK;AAAA,IACjB,aAAa,KAAK;AAAA,IAClB,UAAU,KAAK;AAAA,EACjB;AACF;;;AC1IA,IAAAA,gBAAmE;;;ACKnE,mBAA0D;AA8DjD;AART,IAAM,2BAAuB,4BAAoC,IAAI;AAO9D,SAAS,sBAAsB,EAAE,MAAM,SAAS,GAA+B;AACpF,SAAO,4CAAC,qBAAqB,UAArB,EAA8B,OAAO,MAAO,UAAS;AAC/D;;;ADlDM,IAAAC,sBAAA;AAbN,IAAM,2BAAuB,6BAAoC,IAAI;AAO9D,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AACF,GAAiC;AAC/B,SACE,6CAAC,qBAAqB,UAArB,EAA8B,OAAO,MACpC,uDAAC,yBAAsB,MAAM,KAAK,cAAc,CAAC,GAAI,UAAS,GAChE;AAEJ;AAOO,SAAS,yBAA+C;AAC7D,aAAO,0BAAW,oBAAoB;AACxC;AAGO,SAAS,mBAAkC;AAChD,QAAM,OAAO,uBAAuB;AACpC,MAAI,CAAC,MAAM;AACT,UAAM,IAAI;AAAA,MACR;AAAA,IAGF;AAAA,EACF;AACA,SAAO;AACT;AAGO,SAAS,kBAAgC;AAC9C,QAAM,OAAO,iBAAiB;AAC9B,aAAO,uBAAQ,MAAM,WAAW,IAAI,GAAG,CAAC,IAAI,CAAC;AAC/C;","names":["import_react","import_jsx_runtime"]}
package/dist/host.d.cts CHANGED
@@ -1,15 +1,16 @@
1
1
  export { b as ContentIrErrorReport, a as ContentIrErrorReporter, c as consoleErrorReporter } from './component-resolution.types-C1MzCf2s.cjs';
2
- import { C as ContentIrHost, K as KindRouteEnv } from './host-types-CDcLW0TD.cjs';
3
- export { a as ContentIrRenderSeams, I as IrRenderBlock, b as IrRoutableBlock, c as KindDefinitionSource, S as StructuredValueRenderProps, r as routeEnvOf } from './host-types-CDcLW0TD.cjs';
4
- import * as React from 'react';
2
+ import { C as ContentIrHost, K as KindRouteEnv } from './host-types-C8jR31Wk.cjs';
3
+ export { a as ContentIrRenderSeams, I as IrRenderBlock, b as IrRoutableBlock, c as KindDefinitionSource, S as StructuredValueRenderProps, r as routeEnvOf } from './host-types-C8jR31Wk.cjs';
4
+ import * as react from 'react';
5
5
  import { ReactNode } from 'react';
6
6
  import '@ai-matrx/content-ir';
7
+ import './directive-host-DAb7Keg8.cjs';
7
8
 
8
9
  interface ContentIrRenderProviderProps {
9
10
  host: ContentIrHost;
10
11
  children: ReactNode;
11
12
  }
12
- declare function ContentIrRenderProvider({ host, children, }: ContentIrRenderProviderProps): React.JSX.Element;
13
+ declare function ContentIrRenderProvider({ host, children, }: ContentIrRenderProviderProps): react.JSX.Element;
13
14
  /**
14
15
  * The wired host, or null. For the rare consumer that can legitimately run
15
16
  * without a provider because it was handed its sources explicitly (see