@ai-matrx/content-ir-react 0.18.1 → 0.19.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.cjs CHANGED
@@ -134,7 +134,7 @@ var import_content_ir6 = require("@ai-matrx/content-ir");
134
134
  var import_content_ir2 = require("@ai-matrx/content-ir");
135
135
 
136
136
  // directives/ApplyDirectiveButton.tsx
137
- var import_react4 = require("react");
137
+ var import_react5 = require("react");
138
138
  var import_content_ir = require("@ai-matrx/content-ir");
139
139
 
140
140
  // directives/item-changes.ts
@@ -210,6 +210,73 @@ function itemChanges(item, current, options = {}) {
210
210
  return out;
211
211
  }
212
212
 
213
+ // directives/record-values.ts
214
+ var import_react4 = require("react");
215
+ var STORE_SLOT = /* @__PURE__ */ Symbol.for("@ai-matrx/content-ir-react/record-revisions");
216
+ function revisionStore() {
217
+ const slots = globalThis;
218
+ let store = slots[STORE_SLOT];
219
+ if (!store) {
220
+ store = { revisions: /* @__PURE__ */ new Map(), listeners: /* @__PURE__ */ new Set() };
221
+ slots[STORE_SLOT] = store;
222
+ }
223
+ return store;
224
+ }
225
+ var { revisions, listeners } = revisionStore();
226
+ function recordRevision(id) {
227
+ return revisions.get(id) ?? 0;
228
+ }
229
+ function noteRecordChanged(id) {
230
+ if (!id) return;
231
+ revisions.set(id, recordRevision(id) + 1);
232
+ for (const listener of listeners) listener();
233
+ }
234
+ function subscribeRevisions(listener) {
235
+ listeners.add(listener);
236
+ return () => {
237
+ listeners.delete(listener);
238
+ };
239
+ }
240
+ function useRecordRevision(id) {
241
+ return (0, import_react4.useSyncExternalStore)(
242
+ subscribeRevisions,
243
+ () => recordRevision(id),
244
+ () => 0
245
+ );
246
+ }
247
+ function useRecordValues(read, ref) {
248
+ const revision = useRecordRevision(ref?.id ?? "");
249
+ const key = ref && read ? `${ref.noun}:${ref.id}#${revision}` : "";
250
+ const [result, setResult] = (0, import_react4.useState)({
251
+ key: "",
252
+ values: null,
253
+ status: "idle"
254
+ });
255
+ (0, import_react4.useEffect)(() => {
256
+ if (!key || !read) return;
257
+ const split = key.indexOf(":");
258
+ const target = {
259
+ noun: key.slice(0, split),
260
+ id: key.slice(split + 1, key.lastIndexOf("#"))
261
+ };
262
+ let live = true;
263
+ setResult({ key, values: null, status: "loading" });
264
+ read(target).then((values) => {
265
+ if (!live) return;
266
+ setResult({ key, values, status: values ? "ready" : "unavailable" });
267
+ }).catch(() => {
268
+ if (!live) return;
269
+ setResult({ key, values: null, status: "unavailable" });
270
+ });
271
+ return () => {
272
+ live = false;
273
+ };
274
+ }, [key, read]);
275
+ if (!key) return { values: null, status: "idle" };
276
+ if (result.key !== key) return { values: null, status: "loading" };
277
+ return { values: result.values, status: result.status };
278
+ }
279
+
213
280
  // directives/failure-words.ts
214
281
  var PAST = {
215
282
  create: "created",
@@ -329,6 +396,13 @@ function classIcon(directiveClass, noun) {
329
396
 
330
397
  // directives/ApplyDirectiveButton.tsx
331
398
  var import_jsx_runtime4 = require("react/jsx-runtime");
399
+ function announceChangedRecords(directive, items) {
400
+ if (!TARGETING_CLASSES.has(directive.directiveClass)) return;
401
+ for (const item of items) {
402
+ const id = itemRecordId(item);
403
+ if (id) noteRecordChanged(id);
404
+ }
405
+ }
332
406
  var applyOutcomes = /* @__PURE__ */ new WeakMap();
333
407
  var EMPTY_AUTHORITY = {};
334
408
  var outcomeListeners = /* @__PURE__ */ new Set();
@@ -351,7 +425,7 @@ function subscribeOutcomes(listener) {
351
425
  };
352
426
  }
353
427
  function useApplyOutcome(host, identity) {
354
- return (0, import_react4.useSyncExternalStore)(
428
+ return (0, import_react5.useSyncExternalStore)(
355
429
  subscribeOutcomes,
356
430
  () => applyOutcomes.get(host)?.get(identity) ?? null,
357
431
  () => null
@@ -376,12 +450,12 @@ function ApplyDirectiveButton({
376
450
  const items = directive.items;
377
451
  const readsLedger = readsApplyLedger(host, directive);
378
452
  const initial = readsLedger ? { status: "checking" } : { status: "idle" };
379
- const [state, setState] = (0, import_react4.useState)(initial);
380
- const [elapsed, setElapsed] = (0, import_react4.useState)(0);
381
- const [askingAgain, setAskingAgain] = (0, import_react4.useState)(false);
382
- const startedAt = (0, import_react4.useRef)(null);
453
+ const [state, setState] = (0, import_react5.useState)(initial);
454
+ const [elapsed, setElapsed] = (0, import_react5.useState)(0);
455
+ const [askingAgain, setAskingAgain] = (0, import_react5.useState)(false);
456
+ const startedAt = (0, import_react5.useRef)(null);
383
457
  const identity = JSON.stringify([directive.slug, items, conversationId ?? null]);
384
- const [stateFor, setStateFor] = (0, import_react4.useState)(identity);
458
+ const [stateFor, setStateFor] = (0, import_react5.useState)(identity);
385
459
  if (stateFor !== identity) {
386
460
  setStateFor(identity);
387
461
  setState(initial);
@@ -390,15 +464,15 @@ function ApplyDirectiveButton({
390
464
  }
391
465
  const shared = useApplyOutcome(host.confirm ?? EMPTY_AUTHORITY, identity);
392
466
  const view = state.status === "asking" || !shared ? state : shared;
393
- const currentIdentity = (0, import_react4.useRef)(identity);
394
- (0, import_react4.useEffect)(() => {
467
+ const currentIdentity = (0, import_react5.useRef)(identity);
468
+ (0, import_react5.useEffect)(() => {
395
469
  currentIdentity.current = identity;
396
470
  }, [identity]);
397
471
  const nounText = (0, import_content_ir.nounLabel)(directive.noun, host.nouns);
398
472
  const shellKey = readsLedger ? JSON.stringify([directive.slug, items, conversationId ?? null]) : "";
399
473
  const read = host.applyState;
400
474
  const report = host.reportError;
401
- (0, import_react4.useEffect)(() => {
475
+ (0, import_react5.useEffect)(() => {
402
476
  if (!shellKey || !read) return;
403
477
  const [slug, shellItems, inConversation] = JSON.parse(shellKey);
404
478
  const shell = { __kind: slug, items: shellItems };
@@ -446,14 +520,14 @@ function ApplyDirectiveButton({
446
520
  const appliedClean = view.status === "applied" && view.failed === 0 ? view : null;
447
521
  const appliedBefore = appliedClean ? appliedClean.before : null;
448
522
  const phaseKey = view.status === "checking" || view.status === "finishing" ? "pending" : appliedClean ? `applied:${JSON.stringify(appliedBefore)}` : "open";
449
- (0, import_react4.useEffect)(() => {
523
+ (0, import_react5.useEffect)(() => {
450
524
  if (!onPhase) return;
451
525
  if (phaseKey === "pending") onPhase({ phase: "pending" });
452
526
  else if (phaseKey === "open") onPhase({ phase: "open" });
453
527
  else onPhase({ phase: "applied", before: appliedBefore });
454
528
  }, [phaseKey, onPhase, appliedBefore]);
455
529
  const applyingSince = view.status === "applying" ? view.startedAt : null;
456
- (0, import_react4.useEffect)(() => {
530
+ (0, import_react5.useEffect)(() => {
457
531
  if (applyingSince === null) return;
458
532
  startedAt.current = applyingSince;
459
533
  const id = setInterval(
@@ -505,7 +579,13 @@ function ApplyDirectiveButton({
505
579
  const shell = { __kind: directive.slug, items };
506
580
  if (conversationId) shell.conversationId = conversationId;
507
581
  if (again) shell.force = true;
508
- const result = await confirm(shell);
582
+ let result;
583
+ try {
584
+ result = await confirm(shell);
585
+ } finally {
586
+ announceChangedRecords(directive, items);
587
+ }
588
+ for (const record of result.records ?? []) noteRecordChanged(record.id);
509
589
  publishOutcome(confirm, askedFor, {
510
590
  status: "applied",
511
591
  applied: result.applied,
@@ -666,7 +746,7 @@ function FailureNotice({
666
746
  words,
667
747
  raw
668
748
  }) {
669
- const [open, setOpen] = (0, import_react4.useState)(false);
749
+ const [open, setOpen] = (0, import_react5.useState)(false);
670
750
  const detail = raw?.trim() ?? "";
671
751
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "flex min-w-0 flex-col gap-0.5", "data-apply-failure": true, children: [
672
752
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "min-w-0", children: [
@@ -777,39 +857,6 @@ function getDirectiveRenderer(directive) {
777
857
  // directives/SideEffectDirectiveCard.tsx
778
858
  var import_react6 = require("react");
779
859
  var import_content_ir5 = require("@ai-matrx/content-ir");
780
-
781
- // directives/record-values.ts
782
- var import_react5 = require("react");
783
- function useRecordValues(read, ref) {
784
- const key = ref && read ? `${ref.noun}:${ref.id}` : "";
785
- const [result, setResult] = (0, import_react5.useState)({
786
- key: "",
787
- values: null,
788
- status: "idle"
789
- });
790
- (0, import_react5.useEffect)(() => {
791
- if (!key || !read) return;
792
- const split = key.indexOf(":");
793
- const target = { noun: key.slice(0, split), id: key.slice(split + 1) };
794
- let live = true;
795
- setResult({ key, values: null, status: "loading" });
796
- read(target).then((values) => {
797
- if (!live) return;
798
- setResult({ key, values, status: values ? "ready" : "unavailable" });
799
- }).catch(() => {
800
- if (!live) return;
801
- setResult({ key, values: null, status: "unavailable" });
802
- });
803
- return () => {
804
- live = false;
805
- };
806
- }, [key, read]);
807
- if (!key) return { values: null, status: "idle" };
808
- if (result.key !== key) return { values: null, status: "loading" };
809
- return { values: result.values, status: result.status };
810
- }
811
-
812
- // directives/SideEffectDirectiveCard.tsx
813
860
  var import_jsx_runtime7 = require("react/jsx-runtime");
814
861
  var ROWS_BEFORE_FOLD = 3;
815
862
  function ItemRow({