dsh-desktop-kit 1.0.0 → 2.0.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/README.md +106 -41
- package/client.js +780 -28
- package/docs/ARCHITECTURE.md +114 -0
- package/docs/COMPATIBILITY.md +61 -0
- package/docs/INSTALL.md +84 -0
- package/docs/PRIVACY.md +50 -0
- package/docs/ROADMAP.md +57 -0
- package/docs/TOKENS.md +118 -0
- package/docs/TROUBLESHOOTING.md +68 -0
- package/docs/images/hero.png +0 -0
- package/index.js +4 -0
- package/package.json +15 -5
- package/plugins/dsh-gkb-ru-ui/lib/client.js +276 -28
- package/plugins/dsh-gkb-ru-ui/lib/index.js +45 -1
- package/plugins/dsh-gkb-ru-ui/lib/skills.js +195 -1
- package/plugins/dsh-gkb-ru-ui/package.json +2 -2
- package/plugins/dsh-gkb-ru-ui/screenshots/01-home-tiles-1440-light.png +0 -0
- package/plugins/dsh-gkb-ru-ui/screenshots/02-skills-1440-light.png +0 -0
- package/plugins/dsh-gkb-ru-ui/screenshots/03-skills-editor-1440-light.png +0 -0
- package/plugins/dsh-gkb-ru-ui/screenshots/04-skills-1440-dark.png +0 -0
- package/plugins/dsh-gkb-ru-ui/screenshots/05-skills-390-light.png +0 -0
- package/plugins/dsh-gkb-ru-ui/screenshots/06-home-tiles-1440-dark.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/01-real-time-peak-start.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/02-real-time-peak-end.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/03-peak-card-1440-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/04-peak-card-1440-dark.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/05-peak-card-390-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/05-peak-card-768-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/06-peak-card-390-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/07-live-preview-check.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/07-pre-peak-1440-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/08-peak-start-1440-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/09-peak-end-1440-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/10-pre-peak-card-1440-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/11-pre-peak-card-1440-dark.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/12-pre-peak-card-390-light.png +0 -0
- package/plugins/dsh-peak-guard/screenshots/qa-report.json +74 -0
- package/plugins/dsh-profile-transfer/screenshots/01-settings-transfer-1440-light.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/02-settings-transfer-preview-1440-light.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/03-settings-transfer-1440-dark.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/04-settings-transfer-390-light.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/05-settings-transfer-768-light.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/06-settings-transfer-390-dark.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/07-settings-transfer-export-result.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/08-settings-transfer-workspaces-1440-light.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/09-settings-transfer-workspaces-1440-dark.png +0 -0
- package/plugins/dsh-profile-transfer/screenshots/10-settings-transfer-workspaces-390-light.png +0 -0
- package/plugins/dsh-session-move/screenshots/01-row-menu-1440-light.png +0 -0
- package/plugins/dsh-session-move/screenshots/02-dialog-1440-light.png +0 -0
- package/plugins/dsh-session-move/screenshots/03-dialog-390-light.png +0 -0
- package/plugins/dsh-session-move/screenshots/04-dialog-1440-dark.png +0 -0
- package/plugins/dsh-session-move/screenshots/05-drag-highlight-1440-light.png +0 -0
- package/plugins/dsh-session-move/screenshots/06-drop-ungrouped-highlight.png +0 -0
- package/plugins/dsh-session-move/screenshots/07-after-drop-ungrouped.png +0 -0
- package/plugins/dsh-session-move/screenshots/08-move-dialog.png +0 -0
- package/plugins/dsh-session-move/screenshots/09-moved-to-second-workspace.png +0 -0
- package/plugins/dsh-session-move/screenshots/qa-report.json +62 -0
- package/plugins/dsh-skill-autopilot/cordis.patch.yml +3 -0
- package/plugins/dsh-skill-autopilot/index.js +444 -0
- package/plugins/dsh-skill-autopilot/package.json +19 -0
- package/plugins/dsh-token-lab/cordis.patch.yml +3 -0
- package/plugins/dsh-token-lab/lib/client.js +504 -0
- package/plugins/dsh-token-lab/lib/index.js +275 -0
- package/plugins/dsh-token-lab/package.json +26 -0
- package/plugins/dsh-token-lab/screenshots/01-token-lab-1440-light.png +0 -0
- package/plugins/dsh-token-lab/screenshots/02-token-lab-1440-dark.png +0 -0
- package/skills/code-review-checklist/SKILL.md +48 -30
- package/skills/content-writing/SKILL.md +64 -327
- package/skills/content-writing/references/process.md +123 -0
- package/skills/content-writing/references/quick-recipes.md +99 -0
- package/skills/content-writing/references/rules.md +85 -0
- package/skills/content-writing/references/sources.md +49 -0
- package/skills/context-economy/SKILL.md +60 -0
- package/skills/context-economy/references/long-session.md +35 -0
- package/skills/context-economy/references/token-math.md +42 -0
- package/skills/context-economy/references/tool-recipes.md +34 -0
- package/skills/database-safety/SKILL.md +40 -24
- package/skills/debugging-playbook/SKILL.md +30 -27
- package/skills/design-critique/SKILL.md +57 -110
- package/skills/design-critique/references/a11y.md +25 -0
- package/skills/design-critique/references/diagnoses.md +33 -0
- package/skills/design-critique/references/quick-tests.md +30 -0
- package/skills/design-critique/references/report-template.md +63 -0
- package/skills/design-critique/references/rubric-fast.md +73 -0
- package/skills/dev-workflow/SKILL.md +41 -34
- package/skills/dev-workflow/references/definition-of-done.md +44 -0
- package/skills/dev-workflow/references/memory-and-boundaries.md +31 -0
- package/skills/dev-workflow/references/stages.md +67 -0
- package/skills/git-and-ci/SKILL.md +46 -26
- package/skills/pg-upgrade-verify/SKILL.md +54 -35
- package/skills/project-deep-dive/SKILL.md +39 -164
- package/skills/project-deep-dive/references/recon-commands.md +12 -0
- package/skills/project-deep-dive/references/section-templates.md +2 -0
- package/skills/project-deep-dive/references/tool-and-process.md +86 -0
- package/skills/project-memory-maintenance/SKILL.md +39 -154
- package/skills/project-memory-maintenance/references/api-and-checklist.md +99 -0
- package/skills/project-memory-maintenance/references/journal-template.md +2 -0
- package/skills/project-memory-maintenance/references/section-rules.md +2 -0
- package/skills/release-and-deploy/SKILL.md +46 -32
- package/skills/requirements-and-planning/SKILL.md +44 -110
- package/skills/requirements-and-planning/references/antipatterns-and-checklist.md +20 -0
- package/skills/requirements-and-planning/references/process-steps.md +52 -0
- package/skills/requirements-and-planning/references/templates-and-estimates.md +29 -0
- package/skills/security-review/SKILL.md +33 -29
- package/skills/seo/SKILL.md +53 -305
- package/skills/seo/references/antipatterns.md +66 -0
- package/skills/seo/references/audit-checklist.md +14 -0
- package/skills/seo/references/content-seo.md +180 -174
- package/skills/seo/references/eeat-medical.md +2 -0
- package/skills/seo/references/measurement.md +28 -1
- package/skills/seo/references/on-page.md +5 -0
- package/skills/seo/references/page-templates.md +56 -0
- package/skills/seo/references/stack-recipes.md +2 -0
- package/skills/seo/references/structured-data.md +2 -0
- package/skills/seo/references/technical-seo.md +2 -0
- package/skills/service-desk-conventions/SKILL.md +40 -28
- package/skills/service-desk-db-safety/SKILL.md +39 -28
- package/skills/service-desk-release/SKILL.md +40 -22
- package/skills/service-desk-testing/SKILL.md +47 -33
- package/skills/service-desk-ui/SKILL.md +43 -24
- package/skills/skills-router/SKILL.md +44 -82
- package/skills/skills-router/references/applying-skills.md +25 -0
- package/skills/skills-router/references/routing-matrix.md +52 -0
- package/skills/testing-strategy/SKILL.md +47 -32
- package/skills/ui-design-system/SKILL.md +43 -38
- package/skills/ui-design-system/references/accessibility-and-responsive.md +50 -0
- package/skills/ui-design-system/references/tokens-and-components.md +67 -0
- package/skills/ui-design-system/references/visual-qa.md +44 -0
- package/skills/web-research/SKILL.md +40 -33
- package/skills/web-research/references/answer-and-citations.md +60 -0
- package/skills/web-research/references/query-recipes.md +44 -0
- package/skills/wow-design/SKILL.md +82 -161
- package/skills/wow-design/references/anti-patterns.md +3 -0
- package/skills/wow-design/references/critique-rubric.md +3 -0
- package/skills/wow-design/references/design-brief-template.md +2 -0
- package/skills/wow-design/references/inspiration.md +3 -0
- package/skills/wow-design/references/presets.md +3 -0
- package/skills/wow-design/references/principles.md +3 -0
- package/skills/wow-design/references/quick-presets.md +50 -0
- package/skills/wow-design/references/report-template.md +21 -0
- package/skills/wow-design/references/stack-recipes.md +3 -0
- package/skills/wow-design/references/visual-qa.md +3 -0
- package/skills/wow-design/references/wow-levers.md +43 -0
- package/tools/skill-cost.mjs +76 -0
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "dsh-token-lab",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
const react = require("react");
|
|
8
|
+
const h = react.createElement;
|
|
9
|
+
|
|
10
|
+
const inject = ["slots", "connection"];
|
|
11
|
+
|
|
12
|
+
let pluginCtx;
|
|
13
|
+
|
|
14
|
+
/* ------------------------------------------------------------------ */
|
|
15
|
+
/* session selection + store */
|
|
16
|
+
/* ------------------------------------------------------------------ */
|
|
17
|
+
|
|
18
|
+
function currentSessionSelector(state) {
|
|
19
|
+
try {
|
|
20
|
+
const sessions = Object.values(state.byId || {});
|
|
21
|
+
const main = sessions.find((session) => session && session.retainedBy && (session.retainedBy.mainView || 0) > 0);
|
|
22
|
+
return main ? main.id : undefined;
|
|
23
|
+
} catch { return undefined; }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const store = {
|
|
27
|
+
state: { open: false, sessionId: null, report: null, loading: false, error: null, loadedAt: 0 },
|
|
28
|
+
listeners: new Set(),
|
|
29
|
+
get() { return this.state; },
|
|
30
|
+
subscribe(listener) { this.listeners.add(listener); return () => { this.listeners.delete(listener); }; },
|
|
31
|
+
set(patch) {
|
|
32
|
+
this.state = Object.assign({}, this.state, patch);
|
|
33
|
+
for (const listener of Array.from(this.listeners)) { try { listener(); } catch {} }
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
function useStore() {
|
|
37
|
+
return react.useSyncExternalStore(store.subscribe.bind(store), store.get.bind(store), store.get.bind(store));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
let inflight = null;
|
|
41
|
+
async function callToken(operation, payload) {
|
|
42
|
+
if (!pluginCtx || !pluginCtx.connection) throw new Error("RPC недоступен");
|
|
43
|
+
const result = await pluginCtx.connection.rpc.call("/api", "dsh-token-lab/" + operation, payload || {}, AbortSignal.timeout(20000));
|
|
44
|
+
if (!result || result.ok !== true) throw new Error((result && result.error && result.error.message) || "ошибка RPC");
|
|
45
|
+
return result.value;
|
|
46
|
+
}
|
|
47
|
+
function loadReport(sessionId, force) {
|
|
48
|
+
const state = store.get();
|
|
49
|
+
const wanted = sessionId || state.sessionId || null;
|
|
50
|
+
if (!force && state.report && state.sessionId === wanted && Date.now() - state.loadedAt < 8000) return Promise.resolve(state.report);
|
|
51
|
+
if (inflight) return inflight;
|
|
52
|
+
store.set({ loading: true, error: null, sessionId: wanted });
|
|
53
|
+
inflight = callToken("report", wanted ? { sessionId: wanted } : {})
|
|
54
|
+
.then((report) => {
|
|
55
|
+
store.set({ report, loading: false, error: null, loadedAt: Date.now() });
|
|
56
|
+
return report;
|
|
57
|
+
})
|
|
58
|
+
.catch((error) => {
|
|
59
|
+
store.set({ loading: false, error: String(error && error.message ? error.message : error) });
|
|
60
|
+
return null;
|
|
61
|
+
})
|
|
62
|
+
.finally(() => { inflight = null; });
|
|
63
|
+
return inflight;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* Cross-plugin opener: the skills catalog offers a "Токены" button that
|
|
67
|
+
* works on mobile, where the sidebar tile rail is collapsed. */
|
|
68
|
+
try {
|
|
69
|
+
if (typeof window !== "undefined") {
|
|
70
|
+
window.__DSH_TOKEN_OPEN__ = (sessionId) => {
|
|
71
|
+
const wanted = sessionId || store.state.sessionId || null;
|
|
72
|
+
store.set({ open: true, sessionId: wanted });
|
|
73
|
+
void loadReport(wanted, true);
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
} catch {}
|
|
77
|
+
|
|
78
|
+
/* ------------------------------------------------------------------ */
|
|
79
|
+
/* formatting */
|
|
80
|
+
/* ------------------------------------------------------------------ */
|
|
81
|
+
|
|
82
|
+
function num(value) {
|
|
83
|
+
const n = Number(value) || 0;
|
|
84
|
+
return n.toLocaleString("ru-RU");
|
|
85
|
+
}
|
|
86
|
+
function compact(value) {
|
|
87
|
+
const n = Number(value) || 0;
|
|
88
|
+
if (n >= 1000000) return (n / 1000000).toFixed(1).replace(".0", "").replace(".", ",") + "M";
|
|
89
|
+
if (n >= 1000) return (n / 1000).toFixed(1).replace(".0", "").replace(".", ",") + "k";
|
|
90
|
+
return String(Math.round(n));
|
|
91
|
+
}
|
|
92
|
+
function percent(value, digits) {
|
|
93
|
+
const n = Number(value) || 0;
|
|
94
|
+
return (n * 100).toFixed(digits === undefined ? 0 : digits).replace(".", ",") + "%";
|
|
95
|
+
}
|
|
96
|
+
function pressureColor(pressure) {
|
|
97
|
+
const p = Number(pressure) || 0;
|
|
98
|
+
if (p >= 0.75) return "#8E2B18";
|
|
99
|
+
if (p >= 0.5) return "#B07A2A";
|
|
100
|
+
return "#0E6E5E";
|
|
101
|
+
}
|
|
102
|
+
function pressureLabel(pressure) {
|
|
103
|
+
const p = Number(pressure) || 0;
|
|
104
|
+
if (p >= 0.85) return "Почти переполнение — пора сжимать";
|
|
105
|
+
if (p >= 0.65) return "Заполнено больше половины";
|
|
106
|
+
if (p >= 0.35) return "Рабочий диапазон";
|
|
107
|
+
return "Много свободного места";
|
|
108
|
+
}
|
|
109
|
+
function plural(count, one, few, many) {
|
|
110
|
+
const n = Math.abs(Number(count) || 0) % 100;
|
|
111
|
+
const last = n % 10;
|
|
112
|
+
if (n > 10 && n < 20) return many;
|
|
113
|
+
if (last === 1) return one;
|
|
114
|
+
if (last >= 2 && last <= 4) return few;
|
|
115
|
+
return many;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* ------------------------------------------------------------------ */
|
|
119
|
+
/* primitives */
|
|
120
|
+
/* ------------------------------------------------------------------ */
|
|
121
|
+
|
|
122
|
+
function Ring(props) {
|
|
123
|
+
const pressure = Math.max(0, Math.min(1, Number(props.pressure) || 0));
|
|
124
|
+
const color = pressureColor(pressure);
|
|
125
|
+
const radius = 52;
|
|
126
|
+
const circumference = 2 * Math.PI * radius;
|
|
127
|
+
const dash = circumference * pressure;
|
|
128
|
+
return h("svg", {
|
|
129
|
+
className: "tl-ring",
|
|
130
|
+
width: 132, height: 132, viewBox: "0 0 132 132",
|
|
131
|
+
role: "img",
|
|
132
|
+
"aria-label": "Заполнение контекстного окна " + percent(pressure)
|
|
133
|
+
}, [
|
|
134
|
+
h("circle", { key: "track", cx: 66, cy: 66, r: radius, fill: "none", stroke: "color-mix(in oklab, currentColor 12%, transparent)", strokeWidth: 9 }),
|
|
135
|
+
h("circle", {
|
|
136
|
+
key: "value", cx: 66, cy: 66, r: radius, fill: "none", stroke: color, strokeWidth: 9,
|
|
137
|
+
strokeLinecap: "round", strokeDasharray: dash + " " + circumference,
|
|
138
|
+
transform: "rotate(-90 66 66)",
|
|
139
|
+
style: { transition: "stroke-dasharray 600ms cubic-bezier(.16,1,.3,1), stroke 300ms ease" }
|
|
140
|
+
}),
|
|
141
|
+
h("text", { key: "pct", x: 66, y: 62, textAnchor: "middle", fontSize: 25, fontWeight: 720, fill: "currentColor", style: { fontVariantNumeric: "tabular-nums", letterSpacing: "-.02em" } }, percent(pressure)),
|
|
142
|
+
h("text", { key: "cap", x: 66, y: 82, textAnchor: "middle", fontSize: 9.5, fontWeight: 650, letterSpacing: ".12em", fill: "currentColor", opacity: 0.55 }, "КОНТЕКСТ")
|
|
143
|
+
]);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function Sparkline(props) {
|
|
147
|
+
const values = Array.isArray(props.series) ? props.series.filter((value) => Number.isFinite(value)) : [];
|
|
148
|
+
if (values.length < 2) return null;
|
|
149
|
+
const width = 560;
|
|
150
|
+
const height = 74;
|
|
151
|
+
const max = Math.max(1, ...values);
|
|
152
|
+
const step = width / (values.length - 1);
|
|
153
|
+
const points = values.map((value, index) => [index * step, height - 4 - (value / max) * (height - 12)]);
|
|
154
|
+
const line = points.map((point, index) => (index === 0 ? "M" : "L") + point[0].toFixed(1) + " " + point[1].toFixed(1)).join(" ");
|
|
155
|
+
const area = line + " L" + width + " " + height + " L0 " + height + " Z";
|
|
156
|
+
const id = "tl-spark-" + (props.id || "main");
|
|
157
|
+
return h("svg", { className: "tl-spark", viewBox: "0 0 " + width + " " + height, preserveAspectRatio: "none", "aria-hidden": "true" }, [
|
|
158
|
+
h("defs", { key: "defs" }, h("linearGradient", { id, x1: 0, y1: 0, x2: 0, y2: 1 }, [
|
|
159
|
+
h("stop", { key: "a", offset: "0%", stopColor: "var(--tl-accent, #0E6E5E)", stopOpacity: 0.28 }),
|
|
160
|
+
h("stop", { key: "b", offset: "100%", stopColor: "var(--tl-accent, #0E6E5E)", stopOpacity: 0 })
|
|
161
|
+
])),
|
|
162
|
+
h("path", { key: "area", d: area, fill: "url(#" + id + ")" }),
|
|
163
|
+
h("path", { key: "line", d: line, fill: "none", stroke: "var(--tl-accent, #0E6E5E)", strokeWidth: 2, strokeLinejoin: "round", strokeLinecap: "round", vectorEffect: "non-scaling-stroke" })
|
|
164
|
+
]);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function River(props) {
|
|
168
|
+
const groups = Array.isArray(props.groups) ? props.groups.filter((group) => group.tokens > 0) : [];
|
|
169
|
+
const total = groups.reduce((sum, group) => sum + group.tokens, 0) || 1;
|
|
170
|
+
return h("div", { className: "tl-river", role: "img", "aria-label": "Состав контекста по типам сообщений" },
|
|
171
|
+
groups.map((group) => h("span", {
|
|
172
|
+
key: group.id,
|
|
173
|
+
className: "tl-river-seg",
|
|
174
|
+
style: { width: Math.max(2, (group.tokens / total) * 100) + "%", background: group.color },
|
|
175
|
+
title: group.label + ": " + num(group.tokens)
|
|
176
|
+
}))
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/* ------------------------------------------------------------------ */
|
|
181
|
+
/* tile */
|
|
182
|
+
/* ------------------------------------------------------------------ */
|
|
183
|
+
|
|
184
|
+
function TokenTile(props) {
|
|
185
|
+
const state = useStore();
|
|
186
|
+
const useSessions = props && props.useSessions;
|
|
187
|
+
const sessionId = useSessions ? useSessions(currentSessionSelector) : undefined;
|
|
188
|
+
react.useEffect(() => {
|
|
189
|
+
if (sessionId) void loadReport(sessionId);
|
|
190
|
+
}, [sessionId]);
|
|
191
|
+
react.useEffect(() => {
|
|
192
|
+
const timer = setInterval(() => {
|
|
193
|
+
if (!document.hidden) void loadReport(sessionId, true);
|
|
194
|
+
}, 20000);
|
|
195
|
+
return () => clearInterval(timer);
|
|
196
|
+
}, [sessionId]);
|
|
197
|
+
|
|
198
|
+
const report = state.report && (!sessionId || state.report.sessionId === sessionId) ? state.report : null;
|
|
199
|
+
const total = report ? report.totalTokens : 0;
|
|
200
|
+
const pressure = report ? report.pressure : 0;
|
|
201
|
+
const color = pressureColor(pressure);
|
|
202
|
+
const delta = report && report.surfaceDeltaTokens > 0 ? report.surfaceDeltaTokens : 0;
|
|
203
|
+
|
|
204
|
+
const open = () => {
|
|
205
|
+
store.set({ open: true, sessionId: sessionId || state.sessionId });
|
|
206
|
+
void loadReport(sessionId || state.sessionId, true);
|
|
207
|
+
};
|
|
208
|
+
return h("button", {
|
|
209
|
+
type: "button",
|
|
210
|
+
className: "tl-tile",
|
|
211
|
+
"aria-label": "Открыть токен-лабораторию: " + (total ? num(total) + " токенов в контексте" : "нет данных"),
|
|
212
|
+
onClick: open
|
|
213
|
+
}, [
|
|
214
|
+
h("div", { className: "tl-tile-head", key: "head" }, [
|
|
215
|
+
h("span", { className: "tl-tile-label", key: "label" }, "Токены"),
|
|
216
|
+
h("span", { className: "tl-tile-pct", key: "pct", style: { color } }, report ? percent(pressure) : "—"),
|
|
217
|
+
h("span", { className: "tl-tile-dot", key: "dot", style: { background: color } })
|
|
218
|
+
]),
|
|
219
|
+
h("div", { className: "tl-tile-row", key: "row" }, [
|
|
220
|
+
h("b", { key: "b" }, report ? compact(total) : "—"),
|
|
221
|
+
h("span", { className: "tl-tile-cap", key: "cap" }, report ? "из " + compact(report.contextWindow) : "нет сессии"),
|
|
222
|
+
delta > 0 ? h("span", { className: "tl-tile-delta", key: "d" }, "+" + compact(delta)) : null
|
|
223
|
+
]),
|
|
224
|
+
h("div", { className: "tl-tile-bar", key: "bar" }, h("span", { style: { width: Math.max(2, Math.min(100, pressure * 100)) + "%", background: color } }))
|
|
225
|
+
]);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* The kit's sidebar tile stack owns the footer slot; hand it this tile so
|
|
229
|
+
* both live in one column instead of competing for the slot row. */
|
|
230
|
+
try {
|
|
231
|
+
if (typeof window !== "undefined") window.__DSH_TOKEN_TILE__ = TokenTile;
|
|
232
|
+
} catch {}
|
|
233
|
+
|
|
234
|
+
/* ------------------------------------------------------------------ */
|
|
235
|
+
/* panel */
|
|
236
|
+
/* ------------------------------------------------------------------ */
|
|
237
|
+
|
|
238
|
+
function TokenPanel() {
|
|
239
|
+
const state = useStore();
|
|
240
|
+
react.useEffect(() => {
|
|
241
|
+
if (!state.open) return undefined;
|
|
242
|
+
void loadReport(state.sessionId, true);
|
|
243
|
+
const timer = setInterval(() => {
|
|
244
|
+
if (!document.hidden) void loadReport(state.sessionId, true);
|
|
245
|
+
}, 10000);
|
|
246
|
+
const onKey = (event) => { if (event.key === "Escape") store.set({ open: false }); };
|
|
247
|
+
document.addEventListener("keydown", onKey);
|
|
248
|
+
return () => { clearInterval(timer); document.removeEventListener("keydown", onKey); };
|
|
249
|
+
}, [state.open, state.sessionId]);
|
|
250
|
+
|
|
251
|
+
if (!state.open) return null;
|
|
252
|
+
const report = state.report;
|
|
253
|
+
const close = () => store.set({ open: false });
|
|
254
|
+
const pressure = report ? report.pressure : 0;
|
|
255
|
+
const color = pressureColor(pressure);
|
|
256
|
+
const groups = report ? report.groups : [];
|
|
257
|
+
const top = report ? report.top : [];
|
|
258
|
+
const skills = report ? report.library : null;
|
|
259
|
+
const savings = skills ? Math.max(0, skills.totalTokens - skills.typicalTokens) : 0;
|
|
260
|
+
|
|
261
|
+
return h("div", { className: "tl-backdrop", onClick: (event) => { if (event.target === event.currentTarget) close(); } },
|
|
262
|
+
h("section", { className: "tl-panel", role: "dialog", "aria-modal": "true", "aria-label": "Токен-лаборатория", style: { "--tl-accent": color } }, [
|
|
263
|
+
h("header", { className: "tl-head", key: "head" }, [
|
|
264
|
+
h("div", { key: "titles" }, [
|
|
265
|
+
h("div", { className: "tl-kicker", key: "k" }, "ТОКЕН-ЛАБОРАТОРИЯ"),
|
|
266
|
+
h("h2", { className: "tl-title", key: "t" }, "Куда уходит контекст"),
|
|
267
|
+
h("p", { className: "tl-sub", key: "s" }, report
|
|
268
|
+
? "Текущая сессия · " + num(report.nodes) + " " + plural(report.nodes, "узел", "узла", "узлов") + " · обновляется вживую"
|
|
269
|
+
: "Подключаемся к сессии…")
|
|
270
|
+
]),
|
|
271
|
+
h("button", { className: "tl-close", key: "c", type: "button", onClick: close, "aria-label": "Закрыть" }, "×")
|
|
272
|
+
]),
|
|
273
|
+
|
|
274
|
+
state.error ? h("div", { className: "tl-error", key: "err" }, state.error) : null,
|
|
275
|
+
|
|
276
|
+
report ? h("div", { className: "tl-hero", key: "hero" }, [
|
|
277
|
+
h("div", { className: "tl-hero-main", key: "main" }, [
|
|
278
|
+
h("div", { className: "tl-big", key: "big" }, [
|
|
279
|
+
h("b", { key: "n" }, num(report.totalTokens)),
|
|
280
|
+
h("span", { key: "u" }, "токенов в контексте")
|
|
281
|
+
]),
|
|
282
|
+
h("div", { className: "tl-meta", key: "meta" }, [
|
|
283
|
+
h("span", { key: "window" }, "окно " + num(report.contextWindow)),
|
|
284
|
+
h("span", { key: "left" }, "свободно " + num(Math.max(0, report.contextWindow - report.totalTokens))),
|
|
285
|
+
report.surfaceDeltaTokens > 0 ? h("span", { key: "d", className: "tl-up" }, "+" + num(report.surfaceDeltaTokens) + " за последний шаг") : null
|
|
286
|
+
]),
|
|
287
|
+
h("div", { className: "tl-status", key: "st", style: { color } }, [
|
|
288
|
+
h("span", { className: "tl-status-dot", key: "d", style: { background: color } }),
|
|
289
|
+
h("span", { key: "t" }, pressureLabel(pressure))
|
|
290
|
+
])
|
|
291
|
+
]),
|
|
292
|
+
h("div", { className: "tl-hero-ring", key: "ring" }, [
|
|
293
|
+
h(Ring, { key: "r", pressure }),
|
|
294
|
+
h("div", { className: "tl-hero-hint", key: "h" }, report.baseline && report.baseline.kind === "usage" ? "по факту провайдера" : "оценка DSH")
|
|
295
|
+
]),
|
|
296
|
+
h("div", { className: "tl-spark-wrap", key: "spark" }, [
|
|
297
|
+
h("div", { className: "tl-spark-head", key: "sh" }, [
|
|
298
|
+
h("span", { key: "l" }, "Рост контекста"),
|
|
299
|
+
h("span", { key: "v" }, compact(report.totalTokens))
|
|
300
|
+
]),
|
|
301
|
+
h(Sparkline, { key: "s", series: report.series })
|
|
302
|
+
])
|
|
303
|
+
]) : null,
|
|
304
|
+
|
|
305
|
+
report ? h("div", { className: "tl-grid", key: "grid" }, [
|
|
306
|
+
h("div", { className: "tl-card tl-card-wide", key: "composition" }, [
|
|
307
|
+
h("div", { className: "tl-card-head", key: "h" }, [
|
|
308
|
+
h("h3", { key: "t" }, "Состав"),
|
|
309
|
+
h("span", { key: "s" }, "по типам сообщений")
|
|
310
|
+
]),
|
|
311
|
+
h(River, { key: "river", groups }),
|
|
312
|
+
h("div", { className: "tl-legend", key: "legend" }, groups.map((group) => h("div", { className: "tl-legend-row", key: group.id }, [
|
|
313
|
+
h("span", { className: "tl-swatch", key: "sw", style: { background: group.color } }),
|
|
314
|
+
h("span", { className: "tl-legend-label", key: "l" }, group.label),
|
|
315
|
+
h("span", { className: "tl-legend-count", key: "c" }, group.count + " " + plural(group.count, "узел", "узла", "узлов")),
|
|
316
|
+
h("span", { className: "tl-legend-tokens", key: "t" }, num(group.tokens)),
|
|
317
|
+
h("span", { className: "tl-legend-pct", key: "p" }, report.totalTokens > 0 ? percent(group.tokens / report.totalTokens) : "0%")
|
|
318
|
+
])))
|
|
319
|
+
]),
|
|
320
|
+
h("div", { className: "tl-card", key: "top" }, [
|
|
321
|
+
h("div", { className: "tl-card-head", key: "h" }, [
|
|
322
|
+
h("h3", { key: "t" }, "Самые тяжёлые"),
|
|
323
|
+
h("span", { key: "s" }, "топ-6 узлов")
|
|
324
|
+
]),
|
|
325
|
+
h("div", { className: "tl-top", key: "list" }, top.length > 0 ? top.map((node, index) => {
|
|
326
|
+
const max = Math.max(1, ...top.map((item) => item.tokens));
|
|
327
|
+
return h("div", { className: "tl-top-row", key: node.seq }, [
|
|
328
|
+
h("span", { className: "tl-rank", key: "r" }, String(index + 1).padStart(2, "0")),
|
|
329
|
+
h("div", { className: "tl-top-body", key: "b" }, [
|
|
330
|
+
h("div", { className: "tl-top-text", key: "t" }, node.label || "Системный блок"),
|
|
331
|
+
h("div", { className: "tl-top-bar", key: "bar" }, h("span", { style: { width: Math.max(4, (node.tokens / max) * 100) + "%" } }))
|
|
332
|
+
]),
|
|
333
|
+
h("span", { className: "tl-top-tokens", key: "n" }, num(node.tokens))
|
|
334
|
+
]);
|
|
335
|
+
}) : h("div", { className: "tl-empty", key: "empty" }, "Пока пусто: в этой сессии ещё нет сообщений. Откройте рабочую сессию — состав появится здесь."))
|
|
336
|
+
]),
|
|
337
|
+
h("div", { className: "tl-card tl-savings", key: "savings" }, [
|
|
338
|
+
h("div", { className: "tl-card-head", key: "h" }, [
|
|
339
|
+
h("h3", { key: "t" }, "Экономия навыков"),
|
|
340
|
+
h("span", { key: "s" }, "библиотека " + (skills ? skills.count : 0) + " шт.")
|
|
341
|
+
]),
|
|
342
|
+
h("div", { className: "tl-save-num", key: "n" }, [
|
|
343
|
+
h("b", { key: "b" }, "−" + compact(savings)),
|
|
344
|
+
h("span", { key: "s" }, "токенов против загрузки всей библиотеки")
|
|
345
|
+
]),
|
|
346
|
+
h("div", { className: "tl-save-compare", key: "cmp" }, [
|
|
347
|
+
h("div", { key: "a" }, [h("span", { key: "l" }, "вся библиотека"), h("b", { key: "v" }, compact(skills ? skills.totalTokens : 0))]),
|
|
348
|
+
h("div", { className: "tl-save-arrow", key: "ar" }, "→"),
|
|
349
|
+
h("div", { key: "b" }, [h("span", { key: "l" }, "3 средних ядра"), h("b", { key: "v", style: { color } }, compact(skills ? skills.typicalTokens : 0))])
|
|
350
|
+
]),
|
|
351
|
+
h("div", { className: "tl-save-list", key: "list" }, (skills ? skills.heaviest.slice(0, 4) : []).map((skill) => h("div", { className: "tl-save-row", key: skill.name }, [
|
|
352
|
+
h("span", { key: "n" }, skill.title),
|
|
353
|
+
h("span", { key: "t" }, compact(skill.tokens))
|
|
354
|
+
]))),
|
|
355
|
+
h("div", { className: "tl-save-foot", key: "foot" }, report.autopilot.mode === "on"
|
|
356
|
+
? "Автопилот подключает нужные ядра сам — до " + report.autopilot.maxSkills + " навыков"
|
|
357
|
+
: "Автопилот выключен: навыки грузятся вручную")
|
|
358
|
+
])
|
|
359
|
+
]) : h("div", { className: "tl-skeleton", key: "sk" }, "Считаем контекст…"),
|
|
360
|
+
|
|
361
|
+
h("footer", { className: "tl-foot", key: "foot" }, [
|
|
362
|
+
h("span", { key: "l" }, report ? "Сессия " + report.sessionId.slice(0, 18) + "…" : ""),
|
|
363
|
+
h("span", { key: "r" }, report ? "обновлено " + new Date(report.generatedAt).toLocaleTimeString("ru-RU") : "")
|
|
364
|
+
])
|
|
365
|
+
])
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/* ------------------------------------------------------------------ */
|
|
370
|
+
/* styles */
|
|
371
|
+
/* ------------------------------------------------------------------ */
|
|
372
|
+
|
|
373
|
+
const CSS = `
|
|
374
|
+
.tl-tile{display:flex;flex-direction:column;gap:5px;width:100%;box-sizing:border-box;margin:0;padding:8px 10px;border-radius:12px;border:1px solid var(--dsw-alias-border-l2, rgba(0,0,0,.10));background:var(--dsw-alias-settings-card-fill, rgba(0,0,0,.035));color:var(--dsw-alias-label-secondary,#555);font:inherit;text-align:left;cursor:pointer;transition:background-color .16s ease,border-color .16s ease,transform .16s ease}
|
|
375
|
+
.tl-tile:hover{background:color-mix(in oklab,var(--dsw-alias-label-primary,#111) 6%,transparent);border-color:color-mix(in oklab,var(--dsw-alias-label-primary,#111) 18%,transparent)}
|
|
376
|
+
.tl-tile:active{transform:scale(.995)}
|
|
377
|
+
.tl-tile:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#0E6E5E);outline-offset:2px}
|
|
378
|
+
.tl-tile-head{display:flex;align-items:center;justify-content:space-between;gap:6px}
|
|
379
|
+
.tl-tile-label{font-size:9.5px;font-weight:650;letter-spacing:.1em;text-transform:uppercase;opacity:.72;flex:1 1 auto}
|
|
380
|
+
.tl-tile-pct{font-size:10px;font-weight:700;font-variant-numeric:tabular-nums;letter-spacing:.01em}
|
|
381
|
+
.tl-tile-dot{width:6px;height:6px;border-radius:999px;flex:0 0 auto}
|
|
382
|
+
.tl-tile-row{display:flex;align-items:baseline;gap:5px;color:var(--dsw-alias-label-primary,#111);font-variant-numeric:tabular-nums;line-height:1;min-width:0}
|
|
383
|
+
.tl-tile-row b{font-size:17px;font-weight:720;letter-spacing:-.02em}
|
|
384
|
+
.tl-tile-cap{font-size:9.5px;font-weight:550;opacity:.55;white-space:nowrap}
|
|
385
|
+
.tl-tile-bar{height:3px;border-radius:999px;background:color-mix(in oklab,currentColor 12%,transparent);overflow:hidden}
|
|
386
|
+
.tl-tile-bar span{display:block;height:100%;border-radius:999px;transition:width .5s cubic-bezier(.16,1,.3,1)}
|
|
387
|
+
.tl-tile-delta{font-size:9.5px;font-weight:700;color:#8E2B18;margin-left:auto}
|
|
388
|
+
|
|
389
|
+
.tl-backdrop{position:fixed;inset:0;z-index:9050;display:flex;align-items:center;justify-content:center;padding:24px;background:color-mix(in srgb,#070b0a 44%,transparent);animation:tl-fade .18s ease-out}
|
|
390
|
+
.tl-panel{position:relative;display:flex;flex-direction:column;width:min(1080px,94vw);max-height:90vh;overflow:auto;overscroll-behavior:contain;border-radius:20px;border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.1));background:var(--dsw-alias-bg-base,#fff);color:var(--dsw-alias-label-primary,#111);box-shadow:0 48px 140px -50px rgba(0,10,8,.6),0 1px 0 color-mix(in srgb,#fff 65%,transparent) inset;animation:tl-in .32s cubic-bezier(.16,1,.3,1)}
|
|
391
|
+
.tl-head{flex:none;display:flex;align-items:flex-start;justify-content:space-between;gap:18px;padding:24px 26px 16px;background:radial-gradient(120% 160% at 100% 0%,color-mix(in srgb,var(--tl-accent,#0E6E5E) 10%,transparent) 0%,transparent 58%),linear-gradient(180deg,color-mix(in srgb,var(--dsw-alias-bg-layer-1,#f6f7f6) 70%,transparent),transparent);border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
392
|
+
.tl-kicker{display:flex;align-items:center;gap:9px;font-size:10px;font-weight:750;letter-spacing:.18em;color:var(--dsw-alias-label-secondary,#6b7280)}
|
|
393
|
+
.tl-kicker::before{content:"";width:26px;height:1px;background:color-mix(in srgb,var(--tl-accent,#0E6E5E) 65%,transparent)}
|
|
394
|
+
.tl-title{margin:7px 0 0;font-size:25px;line-height:1.15;font-weight:720;letter-spacing:-.025em}
|
|
395
|
+
.tl-sub{margin:5px 0 0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#5b6472);font-variant-numeric:tabular-nums}
|
|
396
|
+
.tl-close{flex:none;width:34px;height:34px;border-radius:10px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.14));background:transparent;color:var(--dsw-alias-label-secondary,#555);font-size:18px;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center}
|
|
397
|
+
.tl-close:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06));color:var(--dsw-alias-label-primary,#111)}
|
|
398
|
+
.tl-close:focus-visible,.tl-panel button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#0E6E5E);outline-offset:2px}
|
|
399
|
+
.tl-hero{display:grid;grid-template-columns:minmax(0,1.25fr) auto minmax(0,1fr);gap:26px;align-items:center;padding:24px 26px 20px}
|
|
400
|
+
.tl-big{display:flex;align-items:baseline;gap:10px;font-variant-numeric:tabular-nums}
|
|
401
|
+
.tl-big b{font-size:44px;line-height:1;font-weight:730;letter-spacing:-.04em}
|
|
402
|
+
.tl-big span{font-size:12px;font-weight:550;color:var(--dsw-alias-label-secondary,#5b6472)}
|
|
403
|
+
.tl-meta{display:flex;flex-wrap:wrap;gap:6px 14px;margin-top:10px;font-size:11.5px;color:var(--dsw-alias-label-secondary,#5b6472);font-variant-numeric:tabular-nums}
|
|
404
|
+
.tl-meta .tl-up{color:#8E2B18;font-weight:650}
|
|
405
|
+
.tl-status{display:inline-flex;align-items:center;gap:8px;margin-top:14px;font-size:12px;font-weight:650}
|
|
406
|
+
.tl-status-dot{width:8px;height:8px;border-radius:999px;box-shadow:0 0 0 4px color-mix(in srgb,currentColor 14%,transparent)}
|
|
407
|
+
.tl-hero-ring{display:flex;flex-direction:column;align-items:center;gap:6px;color:var(--dsw-alias-label-primary,#111)}
|
|
408
|
+
.tl-ring{display:block}
|
|
409
|
+
.tl-hero-hint{font-size:9.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--dsw-alias-label-secondary,#7b8490)}
|
|
410
|
+
.tl-spark-wrap{display:flex;flex-direction:column;gap:8px;min-width:0}
|
|
411
|
+
.tl-spark-head{display:flex;align-items:baseline;justify-content:space-between;gap:8px;font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--dsw-alias-label-secondary,#7b8490);font-weight:650}
|
|
412
|
+
.tl-spark-head span:last-child{font-size:12px;letter-spacing:0;text-transform:none;color:var(--dsw-alias-label-primary,#111);font-variant-numeric:tabular-nums}
|
|
413
|
+
.tl-spark{width:100%;height:74px;display:block}
|
|
414
|
+
.tl-grid{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);gap:14px;padding:4px 26px 22px}
|
|
415
|
+
.tl-card{border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));border-radius:14px;padding:16px 16px 14px;background:var(--dsw-alias-bg-layer-1,#f7f8f7);min-width:0}
|
|
416
|
+
.tl-card-wide{grid-column:1 / -1}
|
|
417
|
+
.tl-card-head{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin-bottom:12px}
|
|
418
|
+
.tl-card-head h3{margin:0;font-size:12px;font-weight:700;letter-spacing:.02em}
|
|
419
|
+
.tl-card-head span{font-size:10px;letter-spacing:.08em;text-transform:uppercase;color:var(--dsw-alias-label-secondary,#7b8490);font-weight:650}
|
|
420
|
+
.tl-river{display:flex;height:12px;border-radius:999px;overflow:hidden;background:color-mix(in oklab,currentColor 8%,transparent)}
|
|
421
|
+
.tl-river-seg{display:block;height:100%;min-width:3px;transition:width .55s cubic-bezier(.16,1,.3,1);box-shadow:inset -1px 0 0 color-mix(in srgb,#fff 30%,transparent)}
|
|
422
|
+
.tl-legend{display:flex;flex-direction:column;margin-top:12px}
|
|
423
|
+
.tl-legend-row{display:grid;grid-template-columns:10px minmax(0,1fr) 76px 64px 44px;align-items:center;gap:10px;padding:5px 0;border-top:1px solid color-mix(in oklab,currentColor 7%,transparent);font-size:11.5px;font-variant-numeric:tabular-nums}
|
|
424
|
+
.tl-legend-row:first-child{border-top:0}
|
|
425
|
+
.tl-swatch{width:8px;height:8px;border-radius:2px}
|
|
426
|
+
.tl-legend-label{color:var(--dsw-alias-label-primary,#111);font-weight:550;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
427
|
+
.tl-legend-count{color:var(--dsw-alias-label-secondary,#7b8490);font-size:10.5px}
|
|
428
|
+
.tl-legend-tokens{text-align:right;font-weight:650}
|
|
429
|
+
.tl-legend-pct{text-align:right;color:var(--dsw-alias-label-secondary,#7b8490);font-size:10.5px}
|
|
430
|
+
.tl-top{display:flex;flex-direction:column;gap:10px}
|
|
431
|
+
.tl-top-row{display:grid;grid-template-columns:22px minmax(0,1fr) auto;gap:10px;align-items:center}
|
|
432
|
+
.tl-rank{font-size:10px;font-weight:700;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-secondary,#7b8490)}
|
|
433
|
+
.tl-top-body{min-width:0;display:flex;flex-direction:column;gap:5px}
|
|
434
|
+
.tl-top-text{font-size:11.5px;line-height:15px;color:var(--dsw-alias-label-primary,#111);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
435
|
+
.tl-top-bar{height:3px;border-radius:999px;background:color-mix(in oklab,currentColor 9%,transparent);overflow:hidden}
|
|
436
|
+
.tl-top-bar span{display:block;height:100%;border-radius:999px;background:var(--tl-accent,#0E6E5E);opacity:.85;transition:width .5s cubic-bezier(.16,1,.3,1)}
|
|
437
|
+
.tl-top-tokens{font-size:11px;font-weight:650;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-secondary,#5b6472)}
|
|
438
|
+
.tl-savings{display:flex;flex-direction:column}
|
|
439
|
+
.tl-save-num{display:flex;align-items:baseline;gap:8px;margin-bottom:10px}
|
|
440
|
+
.tl-save-num b{font-size:30px;line-height:1;font-weight:730;letter-spacing:-.035em;color:#0E6E5E;font-variant-numeric:tabular-nums}
|
|
441
|
+
.tl-save-num span{font-size:11px;color:var(--dsw-alias-label-secondary,#5b6472);max-width:130px;line-height:14px}
|
|
442
|
+
.tl-save-compare{display:flex;align-items:center;gap:12px;padding:10px 12px;border-radius:10px;border:1px dashed color-mix(in oklab,currentColor 16%,transparent);font-variant-numeric:tabular-nums}
|
|
443
|
+
.tl-save-compare>div{display:flex;flex-direction:column;gap:2px}
|
|
444
|
+
.tl-save-compare span{font-size:9.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--dsw-alias-label-secondary,#7b8490)}
|
|
445
|
+
.tl-save-compare b{font-size:16px;font-weight:700}
|
|
446
|
+
.tl-save-arrow{font-size:15px;opacity:.5;display:flex;align-items:center}
|
|
447
|
+
.tl-save-list{margin-top:10px;display:flex;flex-direction:column}
|
|
448
|
+
.tl-save-row{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:4px 0;border-top:1px solid color-mix(in oklab,currentColor 7%,transparent);font-size:11px;color:var(--dsw-alias-label-secondary,#5b6472)}
|
|
449
|
+
.tl-save-row span:last-child{font-variant-numeric:tabular-nums;font-weight:600;color:var(--dsw-alias-label-primary,#111)}
|
|
450
|
+
.tl-save-foot{margin-top:auto;padding-top:10px;font-size:10.5px;line-height:15px;color:var(--dsw-alias-label-secondary,#7b8490)}
|
|
451
|
+
.tl-error{margin:0 26px 8px;padding:10px 12px;border-radius:10px;border:1px solid color-mix(in srgb,#8E2B18 35%,transparent);background:color-mix(in srgb,#8E2B18 8%,transparent);color:#8E2B18;font-size:12px}
|
|
452
|
+
.tl-skeleton{padding:60px 26px;text-align:center;color:var(--dsw-alias-label-secondary,#7b8490);font-size:13px}
|
|
453
|
+
.tl-empty{padding:18px 2px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#7b8490)}
|
|
454
|
+
.tl-foot{flex:none;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:12px 26px;border-top:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07));font-size:10px;color:var(--dsw-alias-label-secondary,#7b8490);font-variant-numeric:tabular-nums}
|
|
455
|
+
@keyframes tl-fade{from{opacity:0}to{opacity:1}}
|
|
456
|
+
@keyframes tl-in{from{opacity:0;transform:translateY(10px) scale(.985)}to{opacity:1;transform:none}}
|
|
457
|
+
@media (max-width: 900px){
|
|
458
|
+
.tl-hero{grid-template-columns:minmax(0,1fr) auto;gap:18px}
|
|
459
|
+
.tl-spark-wrap{grid-column:1 / -1}
|
|
460
|
+
.tl-grid{grid-template-columns:1fr}
|
|
461
|
+
.tl-card-wide{grid-column:auto}
|
|
462
|
+
}
|
|
463
|
+
@media (max-width: 640px){
|
|
464
|
+
.tl-backdrop{padding:0}
|
|
465
|
+
.tl-panel{width:100%;max-height:100vh;height:100%;border-radius:0;border-left:0;border-right:0}
|
|
466
|
+
.tl-head{padding:18px 16px 12px}
|
|
467
|
+
.tl-title{font-size:21px}
|
|
468
|
+
.tl-hero{grid-template-columns:1fr;padding:18px 16px 14px;gap:14px}
|
|
469
|
+
.tl-hero-ring{flex-direction:row;justify-content:flex-start;gap:14px}
|
|
470
|
+
.tl-big b{font-size:34px}
|
|
471
|
+
.tl-grid{padding:4px 16px 18px}
|
|
472
|
+
.tl-legend-row{grid-template-columns:8px minmax(0,1fr) auto}
|
|
473
|
+
.tl-legend-count{display:none}
|
|
474
|
+
.tl-legend-pct{display:none}
|
|
475
|
+
.tl-foot{padding:10px 16px}
|
|
476
|
+
}
|
|
477
|
+
`;
|
|
478
|
+
|
|
479
|
+
/* ------------------------------------------------------------------ */
|
|
480
|
+
/* apply */
|
|
481
|
+
/* ------------------------------------------------------------------ */
|
|
482
|
+
|
|
483
|
+
function apply(ctx) {
|
|
484
|
+
pluginCtx = ctx;
|
|
485
|
+
ctx.effect(() => {
|
|
486
|
+
const style = document.createElement("style");
|
|
487
|
+
style.id = "dsh-token-lab";
|
|
488
|
+
style.textContent = CSS;
|
|
489
|
+
document.head.appendChild(style);
|
|
490
|
+
return () => style.remove();
|
|
491
|
+
}, "token-lab: styles");
|
|
492
|
+
|
|
493
|
+
ctx.effect(() => ctx.slots.inject("shell.overlay", () => ctx.slots.register({
|
|
494
|
+
name: "shell.overlay",
|
|
495
|
+
id: "token-lab-panel",
|
|
496
|
+
order: 650
|
|
497
|
+
}, TokenPanel)), "token-lab: panel");
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
exports.apply = apply;
|
|
501
|
+
exports.inject = inject;
|
|
502
|
+
return module.exports;
|
|
503
|
+
}
|
|
504
|
+
});
|