@ai-matrx/content-ir-react 0.15.1 → 0.16.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.
- package/CHANGELOG.md +30 -0
- package/dist/{directive-host-DJfF35FD.d.cts → directive-host-C4TcPpxX.d.cts} +19 -2
- package/dist/{directive-host-DJfF35FD.d.ts → directive-host-C4TcPpxX.d.ts} +19 -2
- package/dist/directives.cjs +150 -30
- package/dist/directives.cjs.map +1 -1
- package/dist/directives.d.cts +76 -41
- package/dist/directives.d.ts +76 -41
- package/dist/directives.js +150 -30
- package/dist/directives.js.map +1 -1
- package/dist/{host-types-9NfBeqrG.d.ts → host-types-CEc86R-v.d.ts} +1 -1
- package/dist/{host-types-DVIaaGR_.d.cts → host-types-a0XEr5b3.d.cts} +1 -1
- package/dist/host.cjs.map +1 -1
- package/dist/host.d.cts +3 -3
- package/dist/host.d.ts +3 -3
- package/dist/host.js.map +1 -1
- package/dist/index.cjs +156 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +76 -42
- package/dist/index.d.ts +76 -42
- package/dist/index.js +153 -33
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +149 -35
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js +146 -32
- package/dist/react.js.map +1 -1
- package/dist/{route-DDbgc-ID.d.ts → route-De9c5YgW.d.ts} +19 -2
- package/dist/{route-BCtPhF7W.d.cts → route-YcoyR3Fj.d.cts} +19 -2
- package/dist/route.d.cts +1 -1
- package/dist/route.d.ts +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,35 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.16.0 — 2026-10-02
|
|
4
|
+
|
|
5
|
+
The G3 review of the action cards (nightly clone). Guard: `__tests__/directive-card-g3.test.tsx`
|
|
6
|
+
(7 cases, every one red on 0.15.2).
|
|
7
|
+
|
|
8
|
+
- **An update says old → new.** New host seam `DirectiveHost.readRecord({noun, id})` →
|
|
9
|
+
the record's current fields (or null). `itemChanges(item, current)` now carries `before`
|
|
10
|
+
and `same`; the new `DirectiveChangeList` draws "Label old → new" (old struck through,
|
|
11
|
+
"empty" for a blank field, "(no change)" when it already holds the value). ONE component
|
|
12
|
+
for the card row and a host's confirm dialog. `useRecordValues(read, ref)` reads it.
|
|
13
|
+
- **A Delete card's Apply looks destructive** (`data-apply-tone="destructive"`), not the
|
|
14
|
+
neutral grey of a create's.
|
|
15
|
+
- **Run again shows the copies.** `DirectiveApplyState.copies` (1 + every Run again) and
|
|
16
|
+
`records` = the LATEST copy's. The tally says "2 copies" (create) / "Ran 2 times" and
|
|
17
|
+
links the newest copy; a yes to Run again counts one more.
|
|
18
|
+
- **The card's state belongs to the block, not its slot.** A host that keys blocks by
|
|
19
|
+
position handed one card instance a different directive when a block was inserted
|
|
20
|
+
above it; an applied Update card flipped back to "Apply" until a reload. The Apply
|
|
21
|
+
control now resets and re-reads the ledger whenever the block it draws changes, and an
|
|
22
|
+
apply that finishes after its block moved away never lands on the new one.
|
|
23
|
+
|
|
24
|
+
Consumer action: pass `readRecord` on the directive host for old → new; a host's own
|
|
25
|
+
confirm dialog draws changes through `DirectiveChangeList`; an `applyState` answer passes
|
|
26
|
+
`copies` when its ledger knows it.
|
|
27
|
+
|
|
28
|
+
## 0.15.2 — 2026-10-02
|
|
29
|
+
|
|
30
|
+
- While "Run again" asks, the tally stays on screen (only its own button goes
|
|
31
|
+
quiet) — it was swapped for a disabled Apply. No consumer action.
|
|
32
|
+
|
|
3
33
|
## 0.15.1 — 2026-10-02
|
|
4
34
|
|
|
5
35
|
- The applied tally keeps its glyph beside its sentence: a long server
|
|
@@ -32,8 +32,17 @@ interface DirectiveApplyState {
|
|
|
32
32
|
state: "not_applied" | "in_flight" | "applied";
|
|
33
33
|
/** The ledger row's sentence ("Created task."), never composed client-side. */
|
|
34
34
|
message?: string | null;
|
|
35
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* The records the LATEST apply wrote — the tally's doors. After "Run again"
|
|
37
|
+
* that is the newest copy, never the first.
|
|
38
|
+
*/
|
|
36
39
|
records?: DirectiveRecordRef[];
|
|
40
|
+
/**
|
|
41
|
+
* How many times the ledger holds this block as applied: 1 for the original,
|
|
42
|
+
* one more for every "Run again". Absent → 1. A create that ran twice made
|
|
43
|
+
* two records, and the card says so (reviewer, 2026-10-02).
|
|
44
|
+
*/
|
|
45
|
+
copies?: number;
|
|
37
46
|
}
|
|
38
47
|
/** One record a directive names: the noun it is, and its id. */
|
|
39
48
|
interface DirectiveRecordRef {
|
|
@@ -132,6 +141,14 @@ interface DirectiveHost {
|
|
|
132
141
|
* is idempotent. Absent → the card starts at Apply, as before.
|
|
133
142
|
*/
|
|
134
143
|
applyState?: (shell: DirectiveShell) => Promise<DirectiveApplyState>;
|
|
144
|
+
/**
|
|
145
|
+
* Read the CURRENT values of ONE existing record, so an update says what it
|
|
146
|
+
* replaces — "Status open → done", never just "→ done" (reviewer,
|
|
147
|
+
* 2026-10-02). Resolve `null` when the record cannot be read (no access, no
|
|
148
|
+
* such record, a noun the host cannot read); reject only for a real failure.
|
|
149
|
+
* Absent → an update row shows the new values alone, as before.
|
|
150
|
+
*/
|
|
151
|
+
readRecord?: (ref: DirectiveRecordRef) => Promise<Record<string, unknown> | null>;
|
|
135
152
|
/**
|
|
136
153
|
* Draw ONE existing record by its live name, as a way to open it. Used on
|
|
137
154
|
* update/delete rows (the record that will change), in the host's own ask
|
|
@@ -176,4 +193,4 @@ declare function DirectiveHostProvider({ host, children }: DirectiveHostProvider
|
|
|
176
193
|
*/
|
|
177
194
|
declare function useDirectiveHost(): DirectiveHost;
|
|
178
195
|
|
|
179
|
-
export { type
|
|
196
|
+
export { type DirectiveRecordRef as D, type DirectiveApplyResult as a, type DirectiveApplyState as b, type DirectiveAskRequest as c, type DirectiveCopyProps as d, type DirectiveHost as e, DirectiveHostProvider as f, type DirectiveHostProviderProps as g, type DirectiveOpenItemOptions as h, type DirectiveRecordProps as i, type DirectiveScope as j, DirectiveScopeProvider as k, type DirectiveShell as l, useDirectiveScope as m, useDirectiveHost as u };
|
|
@@ -32,8 +32,17 @@ interface DirectiveApplyState {
|
|
|
32
32
|
state: "not_applied" | "in_flight" | "applied";
|
|
33
33
|
/** The ledger row's sentence ("Created task."), never composed client-side. */
|
|
34
34
|
message?: string | null;
|
|
35
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* The records the LATEST apply wrote — the tally's doors. After "Run again"
|
|
37
|
+
* that is the newest copy, never the first.
|
|
38
|
+
*/
|
|
36
39
|
records?: DirectiveRecordRef[];
|
|
40
|
+
/**
|
|
41
|
+
* How many times the ledger holds this block as applied: 1 for the original,
|
|
42
|
+
* one more for every "Run again". Absent → 1. A create that ran twice made
|
|
43
|
+
* two records, and the card says so (reviewer, 2026-10-02).
|
|
44
|
+
*/
|
|
45
|
+
copies?: number;
|
|
37
46
|
}
|
|
38
47
|
/** One record a directive names: the noun it is, and its id. */
|
|
39
48
|
interface DirectiveRecordRef {
|
|
@@ -132,6 +141,14 @@ interface DirectiveHost {
|
|
|
132
141
|
* is idempotent. Absent → the card starts at Apply, as before.
|
|
133
142
|
*/
|
|
134
143
|
applyState?: (shell: DirectiveShell) => Promise<DirectiveApplyState>;
|
|
144
|
+
/**
|
|
145
|
+
* Read the CURRENT values of ONE existing record, so an update says what it
|
|
146
|
+
* replaces — "Status open → done", never just "→ done" (reviewer,
|
|
147
|
+
* 2026-10-02). Resolve `null` when the record cannot be read (no access, no
|
|
148
|
+
* such record, a noun the host cannot read); reject only for a real failure.
|
|
149
|
+
* Absent → an update row shows the new values alone, as before.
|
|
150
|
+
*/
|
|
151
|
+
readRecord?: (ref: DirectiveRecordRef) => Promise<Record<string, unknown> | null>;
|
|
135
152
|
/**
|
|
136
153
|
* Draw ONE existing record by its live name, as a way to open it. Used on
|
|
137
154
|
* update/delete rows (the record that will change), in the host's own ask
|
|
@@ -176,4 +193,4 @@ declare function DirectiveHostProvider({ host, children }: DirectiveHostProvider
|
|
|
176
193
|
*/
|
|
177
194
|
declare function useDirectiveHost(): DirectiveHost;
|
|
178
195
|
|
|
179
|
-
export { type
|
|
196
|
+
export { type DirectiveRecordRef as D, type DirectiveApplyResult as a, type DirectiveApplyState as b, type DirectiveAskRequest as c, type DirectiveCopyProps as d, type DirectiveHost as e, DirectiveHostProvider as f, type DirectiveHostProviderProps as g, type DirectiveOpenItemOptions as h, type DirectiveRecordProps as i, type DirectiveScope as j, DirectiveScopeProvider as k, type DirectiveShell as l, useDirectiveScope as m, useDirectiveHost as u };
|
package/dist/directives.cjs
CHANGED
|
@@ -23,6 +23,7 @@ var directives_exports = {};
|
|
|
23
23
|
__export(directives_exports, {
|
|
24
24
|
ApplyDirectiveButton: () => ApplyDirectiveButton,
|
|
25
25
|
CHANGE_VALUE_MAX: () => CHANGE_VALUE_MAX,
|
|
26
|
+
DirectiveChangeList: () => DirectiveChangeList,
|
|
26
27
|
DirectiveFallbackCard: () => DirectiveFallbackCard,
|
|
27
28
|
DirectiveHostProvider: () => DirectiveHostProvider,
|
|
28
29
|
DirectiveProvisionalCard: () => DirectiveProvisionalCard,
|
|
@@ -30,6 +31,7 @@ __export(directives_exports, {
|
|
|
30
31
|
DirectiveScopeProvider: () => DirectiveScopeProvider,
|
|
31
32
|
SideEffectDirectiveCard: () => SideEffectDirectiveCard,
|
|
32
33
|
TARGETING_CLASSES: () => TARGETING_CLASSES,
|
|
34
|
+
describeChangeValue: () => describeChangeValue,
|
|
33
35
|
fieldLabel: () => fieldLabel,
|
|
34
36
|
getDirectiveRenderer: () => getDirectiveRenderer,
|
|
35
37
|
itemChanges: () => itemChanges,
|
|
@@ -39,7 +41,8 @@ __export(directives_exports, {
|
|
|
39
41
|
registerDirectiveRenderer: () => registerDirectiveRenderer,
|
|
40
42
|
resetDirectiveRenderers: () => resetDirectiveRenderers,
|
|
41
43
|
useDirectiveHost: () => useDirectiveHost,
|
|
42
|
-
useDirectiveScope: () => useDirectiveScope
|
|
44
|
+
useDirectiveScope: () => useDirectiveScope,
|
|
45
|
+
useRecordValues: () => useRecordValues
|
|
43
46
|
});
|
|
44
47
|
module.exports = __toCommonJS(directives_exports);
|
|
45
48
|
|
|
@@ -107,6 +110,9 @@ function recordFallbackName(nounLabel3, id) {
|
|
|
107
110
|
const short = (0, import_uuid.isUuidShape)(id) ? id.slice(0, 8) : id.length > 12 ? `${id.slice(0, 12)}\u2026` : id;
|
|
108
111
|
return `${nounLabel3} ${short}`;
|
|
109
112
|
}
|
|
113
|
+
function describeChangeValue(value) {
|
|
114
|
+
return describeValue(value);
|
|
115
|
+
}
|
|
110
116
|
function describeValue(value) {
|
|
111
117
|
if (value === null || value === void 0) return { value: "cleared", full: "cleared", clears: true };
|
|
112
118
|
if (typeof value === "string") {
|
|
@@ -135,11 +141,26 @@ function safeJson(value) {
|
|
|
135
141
|
return String(value);
|
|
136
142
|
}
|
|
137
143
|
}
|
|
138
|
-
function
|
|
144
|
+
function sameValue(a, b) {
|
|
145
|
+
if (a === b) return true;
|
|
146
|
+
if (a === null || a === void 0 || b === null || b === void 0) {
|
|
147
|
+
const emptyA = a === null || a === void 0 || a === "";
|
|
148
|
+
const emptyB = b === null || b === void 0 || b === "";
|
|
149
|
+
return emptyA && emptyB;
|
|
150
|
+
}
|
|
151
|
+
if (typeof a === "object" || typeof b === "object") return safeJson(a) === safeJson(b);
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
function itemChanges(item, current) {
|
|
139
155
|
const out = [];
|
|
140
156
|
for (const [key, raw] of Object.entries(item)) {
|
|
141
157
|
if (NOT_A_CHANGE.has(key)) continue;
|
|
142
|
-
|
|
158
|
+
const change = { key, label: fieldLabel(key), ...describeValue(raw) };
|
|
159
|
+
if (current && Object.prototype.hasOwnProperty.call(current, key)) {
|
|
160
|
+
change.before = describeValue(current[key]);
|
|
161
|
+
change.same = sameValue(current[key], raw);
|
|
162
|
+
}
|
|
163
|
+
out.push(change);
|
|
143
164
|
}
|
|
144
165
|
return out;
|
|
145
166
|
}
|
|
@@ -252,11 +273,23 @@ function ApplyDirectiveButton({
|
|
|
252
273
|
const conversationId = scope.conversationId || void 0;
|
|
253
274
|
const items = directive.items;
|
|
254
275
|
const readsLedger = !!host.applyState && !!host.confirm && directive.parsed.executes && items.length > 0;
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
);
|
|
276
|
+
const initial = readsLedger ? { status: "checking" } : { status: "idle" };
|
|
277
|
+
const [state, setState] = (0, import_react2.useState)(initial);
|
|
258
278
|
const [elapsed, setElapsed] = (0, import_react2.useState)(0);
|
|
279
|
+
const [askingAgain, setAskingAgain] = (0, import_react2.useState)(false);
|
|
259
280
|
const startedAt = (0, import_react2.useRef)(null);
|
|
281
|
+
const identity = JSON.stringify([directive.slug, items, conversationId ?? null]);
|
|
282
|
+
const [stateFor, setStateFor] = (0, import_react2.useState)(identity);
|
|
283
|
+
if (stateFor !== identity) {
|
|
284
|
+
setStateFor(identity);
|
|
285
|
+
setState(initial);
|
|
286
|
+
setAskingAgain(false);
|
|
287
|
+
setElapsed(0);
|
|
288
|
+
}
|
|
289
|
+
const currentIdentity = (0, import_react2.useRef)(identity);
|
|
290
|
+
(0, import_react2.useEffect)(() => {
|
|
291
|
+
currentIdentity.current = identity;
|
|
292
|
+
}, [identity]);
|
|
260
293
|
const nounText = (0, import_content_ir2.nounLabel)(directive.noun, host.nouns);
|
|
261
294
|
const shellKey = readsLedger ? JSON.stringify([directive.slug, items, conversationId ?? null]) : "";
|
|
262
295
|
const read = host.applyState;
|
|
@@ -279,7 +312,8 @@ function ApplyDirectiveButton({
|
|
|
279
312
|
applied: shellItems.length,
|
|
280
313
|
failed: 0,
|
|
281
314
|
message: answer.message?.trim() ? answer.message.trim() : null,
|
|
282
|
-
records: answer.records ?? []
|
|
315
|
+
records: answer.records ?? [],
|
|
316
|
+
copies: Math.max(1, answer.copies ?? 1)
|
|
283
317
|
} : prev
|
|
284
318
|
);
|
|
285
319
|
} else if (answer.state === "in_flight" && polls < FINISH_POLLS_MAX) {
|
|
@@ -319,8 +353,12 @@ function ApplyDirectiveButton({
|
|
|
319
353
|
async function handleApply(again = false) {
|
|
320
354
|
if (!confirm) return;
|
|
321
355
|
const before = state;
|
|
356
|
+
const askedFor = identity;
|
|
357
|
+
const stillHere = () => currentIdentity.current === askedFor;
|
|
358
|
+
const priorCopies = state.status === "applied" ? state.copies : 0;
|
|
322
359
|
if (host.ask) {
|
|
323
|
-
|
|
360
|
+
if (again) setAskingAgain(true);
|
|
361
|
+
else setState({ status: "asking" });
|
|
324
362
|
let ok = false;
|
|
325
363
|
try {
|
|
326
364
|
ok = await host.ask({
|
|
@@ -330,12 +368,16 @@ function ApplyDirectiveButton({
|
|
|
330
368
|
...again ? { again: true } : {}
|
|
331
369
|
});
|
|
332
370
|
} catch (error) {
|
|
371
|
+
if (!stillHere()) return;
|
|
372
|
+
setAskingAgain(false);
|
|
333
373
|
setState({
|
|
334
374
|
status: "error",
|
|
335
375
|
message: error instanceof Error && error.message ? error.message : "We could not ask you to confirm this, so nothing was done."
|
|
336
376
|
});
|
|
337
377
|
return;
|
|
338
378
|
}
|
|
379
|
+
if (!stillHere()) return;
|
|
380
|
+
setAskingAgain(false);
|
|
339
381
|
if (!ok) {
|
|
340
382
|
setState(again ? before : { status: "idle" });
|
|
341
383
|
return;
|
|
@@ -348,15 +390,19 @@ function ApplyDirectiveButton({
|
|
|
348
390
|
if (conversationId) shell.conversationId = conversationId;
|
|
349
391
|
if (again) shell.force = true;
|
|
350
392
|
const result = await confirm(shell);
|
|
393
|
+
if (!stillHere()) return;
|
|
351
394
|
setState({
|
|
352
395
|
status: "applied",
|
|
353
396
|
applied: result.applied,
|
|
354
397
|
failed: result.failed,
|
|
355
398
|
message: result.message?.trim() ? result.message.trim() : null,
|
|
356
|
-
records: result.records ?? []
|
|
399
|
+
records: result.records ?? [],
|
|
400
|
+
// "Run again" made one more; a first apply (or a replay of one) is one.
|
|
401
|
+
copies: again ? Math.max(1, priorCopies) + 1 : Math.max(1, priorCopies)
|
|
357
402
|
});
|
|
358
403
|
onApplied?.();
|
|
359
404
|
} catch (error) {
|
|
405
|
+
if (!stillHere()) return;
|
|
360
406
|
setState({
|
|
361
407
|
status: "error",
|
|
362
408
|
message: error instanceof Error && error.message ? error.message : "That couldn't be applied just now. Please try again."
|
|
@@ -381,6 +427,15 @@ function ApplyDirectiveButton({
|
|
|
381
427
|
bad && !state.message ? ` \xB7 ${state.failed} failed` : ""
|
|
382
428
|
] })
|
|
383
429
|
] }),
|
|
430
|
+
state.copies > 1 ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
431
|
+
"span",
|
|
432
|
+
{
|
|
433
|
+
className: "shrink-0 text-muted-foreground",
|
|
434
|
+
"data-apply-copies": state.copies,
|
|
435
|
+
title: records.length > 0 ? directive.directiveClass === "create" ? "Links to the latest copy." : "Links to the latest run." : void 0,
|
|
436
|
+
children: directive.directiveClass === "create" ? `${state.copies} copies` : `Ran ${state.copies} times`
|
|
437
|
+
}
|
|
438
|
+
) : null,
|
|
384
439
|
records.map((record) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "inline-flex min-w-0 items-center", children: host.renderRecord?.({
|
|
385
440
|
...record,
|
|
386
441
|
fallback: recordFallbackName(label2, record.id),
|
|
@@ -391,8 +446,9 @@ function ApplyDirectiveButton({
|
|
|
391
446
|
"button",
|
|
392
447
|
{
|
|
393
448
|
type: "button",
|
|
449
|
+
disabled: askingAgain,
|
|
394
450
|
onClick: () => void handleApply(!bad),
|
|
395
|
-
className: "inline-flex items-center gap-1 rounded px-1 py-0.5 text-muted-foreground hover:bg-accent hover:text-accent-foreground",
|
|
451
|
+
className: "inline-flex items-center gap-1 rounded px-1 py-0.5 text-muted-foreground hover:bg-accent hover:text-accent-foreground disabled:opacity-50",
|
|
396
452
|
"data-apply-action": bad ? "retry" : "run-again",
|
|
397
453
|
children: [
|
|
398
454
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(RotateCcwIcon, { className: "h-3 w-3" }),
|
|
@@ -430,6 +486,7 @@ function ApplyDirectiveButton({
|
|
|
430
486
|
}
|
|
431
487
|
);
|
|
432
488
|
}
|
|
489
|
+
const destructive = directive.directiveClass === "delete";
|
|
433
490
|
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: "inline-flex items-center gap-2", children: [
|
|
434
491
|
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
435
492
|
"button",
|
|
@@ -437,7 +494,8 @@ function ApplyDirectiveButton({
|
|
|
437
494
|
type: "button",
|
|
438
495
|
onClick: () => void handleApply(),
|
|
439
496
|
disabled: state.status === "applying" || state.status === "asking" || items.length === 0,
|
|
440
|
-
|
|
497
|
+
"data-apply-tone": destructive ? "destructive" : "default",
|
|
498
|
+
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"}`,
|
|
441
499
|
children: [
|
|
442
500
|
state.status === "applying" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(LoaderIcon, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PlayIcon, { className: "h-3.5 w-3.5" }),
|
|
443
501
|
state.status === "applying" ? `Applying${itemCount ? ` ${itemCount} ${itemCount === 1 ? "item" : "items"}` : ""}\u2026${elapsed ? ` ${elapsed}s` : ""}` : label
|
|
@@ -452,8 +510,41 @@ function ApplyDirectiveButton({
|
|
|
452
510
|
}
|
|
453
511
|
|
|
454
512
|
// directives/SideEffectDirectiveCard.tsx
|
|
455
|
-
var
|
|
513
|
+
var import_react4 = require("react");
|
|
456
514
|
var import_content_ir3 = require("@ai-matrx/content-ir");
|
|
515
|
+
|
|
516
|
+
// directives/record-values.ts
|
|
517
|
+
var import_react3 = require("react");
|
|
518
|
+
function useRecordValues(read, ref) {
|
|
519
|
+
const key = ref && read ? `${ref.noun}:${ref.id}` : "";
|
|
520
|
+
const [result, setResult] = (0, import_react3.useState)({
|
|
521
|
+
key: "",
|
|
522
|
+
values: null,
|
|
523
|
+
status: "idle"
|
|
524
|
+
});
|
|
525
|
+
(0, import_react3.useEffect)(() => {
|
|
526
|
+
if (!key || !read) return;
|
|
527
|
+
const split = key.indexOf(":");
|
|
528
|
+
const target = { noun: key.slice(0, split), id: key.slice(split + 1) };
|
|
529
|
+
let live = true;
|
|
530
|
+
setResult({ key, values: null, status: "loading" });
|
|
531
|
+
read(target).then((values) => {
|
|
532
|
+
if (!live) return;
|
|
533
|
+
setResult({ key, values, status: values ? "ready" : "unavailable" });
|
|
534
|
+
}).catch(() => {
|
|
535
|
+
if (!live) return;
|
|
536
|
+
setResult({ key, values: null, status: "unavailable" });
|
|
537
|
+
});
|
|
538
|
+
return () => {
|
|
539
|
+
live = false;
|
|
540
|
+
};
|
|
541
|
+
}, [key, read]);
|
|
542
|
+
if (!key) return { values: null, status: "idle" };
|
|
543
|
+
if (result.key !== key) return { values: null, status: "loading" };
|
|
544
|
+
return { values: result.values, status: result.status };
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// directives/SideEffectDirectiveCard.tsx
|
|
457
548
|
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
458
549
|
var ROWS_BEFORE_FOLD = 3;
|
|
459
550
|
function ItemRow({
|
|
@@ -507,44 +598,73 @@ function ItemRow({
|
|
|
507
598
|
host.renderCopy?.({ label: title, value: item, kind, size: "xs" })
|
|
508
599
|
] });
|
|
509
600
|
}
|
|
510
|
-
function
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
recordId
|
|
601
|
+
function DirectiveChangeList({
|
|
602
|
+
changes,
|
|
603
|
+
className
|
|
514
604
|
}) {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
host.renderCopy?.({ label: fallback, value: item, size: "xs" })
|
|
523
|
-
] }),
|
|
524
|
-
directive.directiveClass === "update" ? changes.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "px-1 text-[11px] text-muted-foreground", children: "Sets no fields \u2014 nothing would change." }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: "mt-0.5 space-y-0.5 px-1", children: changes.map((change) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
605
|
+
if (changes.length === 0) {
|
|
606
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: `text-[11px] text-muted-foreground${className ? ` ${className}` : ""}`, children: "Sets no fields \u2014 nothing would change." });
|
|
607
|
+
}
|
|
608
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("ul", { className: `space-y-0.5${className ? ` ${className}` : ""}`, "data-directive-changes": "", children: changes.map((change) => {
|
|
609
|
+
const before = change.before && !change.same ? change.before : null;
|
|
610
|
+
const was = before ? before.clears ? "empty" : before.value : null;
|
|
611
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
|
|
525
612
|
"li",
|
|
526
613
|
{
|
|
527
614
|
className: "flex min-w-0 items-baseline gap-1.5 text-[11px]",
|
|
528
|
-
title: `${change.label} \u2192 ${change.full}`,
|
|
615
|
+
title: before ? `${change.label}: ${before.clears ? "empty" : before.full} \u2192 ${change.full}` : `${change.label} \u2192 ${change.full}`,
|
|
616
|
+
"data-change-key": change.key,
|
|
529
617
|
children: [
|
|
530
618
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "shrink-0 text-muted-foreground", children: change.label }),
|
|
619
|
+
was !== null ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
620
|
+
"span",
|
|
621
|
+
{
|
|
622
|
+
className: "min-w-0 max-w-[40%] shrink truncate text-muted-foreground line-through",
|
|
623
|
+
"data-change-before": "",
|
|
624
|
+
children: was
|
|
625
|
+
}
|
|
626
|
+
) : null,
|
|
531
627
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { "aria-hidden": true, className: "shrink-0 text-muted-foreground", children: "\u2192" }),
|
|
532
628
|
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
|
|
533
629
|
"span",
|
|
534
630
|
{
|
|
535
631
|
className: `min-w-0 truncate ${change.clears ? "italic text-muted-foreground" : "text-foreground"}`,
|
|
632
|
+
"data-change-after": "",
|
|
536
633
|
children: change.value
|
|
537
634
|
}
|
|
538
|
-
)
|
|
635
|
+
),
|
|
636
|
+
change.same ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "shrink-0 text-muted-foreground", children: "(no change)" }) : null
|
|
539
637
|
]
|
|
540
638
|
},
|
|
541
639
|
change.key
|
|
542
|
-
)
|
|
640
|
+
);
|
|
641
|
+
}) });
|
|
642
|
+
}
|
|
643
|
+
function TargetRow({
|
|
644
|
+
directive,
|
|
645
|
+
item,
|
|
646
|
+
recordId
|
|
647
|
+
}) {
|
|
648
|
+
const host = useDirectiveHost();
|
|
649
|
+
const fallback = recordFallbackName((0, import_content_ir3.nounLabel)(directive.noun, host.nouns), recordId);
|
|
650
|
+
const isUpdate = directive.directiveClass === "update";
|
|
651
|
+
const current = useRecordValues(
|
|
652
|
+
isUpdate ? host.readRecord : void 0,
|
|
653
|
+
isUpdate ? { noun: directive.noun, id: recordId } : null
|
|
654
|
+
);
|
|
655
|
+
const changes = isUpdate ? itemChanges(item, current.values) : [];
|
|
656
|
+
const name = host.renderRecord ? host.renderRecord({ noun: directive.noun, id: recordId, fallback, context: "row" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "truncate font-medium text-foreground", children: fallback });
|
|
657
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "py-1 text-xs", "data-directive-target": recordId, children: [
|
|
658
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex min-w-0 items-center gap-2", children: [
|
|
659
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "flex min-w-0 flex-1 items-center gap-1 px-1", children: name }),
|
|
660
|
+
host.renderCopy?.({ label: fallback, value: item, size: "xs" })
|
|
661
|
+
] }),
|
|
662
|
+
isUpdate ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DirectiveChangeList, { changes, className: "mt-0.5 px-1" }) : null
|
|
543
663
|
] });
|
|
544
664
|
}
|
|
545
665
|
function SideEffectDirectiveCard({ directive }) {
|
|
546
666
|
const host = useDirectiveHost();
|
|
547
|
-
const [expanded, setExpanded] = (0,
|
|
667
|
+
const [expanded, setExpanded] = (0, import_react4.useState)(false);
|
|
548
668
|
const items = directive.items;
|
|
549
669
|
const total = items.length;
|
|
550
670
|
const display = (0, import_content_ir3.directiveDisplay)(directive.directiveClass, directive.noun, host.nouns);
|