@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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.25.0
4
+ ### An update that changes nothing says so (G11B, 2026-10-07)
5
+
6
+ - `changesNothing(changes)` (new export, `directives/item-changes`): true when an update sets no field, or every field already holds its value. Unknown current values count as a change.
7
+ - `SideEffectDirectiveCard`: once a row reads its record, an update whose every field already holds its value shows "Nothing to change" and a muted Apply — it used to be full-weight while the confirm listed "(no change)".
8
+ - A row's name wraps to two lines on a phone instead of being cut to one.
9
+
10
+ Consumer action: a host confirm for an update can call `changesNothing(itemChanges(item, values))` to say "Nothing to change" (matrx-frontend `DirectiveConsequence` does).
11
+
3
12
  ## 0.24.4
4
13
 
5
14
  Automatic changed-only republish (docs/metadata drift since the last tag — see
@@ -33,6 +33,7 @@ __export(directives_exports, {
33
33
  NO_TITLE_WORD: () => NO_TITLE_WORD,
34
34
  SideEffectDirectiveCard: () => SideEffectDirectiveCard,
35
35
  TARGETING_CLASSES: () => TARGETING_CLASSES,
36
+ changesNothing: () => changesNothing,
36
37
  defaultFailureWords: () => defaultFailureWords,
37
38
  describeChangeValue: () => describeChangeValue,
38
39
  directiveFieldLabel: () => directiveFieldLabel,
@@ -190,6 +191,9 @@ function itemChanges(item, current, options = {}) {
190
191
  }
191
192
  return out;
192
193
  }
194
+ function changesNothing(changes) {
195
+ return changes.every((change) => change.same === true);
196
+ }
193
197
 
194
198
  // directives/record-values.ts
195
199
  var import_react2 = require("react");
@@ -698,7 +702,7 @@ function ApplyDirectiveButton({
698
702
  onFocus: warm,
699
703
  disabled: view.status === "applying" || view.status === "asking" || items.length === 0,
700
704
  "data-apply-tone": muted ? "muted" : destructive ? "destructive" : "default",
701
- title: muted ? "It sets no fields, so applying it changes nothing." : void 0,
705
+ title: muted ? "Applying it changes nothing." : void 0,
702
706
  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"}`,
703
707
  children: [
704
708
  view.status === "applying" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(LoaderIcon, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(PlayIcon, { className: "h-3.5 w-3.5" }),
@@ -785,12 +789,23 @@ function ItemRow({
785
789
  item,
786
790
  index,
787
791
  total,
788
- phase
792
+ phase,
793
+ onNothing
789
794
  }) {
790
795
  const host = useDirectiveHost();
791
796
  const recordId = TARGETING_CLASSES.has(directive.directiveClass) ? itemRecordId(item) : null;
792
797
  if (recordId) {
793
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(TargetRow, { directive, item, recordId, index, phase });
798
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
799
+ TargetRow,
800
+ {
801
+ directive,
802
+ item,
803
+ recordId,
804
+ index,
805
+ phase,
806
+ onNothing
807
+ }
808
+ );
794
809
  }
795
810
  const titleColumn = (0, import_content_ir4.nounTitleColumn)(directive.noun, host.nouns);
796
811
  const ownName = itemOwnName(item, titleColumn);
@@ -809,7 +824,8 @@ function ItemRow({
809
824
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
810
825
  "span",
811
826
  {
812
- className: `min-w-0 shrink-0 truncate font-medium ${ownName ? "text-foreground" : "italic text-muted-foreground"}`,
827
+ className: `min-w-0 line-clamp-2 font-medium [overflow-wrap:anywhere] sm:shrink-0 sm:truncate ${ownName ? "text-foreground" : "italic text-muted-foreground"}`,
828
+ "data-item-name": "",
813
829
  "data-item-untitled": ownName ? void 0 : "",
814
830
  children: title
815
831
  }
@@ -898,7 +914,8 @@ function TargetRow({
898
914
  item,
899
915
  recordId,
900
916
  index,
901
- phase
917
+ phase,
918
+ onNothing
902
919
  }) {
903
920
  const host = useDirectiveHost();
904
921
  const fallback = recordFallbackName((0, import_content_ir4.nounLabel)(directive.noun, host.nouns));
@@ -913,7 +930,11 @@ function TargetRow({
913
930
  titleColumn: (0, import_content_ir4.nounTitleColumn)(directive.noun, host.nouns),
914
931
  valueWord: directiveValueWord(host, directive.noun)
915
932
  }) : [];
916
- const name = host.renderRecord ? host.renderRecord({ noun: directive.noun, id: recordId, fallback, context: "row" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "truncate font-medium text-foreground", children: fallback });
933
+ const nothing = readsLive && live.values !== null && changesNothing(changes);
934
+ (0, import_react4.useEffect)(() => {
935
+ onNothing?.(index, nothing);
936
+ }, [onNothing, index, nothing]);
937
+ const name = host.renderRecord ? host.renderRecord({ noun: directive.noun, id: recordId, fallback, context: "row" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "line-clamp-2 min-w-0 font-medium text-foreground [overflow-wrap:anywhere]", children: fallback });
917
938
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "py-1 text-xs", "data-directive-target": recordId, children: [
918
939
  /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex min-w-0 items-center gap-2", children: [
919
940
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "flex min-w-0 flex-1 items-center gap-1 px-1", children: name }),
@@ -937,7 +958,9 @@ function SideEffectDirectiveCard({ directive }) {
937
958
  const [phase, setPhase] = (0, import_react4.useState)(
938
959
  () => readsApplyLedger(host, directive) ? { phase: "pending" } : { phase: "open" }
939
960
  );
940
- const nothingToChange = directive.directiveClass === "update" && total > 0 && items.every((item) => itemChanges(item).length === 0);
961
+ const [nothingAt, setNothingAt] = (0, import_react4.useState)({});
962
+ const noteNothing = (index, nothing) => setNothingAt((prev) => prev[index] === nothing ? prev : { ...prev, [index]: nothing });
963
+ const nothingToChange = directive.directiveClass === "update" && total > 0 && items.every((item, index) => itemChanges(item).length === 0 || nothingAt[index] === true);
941
964
  const shown = expanded ? items : items.slice(0, ROWS_BEFORE_FOLD);
942
965
  const hidden = total - shown.length;
943
966
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "my-3 rounded-md border border-border bg-card px-2.5 py-1.5", "data-directive": directive.slug, children: [
@@ -966,7 +989,18 @@ function SideEffectDirectiveCard({ directive }) {
966
989
  total === 0 ? (
967
990
  // Stated, never silent: an empty batch is a real (and suspicious) state.
968
991
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "mt-1 text-xs text-muted-foreground", children: "No items \u2014 nothing would be written." })
969
- ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mt-0.5 divide-y divide-border/60", children: shown.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ItemRow, { directive, item, index: i, total, phase }, i)) }),
992
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "mt-0.5 divide-y divide-border/60", children: shown.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
993
+ ItemRow,
994
+ {
995
+ directive,
996
+ item,
997
+ index: i,
998
+ total,
999
+ phase,
1000
+ onNothing: noteNothing
1001
+ },
1002
+ i
1003
+ )) }),
970
1004
  hidden > 0 || expanded ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
971
1005
  "button",
972
1006
  {