@ai-matrx/content-ir-react 0.24.4 → 0.25.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.
@@ -179,6 +179,15 @@ interface ItemChangesOptions {
179
179
  valueWord?: DirectiveValueWord | null;
180
180
  }
181
181
  declare function itemChanges(item: Record<string, unknown>, current?: Record<string, unknown> | null, options?: ItemChangesOptions): ItemChange[];
182
+ /**
183
+ * 🚨 AN UPDATE THAT WOULD CHANGE NOTHING SAYS SO (G11B review, 2026-10-07: the
184
+ * confirm listed every field "(no change)", Apply stayed full-weight, and the
185
+ * run ended "Updated task. Ran 2 times"). True when the item sets no field, or
186
+ * when the record was read and EVERY field already holds its new value. A field
187
+ * whose current value is unknown counts as a change — doubt never mutes Apply.
188
+ * ONE rule for the card and the host's confirm.
189
+ */
190
+ declare function changesNothing(changes: ReadonlyArray<ItemChange>): boolean;
182
191
 
183
192
  interface SideEffectDirectiveCardProps {
184
193
  directive: DecodedDirective;
@@ -278,4 +287,4 @@ declare function noteRecordChanged(id: string): void;
278
287
  declare function useRecordRevision(id: string): number;
279
288
  declare function useRecordValues(read: RecordReader | undefined, ref: DirectiveRecordRef | null): RecordValues;
280
289
 
281
- export { ApplyDirectiveButton, type ApplyDirectiveButtonProps, CHANGE_VALUE_MAX, type ChangeValue, type DirectiveApplyPhase, DirectiveChangeList, DirectiveFailure, DirectiveFailureWords, DirectiveFallbackCard, type DirectiveFallbackCardProps, DirectiveHost, DirectiveProvisionalCard, type DirectiveProvisionalCardProps, DirectiveRecordRef, DirectiveRender, type DirectiveRenderProps, type DirectiveRenderer, type DirectiveRendererProps, type DirectiveValueWord, EMPTY_VALUE_WORD, type ItemChange, type ItemChangesOptions, NO_TITLE_WORD, type RecordReader, type RecordValues, type RecordValuesStatus, SideEffectDirectiveCard, type SideEffectDirectiveCardProps, TARGETING_CLASSES, defaultFailureWords, describeChangeValue, directiveFieldLabel, directiveValueWord, failureWords, fieldLabel, getDirectiveRenderer, itemChanges, itemOwnName, itemRecordId, noteRecordChanged, provisionalDirectiveSlug, readsApplyLedger, recordFallbackName, recordRevision, registerDirectiveRenderer, resetDirectiveRenderers, useRecordRevision, useRecordValues };
290
+ export { ApplyDirectiveButton, type ApplyDirectiveButtonProps, CHANGE_VALUE_MAX, type ChangeValue, type DirectiveApplyPhase, DirectiveChangeList, DirectiveFailure, DirectiveFailureWords, DirectiveFallbackCard, type DirectiveFallbackCardProps, DirectiveHost, DirectiveProvisionalCard, type DirectiveProvisionalCardProps, DirectiveRecordRef, DirectiveRender, type DirectiveRenderProps, type DirectiveRenderer, type DirectiveRendererProps, type DirectiveValueWord, EMPTY_VALUE_WORD, type ItemChange, type ItemChangesOptions, NO_TITLE_WORD, type RecordReader, type RecordValues, type RecordValuesStatus, SideEffectDirectiveCard, type SideEffectDirectiveCardProps, TARGETING_CLASSES, changesNothing, defaultFailureWords, describeChangeValue, directiveFieldLabel, directiveValueWord, failureWords, fieldLabel, getDirectiveRenderer, itemChanges, itemOwnName, itemRecordId, noteRecordChanged, provisionalDirectiveSlug, readsApplyLedger, recordFallbackName, recordRevision, registerDirectiveRenderer, resetDirectiveRenderers, useRecordRevision, useRecordValues };
@@ -179,6 +179,15 @@ interface ItemChangesOptions {
179
179
  valueWord?: DirectiveValueWord | null;
180
180
  }
181
181
  declare function itemChanges(item: Record<string, unknown>, current?: Record<string, unknown> | null, options?: ItemChangesOptions): ItemChange[];
182
+ /**
183
+ * 🚨 AN UPDATE THAT WOULD CHANGE NOTHING SAYS SO (G11B review, 2026-10-07: the
184
+ * confirm listed every field "(no change)", Apply stayed full-weight, and the
185
+ * run ended "Updated task. Ran 2 times"). True when the item sets no field, or
186
+ * when the record was read and EVERY field already holds its new value. A field
187
+ * whose current value is unknown counts as a change — doubt never mutes Apply.
188
+ * ONE rule for the card and the host's confirm.
189
+ */
190
+ declare function changesNothing(changes: ReadonlyArray<ItemChange>): boolean;
182
191
 
183
192
  interface SideEffectDirectiveCardProps {
184
193
  directive: DecodedDirective;
@@ -278,4 +287,4 @@ declare function noteRecordChanged(id: string): void;
278
287
  declare function useRecordRevision(id: string): number;
279
288
  declare function useRecordValues(read: RecordReader | undefined, ref: DirectiveRecordRef | null): RecordValues;
280
289
 
281
- export { ApplyDirectiveButton, type ApplyDirectiveButtonProps, CHANGE_VALUE_MAX, type ChangeValue, type DirectiveApplyPhase, DirectiveChangeList, DirectiveFailure, DirectiveFailureWords, DirectiveFallbackCard, type DirectiveFallbackCardProps, DirectiveHost, DirectiveProvisionalCard, type DirectiveProvisionalCardProps, DirectiveRecordRef, DirectiveRender, type DirectiveRenderProps, type DirectiveRenderer, type DirectiveRendererProps, type DirectiveValueWord, EMPTY_VALUE_WORD, type ItemChange, type ItemChangesOptions, NO_TITLE_WORD, type RecordReader, type RecordValues, type RecordValuesStatus, SideEffectDirectiveCard, type SideEffectDirectiveCardProps, TARGETING_CLASSES, defaultFailureWords, describeChangeValue, directiveFieldLabel, directiveValueWord, failureWords, fieldLabel, getDirectiveRenderer, itemChanges, itemOwnName, itemRecordId, noteRecordChanged, provisionalDirectiveSlug, readsApplyLedger, recordFallbackName, recordRevision, registerDirectiveRenderer, resetDirectiveRenderers, useRecordRevision, useRecordValues };
290
+ export { ApplyDirectiveButton, type ApplyDirectiveButtonProps, CHANGE_VALUE_MAX, type ChangeValue, type DirectiveApplyPhase, DirectiveChangeList, DirectiveFailure, DirectiveFailureWords, DirectiveFallbackCard, type DirectiveFallbackCardProps, DirectiveHost, DirectiveProvisionalCard, type DirectiveProvisionalCardProps, DirectiveRecordRef, DirectiveRender, type DirectiveRenderProps, type DirectiveRenderer, type DirectiveRendererProps, type DirectiveValueWord, EMPTY_VALUE_WORD, type ItemChange, type ItemChangesOptions, NO_TITLE_WORD, type RecordReader, type RecordValues, type RecordValuesStatus, SideEffectDirectiveCard, type SideEffectDirectiveCardProps, TARGETING_CLASSES, changesNothing, defaultFailureWords, describeChangeValue, directiveFieldLabel, directiveValueWord, failureWords, fieldLabel, getDirectiveRenderer, itemChanges, itemOwnName, itemRecordId, noteRecordChanged, provisionalDirectiveSlug, readsApplyLedger, recordFallbackName, recordRevision, registerDirectiveRenderer, resetDirectiveRenderers, useRecordRevision, useRecordValues };
@@ -135,6 +135,9 @@ function itemChanges(item, current, options = {}) {
135
135
  }
136
136
  return out;
137
137
  }
138
+ function changesNothing(changes) {
139
+ return changes.every((change) => change.same === true);
140
+ }
138
141
 
139
142
  // directives/record-values.ts
140
143
  import { useEffect, useState, useSyncExternalStore } from "react";
@@ -643,7 +646,7 @@ function ApplyDirectiveButton({
643
646
  onFocus: warm,
644
647
  disabled: view.status === "applying" || view.status === "asking" || items.length === 0,
645
648
  "data-apply-tone": muted ? "muted" : destructive ? "destructive" : "default",
646
- title: muted ? "It sets no fields, so applying it changes nothing." : void 0,
649
+ title: muted ? "Applying it changes nothing." : void 0,
647
650
  className: `inline-flex shrink-0 items-center gap-1 whitespace-nowrap 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"}`,
648
651
  children: [
649
652
  view.status === "applying" ? /* @__PURE__ */ jsx3(LoaderIcon, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx3(PlayIcon, { className: "h-3.5 w-3.5" }),
@@ -713,7 +716,7 @@ function FailureNotice({
713
716
  }
714
717
 
715
718
  // directives/SideEffectDirectiveCard.tsx
716
- import { useState as useState3 } from "react";
719
+ import { useEffect as useEffect3, useState as useState3 } from "react";
717
720
  import {
718
721
  asKindInstance,
719
722
  directiveDisplay,
@@ -737,12 +740,23 @@ function ItemRow({
737
740
  item,
738
741
  index,
739
742
  total,
740
- phase
743
+ phase,
744
+ onNothing
741
745
  }) {
742
746
  const host = useDirectiveHost();
743
747
  const recordId = TARGETING_CLASSES.has(directive.directiveClass) ? itemRecordId(item) : null;
744
748
  if (recordId) {
745
- return /* @__PURE__ */ jsx4(TargetRow, { directive, item, recordId, index, phase });
749
+ return /* @__PURE__ */ jsx4(
750
+ TargetRow,
751
+ {
752
+ directive,
753
+ item,
754
+ recordId,
755
+ index,
756
+ phase,
757
+ onNothing
758
+ }
759
+ );
746
760
  }
747
761
  const titleColumn = nounTitleColumn(directive.noun, host.nouns);
748
762
  const ownName = itemOwnName(item, titleColumn);
@@ -761,7 +775,8 @@ function ItemRow({
761
775
  /* @__PURE__ */ jsx4(
762
776
  "span",
763
777
  {
764
- className: `min-w-0 shrink-0 truncate font-medium ${ownName ? "text-foreground" : "italic text-muted-foreground"}`,
778
+ className: `min-w-0 line-clamp-2 font-medium [overflow-wrap:anywhere] sm:shrink-0 sm:truncate ${ownName ? "text-foreground" : "italic text-muted-foreground"}`,
779
+ "data-item-name": "",
765
780
  "data-item-untitled": ownName ? void 0 : "",
766
781
  children: title
767
782
  }
@@ -850,7 +865,8 @@ function TargetRow({
850
865
  item,
851
866
  recordId,
852
867
  index,
853
- phase
868
+ phase,
869
+ onNothing
854
870
  }) {
855
871
  const host = useDirectiveHost();
856
872
  const fallback = recordFallbackName(nounLabel2(directive.noun, host.nouns));
@@ -865,7 +881,11 @@ function TargetRow({
865
881
  titleColumn: nounTitleColumn(directive.noun, host.nouns),
866
882
  valueWord: directiveValueWord(host, directive.noun)
867
883
  }) : [];
868
- const name = host.renderRecord ? host.renderRecord({ noun: directive.noun, id: recordId, fallback, context: "row" }) : /* @__PURE__ */ jsx4("span", { className: "truncate font-medium text-foreground", children: fallback });
884
+ const nothing = readsLive && live.values !== null && changesNothing(changes);
885
+ useEffect3(() => {
886
+ onNothing?.(index, nothing);
887
+ }, [onNothing, index, nothing]);
888
+ const name = host.renderRecord ? host.renderRecord({ noun: directive.noun, id: recordId, fallback, context: "row" }) : /* @__PURE__ */ jsx4("span", { className: "line-clamp-2 min-w-0 font-medium text-foreground [overflow-wrap:anywhere]", children: fallback });
869
889
  return /* @__PURE__ */ jsxs3("div", { className: "py-1 text-xs", "data-directive-target": recordId, children: [
870
890
  /* @__PURE__ */ jsxs3("div", { className: "flex min-w-0 items-center gap-2", children: [
871
891
  /* @__PURE__ */ jsx4("span", { className: "flex min-w-0 flex-1 items-center gap-1 px-1", children: name }),
@@ -889,7 +909,9 @@ function SideEffectDirectiveCard({ directive }) {
889
909
  const [phase, setPhase] = useState3(
890
910
  () => readsApplyLedger(host, directive) ? { phase: "pending" } : { phase: "open" }
891
911
  );
892
- const nothingToChange = directive.directiveClass === "update" && total > 0 && items.every((item) => itemChanges(item).length === 0);
912
+ const [nothingAt, setNothingAt] = useState3({});
913
+ const noteNothing = (index, nothing) => setNothingAt((prev) => prev[index] === nothing ? prev : { ...prev, [index]: nothing });
914
+ const nothingToChange = directive.directiveClass === "update" && total > 0 && items.every((item, index) => itemChanges(item).length === 0 || nothingAt[index] === true);
893
915
  const shown = expanded ? items : items.slice(0, ROWS_BEFORE_FOLD);
894
916
  const hidden = total - shown.length;
895
917
  return /* @__PURE__ */ jsxs3("div", { className: "my-3 rounded-md border border-border bg-card px-2.5 py-1.5", "data-directive": directive.slug, children: [
@@ -918,7 +940,18 @@ function SideEffectDirectiveCard({ directive }) {
918
940
  total === 0 ? (
919
941
  // Stated, never silent: an empty batch is a real (and suspicious) state.
920
942
  /* @__PURE__ */ jsx4("p", { className: "mt-1 text-xs text-muted-foreground", children: "No items \u2014 nothing would be written." })
921
- ) : /* @__PURE__ */ jsx4("div", { className: "mt-0.5 divide-y divide-border/60", children: shown.map((item, i) => /* @__PURE__ */ jsx4(ItemRow, { directive, item, index: i, total, phase }, i)) }),
943
+ ) : /* @__PURE__ */ jsx4("div", { className: "mt-0.5 divide-y divide-border/60", children: shown.map((item, i) => /* @__PURE__ */ jsx4(
944
+ ItemRow,
945
+ {
946
+ directive,
947
+ item,
948
+ index: i,
949
+ total,
950
+ phase,
951
+ onNothing: noteNothing
952
+ },
953
+ i
954
+ )) }),
922
955
  hidden > 0 || expanded ? /* @__PURE__ */ jsxs3(
923
956
  "button",
924
957
  {
@@ -1055,6 +1088,7 @@ export {
1055
1088
  NO_TITLE_WORD,
1056
1089
  SideEffectDirectiveCard,
1057
1090
  TARGETING_CLASSES,
1091
+ changesNothing,
1058
1092
  defaultFailureWords,
1059
1093
  describeChangeValue,
1060
1094
  directiveFieldLabel,