@ai-matrx/content-ir-react 0.15.2 → 0.16.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.
@@ -1,4 +1,5 @@
1
- export { D as DirectiveApplyResult, a as DirectiveApplyState, b as DirectiveAskRequest, c as DirectiveCopyProps, d as DirectiveHost, e as DirectiveHostProvider, f as DirectiveHostProviderProps, g as DirectiveOpenItemOptions, h as DirectiveRecordProps, i as DirectiveRecordRef, j as DirectiveScope, k as DirectiveScopeProvider, l as DirectiveShell, u as useDirectiveHost, m as useDirectiveScope } from './directive-host-DJfF35FD.js';
1
+ import { D as DirectiveRecordRef } from './directive-host-C4TcPpxX.js';
2
+ export { a as DirectiveApplyResult, b as DirectiveApplyState, c as DirectiveAskRequest, d as DirectiveCopyProps, e as DirectiveHost, f as DirectiveHostProvider, g as DirectiveHostProviderProps, h as DirectiveOpenItemOptions, i as DirectiveRecordProps, j as DirectiveScope, k as DirectiveScopeProvider, l as DirectiveShell, u as useDirectiveHost, m as useDirectiveScope } from './directive-host-C4TcPpxX.js';
2
3
  import * as react from 'react';
3
4
  import { ComponentType } from 'react';
4
5
  import { DecodedDirective, DirectiveClass } from '@ai-matrx/content-ir';
@@ -48,9 +49,84 @@ interface ApplyDirectiveButtonProps {
48
49
  }
49
50
  declare function ApplyDirectiveButton({ directive, label, itemCount, onApplied, }: ApplyDirectiveButtonProps): react.JSX.Element | null;
50
51
 
52
+ /**
53
+ * WHAT A DIRECTIVE ITEM WILL DO TO AN EXISTING RECORD — for the card, the
54
+ * confirm dialog, and anything else that must say it before the click.
55
+ *
56
+ * WHY (reviewer, 2026-09-30): an Update card and a Delete card showed each item
57
+ * as "Item" — they never named the record they would change, and an Update card
58
+ * never said what it would change. The item-summary helpers name a thing to be
59
+ * CREATED from its own fields; an update or delete item carries the TARGET's id
60
+ * and, for an update, the NEW values — so its "title" field is the new title,
61
+ * not the name of the record being changed. Naming the target is the host's
62
+ * live lookup (`DirectiveHost.renderRecord`); what changes is right here.
63
+ *
64
+ * Law: common-docs/policies/destructive-and-expensive-actions.md — the sentence
65
+ * names what changes.
66
+ */
67
+ /** Classes whose items point at an EXISTING record by `id`. */
68
+ declare const TARGETING_CLASSES: ReadonlySet<string>;
69
+ /** How long a changed value may read before it is shortened (full value on hover). */
70
+ declare const CHANGE_VALUE_MAX = 60;
71
+ /** The id of the existing record an update/delete item targets, or null. */
72
+ declare function itemRecordId(item: Record<string, unknown>): string | null;
73
+ /** "due_date" → "Due date". Underscores read as developer output. */
74
+ declare function fieldLabel(key: string): string;
75
+ /** A short, honest name for a record we could not look up: "Task 1a2b3c4d". */
76
+ declare function recordFallbackName(nounLabel: string, id: string): string;
77
+ /** One value as a row reads it: shortened, the whole of it, and whether it is empty. */
78
+ interface ChangeValue {
79
+ value: string;
80
+ full: string;
81
+ clears: boolean;
82
+ }
83
+ interface ItemChange extends ChangeValue {
84
+ key: string;
85
+ /** "Due date". */
86
+ label: string;
87
+ /**
88
+ * What the field holds NOW, when the host could read the record — so an
89
+ * update reads "old → new", never just the new value (reviewer, 2026-10-02).
90
+ * Absent when the current record is unknown.
91
+ */
92
+ before?: ChangeValue;
93
+ /** True when the record already holds exactly this value — nothing changes. */
94
+ same?: boolean;
95
+ }
96
+ /** One value, described for a change row (`value` shortened, `full` whole). */
97
+ declare function describeChangeValue(value: unknown): ChangeValue;
98
+ /**
99
+ * Every field an UPDATE item sets, in the order the item carries them — ALL of
100
+ * them, never a sample: a person approving an overwrite sees the whole of it.
101
+ *
102
+ * Pass `current` — the record as it is now (`DirectiveHost.readRecord`) — and
103
+ * each change also carries what it REPLACES (`before`) and whether it changes
104
+ * anything at all (`same`). A field the record does not carry gets no `before`.
105
+ */
106
+ interface ItemChangesOptions {
107
+ /**
108
+ * The noun's title column (catalog `title_column` — `nounTitleColumn`). It is
109
+ * labelled "Title" whatever it is called: a note's title is stored in
110
+ * `label`, and the confirm said "Label" while the form said "Title"
111
+ * (reviewer, 2026-10-02). The same rule the directive builder's form uses.
112
+ */
113
+ titleColumn?: string | null;
114
+ }
115
+ declare function itemChanges(item: Record<string, unknown>, current?: Record<string, unknown> | null, options?: ItemChangesOptions): ItemChange[];
116
+
51
117
  interface SideEffectDirectiveCardProps {
52
118
  directive: DecodedDirective;
53
119
  }
120
+ /**
121
+ * THE CHANGE LIST — every field an update sets, as "Label old → new" when the
122
+ * record's current value is known, "Label → new" when it is not. ONE component
123
+ * for the card row and the host's confirm dialog, so the two can never read
124
+ * differently (reviewer, 2026-10-02: the confirm showed only the new value).
125
+ */
126
+ declare function DirectiveChangeList({ changes, className, }: {
127
+ changes: ItemChange[];
128
+ className?: string;
129
+ }): react.JSX.Element;
54
130
  declare function SideEffectDirectiveCard({ directive }: SideEffectDirectiveCardProps): react.JSX.Element;
55
131
 
56
132
  interface DirectiveFallbackCardProps {
@@ -94,45 +170,13 @@ interface DirectiveRenderProps {
94
170
  }
95
171
  declare function DirectiveRender({ conversationId, ...props }: DirectiveRenderProps): react.JSX.Element;
96
172
 
97
- /**
98
- * WHAT A DIRECTIVE ITEM WILL DO TO AN EXISTING RECORD — for the card, the
99
- * confirm dialog, and anything else that must say it before the click.
100
- *
101
- * WHY (reviewer, 2026-09-30): an Update card and a Delete card showed each item
102
- * as "Item" — they never named the record they would change, and an Update card
103
- * never said what it would change. The item-summary helpers name a thing to be
104
- * CREATED from its own fields; an update or delete item carries the TARGET's id
105
- * and, for an update, the NEW values — so its "title" field is the new title,
106
- * not the name of the record being changed. Naming the target is the host's
107
- * live lookup (`DirectiveHost.renderRecord`); what changes is right here.
108
- *
109
- * Law: common-docs/policies/destructive-and-expensive-actions.md — the sentence
110
- * names what changes.
111
- */
112
- /** Classes whose items point at an EXISTING record by `id`. */
113
- declare const TARGETING_CLASSES: ReadonlySet<string>;
114
- /** How long a changed value may read before it is shortened (full value on hover). */
115
- declare const CHANGE_VALUE_MAX = 60;
116
- /** The id of the existing record an update/delete item targets, or null. */
117
- declare function itemRecordId(item: Record<string, unknown>): string | null;
118
- /** "due_date" → "Due date". Underscores read as developer output. */
119
- declare function fieldLabel(key: string): string;
120
- /** A short, honest name for a record we could not look up: "Task 1a2b3c4d". */
121
- declare function recordFallbackName(nounLabel: string, id: string): string;
122
- interface ItemChange {
123
- key: string;
124
- /** "Due date". */
125
- label: string;
126
- /** What it becomes, shortened for a row; `full` carries the whole value. */
127
- value: string;
128
- full: string;
129
- /** True when the new value empties the field. */
130
- clears: boolean;
173
+ type RecordValuesStatus = "idle" | "loading" | "ready" | "unavailable";
174
+ interface RecordValues {
175
+ /** The record's fields now, or null until (or unless) they are read. */
176
+ values: Record<string, unknown> | null;
177
+ status: RecordValuesStatus;
131
178
  }
132
- /**
133
- * Every field an UPDATE item sets, in the order the item carries them — ALL of
134
- * them, never a sample: a person approving an overwrite sees the whole of it.
135
- */
136
- declare function itemChanges(item: Record<string, unknown>): ItemChange[];
179
+ type RecordReader = (ref: DirectiveRecordRef) => Promise<Record<string, unknown> | null>;
180
+ declare function useRecordValues(read: RecordReader | undefined, ref: DirectiveRecordRef | null): RecordValues;
137
181
 
138
- export { ApplyDirectiveButton, type ApplyDirectiveButtonProps, CHANGE_VALUE_MAX, DirectiveFallbackCard, type DirectiveFallbackCardProps, DirectiveProvisionalCard, type DirectiveProvisionalCardProps, DirectiveRender, type DirectiveRenderProps, type DirectiveRenderer, type DirectiveRendererProps, type ItemChange, SideEffectDirectiveCard, type SideEffectDirectiveCardProps, TARGETING_CLASSES, fieldLabel, getDirectiveRenderer, itemChanges, itemRecordId, provisionalDirectiveSlug, recordFallbackName, registerDirectiveRenderer, resetDirectiveRenderers };
182
+ export { ApplyDirectiveButton, type ApplyDirectiveButtonProps, CHANGE_VALUE_MAX, type ChangeValue, DirectiveChangeList, DirectiveFallbackCard, type DirectiveFallbackCardProps, DirectiveProvisionalCard, type DirectiveProvisionalCardProps, DirectiveRecordRef, DirectiveRender, type DirectiveRenderProps, type DirectiveRenderer, type DirectiveRendererProps, type ItemChange, type ItemChangesOptions, type RecordReader, type RecordValues, type RecordValuesStatus, SideEffectDirectiveCard, type SideEffectDirectiveCardProps, TARGETING_CLASSES, describeChangeValue, fieldLabel, getDirectiveRenderer, itemChanges, itemRecordId, provisionalDirectiveSlug, recordFallbackName, registerDirectiveRenderer, resetDirectiveRenderers, useRecordValues };
@@ -66,6 +66,9 @@ function recordFallbackName(nounLabel3, id) {
66
66
  const short = isUuidShape(id) ? id.slice(0, 8) : id.length > 12 ? `${id.slice(0, 12)}\u2026` : id;
67
67
  return `${nounLabel3} ${short}`;
68
68
  }
69
+ function describeChangeValue(value) {
70
+ return describeValue(value);
71
+ }
69
72
  function describeValue(value) {
70
73
  if (value === null || value === void 0) return { value: "cleared", full: "cleared", clears: true };
71
74
  if (typeof value === "string") {
@@ -94,11 +97,28 @@ function safeJson(value) {
94
97
  return String(value);
95
98
  }
96
99
  }
97
- function itemChanges(item) {
100
+ function sameValue(a, b) {
101
+ if (a === b) return true;
102
+ if (a === null || a === void 0 || b === null || b === void 0) {
103
+ const emptyA = a === null || a === void 0 || a === "";
104
+ const emptyB = b === null || b === void 0 || b === "";
105
+ return emptyA && emptyB;
106
+ }
107
+ if (typeof a === "object" || typeof b === "object") return safeJson(a) === safeJson(b);
108
+ return false;
109
+ }
110
+ function itemChanges(item, current, options = {}) {
98
111
  const out = [];
112
+ const titleColumn = options.titleColumn ?? null;
99
113
  for (const [key, raw] of Object.entries(item)) {
100
114
  if (NOT_A_CHANGE.has(key)) continue;
101
- out.push({ key, label: fieldLabel(key), ...describeValue(raw) });
115
+ const label = key === titleColumn ? "Title" : fieldLabel(key);
116
+ const change = { key, label, ...describeValue(raw) };
117
+ if (current && Object.prototype.hasOwnProperty.call(current, key)) {
118
+ change.before = describeValue(current[key]);
119
+ change.same = sameValue(current[key], raw);
120
+ }
121
+ out.push(change);
102
122
  }
103
123
  return out;
104
124
  }
@@ -211,12 +231,23 @@ function ApplyDirectiveButton({
211
231
  const conversationId = scope.conversationId || void 0;
212
232
  const items = directive.items;
213
233
  const readsLedger = !!host.applyState && !!host.confirm && directive.parsed.executes && items.length > 0;
214
- const [state, setState] = useState(
215
- readsLedger ? { status: "checking" } : { status: "idle" }
216
- );
234
+ const initial = readsLedger ? { status: "checking" } : { status: "idle" };
235
+ const [state, setState] = useState(initial);
217
236
  const [elapsed, setElapsed] = useState(0);
218
237
  const [askingAgain, setAskingAgain] = useState(false);
219
238
  const startedAt = useRef(null);
239
+ const identity = JSON.stringify([directive.slug, items, conversationId ?? null]);
240
+ const [stateFor, setStateFor] = useState(identity);
241
+ if (stateFor !== identity) {
242
+ setStateFor(identity);
243
+ setState(initial);
244
+ setAskingAgain(false);
245
+ setElapsed(0);
246
+ }
247
+ const currentIdentity = useRef(identity);
248
+ useEffect(() => {
249
+ currentIdentity.current = identity;
250
+ }, [identity]);
220
251
  const nounText = nounLabel(directive.noun, host.nouns);
221
252
  const shellKey = readsLedger ? JSON.stringify([directive.slug, items, conversationId ?? null]) : "";
222
253
  const read = host.applyState;
@@ -239,7 +270,8 @@ function ApplyDirectiveButton({
239
270
  applied: shellItems.length,
240
271
  failed: 0,
241
272
  message: answer.message?.trim() ? answer.message.trim() : null,
242
- records: answer.records ?? []
273
+ records: answer.records ?? [],
274
+ copies: Math.max(1, answer.copies ?? 1)
243
275
  } : prev
244
276
  );
245
277
  } else if (answer.state === "in_flight" && polls < FINISH_POLLS_MAX) {
@@ -279,6 +311,9 @@ function ApplyDirectiveButton({
279
311
  async function handleApply(again = false) {
280
312
  if (!confirm) return;
281
313
  const before = state;
314
+ const askedFor = identity;
315
+ const stillHere = () => currentIdentity.current === askedFor;
316
+ const priorCopies = state.status === "applied" ? state.copies : 0;
282
317
  if (host.ask) {
283
318
  if (again) setAskingAgain(true);
284
319
  else setState({ status: "asking" });
@@ -291,6 +326,7 @@ function ApplyDirectiveButton({
291
326
  ...again ? { again: true } : {}
292
327
  });
293
328
  } catch (error) {
329
+ if (!stillHere()) return;
294
330
  setAskingAgain(false);
295
331
  setState({
296
332
  status: "error",
@@ -298,6 +334,7 @@ function ApplyDirectiveButton({
298
334
  });
299
335
  return;
300
336
  }
337
+ if (!stillHere()) return;
301
338
  setAskingAgain(false);
302
339
  if (!ok) {
303
340
  setState(again ? before : { status: "idle" });
@@ -311,15 +348,19 @@ function ApplyDirectiveButton({
311
348
  if (conversationId) shell.conversationId = conversationId;
312
349
  if (again) shell.force = true;
313
350
  const result = await confirm(shell);
351
+ if (!stillHere()) return;
314
352
  setState({
315
353
  status: "applied",
316
354
  applied: result.applied,
317
355
  failed: result.failed,
318
356
  message: result.message?.trim() ? result.message.trim() : null,
319
- records: result.records ?? []
357
+ records: result.records ?? [],
358
+ // "Run again" made one more; a first apply (or a replay of one) is one.
359
+ copies: again ? Math.max(1, priorCopies) + 1 : Math.max(1, priorCopies)
320
360
  });
321
361
  onApplied?.();
322
362
  } catch (error) {
363
+ if (!stillHere()) return;
323
364
  setState({
324
365
  status: "error",
325
366
  message: error instanceof Error && error.message ? error.message : "That couldn't be applied just now. Please try again."
@@ -344,6 +385,15 @@ function ApplyDirectiveButton({
344
385
  bad && !state.message ? ` \xB7 ${state.failed} failed` : ""
345
386
  ] })
346
387
  ] }),
388
+ state.copies > 1 ? /* @__PURE__ */ jsx3(
389
+ "span",
390
+ {
391
+ className: "shrink-0 text-muted-foreground",
392
+ "data-apply-copies": state.copies,
393
+ title: records.length > 0 ? directive.directiveClass === "create" ? "Links to the latest copy." : "Links to the latest run." : void 0,
394
+ children: directive.directiveClass === "create" ? `${state.copies} copies` : `Ran ${state.copies} times`
395
+ }
396
+ ) : null,
347
397
  records.map((record) => /* @__PURE__ */ jsx3("span", { className: "inline-flex min-w-0 items-center", children: host.renderRecord?.({
348
398
  ...record,
349
399
  fallback: recordFallbackName(label2, record.id),
@@ -394,6 +444,7 @@ function ApplyDirectiveButton({
394
444
  }
395
445
  );
396
446
  }
447
+ const destructive = directive.directiveClass === "delete";
397
448
  return /* @__PURE__ */ jsxs2("span", { className: "inline-flex items-center gap-2", children: [
398
449
  /* @__PURE__ */ jsxs2(
399
450
  "button",
@@ -401,7 +452,8 @@ function ApplyDirectiveButton({
401
452
  type: "button",
402
453
  onClick: () => void handleApply(),
403
454
  disabled: state.status === "applying" || state.status === "asking" || items.length === 0,
404
- 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",
455
+ "data-apply-tone": destructive ? "destructive" : "default",
456
+ className: `inline-flex items-center gap-1 rounded-md border px-2 py-1 text-xs font-medium disabled:opacity-50 ${destructive ? "border-destructive/50 bg-background text-destructive hover:bg-destructive/10" : "border-border bg-background hover:bg-accent"}`,
405
457
  children: [
406
458
  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" }),
407
459
  state.status === "applying" ? `Applying${itemCount ? ` ${itemCount} ${itemCount === 1 ? "item" : "items"}` : ""}\u2026${elapsed ? ` ${elapsed}s` : ""}` : label
@@ -416,7 +468,7 @@ function ApplyDirectiveButton({
416
468
  }
417
469
 
418
470
  // directives/SideEffectDirectiveCard.tsx
419
- import { useState as useState2 } from "react";
471
+ import { useState as useState3 } from "react";
420
472
  import {
421
473
  asKindInstance,
422
474
  directiveDisplay,
@@ -426,6 +478,39 @@ import {
426
478
  nounLabel as nounLabel2,
427
479
  nounTitleColumn
428
480
  } from "@ai-matrx/content-ir";
481
+
482
+ // directives/record-values.ts
483
+ import { useEffect as useEffect2, useState as useState2 } from "react";
484
+ function useRecordValues(read, ref) {
485
+ const key = ref && read ? `${ref.noun}:${ref.id}` : "";
486
+ const [result, setResult] = useState2({
487
+ key: "",
488
+ values: null,
489
+ status: "idle"
490
+ });
491
+ useEffect2(() => {
492
+ if (!key || !read) return;
493
+ const split = key.indexOf(":");
494
+ const target = { noun: key.slice(0, split), id: key.slice(split + 1) };
495
+ let live = true;
496
+ setResult({ key, values: null, status: "loading" });
497
+ read(target).then((values) => {
498
+ if (!live) return;
499
+ setResult({ key, values, status: values ? "ready" : "unavailable" });
500
+ }).catch(() => {
501
+ if (!live) return;
502
+ setResult({ key, values: null, status: "unavailable" });
503
+ });
504
+ return () => {
505
+ live = false;
506
+ };
507
+ }, [key, read]);
508
+ if (!key) return { values: null, status: "idle" };
509
+ if (result.key !== key) return { values: null, status: "loading" };
510
+ return { values: result.values, status: result.status };
511
+ }
512
+
513
+ // directives/SideEffectDirectiveCard.tsx
429
514
  import { Fragment, jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
430
515
  var ROWS_BEFORE_FOLD = 3;
431
516
  function ItemRow({
@@ -479,44 +564,73 @@ function ItemRow({
479
564
  host.renderCopy?.({ label: title, value: item, kind, size: "xs" })
480
565
  ] });
481
566
  }
482
- function TargetRow({
483
- directive,
484
- item,
485
- recordId
567
+ function DirectiveChangeList({
568
+ changes,
569
+ className
486
570
  }) {
487
- const host = useDirectiveHost();
488
- const fallback = recordFallbackName(nounLabel2(directive.noun, host.nouns), recordId);
489
- const changes = directive.directiveClass === "update" ? itemChanges(item) : [];
490
- const name = host.renderRecord ? host.renderRecord({ noun: directive.noun, id: recordId, fallback, context: "row" }) : /* @__PURE__ */ jsx4("span", { className: "truncate font-medium text-foreground", children: fallback });
491
- return /* @__PURE__ */ jsxs3("div", { className: "py-1 text-xs", "data-directive-target": recordId, children: [
492
- /* @__PURE__ */ jsxs3("div", { className: "flex min-w-0 items-center gap-2", children: [
493
- /* @__PURE__ */ jsx4("span", { className: "flex min-w-0 flex-1 items-center gap-1 px-1", children: name }),
494
- host.renderCopy?.({ label: fallback, value: item, size: "xs" })
495
- ] }),
496
- directive.directiveClass === "update" ? changes.length === 0 ? /* @__PURE__ */ jsx4("p", { className: "px-1 text-[11px] text-muted-foreground", children: "Sets no fields \u2014 nothing would change." }) : /* @__PURE__ */ jsx4("ul", { className: "mt-0.5 space-y-0.5 px-1", children: changes.map((change) => /* @__PURE__ */ jsxs3(
571
+ if (changes.length === 0) {
572
+ return /* @__PURE__ */ jsx4("p", { className: `text-[11px] text-muted-foreground${className ? ` ${className}` : ""}`, children: "Sets no fields \u2014 nothing would change." });
573
+ }
574
+ return /* @__PURE__ */ jsx4("ul", { className: `space-y-0.5${className ? ` ${className}` : ""}`, "data-directive-changes": "", children: changes.map((change) => {
575
+ const before = change.before && !change.same ? change.before : null;
576
+ const was = before ? before.clears ? "empty" : before.value : null;
577
+ return /* @__PURE__ */ jsxs3(
497
578
  "li",
498
579
  {
499
580
  className: "flex min-w-0 items-baseline gap-1.5 text-[11px]",
500
- title: `${change.label} \u2192 ${change.full}`,
581
+ title: before ? `${change.label}: ${before.clears ? "empty" : before.full} \u2192 ${change.full}` : `${change.label} \u2192 ${change.full}`,
582
+ "data-change-key": change.key,
501
583
  children: [
502
584
  /* @__PURE__ */ jsx4("span", { className: "shrink-0 text-muted-foreground", children: change.label }),
585
+ was !== null ? /* @__PURE__ */ jsx4(
586
+ "span",
587
+ {
588
+ className: "min-w-0 max-w-[40%] shrink truncate text-muted-foreground line-through",
589
+ "data-change-before": "",
590
+ children: was
591
+ }
592
+ ) : null,
503
593
  /* @__PURE__ */ jsx4("span", { "aria-hidden": true, className: "shrink-0 text-muted-foreground", children: "\u2192" }),
504
594
  /* @__PURE__ */ jsx4(
505
595
  "span",
506
596
  {
507
597
  className: `min-w-0 truncate ${change.clears ? "italic text-muted-foreground" : "text-foreground"}`,
598
+ "data-change-after": "",
508
599
  children: change.value
509
600
  }
510
- )
601
+ ),
602
+ change.same ? /* @__PURE__ */ jsx4("span", { className: "shrink-0 text-muted-foreground", children: "(no change)" }) : null
511
603
  ]
512
604
  },
513
605
  change.key
514
- )) }) : null
606
+ );
607
+ }) });
608
+ }
609
+ function TargetRow({
610
+ directive,
611
+ item,
612
+ recordId
613
+ }) {
614
+ const host = useDirectiveHost();
615
+ const fallback = recordFallbackName(nounLabel2(directive.noun, host.nouns), recordId);
616
+ const isUpdate = directive.directiveClass === "update";
617
+ const current = useRecordValues(
618
+ isUpdate ? host.readRecord : void 0,
619
+ isUpdate ? { noun: directive.noun, id: recordId } : null
620
+ );
621
+ const changes = isUpdate ? itemChanges(item, current.values, { titleColumn: nounTitleColumn(directive.noun, host.nouns) }) : [];
622
+ const name = host.renderRecord ? host.renderRecord({ noun: directive.noun, id: recordId, fallback, context: "row" }) : /* @__PURE__ */ jsx4("span", { className: "truncate font-medium text-foreground", children: fallback });
623
+ return /* @__PURE__ */ jsxs3("div", { className: "py-1 text-xs", "data-directive-target": recordId, children: [
624
+ /* @__PURE__ */ jsxs3("div", { className: "flex min-w-0 items-center gap-2", children: [
625
+ /* @__PURE__ */ jsx4("span", { className: "flex min-w-0 flex-1 items-center gap-1 px-1", children: name }),
626
+ host.renderCopy?.({ label: fallback, value: item, size: "xs" })
627
+ ] }),
628
+ isUpdate ? /* @__PURE__ */ jsx4(DirectiveChangeList, { changes, className: "mt-0.5 px-1" }) : null
515
629
  ] });
516
630
  }
517
631
  function SideEffectDirectiveCard({ directive }) {
518
632
  const host = useDirectiveHost();
519
- const [expanded, setExpanded] = useState2(false);
633
+ const [expanded, setExpanded] = useState3(false);
520
634
  const items = directive.items;
521
635
  const total = items.length;
522
636
  const display = directiveDisplay(directive.directiveClass, directive.noun, host.nouns);
@@ -668,6 +782,7 @@ function DirectiveRenderInner({ content, className, streaming }) {
668
782
  export {
669
783
  ApplyDirectiveButton,
670
784
  CHANGE_VALUE_MAX,
785
+ DirectiveChangeList,
671
786
  DirectiveFallbackCard,
672
787
  DirectiveHostProvider,
673
788
  DirectiveProvisionalCard,
@@ -675,6 +790,7 @@ export {
675
790
  DirectiveScopeProvider,
676
791
  SideEffectDirectiveCard,
677
792
  TARGETING_CLASSES,
793
+ describeChangeValue,
678
794
  fieldLabel,
679
795
  getDirectiveRenderer,
680
796
  itemChanges,
@@ -684,6 +800,7 @@ export {
684
800
  registerDirectiveRenderer,
685
801
  resetDirectiveRenderers,
686
802
  useDirectiveHost,
687
- useDirectiveScope
803
+ useDirectiveScope,
804
+ useRecordValues
688
805
  };
689
806
  //# sourceMappingURL=directives.js.map