@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/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,64 @@ 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 revisions = /* @__PURE__ */ new Map();
921
+ var listeners = /* @__PURE__ */ new Set();
922
+ function recordRevision(id) {
923
+ return revisions.get(id) ?? 0;
924
+ }
925
+ function noteRecordChanged(id) {
926
+ if (!id) return;
927
+ revisions.set(id, recordRevision(id) + 1);
928
+ for (const listener of listeners) listener();
929
+ }
930
+ function subscribeRevisions(listener) {
931
+ listeners.add(listener);
932
+ return () => {
933
+ listeners.delete(listener);
934
+ };
935
+ }
936
+ function useRecordRevision(id) {
937
+ return (0, import_react4.useSyncExternalStore)(
938
+ subscribeRevisions,
939
+ () => recordRevision(id),
940
+ () => 0
941
+ );
942
+ }
943
+ function useRecordValues(read, ref) {
944
+ const revision = useRecordRevision(ref?.id ?? "");
945
+ const key = ref && read ? `${ref.noun}:${ref.id}#${revision}` : "";
946
+ const [result, setResult] = (0, import_react4.useState)({
947
+ key: "",
948
+ values: null,
949
+ status: "idle"
950
+ });
951
+ (0, import_react4.useEffect)(() => {
952
+ if (!key || !read) return;
953
+ const split = key.indexOf(":");
954
+ const target = {
955
+ noun: key.slice(0, split),
956
+ id: key.slice(split + 1, key.lastIndexOf("#"))
957
+ };
958
+ let live = true;
959
+ setResult({ key, values: null, status: "loading" });
960
+ read(target).then((values) => {
961
+ if (!live) return;
962
+ setResult({ key, values, status: values ? "ready" : "unavailable" });
963
+ }).catch(() => {
964
+ if (!live) return;
965
+ setResult({ key, values: null, status: "unavailable" });
966
+ });
967
+ return () => {
968
+ live = false;
969
+ };
970
+ }, [key, read]);
971
+ if (!key) return { values: null, status: "idle" };
972
+ if (result.key !== key) return { values: null, status: "loading" };
973
+ return { values: result.values, status: result.status };
974
+ }
975
+
915
976
  // directives/failure-words.ts
916
977
  var PAST = {
917
978
  create: "created",
@@ -1031,6 +1092,13 @@ function classIcon(directiveClass, noun) {
1031
1092
 
1032
1093
  // directives/ApplyDirectiveButton.tsx
1033
1094
  var import_jsx_runtime4 = require("react/jsx-runtime");
1095
+ function announceChangedRecords(directive, items) {
1096
+ if (!TARGETING_CLASSES.has(directive.directiveClass)) return;
1097
+ for (const item of items) {
1098
+ const id = itemRecordId(item);
1099
+ if (id) noteRecordChanged(id);
1100
+ }
1101
+ }
1034
1102
  var applyOutcomes = /* @__PURE__ */ new WeakMap();
1035
1103
  var EMPTY_AUTHORITY = {};
1036
1104
  var outcomeListeners = /* @__PURE__ */ new Set();
@@ -1053,7 +1121,7 @@ function subscribeOutcomes(listener) {
1053
1121
  };
1054
1122
  }
1055
1123
  function useApplyOutcome(host, identity) {
1056
- return (0, import_react4.useSyncExternalStore)(
1124
+ return (0, import_react5.useSyncExternalStore)(
1057
1125
  subscribeOutcomes,
1058
1126
  () => applyOutcomes.get(host)?.get(identity) ?? null,
1059
1127
  () => null
@@ -1078,12 +1146,12 @@ function ApplyDirectiveButton({
1078
1146
  const items = directive.items;
1079
1147
  const readsLedger = readsApplyLedger(host, directive);
1080
1148
  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);
1149
+ const [state, setState] = (0, import_react5.useState)(initial);
1150
+ const [elapsed, setElapsed] = (0, import_react5.useState)(0);
1151
+ const [askingAgain, setAskingAgain] = (0, import_react5.useState)(false);
1152
+ const startedAt = (0, import_react5.useRef)(null);
1085
1153
  const identity = JSON.stringify([directive.slug, items, conversationId ?? null]);
1086
- const [stateFor, setStateFor] = (0, import_react4.useState)(identity);
1154
+ const [stateFor, setStateFor] = (0, import_react5.useState)(identity);
1087
1155
  if (stateFor !== identity) {
1088
1156
  setStateFor(identity);
1089
1157
  setState(initial);
@@ -1092,15 +1160,15 @@ function ApplyDirectiveButton({
1092
1160
  }
1093
1161
  const shared = useApplyOutcome(host.confirm ?? EMPTY_AUTHORITY, identity);
1094
1162
  const view = state.status === "asking" || !shared ? state : shared;
1095
- const currentIdentity = (0, import_react4.useRef)(identity);
1096
- (0, import_react4.useEffect)(() => {
1163
+ const currentIdentity = (0, import_react5.useRef)(identity);
1164
+ (0, import_react5.useEffect)(() => {
1097
1165
  currentIdentity.current = identity;
1098
1166
  }, [identity]);
1099
1167
  const nounText = (0, import_content_ir3.nounLabel)(directive.noun, host.nouns);
1100
1168
  const shellKey = readsLedger ? JSON.stringify([directive.slug, items, conversationId ?? null]) : "";
1101
1169
  const read = host.applyState;
1102
1170
  const report = host.reportError;
1103
- (0, import_react4.useEffect)(() => {
1171
+ (0, import_react5.useEffect)(() => {
1104
1172
  if (!shellKey || !read) return;
1105
1173
  const [slug, shellItems, inConversation] = JSON.parse(shellKey);
1106
1174
  const shell = { __kind: slug, items: shellItems };
@@ -1148,14 +1216,14 @@ function ApplyDirectiveButton({
1148
1216
  const appliedClean = view.status === "applied" && view.failed === 0 ? view : null;
1149
1217
  const appliedBefore = appliedClean ? appliedClean.before : null;
1150
1218
  const phaseKey = view.status === "checking" || view.status === "finishing" ? "pending" : appliedClean ? `applied:${JSON.stringify(appliedBefore)}` : "open";
1151
- (0, import_react4.useEffect)(() => {
1219
+ (0, import_react5.useEffect)(() => {
1152
1220
  if (!onPhase) return;
1153
1221
  if (phaseKey === "pending") onPhase({ phase: "pending" });
1154
1222
  else if (phaseKey === "open") onPhase({ phase: "open" });
1155
1223
  else onPhase({ phase: "applied", before: appliedBefore });
1156
1224
  }, [phaseKey, onPhase, appliedBefore]);
1157
1225
  const applyingSince = view.status === "applying" ? view.startedAt : null;
1158
- (0, import_react4.useEffect)(() => {
1226
+ (0, import_react5.useEffect)(() => {
1159
1227
  if (applyingSince === null) return;
1160
1228
  startedAt.current = applyingSince;
1161
1229
  const id = setInterval(
@@ -1207,7 +1275,13 @@ function ApplyDirectiveButton({
1207
1275
  const shell = { __kind: directive.slug, items };
1208
1276
  if (conversationId) shell.conversationId = conversationId;
1209
1277
  if (again) shell.force = true;
1210
- const result = await confirm(shell);
1278
+ let result;
1279
+ try {
1280
+ result = await confirm(shell);
1281
+ } finally {
1282
+ announceChangedRecords(directive, items);
1283
+ }
1284
+ for (const record of result.records ?? []) noteRecordChanged(record.id);
1211
1285
  publishOutcome(confirm, askedFor, {
1212
1286
  status: "applied",
1213
1287
  applied: result.applied,
@@ -1368,7 +1442,7 @@ function FailureNotice({
1368
1442
  words,
1369
1443
  raw
1370
1444
  }) {
1371
- const [open, setOpen] = (0, import_react4.useState)(false);
1445
+ const [open, setOpen] = (0, import_react5.useState)(false);
1372
1446
  const detail = raw?.trim() ?? "";
1373
1447
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "flex min-w-0 flex-col gap-0.5", "data-apply-failure": true, children: [
1374
1448
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "min-w-0", children: [
@@ -1487,39 +1561,6 @@ function resetDirectiveRenderers() {
1487
1561
  // directives/SideEffectDirectiveCard.tsx
1488
1562
  var import_react6 = require("react");
1489
1563
  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
1564
  var import_jsx_runtime7 = require("react/jsx-runtime");
1524
1565
  var ROWS_BEFORE_FOLD = 3;
1525
1566
  function ItemRow({