@ai-matrx/content-ir-react 0.18.1 → 0.19.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/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,64 @@ 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 revisions = /* @__PURE__ */ new Map();
176
+ var listeners = /* @__PURE__ */ new Set();
177
+ function recordRevision(id) {
178
+ return revisions.get(id) ?? 0;
179
+ }
180
+ function noteRecordChanged(id) {
181
+ if (!id) return;
182
+ revisions.set(id, recordRevision(id) + 1);
183
+ for (const listener of listeners) listener();
184
+ }
185
+ function subscribeRevisions(listener) {
186
+ listeners.add(listener);
187
+ return () => {
188
+ listeners.delete(listener);
189
+ };
190
+ }
191
+ function useRecordRevision(id) {
192
+ return useSyncExternalStore2(
193
+ subscribeRevisions,
194
+ () => recordRevision(id),
195
+ () => 0
196
+ );
197
+ }
198
+ function useRecordValues(read, ref) {
199
+ const revision = useRecordRevision(ref?.id ?? "");
200
+ const key = ref && read ? `${ref.noun}:${ref.id}#${revision}` : "";
201
+ const [result, setResult] = useState({
202
+ key: "",
203
+ values: null,
204
+ status: "idle"
205
+ });
206
+ useEffect(() => {
207
+ if (!key || !read) return;
208
+ const split = key.indexOf(":");
209
+ const target = {
210
+ noun: key.slice(0, split),
211
+ id: key.slice(split + 1, key.lastIndexOf("#"))
212
+ };
213
+ let live = true;
214
+ setResult({ key, values: null, status: "loading" });
215
+ read(target).then((values) => {
216
+ if (!live) return;
217
+ setResult({ key, values, status: values ? "ready" : "unavailable" });
218
+ }).catch(() => {
219
+ if (!live) return;
220
+ setResult({ key, values: null, status: "unavailable" });
221
+ });
222
+ return () => {
223
+ live = false;
224
+ };
225
+ }, [key, read]);
226
+ if (!key) return { values: null, status: "idle" };
227
+ if (result.key !== key) return { values: null, status: "loading" };
228
+ return { values: result.values, status: result.status };
229
+ }
230
+
173
231
  // directives/failure-words.ts
174
232
  var PAST = {
175
233
  create: "created",
@@ -289,6 +347,13 @@ function classIcon(directiveClass, noun) {
289
347
 
290
348
  // directives/ApplyDirectiveButton.tsx
291
349
  import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
350
+ function announceChangedRecords(directive, items) {
351
+ if (!TARGETING_CLASSES.has(directive.directiveClass)) return;
352
+ for (const item of items) {
353
+ const id = itemRecordId(item);
354
+ if (id) noteRecordChanged(id);
355
+ }
356
+ }
292
357
  var applyOutcomes = /* @__PURE__ */ new WeakMap();
293
358
  var EMPTY_AUTHORITY = {};
294
359
  var outcomeListeners = /* @__PURE__ */ new Set();
@@ -311,7 +376,7 @@ function subscribeOutcomes(listener) {
311
376
  };
312
377
  }
313
378
  function useApplyOutcome(host, identity) {
314
- return useSyncExternalStore2(
379
+ return useSyncExternalStore3(
315
380
  subscribeOutcomes,
316
381
  () => applyOutcomes.get(host)?.get(identity) ?? null,
317
382
  () => null
@@ -336,12 +401,12 @@ function ApplyDirectiveButton({
336
401
  const items = directive.items;
337
402
  const readsLedger = readsApplyLedger(host, directive);
338
403
  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);
404
+ const [state, setState] = useState2(initial);
405
+ const [elapsed, setElapsed] = useState2(0);
406
+ const [askingAgain, setAskingAgain] = useState2(false);
342
407
  const startedAt = useRef(null);
343
408
  const identity = JSON.stringify([directive.slug, items, conversationId ?? null]);
344
- const [stateFor, setStateFor] = useState(identity);
409
+ const [stateFor, setStateFor] = useState2(identity);
345
410
  if (stateFor !== identity) {
346
411
  setStateFor(identity);
347
412
  setState(initial);
@@ -351,14 +416,14 @@ function ApplyDirectiveButton({
351
416
  const shared = useApplyOutcome(host.confirm ?? EMPTY_AUTHORITY, identity);
352
417
  const view = state.status === "asking" || !shared ? state : shared;
353
418
  const currentIdentity = useRef(identity);
354
- useEffect(() => {
419
+ useEffect2(() => {
355
420
  currentIdentity.current = identity;
356
421
  }, [identity]);
357
422
  const nounText = nounLabel(directive.noun, host.nouns);
358
423
  const shellKey = readsLedger ? JSON.stringify([directive.slug, items, conversationId ?? null]) : "";
359
424
  const read = host.applyState;
360
425
  const report = host.reportError;
361
- useEffect(() => {
426
+ useEffect2(() => {
362
427
  if (!shellKey || !read) return;
363
428
  const [slug, shellItems, inConversation] = JSON.parse(shellKey);
364
429
  const shell = { __kind: slug, items: shellItems };
@@ -406,14 +471,14 @@ function ApplyDirectiveButton({
406
471
  const appliedClean = view.status === "applied" && view.failed === 0 ? view : null;
407
472
  const appliedBefore = appliedClean ? appliedClean.before : null;
408
473
  const phaseKey = view.status === "checking" || view.status === "finishing" ? "pending" : appliedClean ? `applied:${JSON.stringify(appliedBefore)}` : "open";
409
- useEffect(() => {
474
+ useEffect2(() => {
410
475
  if (!onPhase) return;
411
476
  if (phaseKey === "pending") onPhase({ phase: "pending" });
412
477
  else if (phaseKey === "open") onPhase({ phase: "open" });
413
478
  else onPhase({ phase: "applied", before: appliedBefore });
414
479
  }, [phaseKey, onPhase, appliedBefore]);
415
480
  const applyingSince = view.status === "applying" ? view.startedAt : null;
416
- useEffect(() => {
481
+ useEffect2(() => {
417
482
  if (applyingSince === null) return;
418
483
  startedAt.current = applyingSince;
419
484
  const id = setInterval(
@@ -465,7 +530,13 @@ function ApplyDirectiveButton({
465
530
  const shell = { __kind: directive.slug, items };
466
531
  if (conversationId) shell.conversationId = conversationId;
467
532
  if (again) shell.force = true;
468
- const result = await confirm(shell);
533
+ let result;
534
+ try {
535
+ result = await confirm(shell);
536
+ } finally {
537
+ announceChangedRecords(directive, items);
538
+ }
539
+ for (const record of result.records ?? []) noteRecordChanged(record.id);
469
540
  publishOutcome(confirm, askedFor, {
470
541
  status: "applied",
471
542
  applied: result.applied,
@@ -626,7 +697,7 @@ function FailureNotice({
626
697
  words,
627
698
  raw
628
699
  }) {
629
- const [open, setOpen] = useState(false);
700
+ const [open, setOpen] = useState2(false);
630
701
  const detail = raw?.trim() ?? "";
631
702
  return /* @__PURE__ */ jsxs2("span", { className: "flex min-w-0 flex-col gap-0.5", "data-apply-failure": true, children: [
632
703
  /* @__PURE__ */ jsxs2("span", { className: "min-w-0", children: [
@@ -747,39 +818,6 @@ import {
747
818
  nounLabel as nounLabel2,
748
819
  nounTitleColumn
749
820
  } 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
821
  import { Fragment, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
784
822
  var ROWS_BEFORE_FOLD = 3;
785
823
  function ItemRow({