dsh-skin-galactic-opera 0.1.0 → 0.1.2

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
@@ -2,6 +2,24 @@
2
2
 
3
3
  All notable changes to this package will be documented in this file.
4
4
 
5
+ ## 0.1.2 - 2026-08-27
6
+
7
+ ### Fixed
8
+
9
+ - Keeps model/context popovers above the skin overlay layer.
10
+ - Prevents the telemetry mirror from treating input-bar buttons, popovers, listboxes, and menus as telemetry sources.
11
+
12
+ ## 0.1.1 - 2026-08-27
13
+
14
+ ### Added
15
+
16
+ - Bottom-right HUD telemetry panel that mirrors global DSH session status in a fixed five-line layout: rounds/steps, LLM/tool time, first-token average and throughput, cache hit rate, and input/output token usage.
17
+
18
+ ### Changed
19
+
20
+ - Preserves pipe-separated telemetry segments as complete information blocks.
21
+ - Filters nearby message-level timing and settings/menu text from the telemetry panel.
22
+
5
23
  ## 0.1.0 - 2026-08-27
6
24
 
7
25
  Initial public package version.
package/README.md CHANGED
@@ -24,20 +24,47 @@ This package does not include or recreate any official franchise logos, characte
24
24
  - Subtle mouse parallax.
25
25
  - Click energy pulse.
26
26
  - Hover energy outlines on buttons, inputs, messages, and code blocks.
27
+ - Bottom telemetry/status information is mirrored into a fixed bottom-right sci-fi panel.
27
28
  - Scanner target dot projected from the pointer direction relative to the viewport center; the target lights only when the scanner sweep passes it.
28
29
  - Automatic hyperspace burst after submitting a new instruction or message.
29
30
  - Respects `prefers-reduced-motion: reduce` by disabling major animations.
30
31
 
32
+ ## Telemetry panel
33
+
34
+ When DSH Web exposes bottom status/usage data, the skin keeps the original UI layout in place and mirrors the detected global telemetry into a non-interactive bottom-right HUD panel. The panel is intended to show a consistent five-line status block:
35
+
36
+ ```text
37
+ 312 轮 · 8732 步
38
+ LLM 2253m42s · 工具调用 972m8s
39
+ 首 token 平均 10.5s · 58 tok/s
40
+ 缓存命中 97%
41
+ 输入 1369M tok · 输出 2.5M tok
42
+ ```
43
+
44
+ The skin treats each original pipe-separated bottom-bar segment as a complete information block, so combined items such as `首 token 平均 ... · ... tok/s` and `输入 ... · 输出 ...` remain intact. It also filters nearby message-level timing text and settings/menu text so the panel stays focused on global session telemetry.
45
+
31
46
  ## Installation
32
47
 
33
48
  ### From npm
34
49
 
35
- After this package is published to npm:
50
+ Recommended for most users:
36
51
 
37
52
  ```powershell
38
53
  dsh plugin --profile web add dsh-skin-galactic-opera
39
54
  ```
40
55
 
56
+ npm package page:
57
+
58
+ ```text
59
+ https://www.npmjs.com/package/dsh-skin-galactic-opera
60
+ ```
61
+
62
+ ### From GitHub
63
+
64
+ ```powershell
65
+ dsh plugin --profile web add https://github.com/xmnathan/dsh-skin-galactic-opera.git
66
+ ```
67
+
41
68
  ### From a local checkout
42
69
 
43
70
  From the parent directory of this package:
@@ -111,14 +138,6 @@ dsh-skin-galactic-opera/
111
138
  - `lib/index.js`: host-side no-op entry.
112
139
  - `lib/client.js`: browser-side theme, decoration, and interaction implementation.
113
140
 
114
- ## GitHub topics
115
-
116
- Recommended repository topics:
117
-
118
- ```text
119
- dsh deepseek-harness dsh-plugin dsh-skin dsh-theme space-opera sci-fi dark-theme hud web-ui
120
- ```
121
-
122
141
  ## License
123
142
 
124
143
  MIT
package/lib/client.js CHANGED
@@ -148,6 +148,10 @@ body.dsh-skin-galactic-opera [class*="Frame"] {
148
148
  background-color: transparent !important;
149
149
  }
150
150
 
151
+ body.dsh-skin-galactic-opera :is([role="dialog"], [role="menu"], [role="listbox"], [role="tooltip"], [popover], [data-radix-popper-content-wrapper]) {
152
+ z-index: 2147483647 !important;
153
+ }
154
+
151
155
  body.dsh-skin-galactic-opera [class*="sidebar"],
152
156
  body.dsh-skin-galactic-opera [class*="Sidebar"] {
153
157
  background:
@@ -619,6 +623,100 @@ body.dsh-skin-galactic-opera * {
619
623
  scrollbar-color: rgba(125, 227, 255, 0.42) rgba(5, 7, 17, 0.40);
620
624
  }
621
625
 
626
+ body.dsh-skin-galactic-opera .dsh-skin-telemetry-source-hidden {
627
+ opacity: 0 !important;
628
+ }
629
+
630
+ body.dsh-skin-galactic-opera .dsh-skin-telemetry-panel[hidden] {
631
+ display: none !important;
632
+ }
633
+
634
+ body.dsh-skin-galactic-opera .dsh-skin-telemetry-panel {
635
+ position: fixed;
636
+ right: max(18px, 2.4vmin);
637
+ bottom: max(18px, 2.4vmin);
638
+ z-index: 2147483644;
639
+ display: flex;
640
+ flex-direction: column;
641
+ gap: 4px;
642
+ width: min(260px, 36vw);
643
+ max-height: none;
644
+ overflow: visible;
645
+ padding: 19px 12px 12px;
646
+ border: 1px solid rgba(125,227,255,0.48);
647
+ border-left-color: rgba(255,207,92,0.62);
648
+ border-bottom-color: rgba(255,71,120,0.36);
649
+ clip-path: polygon(14px 0, 100% 0, 100% calc(100% - 13px), calc(100% - 13px) 100%, 0 100%, 0 14px);
650
+ color: rgba(248,251,255,0.92);
651
+ background:
652
+ linear-gradient(135deg, rgba(255,207,92,0.20) 0 10px, transparent 11px),
653
+ linear-gradient(315deg, rgba(125,227,255,0.18) 0 12px, transparent 13px),
654
+ linear-gradient(180deg, rgba(6,10,26,0.92), rgba(10,17,42,0.84));
655
+ box-shadow:
656
+ inset 0 0 0 1px rgba(255,255,255,0.07),
657
+ inset 0 0 24px rgba(125,227,255,0.09),
658
+ 0 0 28px rgba(125,227,255,0.22),
659
+ 0 0 52px rgba(255,71,120,0.10);
660
+ backdrop-filter: blur(12px) saturate(1.35);
661
+ -webkit-backdrop-filter: blur(12px) saturate(1.35);
662
+ pointer-events: none;
663
+ }
664
+
665
+ body.dsh-skin-galactic-opera .dsh-skin-telemetry-panel::before {
666
+ content: "TELEMETRY";
667
+ position: absolute;
668
+ left: 14px;
669
+ top: 5px;
670
+ color: rgba(255,207,92,0.82);
671
+ font: 800 8px/1 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
672
+ letter-spacing: 0.20em;
673
+ text-shadow: 0 0 10px rgba(255,207,92,0.42);
674
+ }
675
+
676
+ body.dsh-skin-galactic-opera .dsh-skin-telemetry-panel::after {
677
+ content: "";
678
+ position: absolute;
679
+ inset: 4px;
680
+ border: 1px solid rgba(125,227,255,0.16);
681
+ clip-path: polygon(11px 0, 100% 0, 100% calc(100% - 10px), calc(100% - 10px) 100%, 0 100%, 0 11px);
682
+ background:
683
+ repeating-linear-gradient(90deg, transparent 0 18px, rgba(125,227,255,0.045) 19px, transparent 20px 36px),
684
+ linear-gradient(90deg, rgba(125,227,255,0.10), transparent 30%, rgba(255,71,120,0.06));
685
+ pointer-events: none;
686
+ }
687
+
688
+ body.dsh-skin-galactic-opera .dsh-skin-telemetry-row {
689
+ position: relative;
690
+ z-index: 1;
691
+ display: flex;
692
+ flex-direction: row;
693
+ align-items: baseline;
694
+ justify-content: flex-start;
695
+ gap: 0;
696
+ min-width: 0;
697
+ padding: 3px 0 3px 9px;
698
+ border-left: 2px solid rgba(125,227,255,0.46);
699
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
700
+ text-shadow: 0 0 10px rgba(125,227,255,0.30);
701
+ white-space: nowrap;
702
+ }
703
+
704
+
705
+ body.dsh-skin-galactic-opera .dsh-skin-telemetry-value {
706
+ flex: 0 0 auto;
707
+ color: rgba(248,251,255,0.96);
708
+ font-size: 10.5px;
709
+ line-height: 1.25;
710
+ letter-spacing: 0.035em;
711
+ text-align: right;
712
+ }
713
+
714
+ body.dsh-skin-galactic-opera .dsh-skin-telemetry-value-full {
715
+ flex: 1 1 auto;
716
+ text-align: left;
717
+ white-space: nowrap;
718
+ }
719
+
622
720
  @keyframes dsh-skin-stars-drift {
623
721
  from { transform: translate3d(0, 0, 0); }
624
722
  to { transform: translate3d(-115px, 115px, 0); }
@@ -957,6 +1055,187 @@ body.dsh-skin-galactic-opera * {
957
1055
  };
958
1056
  }
959
1057
 
1058
+ function installTelemetryPanel() {
1059
+ if (typeof document === "undefined" || typeof window === "undefined") return () => {};
1060
+ const telemetryPattern = /(?:\d[\d.,KkMm%/]*\s*轮\s*[·•]?\s*\d[\d.,KkMm%/]*\s*步)|(?:\d[\d.,KkMm%/]*\s*(?:rounds?|turns?)\s*[·•]?\s*\d[\d.,KkMm%/]*\s*steps?)|LLM\b|工具调用|(?:首\s*)?(?:token|tokens|tok|toks)\s*平均|(?:token|tokens|tok|toks)\/s|缓存命中|缓存|cache\b|cached\b|输入|输出|prompt\b|completion\b/i;
1061
+ const skipSelector = `#${DECOR_ID}, style, script, textarea, input, select, button, [role=button], [aria-haspopup], [contenteditable=true], [role=dialog], [role=menu], [role=listbox], [role=tooltip], [popover], .dsh-skin-pulse, .dsh-skin-telemetry-panel`;
1062
+ const editableDescendantSelector = "textarea, input, select, [contenteditable=true]";
1063
+ let currentSources = new Set();
1064
+ let lastText = "";
1065
+ let frame = 0;
1066
+ const panel = document.createElement("div");
1067
+ panel.className = "dsh-skin-telemetry-panel";
1068
+ panel.setAttribute("aria-hidden", "true");
1069
+ panel.hidden = true;
1070
+ document.body.appendChild(panel);
1071
+ const isSkippable = (element) => element.closest(skipSelector) !== null;
1072
+ const hasEditableDescendant = (element) => element.querySelector(editableDescendantSelector) !== null;
1073
+ const getTelemetryText = (element) => (element.innerText || element.textContent || "").replace(/\s+/g, " ").trim();
1074
+ const scoreCandidate = (element) => {
1075
+ if (!(element instanceof HTMLElement) || isSkippable(element) || hasEditableDescendant(element)) return 0;
1076
+ const rect = element.getBoundingClientRect();
1077
+ const compactBottomBar = rect.height <= 46 && rect.width <= window.innerWidth && rect.bottom > window.innerHeight - 150;
1078
+ if (rect.width < 12 || rect.height < 8 || (!compactBottomBar && rect.width > Math.min(620, window.innerWidth * 0.72)) || rect.height > 86) return 0;
1079
+ if (rect.bottom < window.innerHeight - 170) return 0;
1080
+ const text = getTelemetryText(element);
1081
+ if (text.length < 2 || text.length > 360 || !telemetryPattern.test(text) || !/\d/.test(text)) return 0;
1082
+ if (/设置\s+设置\s+通用设置|Cmd\/Ctrl\+Enter|使用另一行|排队发送|Agent\s+预设/i.test(text)) return 0;
1083
+ let score = compactBottomBar ? 4 : 1;
1084
+ if (rect.bottom > window.innerHeight - 72) score += 3;
1085
+ if (rect.right > window.innerWidth * 0.45) score += 1;
1086
+ if (/token|tokens|tok|toks/i.test(text)) score += 3;
1087
+ if (/调用|消耗|上下文|usage|context|缓存|命中|输入|输出/i.test(text)) score += 2;
1088
+ if (/轮次|轮|步骤|步数|步|steps?|rounds?|turns?/i.test(text)) score += 2;
1089
+ if (/[·•|;;]/.test(text)) score += 2;
1090
+ if (/button|input|textarea|select/i.test(element.tagName)) score -= 4;
1091
+ return score;
1092
+ };
1093
+ const chooseTelemetryElements = () => {
1094
+ const xs = [0.10, 0.22, 0.34, 0.46, 0.58, 0.70, 0.82, 0.94];
1095
+ const ys = [18, 38, 62, 90, 122, 150];
1096
+ const candidates = new Map();
1097
+ for (const xRatio of xs) for (const yOffset of ys) {
1098
+ const x = window.innerWidth * xRatio;
1099
+ const y = window.innerHeight - yOffset;
1100
+ if (y < 0) continue;
1101
+ for (const base of document.elementsFromPoint(x, y)) {
1102
+ let element = base;
1103
+ for (let depth = 0; element instanceof HTMLElement && depth < 8; depth += 1, element = element.parentElement) {
1104
+ const score = scoreCandidate(element);
1105
+ if (score > 0) candidates.set(element, Math.max(candidates.get(element) ?? 0, score));
1106
+ }
1107
+ }
1108
+ }
1109
+ return [...candidates]
1110
+ .sort(([a, aScore], [b, bScore]) => {
1111
+ if (bScore !== aScore) return bScore - aScore;
1112
+ const ar = a.getBoundingClientRect();
1113
+ const br = b.getBoundingClientRect();
1114
+ return ar.top - br.top || ar.left - br.left;
1115
+ })
1116
+ .filter(([element], index, entries) => !entries.slice(0, index).some(([selected]) => selected.contains(element) || element.contains(selected)))
1117
+ .slice(0, 10)
1118
+ .map(([element]) => element)
1119
+ .sort((a, b) => {
1120
+ const ar = a.getBoundingClientRect();
1121
+ const br = b.getBoundingClientRect();
1122
+ return ar.top - br.top || ar.left - br.left;
1123
+ });
1124
+ };
1125
+ const splitTelemetryRows = (text) => {
1126
+ const normalized = text
1127
+ .replace(/\s*([|;;])\s*/g, " | ")
1128
+ .replace(/\s*([·•])\s*/g, " $1 ")
1129
+ .replace(/\s{2,}/g, " ")
1130
+ .trim();
1131
+ const hasExplicitBlocks = /[|;;]/.test(normalized);
1132
+ const strongParts = normalized.split(/\s+\|\s+|\s*;\s*|\s*;\s*/).map((part) => part.trim()).filter(Boolean);
1133
+ const labelBoundary = /\s+(?=(?:LLM\b|工具调用|缓存命中|缓存|命中|模型|费用|上下文|输入|context\b|usage\b|model\b|cost\b|cache\b|cached\b|prompt\b))/gi;
1134
+ const parts = hasExplicitBlocks ? strongParts : strongParts.flatMap((part) => part
1135
+ .replace(labelBoundary, " | ")
1136
+ .split(/\s+\|\s+/)
1137
+ .map((item) => item.trim())
1138
+ .filter(Boolean));
1139
+ const rows = [];
1140
+ const isRoundOnly = (value) => /^[\d.,KkMm%/]+\s*(?:轮|rounds?|turns?)$/i.test(value);
1141
+ const isStepOnly = (value) => /^[\d.,KkMm%/]+\s*(?:步|steps?)$/i.test(value);
1142
+ const isFirstTokenPrefix = (value) => /^首$/i.test(value);
1143
+ const isTokenAverage = (value) => /^(?:首\s*)?(?:token|tokens|tok|toks)\s*平均(?:\s|[::]|\d|$)/i.test(value);
1144
+ const isTokenSpeedUnit = (value) => /^(?:token|tokens|tok|toks)\/s$/i.test(value);
1145
+ const isInputOnly = (value) => /^(?:输入|prompt\b)/i.test(value);
1146
+ const isOutputOnly = (value) => /^(?:输出|completion\b)/i.test(value);
1147
+ const isTelemetryRow = (value) => {
1148
+ if (!/\d/.test(value)) return false;
1149
+ if (/^\d{1,2}:\d{2}\b|\b\d{1,2}:\d{2}\b\s*[·•]|用时|发送|排队|Cmd\/Ctrl/i.test(value)) return false;
1150
+ return /(?:\d[\d.,KkMm%/]*\s*轮\s*[·•]?\s*\d[\d.,KkMm%/]*\s*步)|(?:\d[\d.,KkMm%/]*\s*(?:rounds?|turns?)\s*[·•]?\s*\d[\d.,KkMm%/]*\s*steps?)|^LLM\b|^工具调用|^(?:首\s*)?(?:token|tokens|tok|toks)\s*平均(?:\s|[::]|\d|$)|^(?:缓存命中|缓存|cache\b|cached\b)|^(?:输入|输出|prompt\b|completion\b)/i.test(value);
1151
+ };
1152
+ for (let index = 0; index < parts.length; index += 1) {
1153
+ const part = parts[index];
1154
+ const next = parts[index + 1];
1155
+ const afterNext = parts[index + 2];
1156
+ if (next !== undefined && isFirstTokenPrefix(part) && isTokenAverage(next)) {
1157
+ if (afterNext !== undefined && isTokenSpeedUnit(afterNext)) {
1158
+ rows.push(`${part} ${next} ${afterNext}`);
1159
+ index += 2;
1160
+ } else {
1161
+ rows.push(`${part} ${next}`);
1162
+ index += 1;
1163
+ }
1164
+ continue;
1165
+ }
1166
+ if (next !== undefined && isTokenAverage(part) && isTokenSpeedUnit(next)) {
1167
+ rows.push(`${part} ${next}`);
1168
+ index += 1;
1169
+ continue;
1170
+ }
1171
+ if (next !== undefined && isRoundOnly(part) && isStepOnly(next)) {
1172
+ rows.push(`${part} · ${next}`);
1173
+ index += 1;
1174
+ continue;
1175
+ }
1176
+ if (next !== undefined && isInputOnly(part) && isOutputOnly(next)) {
1177
+ rows.push(`${part} · ${next}`);
1178
+ index += 1;
1179
+ continue;
1180
+ }
1181
+ rows.push(part);
1182
+ }
1183
+ const firstMatch = (predicate) => rows.find((row) => isTelemetryRow(row) && predicate(row));
1184
+ const roundRow = firstMatch((row) => /(?:\d[\d.,KkMm%/]*\s*轮\s*[·•]?\s*\d[\d.,KkMm%/]*\s*步)|(?:\d[\d.,KkMm%/]*\s*(?:rounds?|turns?)\s*[·•]?\s*\d[\d.,KkMm%/]*\s*steps?)/i.test(row));
1185
+ const llmRow = firstMatch((row) => /^LLM\b/i.test(row));
1186
+ const toolRow = firstMatch((row) => /^工具调用/i.test(row));
1187
+ const durationRow = llmRow !== undefined && toolRow !== undefined && !/工具调用/i.test(llmRow) ? `${llmRow} · ${toolRow}` : llmRow ?? toolRow;
1188
+ const firstTokenRow = firstMatch((row) => /^(?:首\s*)?(?:token|tokens|tok|toks)\s*平均(?:\s|[::]|\d|$)/i.test(row));
1189
+ const cacheRow = firstMatch((row) => /^(?:缓存命中|缓存|cache\b|cached\b)/i.test(row));
1190
+ const inputOutputRow = firstMatch((row) => /^(?:输入|prompt\b)/i.test(row) && /(?:输出|completion\b)/i.test(row))
1191
+ ?? (() => {
1192
+ const inputRow = firstMatch((row) => /^(?:输入|prompt\b)/i.test(row));
1193
+ const outputRow = firstMatch((row) => /^(?:输出|completion\b)/i.test(row));
1194
+ return inputRow !== undefined && outputRow !== undefined ? `${inputRow} · ${outputRow}` : inputRow ?? outputRow;
1195
+ })();
1196
+ const fixedRows = [roundRow, durationRow, firstTokenRow, cacheRow, inputOutputRow].filter(Boolean);
1197
+ return fixedRows.slice(0, 5);
1198
+ };
1199
+ const renderPanel = (text) => {
1200
+ const rows = splitTelemetryRows(text);
1201
+ panel.hidden = rows.length === 0;
1202
+ panel.replaceChildren(...rows.map((text) => {
1203
+ const row = document.createElement("div");
1204
+ row.className = "dsh-skin-telemetry-row";
1205
+ const metric = document.createElement("span");
1206
+ metric.className = "dsh-skin-telemetry-value dsh-skin-telemetry-value-full";
1207
+ metric.textContent = text;
1208
+ row.append(metric);
1209
+ return row;
1210
+ }));
1211
+ };
1212
+ const update = () => {
1213
+ frame = 0;
1214
+ const nextSources = new Set(chooseTelemetryElements());
1215
+ const text = [...nextSources].map(getTelemetryText).filter(Boolean).join(" | ");
1216
+ for (const source of currentSources) if (!nextSources.has(source)) source.classList.remove("dsh-skin-telemetry-source-hidden");
1217
+ for (const source of nextSources) if (!currentSources.has(source)) source.classList.add("dsh-skin-telemetry-source-hidden");
1218
+ currentSources = nextSources;
1219
+ if (text === lastText) return;
1220
+ lastText = text;
1221
+ renderPanel(text);
1222
+ };
1223
+ const schedule = () => {
1224
+ if (frame === 0) frame = requestAnimationFrame(update);
1225
+ };
1226
+ const observer = typeof MutationObserver === "undefined" ? undefined : new MutationObserver(schedule);
1227
+ schedule();
1228
+ observer?.observe(document.body, { childList: true, subtree: true, characterData: true });
1229
+ window.addEventListener("resize", schedule, { passive: true });
1230
+ return () => {
1231
+ observer?.disconnect();
1232
+ window.removeEventListener("resize", schedule);
1233
+ if (frame !== 0) cancelAnimationFrame(frame);
1234
+ for (const source of currentSources) source.classList.remove("dsh-skin-telemetry-source-hidden");
1235
+ panel.remove();
1236
+ };
1237
+ }
1238
+
960
1239
  const inject = ["theme"];
961
1240
 
962
1241
  function apply(ctx) {
@@ -965,7 +1244,9 @@ body.dsh-skin-galactic-opera * {
965
1244
  if (typeof document !== "undefined") document.body.classList.add("dsh-skin-galactic-opera");
966
1245
  const decor = ensureDecor();
967
1246
  const disposeInteractions = installInteractions(decor);
1247
+ const disposeTelemetryPanel = installTelemetryPanel();
968
1248
  return () => {
1249
+ disposeTelemetryPanel();
969
1250
  disposeInteractions();
970
1251
  if (typeof document !== "undefined") document.body.classList.remove("dsh-skin-galactic-opera");
971
1252
  decor?.remove();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-skin-galactic-opera",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Unofficial cinematic space-opera dark skin bundle for the DSH Web GUI.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",