@ai-matrx/content-ir-react 0.18.0 → 0.18.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
@@ -1031,6 +1031,34 @@ function classIcon(directiveClass, noun) {
1031
1031
 
1032
1032
  // directives/ApplyDirectiveButton.tsx
1033
1033
  var import_jsx_runtime4 = require("react/jsx-runtime");
1034
+ var applyOutcomes = /* @__PURE__ */ new WeakMap();
1035
+ var EMPTY_AUTHORITY = {};
1036
+ var outcomeListeners = /* @__PURE__ */ new Set();
1037
+ function outcomesOf(host) {
1038
+ let held = applyOutcomes.get(host);
1039
+ if (!held) {
1040
+ held = /* @__PURE__ */ new Map();
1041
+ applyOutcomes.set(host, held);
1042
+ }
1043
+ return held;
1044
+ }
1045
+ function publishOutcome(host, identity, outcome) {
1046
+ outcomesOf(host).set(identity, outcome);
1047
+ for (const listener of outcomeListeners) listener();
1048
+ }
1049
+ function subscribeOutcomes(listener) {
1050
+ outcomeListeners.add(listener);
1051
+ return () => {
1052
+ outcomeListeners.delete(listener);
1053
+ };
1054
+ }
1055
+ function useApplyOutcome(host, identity) {
1056
+ return (0, import_react4.useSyncExternalStore)(
1057
+ subscribeOutcomes,
1058
+ () => applyOutcomes.get(host)?.get(identity) ?? null,
1059
+ () => null
1060
+ );
1061
+ }
1034
1062
  var FINISH_POLL_MS = 2e3;
1035
1063
  var FINISH_POLLS_MAX = 30;
1036
1064
  function readsApplyLedger(host, directive) {
@@ -1062,6 +1090,8 @@ function ApplyDirectiveButton({
1062
1090
  setAskingAgain(false);
1063
1091
  setElapsed(0);
1064
1092
  }
1093
+ const shared = useApplyOutcome(host.confirm ?? EMPTY_AUTHORITY, identity);
1094
+ const view = state.status === "asking" || !shared ? state : shared;
1065
1095
  const currentIdentity = (0, import_react4.useRef)(identity);
1066
1096
  (0, import_react4.useEffect)(() => {
1067
1097
  currentIdentity.current = identity;
@@ -1115,33 +1145,34 @@ function ApplyDirectiveButton({
1115
1145
  if (timer) clearTimeout(timer);
1116
1146
  };
1117
1147
  }, [shellKey, read, report]);
1118
- const appliedClean = state.status === "applied" && state.failed === 0 ? state : null;
1148
+ const appliedClean = view.status === "applied" && view.failed === 0 ? view : null;
1119
1149
  const appliedBefore = appliedClean ? appliedClean.before : null;
1120
- const phaseKey = state.status === "checking" || state.status === "finishing" ? "pending" : appliedClean ? `applied:${JSON.stringify(appliedBefore)}` : "open";
1150
+ const phaseKey = view.status === "checking" || view.status === "finishing" ? "pending" : appliedClean ? `applied:${JSON.stringify(appliedBefore)}` : "open";
1121
1151
  (0, import_react4.useEffect)(() => {
1122
1152
  if (!onPhase) return;
1123
1153
  if (phaseKey === "pending") onPhase({ phase: "pending" });
1124
1154
  else if (phaseKey === "open") onPhase({ phase: "open" });
1125
1155
  else onPhase({ phase: "applied", before: appliedBefore });
1126
1156
  }, [phaseKey, onPhase, appliedBefore]);
1157
+ const applyingSince = view.status === "applying" ? view.startedAt : null;
1127
1158
  (0, import_react4.useEffect)(() => {
1128
- if (state.status !== "applying") return;
1129
- startedAt.current = Date.now();
1159
+ if (applyingSince === null) return;
1160
+ startedAt.current = applyingSince;
1130
1161
  const id = setInterval(
1131
1162
  () => setElapsed(Math.round((Date.now() - (startedAt.current ?? 0)) / 1e3)),
1132
1163
  1e3
1133
1164
  );
1134
1165
  return () => clearInterval(id);
1135
- }, [state.status]);
1166
+ }, [applyingSince]);
1136
1167
  if (!directive.parsed.executes) return null;
1137
1168
  const confirm = host.confirm;
1138
1169
  if (!confirm) return null;
1139
1170
  async function handleApply(again = false) {
1140
1171
  if (!confirm) return;
1141
- const prior = state;
1172
+ const prior = view;
1142
1173
  const askedFor = identity;
1143
1174
  const stillHere = () => currentIdentity.current === askedFor;
1144
- const priorCopies = state.status === "applied" ? state.copies : 0;
1175
+ const priorCopies = view.status === "applied" ? view.copies : 0;
1145
1176
  if (host.ask) {
1146
1177
  if (again) setAskingAgain(true);
1147
1178
  else setState({ status: "asking" });
@@ -1170,14 +1201,14 @@ function ApplyDirectiveButton({
1170
1201
  }
1171
1202
  }
1172
1203
  setElapsed(0);
1173
- setState({ status: "applying" });
1204
+ setState((prev) => prev.status === "asking" ? { status: "idle" } : prev);
1205
+ publishOutcome(confirm, askedFor, { status: "applying", startedAt: Date.now() });
1174
1206
  try {
1175
1207
  const shell = { __kind: directive.slug, items };
1176
1208
  if (conversationId) shell.conversationId = conversationId;
1177
1209
  if (again) shell.force = true;
1178
1210
  const result = await confirm(shell);
1179
- if (!stillHere()) return;
1180
- setState({
1211
+ publishOutcome(confirm, askedFor, {
1181
1212
  status: "applied",
1182
1213
  applied: result.applied,
1183
1214
  failed: result.failed,
@@ -1189,20 +1220,19 @@ function ApplyDirectiveButton({
1189
1220
  // apply replaced is still the change this block made.
1190
1221
  before: again && prior.status === "applied" && prior.before ? prior.before : result.before ?? null
1191
1222
  });
1192
- onApplied?.();
1223
+ if (stillHere()) onApplied?.();
1193
1224
  } catch (error) {
1194
- if (!stillHere()) return;
1195
- setState({
1225
+ publishOutcome(confirm, askedFor, {
1196
1226
  status: "error",
1197
1227
  message: error instanceof Error && error.message ? error.message : "That couldn't be applied just now. Please try again."
1198
1228
  });
1199
1229
  }
1200
1230
  }
1201
- if (state.status === "applied") {
1202
- const bad = state.failed > 0;
1231
+ if (view.status === "applied") {
1232
+ const bad = view.failed > 0;
1203
1233
  const trashed = directive.directiveClass === "delete";
1204
1234
  const label2 = nounText;
1205
- const records = host.renderRecord ? state.records : [];
1235
+ const records = host.renderRecord ? view.records : [];
1206
1236
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1207
1237
  "span",
1208
1238
  {
@@ -1216,26 +1246,26 @@ function ApplyDirectiveButton({
1216
1246
  {
1217
1247
  words: failureWords(
1218
1248
  {
1219
- raw: state.message ?? `${state.failed} failed`,
1249
+ raw: view.message ?? `${view.failed} failed`,
1220
1250
  directiveClass: directive.directiveClass,
1221
1251
  noun: directive.noun,
1222
1252
  nounLabel: nounText,
1223
- failed: state.failed,
1224
- total: state.applied + state.failed
1253
+ failed: view.failed,
1254
+ total: view.applied + view.failed
1225
1255
  },
1226
1256
  host.explainFailure
1227
1257
  ),
1228
- raw: state.message
1258
+ raw: view.message
1229
1259
  }
1230
- ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "min-w-0", children: state.message ?? `Applied ${state.applied}` })
1260
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "min-w-0", children: view.message ?? `Applied ${view.applied}` })
1231
1261
  ] }),
1232
- state.copies > 1 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1262
+ view.copies > 1 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1233
1263
  "span",
1234
1264
  {
1235
1265
  className: "shrink-0 text-muted-foreground",
1236
- "data-apply-copies": state.copies,
1266
+ "data-apply-copies": view.copies,
1237
1267
  title: records.length > 0 ? directive.directiveClass === "create" ? "Links to the latest copy." : "Links to the latest run." : void 0,
1238
- children: directive.directiveClass === "create" ? `${state.copies} copies` : `Ran ${state.copies} times`
1268
+ children: directive.directiveClass === "create" ? `${view.copies} copies` : `Ran ${view.copies} times`
1239
1269
  }
1240
1270
  ) : null,
1241
1271
  records.map((record) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "inline-flex min-w-0 items-center", children: host.renderRecord?.({
@@ -1262,7 +1292,7 @@ function ApplyDirectiveButton({
1262
1292
  }
1263
1293
  );
1264
1294
  }
1265
- if (state.status === "checking") {
1295
+ if (view.status === "checking") {
1266
1296
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1267
1297
  "span",
1268
1298
  {
@@ -1274,7 +1304,7 @@ function ApplyDirectiveButton({
1274
1304
  }
1275
1305
  );
1276
1306
  }
1277
- if (state.status === "finishing") {
1307
+ if (view.status === "finishing") {
1278
1308
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1279
1309
  "span",
1280
1310
  {
@@ -1289,7 +1319,7 @@ function ApplyDirectiveButton({
1289
1319
  );
1290
1320
  }
1291
1321
  const destructive = directive.directiveClass === "delete";
1292
- const muted = nothingToChange && state.status === "idle";
1322
+ const muted = nothingToChange && view.status === "idle";
1293
1323
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "inline-flex items-center gap-2", children: [
1294
1324
  muted ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "text-xs text-muted-foreground", "data-apply-nothing": "", children: "Nothing to change" }) : null,
1295
1325
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
@@ -1297,17 +1327,17 @@ function ApplyDirectiveButton({
1297
1327
  {
1298
1328
  type: "button",
1299
1329
  onClick: () => void handleApply(),
1300
- disabled: state.status === "applying" || state.status === "asking" || items.length === 0,
1330
+ disabled: view.status === "applying" || view.status === "asking" || items.length === 0,
1301
1331
  "data-apply-tone": muted ? "muted" : destructive ? "destructive" : "default",
1302
1332
  title: muted ? "It sets no fields, so applying it changes nothing." : void 0,
1303
1333
  className: `inline-flex items-center gap-1 rounded-md border px-2 py-1 text-xs font-medium disabled:opacity-50 ${muted ? "border-border/60 bg-background text-muted-foreground hover:bg-accent" : destructive ? "border-destructive/50 bg-background text-destructive hover:bg-destructive/10" : "border-border bg-background hover:bg-accent"}`,
1304
1334
  children: [
1305
- state.status === "applying" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(LoaderIcon, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PlayIcon, { className: "h-3.5 w-3.5" }),
1306
- state.status === "applying" ? `Applying${itemCount ? ` ${itemCount} ${itemCount === 1 ? "item" : "items"}` : ""}\u2026${elapsed ? ` ${elapsed}s` : ""}` : label
1335
+ view.status === "applying" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(LoaderIcon, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(PlayIcon, { className: "h-3.5 w-3.5" }),
1336
+ view.status === "applying" ? `Applying${itemCount ? ` ${itemCount} ${itemCount === 1 ? "item" : "items"}` : ""}\u2026${elapsed ? ` ${elapsed}s` : ""}` : label
1307
1337
  ]
1308
1338
  }
1309
1339
  ),
1310
- state.status === "error" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1340
+ view.status === "error" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1311
1341
  "span",
1312
1342
  {
1313
1343
  className: "inline-flex max-w-sm items-start gap-1 text-xs text-destructive",
@@ -1319,14 +1349,14 @@ function ApplyDirectiveButton({
1319
1349
  {
1320
1350
  words: failureWords(
1321
1351
  {
1322
- raw: state.message,
1352
+ raw: view.message,
1323
1353
  directiveClass: directive.directiveClass,
1324
1354
  noun: directive.noun,
1325
1355
  nounLabel: nounText
1326
1356
  },
1327
1357
  host.explainFailure
1328
1358
  ),
1329
- raw: state.message
1359
+ raw: view.message
1330
1360
  }
1331
1361
  )
1332
1362
  ]