@ai-matrx/content-ir-react 0.13.18 → 0.15.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
@@ -54,6 +54,16 @@ var import_react2 = require("react");
54
54
  var import_react = require("react");
55
55
  var import_jsx_runtime = require("react/jsx-runtime");
56
56
  var DirectiveHostContext = (0, import_react.createContext)(null);
57
+ var DirectiveScopeContext = (0, import_react.createContext)({});
58
+ function DirectiveScopeProvider({
59
+ conversationId,
60
+ children
61
+ }) {
62
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DirectiveScopeContext.Provider, { value: { conversationId: conversationId ?? null }, children });
63
+ }
64
+ function useDirectiveScope() {
65
+ return (0, import_react.useContext)(DirectiveScopeContext);
66
+ }
57
67
  var EMPTY_HOST = {};
58
68
  function useDirectiveHost() {
59
69
  return (0, import_react.useContext)(DirectiveHostContext) ?? EMPTY_HOST;
@@ -248,6 +258,10 @@ var MaximizeIcon = (p) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Svg, { .
248
258
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("line", { x1: "3", x2: "10", y1: "21", y2: "14" })
249
259
  ] });
250
260
  var LoaderIcon = (p) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Svg, { ...p, children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) });
261
+ var RotateCcwIcon = (p) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Svg, { ...p, children: [
262
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" }),
263
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "M3 3v5h5" })
264
+ ] });
251
265
  var CheckCircleIcon = (p) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Svg, { ...p, children: [
252
266
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { cx: "12", cy: "12", r: "10" }),
253
267
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("path", { d: "m9 12 2 2 4-4" })
@@ -274,6 +288,8 @@ function classIcon(directiveClass, noun) {
274
288
 
275
289
  // directives/ApplyDirectiveButton.tsx
276
290
  var import_jsx_runtime4 = require("react/jsx-runtime");
291
+ var FINISH_POLL_MS = 2e3;
292
+ var FINISH_POLLS_MAX = 30;
277
293
  function ApplyDirectiveButton({
278
294
  directive,
279
295
  label = "Apply",
@@ -281,9 +297,62 @@ function ApplyDirectiveButton({
281
297
  onApplied
282
298
  }) {
283
299
  const host = useDirectiveHost();
284
- const [state, setState] = (0, import_react4.useState)({ status: "idle" });
300
+ const scope = useDirectiveScope();
301
+ const conversationId = scope.conversationId || void 0;
302
+ const items = directive.items;
303
+ const readsLedger = !!host.applyState && !!host.confirm && directive.parsed.executes && items.length > 0;
304
+ const [state, setState] = (0, import_react4.useState)(
305
+ readsLedger ? { status: "checking" } : { status: "idle" }
306
+ );
285
307
  const [elapsed, setElapsed] = (0, import_react4.useState)(0);
286
308
  const startedAt = (0, import_react4.useRef)(null);
309
+ const nounText = (0, import_content_ir.nounLabel)(directive.noun, host.nouns);
310
+ const shellKey = readsLedger ? JSON.stringify([directive.slug, items, conversationId ?? null]) : "";
311
+ const read = host.applyState;
312
+ const report = host.reportError;
313
+ (0, import_react4.useEffect)(() => {
314
+ if (!shellKey || !read) return;
315
+ const [slug, shellItems, inConversation] = JSON.parse(shellKey);
316
+ const shell = { __kind: slug, items: shellItems };
317
+ if (inConversation) shell.conversationId = inConversation;
318
+ let live = true;
319
+ let polls = 0;
320
+ let timer = null;
321
+ const ask = () => {
322
+ read(shell).then((answer) => {
323
+ if (!live) return;
324
+ if (answer.state === "applied") {
325
+ setState(
326
+ (prev) => prev.status === "checking" || prev.status === "finishing" ? {
327
+ status: "applied",
328
+ applied: shellItems.length,
329
+ failed: 0,
330
+ message: answer.message?.trim() ? answer.message.trim() : null,
331
+ records: answer.records ?? []
332
+ } : prev
333
+ );
334
+ } else if (answer.state === "in_flight" && polls < FINISH_POLLS_MAX) {
335
+ polls += 1;
336
+ setState((prev) => prev.status === "checking" || prev.status === "finishing" ? { status: "finishing" } : prev);
337
+ timer = setTimeout(ask, FINISH_POLL_MS);
338
+ } else {
339
+ setState((prev) => prev.status === "checking" || prev.status === "finishing" ? { status: "idle" } : prev);
340
+ }
341
+ }).catch((error) => {
342
+ if (!live) return;
343
+ const why = error instanceof Error && error.message ? error.message : String(error);
344
+ (report ?? console.error)(
345
+ `[kind-directives] Could not read whether ${slug} was already applied: ${why}`
346
+ );
347
+ setState((prev) => prev.status === "checking" || prev.status === "finishing" ? { status: "idle" } : prev);
348
+ });
349
+ };
350
+ ask();
351
+ return () => {
352
+ live = false;
353
+ if (timer) clearTimeout(timer);
354
+ };
355
+ }, [shellKey, read, report]);
287
356
  (0, import_react4.useEffect)(() => {
288
357
  if (state.status !== "applying") return;
289
358
  startedAt.current = Date.now();
@@ -296,9 +365,9 @@ function ApplyDirectiveButton({
296
365
  if (!directive.parsed.executes) return null;
297
366
  const confirm = host.confirm;
298
367
  if (!confirm) return null;
299
- const items = directive.items;
300
- async function handleApply() {
368
+ async function handleApply(again = false) {
301
369
  if (!confirm) return;
370
+ const before = state;
302
371
  if (host.ask) {
303
372
  setState({ status: "asking" });
304
373
  let ok = false;
@@ -306,7 +375,8 @@ function ApplyDirectiveButton({
306
375
  ok = await host.ask({
307
376
  directive,
308
377
  items,
309
- nounLabel: (0, import_content_ir.nounLabel)(directive.noun, host.nouns)
378
+ nounLabel: nounText,
379
+ ...again ? { again: true } : {}
310
380
  });
311
381
  } catch (error) {
312
382
  setState({
@@ -316,14 +386,17 @@ function ApplyDirectiveButton({
316
386
  return;
317
387
  }
318
388
  if (!ok) {
319
- setState({ status: "idle" });
389
+ setState(again ? before : { status: "idle" });
320
390
  return;
321
391
  }
322
392
  }
323
393
  setElapsed(0);
324
394
  setState({ status: "applying" });
325
395
  try {
326
- const result = await confirm({ __kind: directive.slug, items });
396
+ const shell = { __kind: directive.slug, items };
397
+ if (conversationId) shell.conversationId = conversationId;
398
+ if (again) shell.force = true;
399
+ const result = await confirm(shell);
327
400
  setState({
328
401
  status: "applied",
329
402
  applied: result.applied,
@@ -342,7 +415,7 @@ function ApplyDirectiveButton({
342
415
  if (state.status === "applied") {
343
416
  const bad = state.failed > 0;
344
417
  const trashed = directive.directiveClass === "delete";
345
- const label2 = (0, import_content_ir.nounLabel)(directive.noun, host.nouns);
418
+ const label2 = nounText;
346
419
  const records = host.renderRecord ? state.records : [];
347
420
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
348
421
  "span",
@@ -360,7 +433,46 @@ function ApplyDirectiveButton({
360
433
  fallback: recordFallbackName(label2, record.id),
361
434
  context: "applied",
362
435
  trashed
363
- }) }, `${record.noun}:${record.id}`))
436
+ }) }, `${record.noun}:${record.id}`)),
437
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
438
+ "button",
439
+ {
440
+ type: "button",
441
+ onClick: () => void handleApply(!bad),
442
+ className: "inline-flex items-center gap-1 rounded px-1 py-0.5 text-muted-foreground hover:bg-accent hover:text-accent-foreground",
443
+ "data-apply-action": bad ? "retry" : "run-again",
444
+ children: [
445
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RotateCcwIcon, { className: "h-3 w-3" }),
446
+ bad ? "Retry" : "Run again"
447
+ ]
448
+ }
449
+ )
450
+ ]
451
+ }
452
+ );
453
+ }
454
+ if (state.status === "checking") {
455
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
456
+ "span",
457
+ {
458
+ className: "inline-flex h-6 w-6 items-center justify-center text-muted-foreground",
459
+ role: "status",
460
+ "aria-label": "Checking whether this already ran",
461
+ "data-apply-state": "checking",
462
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(LoaderIcon, { className: "h-3.5 w-3.5 animate-spin" })
463
+ }
464
+ );
465
+ }
466
+ if (state.status === "finishing") {
467
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
468
+ "span",
469
+ {
470
+ className: "inline-flex items-center gap-1 text-xs text-muted-foreground",
471
+ role: "status",
472
+ "data-apply-state": "finishing",
473
+ children: [
474
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(LoaderIcon, { className: "h-3.5 w-3.5 animate-spin" }),
475
+ "Finishing\u2026"
364
476
  ]
365
477
  }
366
478
  );
@@ -608,7 +720,11 @@ function isWholeJson(content) {
608
720
  return false;
609
721
  }
610
722
  }
611
- function DirectiveRender({ content, className, streaming }) {
723
+ function DirectiveRender({ conversationId, ...props }) {
724
+ if (conversationId === void 0) return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DirectiveRenderInner, { ...props });
725
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DirectiveScopeProvider, { conversationId, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(DirectiveRenderInner, { ...props }) });
726
+ }
727
+ function DirectiveRenderInner({ content, className, streaming }) {
612
728
  const host = useDirectiveHost();
613
729
  const directive = (0, import_content_ir6.tryDecodeDirectiveContent)(content, (message) => {
614
730
  const line = `[kind-directives] ${message}`;