dsh-ws-recency 0.10.2 → 0.10.4

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 +123 -24
  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.4/dsh-ws-recency-0.10.4.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,72 @@
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
+
850
+ /** Copy the host pill's own computed metrics onto the clone. */
851
+ function copyPillMetrics(template, wrapper, button) {
852
+ if (template === null || template === undefined || typeof window === "undefined" || typeof window.getComputedStyle !== "function") return;
853
+ var read = function (name) {
854
+ try {
855
+ return window.getComputedStyle(template).getPropertyValue(name) || "";
856
+ } catch (error) {
857
+ return "";
858
+ }
859
+ };
860
+ var inherited = ["font-size", "font-family", "font-weight", "line-height", "letter-spacing"];
861
+ for (var i = 0; i < inherited.length; i += 1) {
862
+ var value = read(inherited[i]);
863
+ if (value !== "") {
864
+ wrapper.style[inherited[i]] = value;
865
+ button.style[inherited[i]] = value;
866
+ }
867
+ }
868
+ var own = ["padding", "gap", "border-radius", "color"];
869
+ for (var j = 0; j < own.length; j += 1) {
870
+ var metric = read(own[j]);
871
+ if (metric !== "") button.style[own[j]] = metric;
872
+ }
873
+ }
874
+
802
875
  /**
803
- * Add one pill to the composer's statistics row ("turns / steps / tokens")
804
- * that opens the same analysis panel as the session menu.
876
+ * Add one pill at the END of the composer footer that opens the analysis
877
+ * panel for the conversation on screen.
805
878
  *
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.
879
+ * It reuses the host pill's own class names and icon so it reads as part of
880
+ * the same row, sits after the context ring rather than between the stats,
881
+ * is re-attached whenever the host re-renders, and disappears with the row.
808
882
  */
809
883
  function decorateComposerStats() {
810
884
  if (typeof document === "undefined") return;
@@ -818,21 +892,46 @@
818
892
  }
819
893
  var row = anchors[0].parentElement;
820
894
  if (row === null) return;
895
+ var dock = composerDockOf(row);
896
+
821
897
  if (existing === null) {
898
+ var template = pillTemplateOf(row);
899
+ /* Wrapped exactly like a host pill (its anchor span) and carrying the
900
+ host's own computed metrics: appended to the footer dock rather than
901
+ into the stats row, this pill would otherwise inherit the dock's font
902
+ size and read larger than its neighbours. */
903
+ var wrapper = document.createElement("span");
904
+ wrapper.setAttribute("data-wsr-composer-analysis", "true");
905
+ var anchorTemplate = template === null ? null : template.parentElement;
906
+ var anchorClass = anchorTemplate !== null && typeof anchorTemplate.className === "string" ? anchorTemplate.className : "";
907
+ if (anchorClass !== "") wrapper.className = anchorClass;
822
908
  var button = document.createElement("button");
823
909
  button.type = "button";
824
- button.className = "wsr-pill";
825
- button.setAttribute("data-wsr-composer-analysis", "true");
826
- button.textContent = t("composer.analysis");
910
+ var base = template !== null && typeof template.className === "string" ? template.className : "";
911
+ button.className = base === "" ? "wsr-pill" : base + " wsr-pill";
912
+ var icon = cloneIconOf(template);
913
+ if (icon !== null) button.appendChild(icon);
914
+ var label = document.createElement("span");
915
+ var labelTemplate = template === null || typeof template.querySelector !== "function" ? null : template.querySelector('[class*="_label"]');
916
+ label.className = labelTemplate !== null && typeof labelTemplate.className === "string" && labelTemplate.className !== ""
917
+ ? labelTemplate.className
918
+ : "wsr-pill-label";
919
+ label.textContent = t("composer.analysis");
920
+ button.appendChild(label);
827
921
  button.addEventListener("click", function () {
828
922
  var id = currentConversationId();
829
923
  if (id !== null) openStatsPanel(id);
830
924
  });
831
- composerPill = button;
925
+ copyPillMetrics(template, wrapper, button);
926
+ wrapper.appendChild(button);
927
+ composerPill = wrapper;
832
928
  } else {
833
929
  composerPill = existing;
834
930
  }
835
- if (composerPill.parentElement !== row) row.appendChild(composerPill);
931
+ var children = dock.children || [];
932
+ if (composerPill.parentElement !== dock || children[children.length - 1] !== composerPill) {
933
+ dock.appendChild(composerPill);
934
+ }
836
935
  }
837
936
 
838
937
  function apply() {
@@ -903,7 +1002,7 @@
903
1002
  return h(Item, {
904
1003
  icon: typeof Icon === "function" ? h(Icon, {}) : undefined,
905
1004
  onSelect: function () { props.actions.toggleFollowUp(props.sessionId); },
906
- }, props.t(flagged ? "menu.unflag" : "menu.flag"));
1005
+ }, t(flagged ? "menu.unflag" : "menu.flag"));
907
1006
  }
908
1007
 
909
1008
  /**
@@ -967,7 +1066,7 @@
967
1066
  if (typeof window.alert === "function") window.alert(t("trash.failed") + (error && error.message ? error.message : error));
968
1067
  });
969
1068
  },
970
- }, props.t("menu.trash"));
1069
+ }, t("menu.trash"));
971
1070
  }
972
1071
 
973
1072
  /* ------------------------- token statistics ------------------------- */
@@ -1175,7 +1274,7 @@
1175
1274
  return h(Item, {
1176
1275
  icon: typeof Icon === "function" ? h(Icon, {}) : undefined,
1177
1276
  onSelect: function () { openStatsPanel(props.sessionId); },
1178
- }, props.t("menu.stats"));
1277
+ }, t("menu.stats"));
1179
1278
  }
1180
1279
 
1181
1280
  /** Settings -> General row: how old a session must be before it is folded. */
@@ -1184,11 +1283,11 @@
1184
1283
  var days = Number(settings.foldDays);
1185
1284
  if (!Number.isFinite(days)) days = DEFAULT_DAYS;
1186
1285
  var keep = settings.keepActive !== false;
1187
- var text = props.t("settings.title");
1286
+ var text = t("settings.title");
1188
1287
  return h("div", { className: "wsr-setRow" },
1189
1288
  h("div", { className: "wsr-setText" },
1190
1289
  h("div", { className: "wsr-setTitle" }, text),
1191
- h("div", { className: "wsr-setDesc" }, props.t("settings.description")),
1290
+ h("div", { className: "wsr-setDesc" }, t("settings.description")),
1192
1291
  ),
1193
1292
  h("div", { className: "wsr-controls" },
1194
1293
  h("button", {
@@ -1218,7 +1317,7 @@
1218
1317
  checked: keep,
1219
1318
  onChange: function (event) { props.actions.setKeepActive(event.target.checked); },
1220
1319
  }),
1221
- props.t("settings.exempt")),
1320
+ t("settings.exempt")),
1222
1321
  ),
1223
1322
  );
1224
1323
  }
@@ -1227,11 +1326,11 @@
1227
1326
  function ColorsRow(props) {
1228
1327
  var settings = props.useStore(function (state) { return state; });
1229
1328
  var enabled = settings.colors !== false;
1230
- var text = props.t("color.title");
1329
+ var text = t("color.title");
1231
1330
  return h("div", { className: "wsr-setRow" },
1232
1331
  h("div", { className: "wsr-setText" },
1233
1332
  h("div", { className: "wsr-setTitle" }, text),
1234
- h("div", { className: "wsr-setDesc" }, props.t("color.description")),
1333
+ h("div", { className: "wsr-setDesc" }, t("color.description")),
1235
1334
  ),
1236
1335
  h("div", { className: "wsr-controls" },
1237
1336
  h("label", { className: "wsr-exempt" },
@@ -1240,7 +1339,7 @@
1240
1339
  checked: enabled,
1241
1340
  onChange: function (event) { props.actions.setColors(event.target.checked); },
1242
1341
  }),
1243
- props.t("color.enabled")),
1342
+ t("color.enabled")),
1244
1343
  ),
1245
1344
  );
1246
1345
  }
@@ -1249,11 +1348,11 @@
1249
1348
  function PinSectionRow(props) {
1250
1349
  var settings = props.useStore(function (state) { return state; });
1251
1350
  var enabled = settings.pinnedSection !== false;
1252
- var text = props.t("pin.title");
1351
+ var text = t("pin.title");
1253
1352
  return h("div", { className: "wsr-setRow" },
1254
1353
  h("div", { className: "wsr-setText" },
1255
1354
  h("div", { className: "wsr-setTitle" }, text),
1256
- h("div", { className: "wsr-setDesc" }, props.t("pin.description")),
1355
+ h("div", { className: "wsr-setDesc" }, t("pin.description")),
1257
1356
  ),
1258
1357
  h("div", { className: "wsr-controls" },
1259
1358
  h("label", { className: "wsr-exempt" },
@@ -1262,7 +1361,7 @@
1262
1361
  checked: enabled,
1263
1362
  onChange: function (event) { props.actions.setPinnedSection(event.target.checked); },
1264
1363
  }),
1265
- props.t("pin.enabled")),
1364
+ t("pin.enabled")),
1266
1365
  ),
1267
1366
  );
1268
1367
  }
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.4",
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",