@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.js CHANGED
@@ -94,7 +94,7 @@ import { looksLikeDirectiveHead, tryDecodeDirectiveContent } from "@ai-matrx/con
94
94
  import { directiveDisplay } from "@ai-matrx/content-ir";
95
95
 
96
96
  // directives/ApplyDirectiveButton.tsx
97
- import { useEffect, useRef, useState, useSyncExternalStore as useSyncExternalStore2 } from "react";
97
+ import { useEffect as useEffect2, useRef, useState as useState2, useSyncExternalStore as useSyncExternalStore3 } from "react";
98
98
  import { nounLabel } from "@ai-matrx/content-ir";
99
99
 
100
100
  // directives/item-changes.ts
@@ -170,6 +170,73 @@ function itemChanges(item, current, options = {}) {
170
170
  return out;
171
171
  }
172
172
 
173
+ // directives/record-values.ts
174
+ import { useEffect, useState, useSyncExternalStore as useSyncExternalStore2 } from "react";
175
+ var STORE_SLOT = /* @__PURE__ */ Symbol.for("@ai-matrx/content-ir-react/record-revisions");
176
+ function revisionStore() {
177
+ const slots = globalThis;
178
+ let store = slots[STORE_SLOT];
179
+ if (!store) {
180
+ store = { revisions: /* @__PURE__ */ new Map(), listeners: /* @__PURE__ */ new Set() };
181
+ slots[STORE_SLOT] = store;
182
+ }
183
+ return store;
184
+ }
185
+ var { revisions, listeners } = revisionStore();
186
+ function recordRevision(id) {
187
+ return revisions.get(id) ?? 0;
188
+ }
189
+ function noteRecordChanged(id) {
190
+ if (!id) return;
191
+ revisions.set(id, recordRevision(id) + 1);
192
+ for (const listener of listeners) listener();
193
+ }
194
+ function subscribeRevisions(listener) {
195
+ listeners.add(listener);
196
+ return () => {
197
+ listeners.delete(listener);
198
+ };
199
+ }
200
+ function useRecordRevision(id) {
201
+ return useSyncExternalStore2(
202
+ subscribeRevisions,
203
+ () => recordRevision(id),
204
+ () => 0
205
+ );
206
+ }
207
+ function useRecordValues(read, ref) {
208
+ const revision = useRecordRevision(ref?.id ?? "");
209
+ const key = ref && read ? `${ref.noun}:${ref.id}#${revision}` : "";
210
+ const [result, setResult] = useState({
211
+ key: "",
212
+ values: null,
213
+ status: "idle"
214
+ });
215
+ useEffect(() => {
216
+ if (!key || !read) return;
217
+ const split = key.indexOf(":");
218
+ const target = {
219
+ noun: key.slice(0, split),
220
+ id: key.slice(split + 1, key.lastIndexOf("#"))
221
+ };
222
+ let live = true;
223
+ setResult({ key, values: null, status: "loading" });
224
+ read(target).then((values) => {
225
+ if (!live) return;
226
+ setResult({ key, values, status: values ? "ready" : "unavailable" });
227
+ }).catch(() => {
228
+ if (!live) return;
229
+ setResult({ key, values: null, status: "unavailable" });
230
+ });
231
+ return () => {
232
+ live = false;
233
+ };
234
+ }, [key, read]);
235
+ if (!key) return { values: null, status: "idle" };
236
+ if (result.key !== key) return { values: null, status: "loading" };
237
+ return { values: result.values, status: result.status };
238
+ }
239
+
173
240
  // directives/failure-words.ts
174
241
  var PAST = {
175
242
  create: "created",
@@ -289,6 +356,13 @@ function classIcon(directiveClass, noun) {
289
356
 
290
357
  // directives/ApplyDirectiveButton.tsx
291
358
  import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
359
+ function announceChangedRecords(directive, items) {
360
+ if (!TARGETING_CLASSES.has(directive.directiveClass)) return;
361
+ for (const item of items) {
362
+ const id = itemRecordId(item);
363
+ if (id) noteRecordChanged(id);
364
+ }
365
+ }
292
366
  var applyOutcomes = /* @__PURE__ */ new WeakMap();
293
367
  var EMPTY_AUTHORITY = {};
294
368
  var outcomeListeners = /* @__PURE__ */ new Set();
@@ -311,7 +385,7 @@ function subscribeOutcomes(listener) {
311
385
  };
312
386
  }
313
387
  function useApplyOutcome(host, identity) {
314
- return useSyncExternalStore2(
388
+ return useSyncExternalStore3(
315
389
  subscribeOutcomes,
316
390
  () => applyOutcomes.get(host)?.get(identity) ?? null,
317
391
  () => null
@@ -336,12 +410,12 @@ function ApplyDirectiveButton({
336
410
  const items = directive.items;
337
411
  const readsLedger = readsApplyLedger(host, directive);
338
412
  const initial = readsLedger ? { status: "checking" } : { status: "idle" };
339
- const [state, setState] = useState(initial);
340
- const [elapsed, setElapsed] = useState(0);
341
- const [askingAgain, setAskingAgain] = useState(false);
413
+ const [state, setState] = useState2(initial);
414
+ const [elapsed, setElapsed] = useState2(0);
415
+ const [askingAgain, setAskingAgain] = useState2(false);
342
416
  const startedAt = useRef(null);
343
417
  const identity = JSON.stringify([directive.slug, items, conversationId ?? null]);
344
- const [stateFor, setStateFor] = useState(identity);
418
+ const [stateFor, setStateFor] = useState2(identity);
345
419
  if (stateFor !== identity) {
346
420
  setStateFor(identity);
347
421
  setState(initial);
@@ -351,14 +425,14 @@ function ApplyDirectiveButton({
351
425
  const shared = useApplyOutcome(host.confirm ?? EMPTY_AUTHORITY, identity);
352
426
  const view = state.status === "asking" || !shared ? state : shared;
353
427
  const currentIdentity = useRef(identity);
354
- useEffect(() => {
428
+ useEffect2(() => {
355
429
  currentIdentity.current = identity;
356
430
  }, [identity]);
357
431
  const nounText = nounLabel(directive.noun, host.nouns);
358
432
  const shellKey = readsLedger ? JSON.stringify([directive.slug, items, conversationId ?? null]) : "";
359
433
  const read = host.applyState;
360
434
  const report = host.reportError;
361
- useEffect(() => {
435
+ useEffect2(() => {
362
436
  if (!shellKey || !read) return;
363
437
  const [slug, shellItems, inConversation] = JSON.parse(shellKey);
364
438
  const shell = { __kind: slug, items: shellItems };
@@ -406,14 +480,14 @@ function ApplyDirectiveButton({
406
480
  const appliedClean = view.status === "applied" && view.failed === 0 ? view : null;
407
481
  const appliedBefore = appliedClean ? appliedClean.before : null;
408
482
  const phaseKey = view.status === "checking" || view.status === "finishing" ? "pending" : appliedClean ? `applied:${JSON.stringify(appliedBefore)}` : "open";
409
- useEffect(() => {
483
+ useEffect2(() => {
410
484
  if (!onPhase) return;
411
485
  if (phaseKey === "pending") onPhase({ phase: "pending" });
412
486
  else if (phaseKey === "open") onPhase({ phase: "open" });
413
487
  else onPhase({ phase: "applied", before: appliedBefore });
414
488
  }, [phaseKey, onPhase, appliedBefore]);
415
489
  const applyingSince = view.status === "applying" ? view.startedAt : null;
416
- useEffect(() => {
490
+ useEffect2(() => {
417
491
  if (applyingSince === null) return;
418
492
  startedAt.current = applyingSince;
419
493
  const id = setInterval(
@@ -465,7 +539,13 @@ function ApplyDirectiveButton({
465
539
  const shell = { __kind: directive.slug, items };
466
540
  if (conversationId) shell.conversationId = conversationId;
467
541
  if (again) shell.force = true;
468
- const result = await confirm(shell);
542
+ let result;
543
+ try {
544
+ result = await confirm(shell);
545
+ } finally {
546
+ announceChangedRecords(directive, items);
547
+ }
548
+ for (const record of result.records ?? []) noteRecordChanged(record.id);
469
549
  publishOutcome(confirm, askedFor, {
470
550
  status: "applied",
471
551
  applied: result.applied,
@@ -626,7 +706,7 @@ function FailureNotice({
626
706
  words,
627
707
  raw
628
708
  }) {
629
- const [open, setOpen] = useState(false);
709
+ const [open, setOpen] = useState2(false);
630
710
  const detail = raw?.trim() ?? "";
631
711
  return /* @__PURE__ */ jsxs2("span", { className: "flex min-w-0 flex-col gap-0.5", "data-apply-failure": true, children: [
632
712
  /* @__PURE__ */ jsxs2("span", { className: "min-w-0", children: [
@@ -747,39 +827,6 @@ import {
747
827
  nounLabel as nounLabel2,
748
828
  nounTitleColumn
749
829
  } from "@ai-matrx/content-ir";
750
-
751
- // directives/record-values.ts
752
- import { useEffect as useEffect2, useState as useState2 } from "react";
753
- function useRecordValues(read, ref) {
754
- const key = ref && read ? `${ref.noun}:${ref.id}` : "";
755
- const [result, setResult] = useState2({
756
- key: "",
757
- values: null,
758
- status: "idle"
759
- });
760
- useEffect2(() => {
761
- if (!key || !read) return;
762
- const split = key.indexOf(":");
763
- const target = { noun: key.slice(0, split), id: key.slice(split + 1) };
764
- let live = true;
765
- setResult({ key, values: null, status: "loading" });
766
- read(target).then((values) => {
767
- if (!live) return;
768
- setResult({ key, values, status: values ? "ready" : "unavailable" });
769
- }).catch(() => {
770
- if (!live) return;
771
- setResult({ key, values: null, status: "unavailable" });
772
- });
773
- return () => {
774
- live = false;
775
- };
776
- }, [key, read]);
777
- if (!key) return { values: null, status: "idle" };
778
- if (result.key !== key) return { values: null, status: "loading" };
779
- return { values: result.values, status: result.status };
780
- }
781
-
782
- // directives/SideEffectDirectiveCard.tsx
783
830
  import { Fragment, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
784
831
  var ROWS_BEFORE_FOLD = 3;
785
832
  function ItemRow({