@ai-matrx/content-ir-react 0.11.11 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.d.cts CHANGED
@@ -1,9 +1,9 @@
1
- import { c as KindDefinitionSource, C as ContentIrHost } from './host-types-C8jR31Wk.cjs';
1
+ import { c as KindDefinitionSource, C as ContentIrHost } from './host-types-BgROpyjE.cjs';
2
2
  import { d as ComponentResolutionSource } from './component-resolution.types-C1MzCf2s.cjs';
3
3
  import * as react from 'react';
4
4
  import react__default, { ReactNode } from 'react';
5
5
  import '@ai-matrx/content-ir';
6
- import './directive-host-DAb7Keg8.cjs';
6
+ import './directive-host-Bl-ZgJWp.cjs';
7
7
 
8
8
  /**
9
9
  * The two registries this hook watches. Passed explicitly by a host whose
package/dist/react.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { c as KindDefinitionSource, C as ContentIrHost } from './host-types-IUEDmso7.js';
1
+ import { c as KindDefinitionSource, C as ContentIrHost } from './host-types-Cz-1pGHz.js';
2
2
  import { d as ComponentResolutionSource } from './component-resolution.types-C1MzCf2s.js';
3
3
  import * as react from 'react';
4
4
  import react__default, { ReactNode } from 'react';
5
5
  import '@ai-matrx/content-ir';
6
- import './directive-host-DAb7Keg8.js';
6
+ import './directive-host-Bl-ZgJWp.js';
7
7
 
8
8
  /**
9
9
  * The two registries this hook watches. Passed explicitly by a host whose
package/dist/react.js CHANGED
@@ -85,6 +85,63 @@ import { directiveDisplay } from "@ai-matrx/content-ir";
85
85
 
86
86
  // directives/ApplyDirectiveButton.tsx
87
87
  import { useEffect, useRef, useState } from "react";
88
+ import { nounLabel } from "@ai-matrx/content-ir";
89
+
90
+ // directives/item-changes.ts
91
+ import { isUuidShape } from "@ai-matrx/kit/uuid";
92
+ var TARGETING_CLASSES = /* @__PURE__ */ new Set(["update", "delete"]);
93
+ var NOT_A_CHANGE = /* @__PURE__ */ new Set(["__kind", "id"]);
94
+ var CHANGE_VALUE_MAX = 60;
95
+ function itemRecordId(item) {
96
+ const id = item.id;
97
+ if (typeof id === "string" && id.trim()) return id.trim();
98
+ if (typeof id === "number") return String(id);
99
+ return null;
100
+ }
101
+ function fieldLabel(key) {
102
+ const spaced = key.replace(/_/g, " ").trim();
103
+ return spaced.charAt(0).toUpperCase() + spaced.slice(1);
104
+ }
105
+ function recordFallbackName(nounLabel3, id) {
106
+ const short = isUuidShape(id) ? id.slice(0, 8) : id.length > 12 ? `${id.slice(0, 12)}\u2026` : id;
107
+ return `${nounLabel3} ${short}`;
108
+ }
109
+ function describeValue(value) {
110
+ if (value === null || value === void 0) return { value: "cleared", full: "cleared", clears: true };
111
+ if (typeof value === "string") {
112
+ const trimmed = value.trim();
113
+ if (!trimmed) return { value: "cleared", full: "cleared", clears: true };
114
+ const oneLine = trimmed.replace(/\s+/g, " ");
115
+ const shown = oneLine.length > CHANGE_VALUE_MAX ? `${oneLine.slice(0, CHANGE_VALUE_MAX - 1)}\u2026` : oneLine;
116
+ return { value: shown, full: trimmed, clears: false };
117
+ }
118
+ if (typeof value === "number" || typeof value === "boolean") {
119
+ return { value: String(value), full: String(value), clears: false };
120
+ }
121
+ if (Array.isArray(value)) {
122
+ const n2 = value.length;
123
+ const text2 = n2 === 0 ? "cleared" : `${n2} ${n2 === 1 ? "entry" : "entries"}`;
124
+ return { value: text2, full: safeJson(value), clears: n2 === 0 };
125
+ }
126
+ const n = Object.keys(value).length;
127
+ const text = n === 0 ? "cleared" : `${n} ${n === 1 ? "field" : "fields"}`;
128
+ return { value: text, full: safeJson(value), clears: n === 0 };
129
+ }
130
+ function safeJson(value) {
131
+ try {
132
+ return JSON.stringify(value, null, 2);
133
+ } catch {
134
+ return String(value);
135
+ }
136
+ }
137
+ function itemChanges(item) {
138
+ const out = [];
139
+ for (const [key, raw] of Object.entries(item)) {
140
+ if (NOT_A_CHANGE.has(key)) continue;
141
+ out.push({ key, label: fieldLabel(key), ...describeValue(raw) });
142
+ }
143
+ return out;
144
+ }
88
145
 
89
146
  // directives/icons.tsx
90
147
  import { jsx as jsx3, jsxs } from "react/jsx-runtime";
@@ -202,10 +259,38 @@ function ApplyDirectiveButton({
202
259
  const items = directive.items;
203
260
  async function handleApply() {
204
261
  if (!confirm) return;
262
+ if (host.ask) {
263
+ setState({ status: "asking" });
264
+ let ok = false;
265
+ try {
266
+ ok = await host.ask({
267
+ directive,
268
+ items,
269
+ nounLabel: nounLabel(directive.noun, host.nouns)
270
+ });
271
+ } catch (error) {
272
+ setState({
273
+ status: "error",
274
+ message: error instanceof Error && error.message ? error.message : "We could not ask you to confirm this, so nothing was done."
275
+ });
276
+ return;
277
+ }
278
+ if (!ok) {
279
+ setState({ status: "idle" });
280
+ return;
281
+ }
282
+ }
283
+ setElapsed(0);
205
284
  setState({ status: "applying" });
206
285
  try {
207
286
  const result = await confirm({ __kind: directive.slug, items });
208
- setState({ status: "applied", applied: result.applied, failed: result.failed });
287
+ setState({
288
+ status: "applied",
289
+ applied: result.applied,
290
+ failed: result.failed,
291
+ message: result.message?.trim() ? result.message.trim() : null,
292
+ records: result.records ?? []
293
+ });
209
294
  onApplied?.();
210
295
  } catch (error) {
211
296
  setState({
@@ -216,15 +301,26 @@ function ApplyDirectiveButton({
216
301
  }
217
302
  if (state.status === "applied") {
218
303
  const bad = state.failed > 0;
304
+ const trashed = directive.directiveClass === "delete";
305
+ const label2 = nounLabel(directive.noun, host.nouns);
306
+ const records = host.renderRecord ? state.records : [];
219
307
  return /* @__PURE__ */ jsxs2(
220
308
  "span",
221
309
  {
222
- className: `inline-flex items-center gap-1 text-xs ${bad ? "text-destructive" : "text-primary"}`,
310
+ className: `inline-flex min-w-0 flex-wrap items-center gap-1 text-xs ${bad ? "text-destructive" : "text-primary"}`,
311
+ "data-apply-state": "applied",
223
312
  children: [
224
- bad ? /* @__PURE__ */ jsx4(AlertIcon, { className: "h-3.5 w-3.5" }) : /* @__PURE__ */ jsx4(CheckCircleIcon, { className: "h-3.5 w-3.5" }),
225
- "Applied ",
226
- state.applied,
227
- bad ? ` \xB7 ${state.failed} failed` : ""
313
+ bad ? /* @__PURE__ */ jsx4(AlertIcon, { className: "h-3.5 w-3.5 shrink-0" }) : /* @__PURE__ */ jsx4(CheckCircleIcon, { className: "h-3.5 w-3.5 shrink-0" }),
314
+ /* @__PURE__ */ jsxs2("span", { children: [
315
+ state.message ?? `Applied ${state.applied}`,
316
+ bad && !state.message ? ` \xB7 ${state.failed} failed` : ""
317
+ ] }),
318
+ records.map((record) => /* @__PURE__ */ jsx4("span", { className: "inline-flex min-w-0 items-center", children: host.renderRecord?.({
319
+ ...record,
320
+ fallback: recordFallbackName(label2, record.id),
321
+ context: "applied",
322
+ trashed
323
+ }) }, `${record.noun}:${record.id}`))
228
324
  ]
229
325
  }
230
326
  );
@@ -235,7 +331,7 @@ function ApplyDirectiveButton({
235
331
  {
236
332
  type: "button",
237
333
  onClick: () => void handleApply(),
238
- disabled: state.status === "applying" || items.length === 0,
334
+ disabled: state.status === "applying" || state.status === "asking" || items.length === 0,
239
335
  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",
240
336
  children: [
241
337
  state.status === "applying" ? /* @__PURE__ */ jsx4(LoaderIcon, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx4(PlayIcon, { className: "h-3.5 w-3.5" }),
@@ -338,6 +434,7 @@ import {
338
434
  itemFacts,
339
435
  itemSubtitle,
340
436
  itemTitle,
437
+ nounLabel as nounLabel2,
341
438
  nounTitleColumn
342
439
  } from "@ai-matrx/content-ir";
343
440
  import { Fragment, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
@@ -349,6 +446,10 @@ function ItemRow({
349
446
  total
350
447
  }) {
351
448
  const host = useDirectiveHost();
449
+ const recordId = TARGETING_CLASSES.has(directive.directiveClass) ? itemRecordId(item) : null;
450
+ if (recordId) {
451
+ return /* @__PURE__ */ jsx7(TargetRow, { directive, item, recordId });
452
+ }
352
453
  const titleColumn = nounTitleColumn(directive.noun, host.nouns);
353
454
  const title = itemTitle(item, titleColumn, index, total);
354
455
  const subtitle = itemSubtitle(item);
@@ -389,6 +490,41 @@ function ItemRow({
389
490
  host.renderCopy?.({ label: title, value: item, kind, size: "xs" })
390
491
  ] });
391
492
  }
493
+ function TargetRow({
494
+ directive,
495
+ item,
496
+ recordId
497
+ }) {
498
+ const host = useDirectiveHost();
499
+ const fallback = recordFallbackName(nounLabel2(directive.noun, host.nouns), recordId);
500
+ const changes = directive.directiveClass === "update" ? itemChanges(item) : [];
501
+ const name = host.renderRecord ? host.renderRecord({ noun: directive.noun, id: recordId, fallback, context: "row" }) : /* @__PURE__ */ jsx7("span", { className: "truncate font-medium text-foreground", children: fallback });
502
+ return /* @__PURE__ */ jsxs5("div", { className: "py-1 text-xs", "data-directive-target": recordId, children: [
503
+ /* @__PURE__ */ jsxs5("div", { className: "flex min-w-0 items-center gap-2", children: [
504
+ /* @__PURE__ */ jsx7("span", { className: "flex min-w-0 flex-1 items-center gap-1 px-1", children: name }),
505
+ host.renderCopy?.({ label: fallback, value: item, size: "xs" })
506
+ ] }),
507
+ directive.directiveClass === "update" ? changes.length === 0 ? /* @__PURE__ */ jsx7("p", { className: "px-1 text-[11px] text-muted-foreground", children: "Sets no fields \u2014 nothing would change." }) : /* @__PURE__ */ jsx7("ul", { className: "mt-0.5 space-y-0.5 px-1", children: changes.map((change) => /* @__PURE__ */ jsxs5(
508
+ "li",
509
+ {
510
+ className: "flex min-w-0 items-baseline gap-1.5 text-[11px]",
511
+ title: `${change.label} \u2192 ${change.full}`,
512
+ children: [
513
+ /* @__PURE__ */ jsx7("span", { className: "shrink-0 text-muted-foreground", children: change.label }),
514
+ /* @__PURE__ */ jsx7("span", { "aria-hidden": true, className: "shrink-0 text-muted-foreground", children: "\u2192" }),
515
+ /* @__PURE__ */ jsx7(
516
+ "span",
517
+ {
518
+ className: `min-w-0 truncate ${change.clears ? "italic text-muted-foreground" : "text-foreground"}`,
519
+ children: change.value
520
+ }
521
+ )
522
+ ]
523
+ },
524
+ change.key
525
+ )) }) : null
526
+ ] });
527
+ }
392
528
  function SideEffectDirectiveCard({ directive }) {
393
529
  const host = useDirectiveHost();
394
530
  const [expanded, setExpanded] = useState2(false);