@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.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,64 @@ 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 revisions = /* @__PURE__ */ new Map();
216
+ var listeners = /* @__PURE__ */ new Set();
217
+ function recordRevision(id) {
218
+ return revisions.get(id) ?? 0;
219
+ }
220
+ function noteRecordChanged(id) {
221
+ if (!id) return;
222
+ revisions.set(id, recordRevision(id) + 1);
223
+ for (const listener of listeners) listener();
224
+ }
225
+ function subscribeRevisions(listener) {
226
+ listeners.add(listener);
227
+ return () => {
228
+ listeners.delete(listener);
229
+ };
230
+ }
231
+ function useRecordRevision(id) {
232
+ return (0, import_react4.useSyncExternalStore)(
233
+ subscribeRevisions,
234
+ () => recordRevision(id),
235
+ () => 0
236
+ );
237
+ }
238
+ function useRecordValues(read, ref) {
239
+ const revision = useRecordRevision(ref?.id ?? "");
240
+ const key = ref && read ? `${ref.noun}:${ref.id}#${revision}` : "";
241
+ const [result, setResult] = (0, import_react4.useState)({
242
+ key: "",
243
+ values: null,
244
+ status: "idle"
245
+ });
246
+ (0, import_react4.useEffect)(() => {
247
+ if (!key || !read) return;
248
+ const split = key.indexOf(":");
249
+ const target = {
250
+ noun: key.slice(0, split),
251
+ id: key.slice(split + 1, key.lastIndexOf("#"))
252
+ };
253
+ let live = true;
254
+ setResult({ key, values: null, status: "loading" });
255
+ read(target).then((values) => {
256
+ if (!live) return;
257
+ setResult({ key, values, status: values ? "ready" : "unavailable" });
258
+ }).catch(() => {
259
+ if (!live) return;
260
+ setResult({ key, values: null, status: "unavailable" });
261
+ });
262
+ return () => {
263
+ live = false;
264
+ };
265
+ }, [key, read]);
266
+ if (!key) return { values: null, status: "idle" };
267
+ if (result.key !== key) return { values: null, status: "loading" };
268
+ return { values: result.values, status: result.status };
269
+ }
270
+
213
271
  // directives/failure-words.ts
214
272
  var PAST = {
215
273
  create: "created",
@@ -329,6 +387,13 @@ function classIcon(directiveClass, noun) {
329
387
 
330
388
  // directives/ApplyDirectiveButton.tsx
331
389
  var import_jsx_runtime4 = require("react/jsx-runtime");
390
+ function announceChangedRecords(directive, items) {
391
+ if (!TARGETING_CLASSES.has(directive.directiveClass)) return;
392
+ for (const item of items) {
393
+ const id = itemRecordId(item);
394
+ if (id) noteRecordChanged(id);
395
+ }
396
+ }
332
397
  var applyOutcomes = /* @__PURE__ */ new WeakMap();
333
398
  var EMPTY_AUTHORITY = {};
334
399
  var outcomeListeners = /* @__PURE__ */ new Set();
@@ -351,7 +416,7 @@ function subscribeOutcomes(listener) {
351
416
  };
352
417
  }
353
418
  function useApplyOutcome(host, identity) {
354
- return (0, import_react4.useSyncExternalStore)(
419
+ return (0, import_react5.useSyncExternalStore)(
355
420
  subscribeOutcomes,
356
421
  () => applyOutcomes.get(host)?.get(identity) ?? null,
357
422
  () => null
@@ -376,12 +441,12 @@ function ApplyDirectiveButton({
376
441
  const items = directive.items;
377
442
  const readsLedger = readsApplyLedger(host, directive);
378
443
  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);
444
+ const [state, setState] = (0, import_react5.useState)(initial);
445
+ const [elapsed, setElapsed] = (0, import_react5.useState)(0);
446
+ const [askingAgain, setAskingAgain] = (0, import_react5.useState)(false);
447
+ const startedAt = (0, import_react5.useRef)(null);
383
448
  const identity = JSON.stringify([directive.slug, items, conversationId ?? null]);
384
- const [stateFor, setStateFor] = (0, import_react4.useState)(identity);
449
+ const [stateFor, setStateFor] = (0, import_react5.useState)(identity);
385
450
  if (stateFor !== identity) {
386
451
  setStateFor(identity);
387
452
  setState(initial);
@@ -390,15 +455,15 @@ function ApplyDirectiveButton({
390
455
  }
391
456
  const shared = useApplyOutcome(host.confirm ?? EMPTY_AUTHORITY, identity);
392
457
  const view = state.status === "asking" || !shared ? state : shared;
393
- const currentIdentity = (0, import_react4.useRef)(identity);
394
- (0, import_react4.useEffect)(() => {
458
+ const currentIdentity = (0, import_react5.useRef)(identity);
459
+ (0, import_react5.useEffect)(() => {
395
460
  currentIdentity.current = identity;
396
461
  }, [identity]);
397
462
  const nounText = (0, import_content_ir.nounLabel)(directive.noun, host.nouns);
398
463
  const shellKey = readsLedger ? JSON.stringify([directive.slug, items, conversationId ?? null]) : "";
399
464
  const read = host.applyState;
400
465
  const report = host.reportError;
401
- (0, import_react4.useEffect)(() => {
466
+ (0, import_react5.useEffect)(() => {
402
467
  if (!shellKey || !read) return;
403
468
  const [slug, shellItems, inConversation] = JSON.parse(shellKey);
404
469
  const shell = { __kind: slug, items: shellItems };
@@ -446,14 +511,14 @@ function ApplyDirectiveButton({
446
511
  const appliedClean = view.status === "applied" && view.failed === 0 ? view : null;
447
512
  const appliedBefore = appliedClean ? appliedClean.before : null;
448
513
  const phaseKey = view.status === "checking" || view.status === "finishing" ? "pending" : appliedClean ? `applied:${JSON.stringify(appliedBefore)}` : "open";
449
- (0, import_react4.useEffect)(() => {
514
+ (0, import_react5.useEffect)(() => {
450
515
  if (!onPhase) return;
451
516
  if (phaseKey === "pending") onPhase({ phase: "pending" });
452
517
  else if (phaseKey === "open") onPhase({ phase: "open" });
453
518
  else onPhase({ phase: "applied", before: appliedBefore });
454
519
  }, [phaseKey, onPhase, appliedBefore]);
455
520
  const applyingSince = view.status === "applying" ? view.startedAt : null;
456
- (0, import_react4.useEffect)(() => {
521
+ (0, import_react5.useEffect)(() => {
457
522
  if (applyingSince === null) return;
458
523
  startedAt.current = applyingSince;
459
524
  const id = setInterval(
@@ -505,7 +570,13 @@ function ApplyDirectiveButton({
505
570
  const shell = { __kind: directive.slug, items };
506
571
  if (conversationId) shell.conversationId = conversationId;
507
572
  if (again) shell.force = true;
508
- const result = await confirm(shell);
573
+ let result;
574
+ try {
575
+ result = await confirm(shell);
576
+ } finally {
577
+ announceChangedRecords(directive, items);
578
+ }
579
+ for (const record of result.records ?? []) noteRecordChanged(record.id);
509
580
  publishOutcome(confirm, askedFor, {
510
581
  status: "applied",
511
582
  applied: result.applied,
@@ -666,7 +737,7 @@ function FailureNotice({
666
737
  words,
667
738
  raw
668
739
  }) {
669
- const [open, setOpen] = (0, import_react4.useState)(false);
740
+ const [open, setOpen] = (0, import_react5.useState)(false);
670
741
  const detail = raw?.trim() ?? "";
671
742
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "flex min-w-0 flex-col gap-0.5", "data-apply-failure": true, children: [
672
743
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "min-w-0", children: [
@@ -777,39 +848,6 @@ function getDirectiveRenderer(directive) {
777
848
  // directives/SideEffectDirectiveCard.tsx
778
849
  var import_react6 = require("react");
779
850
  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
851
  var import_jsx_runtime7 = require("react/jsx-runtime");
814
852
  var ROWS_BEFORE_FOLD = 3;
815
853
  function ItemRow({