@lmzhen/dsh-evolution-settings-ui 0.26.15 → 0.26.26

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/lib/client.js CHANGED
@@ -760,9 +760,13 @@ window.__ModuleLoader__.load({
760
760
  * answers are ordered the way the CLI orders them (waiting first); either one failing fails the
761
761
  * read, because a half-list would read as a complete window.
762
762
  */
763
+ const readTicket = (0, react.useRef)(0);
763
764
  const load = async () => {
764
765
  apply(loading);
766
+ const ticket = readTicket.current + 1;
767
+ readTicket.current = ticket;
765
768
  const [waiting, running] = await Promise.all([api.pending(), api.pending("executing")]);
769
+ if (readTicket.current !== ticket) return;
766
770
  if (!waiting.ok) {
767
771
  apply((current) => failed(current, waiting.message));
768
772
  return;
@@ -852,18 +856,18 @@ window.__ModuleLoader__.load({
852
856
  }, t("approvalDiffCut"))] : []
853
857
  ];
854
858
  const lines = [];
855
- for (const hunk of view.answer.hunks) {
859
+ for (const [hunkIndex, hunk] of view.answer.hunks.entries()) {
856
860
  for (const [index, line] of hunk.oldText.split("\n").entries()) {
857
861
  if (hunk.oldText === "" && line === "") continue;
858
862
  lines.push((0, react.createElement)("div", {
859
- key: "old-" + String(index),
863
+ key: "old-" + String(hunkIndex) + "-" + String(index),
860
864
  className: "evolution-param-line-del"
861
865
  }, "- " + line));
862
866
  }
863
867
  for (const [index, line] of hunk.newText.split("\n").entries()) {
864
868
  if (hunk.newText === "" && line === "") continue;
865
869
  lines.push((0, react.createElement)("div", {
866
- key: "new-" + String(index),
870
+ key: "new-" + String(hunkIndex) + "-" + String(index),
867
871
  className: "evolution-param-line-add"
868
872
  }, "+ " + line));
869
873
  }
@@ -1123,7 +1127,7 @@ window.__ModuleLoader__.load({
1123
1127
  /**
1124
1128
  * Resolve one message without the locale seat (tests and the fallback path).
1125
1129
  * @param key - the message key.
1126
- * @param language - which dictionary to read; defaults to Chinese.
1130
+ * @param language - which dictionary to read; defaults to English (the platform FALLBACK_LOCALE).
1127
1131
  * @returns the copy this bundle carries.
1128
1132
  */
1129
1133
  function message(key, language = "en") {
@@ -1319,35 +1323,6 @@ window.__ModuleLoader__.load({
1319
1323
  const [error, setError] = (0, react.useState)("");
1320
1324
  const [pending, setPending] = (0, react.useState)(null);
1321
1325
  const [clearing, setClearing] = (0, react.useState)(null);
1322
- if (snapshot.status === "loading") return (0, react.createElement)("p", {
1323
- className: "evolution-param-note",
1324
- role: "status"
1325
- }, t("loading"));
1326
- if (snapshot.status === "unavailable") return (0, react.createElement)("p", {
1327
- className: "evolution-param-note",
1328
- role: "status"
1329
- }, t(snapshot.reason === "seat-missing" ? "seatMissing" : snapshot.reason === "projection-failed" ? "projectionFailed" : "unavailable"));
1330
- const user = isRecord(snapshot.user) ? snapshot.user : {};
1331
- const value = isRecord(snapshot.value) ? snapshot.value : {};
1332
- const disabled = !snapshot.writable;
1333
- const locked = disabled || busy || clearing !== null;
1334
- const titleKey = NAMESPACE_TITLES[namespace];
1335
- const title = titleKey === void 0 ? namespace : t(titleKey);
1336
- const changed = fields.filter((field) => Object.hasOwn(user, field.id)).length;
1337
- const textOf = (field) => draft[field.id] ?? format(value[field.id]);
1338
- const dirty = fields.filter((field) => draft[field.id] !== void 0 && draft[field.id] !== format(value[field.id]));
1339
- const peerTextOf = (field) => {
1340
- if (field.atLeast === "") return void 0;
1341
- const related = fields.find((candidate) => candidate.id === field.atLeast);
1342
- return related === void 0 ? void 0 : textOf(related);
1343
- };
1344
- const blocked = fields.some((field) => invalidReason(field, textOf(field), peerTextOf(field)) !== null);
1345
- const change = (id, next) => {
1346
- setDraft({
1347
- ...draft,
1348
- [id]: next
1349
- });
1350
- };
1351
1326
  /**
1352
1327
  * Settle a staged write against the Host's answer.
1353
1328
  *
@@ -1376,6 +1351,35 @@ window.__ModuleLoader__.load({
1376
1351
  snapshot.user,
1377
1352
  t
1378
1353
  ]);
1354
+ if (snapshot.status === "loading") return (0, react.createElement)("p", {
1355
+ className: "evolution-param-note",
1356
+ role: "status"
1357
+ }, t("loading"));
1358
+ if (snapshot.status === "unavailable") return (0, react.createElement)("p", {
1359
+ className: "evolution-param-note",
1360
+ role: "status"
1361
+ }, t(snapshot.reason === "seat-missing" ? "seatMissing" : snapshot.reason === "projection-failed" ? "projectionFailed" : snapshot.reason === "session-memory" ? "destMemory" : "unavailable"));
1362
+ const user = isRecord(snapshot.user) ? snapshot.user : {};
1363
+ const value = isRecord(snapshot.value) ? snapshot.value : {};
1364
+ const disabled = !snapshot.writable;
1365
+ const locked = disabled || busy || clearing !== null;
1366
+ const titleKey = NAMESPACE_TITLES[namespace];
1367
+ const title = titleKey === void 0 ? namespace : t(titleKey);
1368
+ const changed = fields.filter((field) => Object.hasOwn(user, field.id)).length;
1369
+ const textOf = (field) => draft[field.id] ?? format(value[field.id]);
1370
+ const dirty = fields.filter((field) => draft[field.id] !== void 0 && draft[field.id] !== format(value[field.id]));
1371
+ const peerTextOf = (field) => {
1372
+ if (field.atLeast === "") return void 0;
1373
+ const related = fields.find((candidate) => candidate.id === field.atLeast);
1374
+ return related === void 0 ? void 0 : textOf(related);
1375
+ };
1376
+ const blocked = fields.some((field) => invalidReason(field, textOf(field), peerTextOf(field)) !== null);
1377
+ const change = (id, next) => {
1378
+ setDraft({
1379
+ ...draft,
1380
+ [id]: next
1381
+ });
1382
+ };
1379
1383
  /**
1380
1384
  * Restore one field's deployment default (T5-05/A60).
1381
1385
  *
@@ -1522,7 +1526,7 @@ window.__ModuleLoader__.load({
1522
1526
  writable: snapshot.writable,
1523
1527
  base: isRecord(snapshot.base) ? snapshot.base : void 0,
1524
1528
  mode: snapshot.mode,
1525
- ...snapshot.status === "unavailable" ? { reason: "not-served" } : {}
1529
+ ...snapshot.status === "unavailable" ? { reason: snapshot.mode === "memory" ? "session-memory" : "not-served" } : {}
1526
1530
  };
1527
1531
  }
1528
1532
  /**
@@ -1629,14 +1633,12 @@ window.__ModuleLoader__.load({
1629
1633
  mutateOnce: async (ops) => {
1630
1634
  const seat = form();
1631
1635
  if (seat === void 0) return false;
1632
- await seat.mutate(ops);
1633
- return true;
1636
+ return await seat.mutate(ops);
1634
1637
  },
1635
1638
  clear: async (field) => {
1636
1639
  const seat = form();
1637
1640
  if (seat === void 0) return false;
1638
- await seat.unset(field);
1639
- return true;
1641
+ return await seat.unset(field);
1640
1642
  }
1641
1643
  };
1642
1644
  }
@@ -1853,17 +1855,26 @@ window.__ModuleLoader__.load({
1853
1855
  ].join("\n");
1854
1856
  /**
1855
1857
  * Inject the stylesheet once per document.
1858
+ *
1859
+ * S5-08: it RETURNS the disposer. The tag check alone answered only half the question - "is it
1860
+ * there" - and left "who takes it away" to nobody, so a hot reload (unmount, remount) found the old
1861
+ * tag, skipped the injection, and kept serving the PREVIOUS bundle CSS. skill-history hands its
1862
+ * disposer back to the fiber; this is the same shape for the same concern.
1856
1863
  * @param host - the document to inject into; defaults to the global one.
1864
+ * @returns the disposal; a no-op when there is no document or the tag was already present.
1857
1865
  */
1858
1866
  function injectStyles(host) {
1859
1867
  const doc = host ?? globalThis.document;
1860
- if (doc === void 0) return;
1861
- if (doc.querySelector("style[data-plugin-css=\"@lmzhen/dsh-evolution-settings-ui/settings.css\"]") !== null) return;
1868
+ if (doc === void 0) return () => {};
1869
+ if (doc.querySelector("style[data-plugin-css=\"@lmzhen/dsh-evolution-settings-ui/settings.css\"]") !== null) return () => {};
1862
1870
  const tag = doc.createElement("style");
1863
1871
  tag.dataset.plugin = "@lmzhen/dsh-evolution-settings-ui";
1864
1872
  tag.dataset.pluginCss = CSS_TAG_ID;
1865
1873
  tag.textContent = CSS;
1866
1874
  doc.head.appendChild(tag);
1875
+ return () => {
1876
+ tag.remove();
1877
+ };
1867
1878
  }
1868
1879
  //#endregion
1869
1880
  //#region src/client/index.ts
@@ -1884,7 +1895,7 @@ window.__ModuleLoader__.load({
1884
1895
  * @param ctx - client cordis context.
1885
1896
  */
1886
1897
  function apply(ctx) {
1887
- injectStyles();
1898
+ ctx.effect(() => injectStyles(), "evolution-settings-ui: styles");
1888
1899
  const seam = ctx;
1889
1900
  let registered = false;
1890
1901
  ctx.effect(() => {
@@ -86,7 +86,7 @@ export type MessageKey = keyof typeof zh;
86
86
  /**
87
87
  * Resolve one message without the locale seat (tests and the fallback path).
88
88
  * @param key - the message key.
89
- * @param language - which dictionary to read; defaults to Chinese.
89
+ * @param language - which dictionary to read; defaults to English (the platform FALLBACK_LOCALE).
90
90
  * @returns the copy this bundle carries.
91
91
  */
92
92
  export declare function message(key: MessageKey, language?: 'zh' | 'en'): string;
@@ -51,7 +51,7 @@ export interface ParamSectionSnapshot {
51
51
  * all, or the seat is there and serves no such row. The two read differently to the
52
52
  * operator (one is the UI's own gap, the other is a row that is not composed).
53
53
  */
54
- reason?: 'seat-missing' | 'not-served' | 'projection-failed';
54
+ reason?: 'seat-missing' | 'not-served' | 'projection-failed' | 'session-memory';
55
55
  }
56
56
  /** The reactive source the renderer binds to a `use<Name>` seat. */
57
57
  export interface ParamSectionSource {
@@ -5,9 +5,13 @@ export declare const CSS: string;
5
5
  /** The slice of the DOM the injection touches (this package typechecks without DOM lib). */
6
6
  interface StyleHost {
7
7
  querySelector(selector: string): unknown;
8
+ /** The element shape the injection uses. `remove()` is on the real DOM element and is what the
9
+ * S5-08 disposer calls, so the host shape names it too - otherwise the package cannot typecheck
10
+ * without the DOM lib while still using the disposal the browser offers. */
8
11
  createElement(tag: string): {
9
12
  dataset: Record<string, string>;
10
13
  textContent: string;
14
+ remove(): void;
11
15
  };
12
16
  head: {
13
17
  appendChild(node: unknown): void;
@@ -15,8 +19,14 @@ interface StyleHost {
15
19
  }
16
20
  /**
17
21
  * Inject the stylesheet once per document.
22
+ *
23
+ * S5-08: it RETURNS the disposer. The tag check alone answered only half the question - "is it
24
+ * there" - and left "who takes it away" to nobody, so a hot reload (unmount, remount) found the old
25
+ * tag, skipped the injection, and kept serving the PREVIOUS bundle CSS. skill-history hands its
26
+ * disposer back to the fiber; this is the same shape for the same concern.
18
27
  * @param host - the document to inject into; defaults to the global one.
28
+ * @returns the disposal; a no-op when there is no document or the tag was already present.
19
29
  */
20
- export declare function injectStyles(host?: StyleHost): void;
30
+ export declare function injectStyles(host?: StyleHost): () => void;
21
31
  export {};
22
32
  //# sourceMappingURL=styles.d.ts.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-settings-ui",
3
3
  "description": "Web settings cards for the evolution family's parameter namespaces (community build)",
4
- "version": "0.26.15",
4
+ "version": "0.26.26",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },