dsh-ws-recency 0.10.2 → 0.10.3

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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/client.js +86 -22
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -110,7 +110,7 @@ dsh plugin --profile <profile> add github:chyxhtc/dsh-ws-recency
110
110
  或固定版本的预构建包:
111
111
 
112
112
  ```powershell
113
- dsh plugin --profile <profile> add https://github.com/chyxhtc/dsh-ws-recency/releases/download/v0.10.2/dsh-ws-recency-0.10.2.tgz
113
+ dsh plugin --profile <profile> add https://github.com/chyxhtc/dsh-ws-recency/releases/download/v0.10.3/dsh-ws-recency-0.10.3.tgz
114
114
  ```
115
115
 
116
116
  装完把 `dsh-ws-recency` 加进 `~/.hypers-work/profiles/<profile>/package.json` 的 `dsh.profile.bundles`,重启应用。
package/client.js CHANGED
@@ -230,13 +230,27 @@
230
230
  "trash.failed": "Delete failed: ",
231
231
  };
232
232
 
233
- var FALLBACK = { "fold.show": "Show history ({n})", "fold.hide": "Hide history" };
233
+ /* Resolved lazily: the dictionaries are declared further down this closure. */
234
+ var resolvedDictionary = null;
235
+
236
+ function fallbackDictionary() {
237
+ if (resolvedDictionary !== null) return resolvedDictionary;
238
+ var language = "";
239
+ /* The page's own lang wins: it is what the UI is actually rendered in. */
240
+ try { if (typeof document !== "undefined" && document.documentElement !== null && document.documentElement !== undefined) language = document.documentElement.lang || ""; } catch (error) { language = ""; }
241
+ try { if (language === "" && typeof navigator !== "undefined" && navigator.language) language = navigator.language; } catch (error) { /* keep empty */ }
242
+ resolvedDictionary = String(language).toLowerCase().indexOf("zh") === 0 ? ZH : EN;
243
+ return resolvedDictionary;
244
+ }
234
245
 
235
246
  function t(key, params) {
236
247
  if (typeof data.t === "function") {
237
- try { return data.t(key, params); } catch (error) { /* fall back below */ }
248
+ var translated = null;
249
+ try { translated = data.t(key, params); } catch (error) { translated = null; }
250
+ if (typeof translated === "string" && translated !== key) return translated;
238
251
  }
239
- var text = FALLBACK[key] || key;
252
+ var dictionary = fallbackDictionary();
253
+ var text = (dictionary && dictionary[key]) || key;
240
254
  if (params !== undefined && params.n !== undefined) text = text.replace("{n}", String(params.n));
241
255
  return text;
242
256
  }
@@ -799,12 +813,47 @@
799
813
  return typeof id === "string" && id !== "" ? id : null;
800
814
  }
801
815
 
816
+ /** The nearest horizontal flex row above the stats: the whole footer dock. */
817
+ function composerDockOf(row) {
818
+ var node = row;
819
+ for (var depth = 0; depth < 4; depth += 1) {
820
+ var parent = node.parentElement;
821
+ if (parent === null) break;
822
+ var style = null;
823
+ try {
824
+ style = typeof window !== "undefined" && typeof window.getComputedStyle === "function"
825
+ ? window.getComputedStyle(parent)
826
+ : null;
827
+ } catch (error) {
828
+ style = null;
829
+ }
830
+ if (style !== null && style.display === "flex" && (style.flexDirection === "row" || style.flexDirection === "")) return parent;
831
+ node = parent;
832
+ }
833
+ return row.parentElement === null ? row : row.parentElement;
834
+ }
835
+
836
+ /** The host's own pill in that row, used as the visual template. */
837
+ function pillTemplateOf(row) {
838
+ if (typeof row.querySelector !== "function") return null;
839
+ var button = row.querySelector('button[class*="_pill"]');
840
+ return button !== null ? button : row.querySelector('[class*="_pill"]');
841
+ }
842
+
843
+ function cloneIconOf(template) {
844
+ if (template === null || typeof template.querySelector !== "function") return null;
845
+ var icon = template.querySelector("svg");
846
+ if (icon === null || typeof icon.cloneNode !== "function") return null;
847
+ try { return icon.cloneNode(true); } catch (error) { return null; }
848
+ }
849
+
802
850
  /**
803
- * Add one pill to the composer's statistics row ("turns / steps / tokens")
804
- * that opens the same analysis panel as the session menu.
851
+ * Add one pill at the END of the composer footer that opens the analysis
852
+ * panel for the conversation on screen.
805
853
  *
806
- * The row is host-rendered and re-renders often, so the pill is re-attached
807
- * on every pass and dropped as soon as the row or the session goes away.
854
+ * It reuses the host pill's own class names and icon so it reads as part of
855
+ * the same row, sits after the context ring rather than between the stats,
856
+ * is re-attached whenever the host re-renders, and disappears with the row.
808
857
  */
809
858
  function decorateComposerStats() {
810
859
  if (typeof document === "undefined") return;
@@ -818,12 +867,24 @@
818
867
  }
819
868
  var row = anchors[0].parentElement;
820
869
  if (row === null) return;
870
+ var dock = composerDockOf(row);
871
+
821
872
  if (existing === null) {
873
+ var template = pillTemplateOf(row);
822
874
  var button = document.createElement("button");
823
875
  button.type = "button";
824
- button.className = "wsr-pill";
825
876
  button.setAttribute("data-wsr-composer-analysis", "true");
826
- button.textContent = t("composer.analysis");
877
+ var base = template !== null && typeof template.className === "string" ? template.className : "";
878
+ button.className = base === "" ? "wsr-pill" : base + " wsr-pill";
879
+ var icon = cloneIconOf(template);
880
+ if (icon !== null) button.appendChild(icon);
881
+ var label = document.createElement("span");
882
+ var labelTemplate = template === null || typeof template.querySelector !== "function" ? null : template.querySelector('[class*="_label"]');
883
+ label.className = labelTemplate !== null && typeof labelTemplate.className === "string" && labelTemplate.className !== ""
884
+ ? labelTemplate.className
885
+ : "wsr-pill-label";
886
+ label.textContent = t("composer.analysis");
887
+ button.appendChild(label);
827
888
  button.addEventListener("click", function () {
828
889
  var id = currentConversationId();
829
890
  if (id !== null) openStatsPanel(id);
@@ -832,7 +893,10 @@
832
893
  } else {
833
894
  composerPill = existing;
834
895
  }
835
- if (composerPill.parentElement !== row) row.appendChild(composerPill);
896
+ var children = dock.children || [];
897
+ if (composerPill.parentElement !== dock || children[children.length - 1] !== composerPill) {
898
+ dock.appendChild(composerPill);
899
+ }
836
900
  }
837
901
 
838
902
  function apply() {
@@ -903,7 +967,7 @@
903
967
  return h(Item, {
904
968
  icon: typeof Icon === "function" ? h(Icon, {}) : undefined,
905
969
  onSelect: function () { props.actions.toggleFollowUp(props.sessionId); },
906
- }, props.t(flagged ? "menu.unflag" : "menu.flag"));
970
+ }, t(flagged ? "menu.unflag" : "menu.flag"));
907
971
  }
908
972
 
909
973
  /**
@@ -967,7 +1031,7 @@
967
1031
  if (typeof window.alert === "function") window.alert(t("trash.failed") + (error && error.message ? error.message : error));
968
1032
  });
969
1033
  },
970
- }, props.t("menu.trash"));
1034
+ }, t("menu.trash"));
971
1035
  }
972
1036
 
973
1037
  /* ------------------------- token statistics ------------------------- */
@@ -1175,7 +1239,7 @@
1175
1239
  return h(Item, {
1176
1240
  icon: typeof Icon === "function" ? h(Icon, {}) : undefined,
1177
1241
  onSelect: function () { openStatsPanel(props.sessionId); },
1178
- }, props.t("menu.stats"));
1242
+ }, t("menu.stats"));
1179
1243
  }
1180
1244
 
1181
1245
  /** Settings -> General row: how old a session must be before it is folded. */
@@ -1184,11 +1248,11 @@
1184
1248
  var days = Number(settings.foldDays);
1185
1249
  if (!Number.isFinite(days)) days = DEFAULT_DAYS;
1186
1250
  var keep = settings.keepActive !== false;
1187
- var text = props.t("settings.title");
1251
+ var text = t("settings.title");
1188
1252
  return h("div", { className: "wsr-setRow" },
1189
1253
  h("div", { className: "wsr-setText" },
1190
1254
  h("div", { className: "wsr-setTitle" }, text),
1191
- h("div", { className: "wsr-setDesc" }, props.t("settings.description")),
1255
+ h("div", { className: "wsr-setDesc" }, t("settings.description")),
1192
1256
  ),
1193
1257
  h("div", { className: "wsr-controls" },
1194
1258
  h("button", {
@@ -1218,7 +1282,7 @@
1218
1282
  checked: keep,
1219
1283
  onChange: function (event) { props.actions.setKeepActive(event.target.checked); },
1220
1284
  }),
1221
- props.t("settings.exempt")),
1285
+ t("settings.exempt")),
1222
1286
  ),
1223
1287
  );
1224
1288
  }
@@ -1227,11 +1291,11 @@
1227
1291
  function ColorsRow(props) {
1228
1292
  var settings = props.useStore(function (state) { return state; });
1229
1293
  var enabled = settings.colors !== false;
1230
- var text = props.t("color.title");
1294
+ var text = t("color.title");
1231
1295
  return h("div", { className: "wsr-setRow" },
1232
1296
  h("div", { className: "wsr-setText" },
1233
1297
  h("div", { className: "wsr-setTitle" }, text),
1234
- h("div", { className: "wsr-setDesc" }, props.t("color.description")),
1298
+ h("div", { className: "wsr-setDesc" }, t("color.description")),
1235
1299
  ),
1236
1300
  h("div", { className: "wsr-controls" },
1237
1301
  h("label", { className: "wsr-exempt" },
@@ -1240,7 +1304,7 @@
1240
1304
  checked: enabled,
1241
1305
  onChange: function (event) { props.actions.setColors(event.target.checked); },
1242
1306
  }),
1243
- props.t("color.enabled")),
1307
+ t("color.enabled")),
1244
1308
  ),
1245
1309
  );
1246
1310
  }
@@ -1249,11 +1313,11 @@
1249
1313
  function PinSectionRow(props) {
1250
1314
  var settings = props.useStore(function (state) { return state; });
1251
1315
  var enabled = settings.pinnedSection !== false;
1252
- var text = props.t("pin.title");
1316
+ var text = t("pin.title");
1253
1317
  return h("div", { className: "wsr-setRow" },
1254
1318
  h("div", { className: "wsr-setText" },
1255
1319
  h("div", { className: "wsr-setTitle" }, text),
1256
- h("div", { className: "wsr-setDesc" }, props.t("pin.description")),
1320
+ h("div", { className: "wsr-setDesc" }, t("pin.description")),
1257
1321
  ),
1258
1322
  h("div", { className: "wsr-controls" },
1259
1323
  h("label", { className: "wsr-exempt" },
@@ -1262,7 +1326,7 @@
1262
1326
  checked: enabled,
1263
1327
  onChange: function (event) { props.actions.setPinnedSection(event.target.checked); },
1264
1328
  }),
1265
- props.t("pin.enabled")),
1329
+ t("pin.enabled")),
1266
1330
  ),
1267
1331
  );
1268
1332
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-ws-recency",
3
- "version": "0.10.2",
3
+ "version": "0.10.3",
4
4
  "description": "Sidebar session management for DeepSeek Harness: workspace groups sorted by latest activity, stale history folded, a global pinned section, follow-up marks, session delete into a recoverable trash, and per-session token statistics split by tool.",
5
5
  "license": "MIT",
6
6
  "author": "chyxhtc",