@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/index.cjs CHANGED
@@ -74,10 +74,12 @@ __export(index_exports, {
74
74
  kindIsRoutable: () => kindIsRoutable,
75
75
  kindServerDataFromStoredValue: () => kindServerDataFromStoredValue,
76
76
  markKindPartialUnsafe: () => markKindPartialUnsafe,
77
+ noteRecordChanged: () => noteRecordChanged,
77
78
  provisionalDirectiveSlug: () => provisionalDirectiveSlug,
78
79
  readIrRouteMarker: () => readIrRouteMarker,
79
80
  readsApplyLedger: () => readsApplyLedger,
80
81
  recordFallbackName: () => recordFallbackName,
82
+ recordRevision: () => recordRevision,
81
83
  registerDirectiveRenderer: () => registerDirectiveRenderer,
82
84
  resetDirectiveRenderers: () => resetDirectiveRenderers,
83
85
  resetPartialUnsafeKinds: () => resetPartialUnsafeKinds,
@@ -92,6 +94,7 @@ __export(index_exports, {
92
94
  useDirectiveHost: () => useDirectiveHost,
93
95
  useDirectiveScope: () => useDirectiveScope,
94
96
  useKindRouteEnv: () => useKindRouteEnv,
97
+ useRecordRevision: () => useRecordRevision,
95
98
  useRecordValues: () => useRecordValues
96
99
  });
97
100
  module.exports = __toCommonJS(index_exports);
@@ -366,8 +369,8 @@ var ComponentResolver = class {
366
369
  }
367
370
  bumpKind(kind) {
368
371
  this.kindVersions.set(kind, (this.kindVersions.get(kind) ?? 0) + 1);
369
- const listeners = this.kindListeners.get(kind);
370
- if (listeners) for (const listener of listeners) listener();
372
+ const listeners2 = this.kindListeners.get(kind);
373
+ if (listeners2) for (const listener of listeners2) listener();
371
374
  }
372
375
  notifyChanged() {
373
376
  this.version += 1;
@@ -833,7 +836,7 @@ var import_content_ir8 = require("@ai-matrx/content-ir");
833
836
  var import_content_ir4 = require("@ai-matrx/content-ir");
834
837
 
835
838
  // directives/ApplyDirectiveButton.tsx
836
- var import_react4 = require("react");
839
+ var import_react5 = require("react");
837
840
  var import_content_ir3 = require("@ai-matrx/content-ir");
838
841
 
839
842
  // directives/item-changes.ts
@@ -912,6 +915,73 @@ function itemChanges(item, current, options = {}) {
912
915
  return out;
913
916
  }
914
917
 
918
+ // directives/record-values.ts
919
+ var import_react4 = require("react");
920
+ var STORE_SLOT = /* @__PURE__ */ Symbol.for("@ai-matrx/content-ir-react/record-revisions");
921
+ function revisionStore() {
922
+ const slots = globalThis;
923
+ let store = slots[STORE_SLOT];
924
+ if (!store) {
925
+ store = { revisions: /* @__PURE__ */ new Map(), listeners: /* @__PURE__ */ new Set() };
926
+ slots[STORE_SLOT] = store;
927
+ }
928
+ return store;
929
+ }
930
+ var { revisions, listeners } = revisionStore();
931
+ function recordRevision(id) {
932
+ return revisions.get(id) ?? 0;
933
+ }
934
+ function noteRecordChanged(id) {
935
+ if (!id) return;
936
+ revisions.set(id, recordRevision(id) + 1);
937
+ for (const listener of listeners) listener();
938
+ }
939
+ function subscribeRevisions(listener) {
940
+ listeners.add(listener);
941
+ return () => {
942
+ listeners.delete(listener);
943
+ };
944
+ }
945
+ function useRecordRevision(id) {
946
+ return (0, import_react4.useSyncExternalStore)(
947
+ subscribeRevisions,
948
+ () => recordRevision(id),
949
+ () => 0
950
+ );
951
+ }
952
+ function useRecordValues(read, ref) {
953
+ const revision = useRecordRevision(ref?.id ?? "");
954
+ const key = ref && read ? `${ref.noun}:${ref.id}#${revision}` : "";
955
+ const [result, setResult] = (0, import_react4.useState)({
956
+ key: "",
957
+ values: null,
958
+ status: "idle"
959
+ });
960
+ (0, import_react4.useEffect)(() => {
961
+ if (!key || !read) return;
962
+ const split = key.indexOf(":");
963
+ const target = {
964
+ noun: key.slice(0, split),
965
+ id: key.slice(split + 1, key.lastIndexOf("#"))
966
+ };
967
+ let live = true;
968
+ setResult({ key, values: null, status: "loading" });
969
+ read(target).then((values) => {
970
+ if (!live) return;
971
+ setResult({ key, values, status: values ? "ready" : "unavailable" });
972
+ }).catch(() => {
973
+ if (!live) return;
974
+ setResult({ key, values: null, status: "unavailable" });
975
+ });
976
+ return () => {
977
+ live = false;
978
+ };
979
+ }, [key, read]);
980
+ if (!key) return { values: null, status: "idle" };
981
+ if (result.key !== key) return { values: null, status: "loading" };
982
+ return { values: result.values, status: result.status };
983
+ }
984
+
915
985
  // directives/failure-words.ts
916
986
  var PAST = {
917
987
  create: "created",
@@ -1031,6 +1101,13 @@ function classIcon(directiveClass, noun) {
1031
1101
 
1032
1102
  // directives/ApplyDirectiveButton.tsx
1033
1103
  var import_jsx_runtime4 = require("react/jsx-runtime");
1104
+ function announceChangedRecords(directive, items) {
1105
+ if (!TARGETING_CLASSES.has(directive.directiveClass)) return;
1106
+ for (const item of items) {
1107
+ const id = itemRecordId(item);
1108
+ if (id) noteRecordChanged(id);
1109
+ }
1110
+ }
1034
1111
  var applyOutcomes = /* @__PURE__ */ new WeakMap();
1035
1112
  var EMPTY_AUTHORITY = {};
1036
1113
  var outcomeListeners = /* @__PURE__ */ new Set();
@@ -1053,7 +1130,7 @@ function subscribeOutcomes(listener) {
1053
1130
  };
1054
1131
  }
1055
1132
  function useApplyOutcome(host, identity) {
1056
- return (0, import_react4.useSyncExternalStore)(
1133
+ return (0, import_react5.useSyncExternalStore)(
1057
1134
  subscribeOutcomes,
1058
1135
  () => applyOutcomes.get(host)?.get(identity) ?? null,
1059
1136
  () => null
@@ -1078,12 +1155,12 @@ function ApplyDirectiveButton({
1078
1155
  const items = directive.items;
1079
1156
  const readsLedger = readsApplyLedger(host, directive);
1080
1157
  const initial = readsLedger ? { status: "checking" } : { status: "idle" };
1081
- const [state, setState] = (0, import_react4.useState)(initial);
1082
- const [elapsed, setElapsed] = (0, import_react4.useState)(0);
1083
- const [askingAgain, setAskingAgain] = (0, import_react4.useState)(false);
1084
- const startedAt = (0, import_react4.useRef)(null);
1158
+ const [state, setState] = (0, import_react5.useState)(initial);
1159
+ const [elapsed, setElapsed] = (0, import_react5.useState)(0);
1160
+ const [askingAgain, setAskingAgain] = (0, import_react5.useState)(false);
1161
+ const startedAt = (0, import_react5.useRef)(null);
1085
1162
  const identity = JSON.stringify([directive.slug, items, conversationId ?? null]);
1086
- const [stateFor, setStateFor] = (0, import_react4.useState)(identity);
1163
+ const [stateFor, setStateFor] = (0, import_react5.useState)(identity);
1087
1164
  if (stateFor !== identity) {
1088
1165
  setStateFor(identity);
1089
1166
  setState(initial);
@@ -1092,15 +1169,15 @@ function ApplyDirectiveButton({
1092
1169
  }
1093
1170
  const shared = useApplyOutcome(host.confirm ?? EMPTY_AUTHORITY, identity);
1094
1171
  const view = state.status === "asking" || !shared ? state : shared;
1095
- const currentIdentity = (0, import_react4.useRef)(identity);
1096
- (0, import_react4.useEffect)(() => {
1172
+ const currentIdentity = (0, import_react5.useRef)(identity);
1173
+ (0, import_react5.useEffect)(() => {
1097
1174
  currentIdentity.current = identity;
1098
1175
  }, [identity]);
1099
1176
  const nounText = (0, import_content_ir3.nounLabel)(directive.noun, host.nouns);
1100
1177
  const shellKey = readsLedger ? JSON.stringify([directive.slug, items, conversationId ?? null]) : "";
1101
1178
  const read = host.applyState;
1102
1179
  const report = host.reportError;
1103
- (0, import_react4.useEffect)(() => {
1180
+ (0, import_react5.useEffect)(() => {
1104
1181
  if (!shellKey || !read) return;
1105
1182
  const [slug, shellItems, inConversation] = JSON.parse(shellKey);
1106
1183
  const shell = { __kind: slug, items: shellItems };
@@ -1148,14 +1225,14 @@ function ApplyDirectiveButton({
1148
1225
  const appliedClean = view.status === "applied" && view.failed === 0 ? view : null;
1149
1226
  const appliedBefore = appliedClean ? appliedClean.before : null;
1150
1227
  const phaseKey = view.status === "checking" || view.status === "finishing" ? "pending" : appliedClean ? `applied:${JSON.stringify(appliedBefore)}` : "open";
1151
- (0, import_react4.useEffect)(() => {
1228
+ (0, import_react5.useEffect)(() => {
1152
1229
  if (!onPhase) return;
1153
1230
  if (phaseKey === "pending") onPhase({ phase: "pending" });
1154
1231
  else if (phaseKey === "open") onPhase({ phase: "open" });
1155
1232
  else onPhase({ phase: "applied", before: appliedBefore });
1156
1233
  }, [phaseKey, onPhase, appliedBefore]);
1157
1234
  const applyingSince = view.status === "applying" ? view.startedAt : null;
1158
- (0, import_react4.useEffect)(() => {
1235
+ (0, import_react5.useEffect)(() => {
1159
1236
  if (applyingSince === null) return;
1160
1237
  startedAt.current = applyingSince;
1161
1238
  const id = setInterval(
@@ -1207,7 +1284,13 @@ function ApplyDirectiveButton({
1207
1284
  const shell = { __kind: directive.slug, items };
1208
1285
  if (conversationId) shell.conversationId = conversationId;
1209
1286
  if (again) shell.force = true;
1210
- const result = await confirm(shell);
1287
+ let result;
1288
+ try {
1289
+ result = await confirm(shell);
1290
+ } finally {
1291
+ announceChangedRecords(directive, items);
1292
+ }
1293
+ for (const record of result.records ?? []) noteRecordChanged(record.id);
1211
1294
  publishOutcome(confirm, askedFor, {
1212
1295
  status: "applied",
1213
1296
  applied: result.applied,
@@ -1368,7 +1451,7 @@ function FailureNotice({
1368
1451
  words,
1369
1452
  raw
1370
1453
  }) {
1371
- const [open, setOpen] = (0, import_react4.useState)(false);
1454
+ const [open, setOpen] = (0, import_react5.useState)(false);
1372
1455
  const detail = raw?.trim() ?? "";
1373
1456
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "flex min-w-0 flex-col gap-0.5", "data-apply-failure": true, children: [
1374
1457
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "min-w-0", children: [
@@ -1487,39 +1570,6 @@ function resetDirectiveRenderers() {
1487
1570
  // directives/SideEffectDirectiveCard.tsx
1488
1571
  var import_react6 = require("react");
1489
1572
  var import_content_ir7 = require("@ai-matrx/content-ir");
1490
-
1491
- // directives/record-values.ts
1492
- var import_react5 = require("react");
1493
- function useRecordValues(read, ref) {
1494
- const key = ref && read ? `${ref.noun}:${ref.id}` : "";
1495
- const [result, setResult] = (0, import_react5.useState)({
1496
- key: "",
1497
- values: null,
1498
- status: "idle"
1499
- });
1500
- (0, import_react5.useEffect)(() => {
1501
- if (!key || !read) return;
1502
- const split = key.indexOf(":");
1503
- const target = { noun: key.slice(0, split), id: key.slice(split + 1) };
1504
- let live = true;
1505
- setResult({ key, values: null, status: "loading" });
1506
- read(target).then((values) => {
1507
- if (!live) return;
1508
- setResult({ key, values, status: values ? "ready" : "unavailable" });
1509
- }).catch(() => {
1510
- if (!live) return;
1511
- setResult({ key, values: null, status: "unavailable" });
1512
- });
1513
- return () => {
1514
- live = false;
1515
- };
1516
- }, [key, read]);
1517
- if (!key) return { values: null, status: "idle" };
1518
- if (result.key !== key) return { values: null, status: "loading" };
1519
- return { values: result.values, status: result.status };
1520
- }
1521
-
1522
- // directives/SideEffectDirectiveCard.tsx
1523
1573
  var import_jsx_runtime7 = require("react/jsx-runtime");
1524
1574
  var ROWS_BEFORE_FOLD = 3;
1525
1575
  function ItemRow({