dsh-desktop-kit 1.0.0 → 2.0.1
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 +97 -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
package/client.js
CHANGED
|
@@ -88,6 +88,11 @@ window.__ModuleLoader__.load({
|
|
|
88
88
|
if (value % 10 >= 2 && value % 10 <= 4 && (value % 100 < 12 || value % 100 > 14)) return value + " запуска";
|
|
89
89
|
return value + " запусков";
|
|
90
90
|
}
|
|
91
|
+
function compactTokens(value) {
|
|
92
|
+
const n = Number(value) || 0;
|
|
93
|
+
if (n >= 1000) return (n / 1000).toFixed(1).replace(".0", "").replace(".", ",") + "k";
|
|
94
|
+
return String(n);
|
|
95
|
+
}
|
|
91
96
|
function shortDate(iso) {
|
|
92
97
|
if (!iso) return "—";
|
|
93
98
|
try {
|
|
@@ -448,7 +453,7 @@ window.__ModuleLoader__.load({
|
|
|
448
453
|
{ id: "design", label: "Дизайн", color: "#0E6E5E", skills: ["wow-design", "design-critique", "ui-design-system"] },
|
|
449
454
|
{ id: "seo", label: "SEO", color: "#2C5F8A", skills: ["seo"] },
|
|
450
455
|
{ id: "content", label: "Контент", color: "#B36A43", skills: ["content-writing"] },
|
|
451
|
-
{ id: "process", label: "Процесс", color: "#6B7275", skills: ["dev-workflow", "requirements-and-planning", "skills-router"] },
|
|
456
|
+
{ id: "process", label: "Процесс", color: "#6B7275", skills: ["dev-workflow", "requirements-and-planning", "skills-router", "context-economy"] },
|
|
452
457
|
{ id: "quality", label: "Качество", color: "#1F7A57", skills: ["testing-strategy", "debugging-playbook", "code-review-checklist"] },
|
|
453
458
|
{ id: "safety", label: "Данные", color: "#94661C", skills: ["database-safety", "security-review", "pg-upgrade-verify"] },
|
|
454
459
|
{ id: "release", label: "Релиз", color: "#2C5F8A", skills: ["release-and-deploy", "git-and-ci"] },
|
|
@@ -456,7 +461,7 @@ window.__ModuleLoader__.load({
|
|
|
456
461
|
{ id: "memory", label: "Память", color: "#5B4B8A", skills: ["project-deep-dive", "project-memory-maintenance"] }
|
|
457
462
|
];
|
|
458
463
|
const SKILL_NAMES = SKILL_GROUPS.reduce((acc, group) => acc.concat(group.skills), []);
|
|
459
|
-
const SKILLS_TILE_STYLE = Object.assign({}, TILE_BASE, { display: "block", padding: "10px
|
|
464
|
+
const SKILLS_TILE_STYLE = Object.assign({}, TILE_BASE, { display: "block", padding: "8px 10px", cursor: "pointer" });
|
|
460
465
|
const SKILLS_HEAD_STYLE = { display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: "8px" };
|
|
461
466
|
const SKILLS_COUNT_STYLE = {
|
|
462
467
|
display: "inline-flex", alignItems: "center", justifyContent: "center",
|
|
@@ -469,14 +474,14 @@ window.__ModuleLoader__.load({
|
|
|
469
474
|
fontSize: "10px", fontWeight: 500, marginTop: "2px", letterSpacing: "0.02em",
|
|
470
475
|
color: "var(--dsw-alias-label-secondary, #777)"
|
|
471
476
|
};
|
|
472
|
-
const SKILLS_CHIPS_STYLE = { display: "flex", flexWrap: "
|
|
477
|
+
const SKILLS_CHIPS_STYLE = { display: "flex", flexWrap: "nowrap", gap: "4px", marginTop: "6px", overflow: "hidden", maskImage: "linear-gradient(to right, #000 88%, transparent 100%)", WebkitMaskImage: "linear-gradient(to right, #000 88%, transparent 100%)" };
|
|
473
478
|
const SKILLS_CHIP_STYLE = {
|
|
474
|
-
fontSize: "
|
|
479
|
+
fontSize: "9.5px", fontWeight: 600, lineHeight: 1.45,
|
|
475
480
|
color: "var(--dsw-alias-label-secondary, #666)",
|
|
476
481
|
border: "1px solid var(--dsw-alias-border-l2, rgba(0,0,0,.10))",
|
|
477
482
|
borderRadius: "999px", padding: "1px 6px",
|
|
478
483
|
background: "var(--dsw-alias-settings-card-fill, rgba(0,0,0,.03))",
|
|
479
|
-
whiteSpace: "nowrap"
|
|
484
|
+
whiteSpace: "nowrap", flex: "0 0 auto"
|
|
480
485
|
};
|
|
481
486
|
const SKILLS_TOGGLE_STYLE = {
|
|
482
487
|
marginTop: "8px", display: "flex", alignItems: "center", justifyContent: "space-between", gap: "8px",
|
|
@@ -540,10 +545,20 @@ window.__ModuleLoader__.load({
|
|
|
540
545
|
.wtg9ga_root .GAkraW_groupSection.gkb-collapsed > .wnFoaW_projectRow::after { content: "▸"; }
|
|
541
546
|
.wtg9ga_root .GAkraW_groupSection.gkb-collapsed > span:not(:first-child) { display: none !important; }
|
|
542
547
|
.wtg9ga_root .wnFoaW_projectRow:hover::after { opacity: 1; color: var(--dsw-alias-label-primary, #111); }
|
|
548
|
+
/* Short windows: the sidebar must still show the account/settings foot. */
|
|
549
|
+
@media (max-height: 820px) {
|
|
550
|
+
.wtg9ga_root > .wtg9ga_regionArea { min-height: 140px; }
|
|
551
|
+
.gkb-skills-chips { display: none !important; }
|
|
552
|
+
.gkb-skills-active { margin-top: 4px !important; }
|
|
553
|
+
}
|
|
554
|
+
/* Mobile rail is 55px: tiles cannot fit, so they live behind the FAB. */
|
|
555
|
+
@media (max-width: 760px) {
|
|
556
|
+
[data-gkb-tiles] { display: none !important; }
|
|
557
|
+
}
|
|
543
558
|
`;
|
|
544
559
|
const SKILLS_CATALOG_CSS = `
|
|
545
560
|
.gkb-sk-backdrop{position:fixed;inset:0;z-index:9000;display:flex;align-items:center;justify-content:center;padding:24px;background:color-mix(in srgb,#070b0a 46%,transparent);backdrop-filter:blur(4px);animation:gkb-sk-fade .18s ease-out}
|
|
546
|
-
.gkb-sk-panel{position:relative;display:flex;flex-direction:column;width:min(1120px,94vw);max-height:
|
|
561
|
+
.gkb-sk-panel{position:relative;display:flex;flex-direction:column;width:min(1120px,94vw);max-height:92vh;border-radius:18px;border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.1));background:var(--dsw-alias-bg-base,#fff);box-shadow:0 48px 140px -50px rgba(0,10,8,.55),0 1px 0 color-mix(in srgb,#fff 65%,transparent) inset;overflow:hidden;animation:gkb-sk-in .28s cubic-bezier(.16,1,.3,1)}
|
|
547
562
|
.gkb-sk-head{flex:none;display:flex;flex-direction:column;gap:12px;padding:22px 24px 14px;background:
|
|
548
563
|
radial-gradient(120% 160% at 100% 0%,color-mix(in srgb,var(--dsw-alias-brand-primary,#0E6E5E) 10%,transparent) 0%,transparent 55%),
|
|
549
564
|
linear-gradient(180deg,color-mix(in srgb,var(--dsw-alias-bg-layer-1,#f6f7f6) 70%,transparent) 0%,transparent 100%);
|
|
@@ -556,7 +571,8 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
556
571
|
.gkb-sk-sub b{color:var(--dsw-alias-label-primary,#111);font-weight:650}
|
|
557
572
|
.gkb-sk-close{flex:none;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.14));background:transparent;color:var(--dsw-alias-label-secondary,#555);border-radius:9px;width:32px;height:32px;font-size:17px;line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center}
|
|
558
573
|
.gkb-sk-close:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06));color:var(--dsw-alias-label-primary,#111)}
|
|
559
|
-
.gkb-sk-close:focus-visible,.gkb-sk-card:focus-visible,.gkb-sk-chip:focus-visible,.gkb-sk-btn:focus-visible,.gkb-sk-
|
|
574
|
+
.gkb-sk-close:focus-visible,.gkb-sk-card:focus-visible,.gkb-sk-chip:focus-visible,.gkb-sk-btn:focus-visible,.gkb-sk-sort:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#0E6E5E);outline-offset:2px}
|
|
575
|
+
.gkb-sk-search:focus,.gkb-sk-search:focus-visible,.gkb-sk-input:focus,.gkb-sk-input:focus-visible{border-color:color-mix(in srgb,var(--dsw-alias-brand-primary,#0E6E5E) 55%,transparent);outline:none;box-shadow:0 0 0 3px color-mix(in srgb,var(--dsw-alias-brand-primary,#0E6E5E) 13%,transparent)}
|
|
560
576
|
.gkb-sk-search-row{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
|
|
561
577
|
.gkb-sk-search{flex:1 1 280px;min-width:0;box-sizing:border-box;padding:10px 13px;border-radius:10px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.14));background:var(--dsw-alias-bg-layer-1,#f7f8f7);color:var(--dsw-alias-label-primary,#111);font:inherit;font-size:13px;line-height:18px}
|
|
562
578
|
.gkb-sk-search::placeholder{color:var(--dsw-alias-label-tertiary,#98a0a8)}
|
|
@@ -612,18 +628,52 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
612
628
|
.gkb-sk-btn:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.05))}
|
|
613
629
|
.gkb-sk-btn[data-tone="primary"]{border-color:transparent;background:var(--dsw-alias-label-primary,#111);color:var(--dsw-alias-bg-base,#fff)}
|
|
614
630
|
.gkb-sk-foot-note{margin-left:auto;font-size:10px;color:var(--dsw-alias-label-tertiary,#8a939b);align-self:center}
|
|
631
|
+
.gkb-sk-head-actions{display:flex;align-items:center;gap:8px;flex:none}
|
|
632
|
+
.gkb-sk-cost{color:var(--dsw-alias-label-secondary,#7b8490)}
|
|
633
|
+
.gkb-sk-cost[data-heavy="true"]{color:#8E2B18;font-weight:650}
|
|
634
|
+
.gkb-sk-notice{align-self:flex-start;padding:6px 11px;border-radius:999px;border:1px solid color-mix(in srgb,#0E6E5E 28%,transparent);background:color-mix(in srgb,#0E6E5E 9%,transparent);color:color-mix(in srgb,#0E6E5E 80%,#111);font-size:11px;font-weight:600;animation:gkb-sk-in .2s ease-out}
|
|
635
|
+
.gkb-sk-btn[data-tone="danger"]{border-color:color-mix(in srgb,#8E2B18 40%,transparent);color:#8E2B18}
|
|
636
|
+
.gkb-sk-btn[data-tone="danger"]:hover{background:color-mix(in srgb,#8E2B18 8%,transparent)}
|
|
637
|
+
.gkb-sk-btn:disabled{opacity:.5;cursor:default}
|
|
638
|
+
.gkb-sk-editor{display:flex;flex-direction:column;gap:8px}
|
|
639
|
+
.gkb-sk-editor-head{display:flex;align-items:baseline;justify-content:space-between;gap:12px;font-size:11px;font-weight:700;letter-spacing:.02em;color:var(--dsw-alias-label-primary,#111)}
|
|
640
|
+
.gkb-sk-editor-cost{font-size:10.5px;font-weight:650;color:var(--dsw-alias-label-secondary,#7b8490);font-variant-numeric:tabular-nums}
|
|
641
|
+
.gkb-sk-editor-cost[data-heavy="true"]{color:#8E2B18}
|
|
642
|
+
.gkb-sk-editor-area{width:100%;box-sizing:border-box;min-height:320px;max-height:46vh;padding:12px 14px;border-radius:11px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.14));background:var(--dsw-alias-bg-layer-1,#f7f8f7);color:var(--dsw-alias-label-primary,#1f2937);font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:11.5px;line-height:17px;resize:vertical;tab-size:2;scrollbar-width:thin}
|
|
643
|
+
.gkb-sk-editor-area:focus{outline:none;border-color:color-mix(in srgb,var(--dsw-alias-brand-primary,#0E6E5E) 55%,transparent);box-shadow:0 0 0 3px color-mix(in srgb,var(--dsw-alias-brand-primary,#0E6E5E) 13%,transparent)}
|
|
644
|
+
.gkb-sk-editor-hint{font-size:10.5px;line-height:15px;color:var(--dsw-alias-label-secondary,#7b8490)}
|
|
645
|
+
.gkb-sk-modal-backdrop{position:fixed;inset:0;z-index:9020;display:flex;align-items:center;justify-content:center;padding:20px;background:color-mix(in srgb,#070b0a 40%,transparent);animation:gkb-sk-fade .16s ease-out}
|
|
646
|
+
.gkb-sk-modal{width:min(460px,92vw);display:flex;flex-direction:column;gap:12px;padding:20px;border-radius:16px;border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.1));background:var(--dsw-alias-bg-base,#fff);box-shadow:0 32px 90px -40px rgba(0,10,8,.6);animation:gkb-sk-in .22s cubic-bezier(.16,1,.3,1)}
|
|
647
|
+
.gkb-sk-modal-title{margin:0;font-size:17px;line-height:23px;font-weight:720;letter-spacing:-.01em;color:var(--dsw-alias-label-primary,#111)}
|
|
648
|
+
.gkb-sk-modal-text{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#5b6472)}
|
|
649
|
+
.gkb-sk-modal-code{display:block;padding:8px 11px;border-radius:8px;background:var(--dsw-alias-bg-layer-1,#f7f8f7);border:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.08));font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px;color:var(--dsw-alias-label-primary,#111)}
|
|
650
|
+
.gkb-sk-field{display:flex;flex-direction:column;gap:5px;font-size:10.5px;font-weight:650;letter-spacing:.04em;text-transform:uppercase;color:var(--dsw-alias-label-secondary,#7b8490)}
|
|
651
|
+
.gkb-sk-input{padding:9px 11px;border-radius:9px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.16));background:var(--dsw-alias-bg-layer-1,#f7f8f7);color:var(--dsw-alias-label-primary,#111);font:inherit;font-size:13px;letter-spacing:0;text-transform:none}
|
|
652
|
+
.gkb-sk-input:focus,.gkb-sk-input:focus-visible{outline:none;border-color:color-mix(in srgb,var(--dsw-alias-brand-primary,#0E6E5E) 55%,transparent);box-shadow:0 0 0 3px color-mix(in srgb,var(--dsw-alias-brand-primary,#0E6E5E) 13%,transparent)}
|
|
653
|
+
.gkb-sk-modal-foot{display:flex;justify-content:flex-end;gap:8px;margin-top:4px}
|
|
654
|
+
.gkb-fab{position:fixed;right:16px;bottom:96px;z-index:8500;display:none;align-items:center;gap:8px;padding:10px 14px;border-radius:999px;border:1px solid var(--dsw-alias-border-l2,rgba(0,0,0,.14));background:var(--dsw-alias-bg-base,#fff);color:var(--dsw-alias-label-primary,#111);font:inherit;font-size:12px;font-weight:650;box-shadow:0 12px 32px -14px rgba(0,10,8,.45);cursor:pointer}
|
|
655
|
+
.gkb-fab b{display:inline-flex;align-items:center;justify-content:center;min-width:20px;height:18px;padding:0 5px;border-radius:999px;background:color-mix(in srgb,var(--dsw-alias-brand-primary,#0E6E5E) 12%,transparent);color:var(--dsw-alias-brand-primary,#0E6E5E);font-size:10px;font-weight:750}
|
|
656
|
+
.gkb-fab:active{transform:scale(.98)}
|
|
657
|
+
@media (max-width: 760px){.gkb-fab{display:inline-flex}}
|
|
615
658
|
@media (max-width:760px){
|
|
616
659
|
.gkb-sk-backdrop{padding:0}
|
|
617
660
|
.gkb-sk-panel{width:100vw;height:100dvh;max-height:none;border-radius:0;border:none}
|
|
618
|
-
.gkb-sk-head{padding:
|
|
619
|
-
.gkb-sk-
|
|
620
|
-
.gkb-sk-
|
|
661
|
+
.gkb-sk-head{padding:14px 14px 10px;gap:9px}
|
|
662
|
+
.gkb-sk-head-row{flex-wrap:wrap;gap:8px}
|
|
663
|
+
.gkb-sk-head-row > div:first-child{flex:1 1 100%;min-width:0}
|
|
664
|
+
.gkb-sk-kicker{white-space:nowrap;font-size:9px;letter-spacing:.13em}
|
|
665
|
+
.gkb-sk-title{font-size:22px;line-height:27px;margin-top:4px}
|
|
666
|
+
.gkb-sk-sub{font-size:11px;line-height:16px}
|
|
667
|
+
.gkb-sk-head-actions{width:100%;justify-content:flex-start;gap:7px}
|
|
668
|
+
.gkb-sk-title{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
669
|
+
.gkb-sk-body{padding:12px 14px 20px}
|
|
621
670
|
.gkb-sk-grid,.gkb-sk-skeleton{grid-template-columns:1fr}
|
|
622
671
|
.gkb-sk-card-desc{min-height:0;-webkit-line-clamp:4}
|
|
623
672
|
.gkb-sk-detail{width:100%;max-height:100%}
|
|
624
673
|
.gkb-sk-detail-body{padding:14px 14px 16px}
|
|
625
674
|
.gkb-sk-detail-head{padding:14px 14px 12px}
|
|
626
675
|
.gkb-sk-detail-foot{padding:10px 14px 14px}
|
|
676
|
+
.gkb-sk-editor-area{min-height:240px;max-height:40vh}
|
|
627
677
|
}
|
|
628
678
|
@media (prefers-color-scheme:dark){
|
|
629
679
|
.gkb-sk-panel{box-shadow:0 48px 140px -40px rgba(0,0,0,.9),inset 0 1px 0 rgba(255,255,255,.05)}
|
|
@@ -688,25 +738,21 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
688
738
|
h("div", { key: "titles" }, [
|
|
689
739
|
h("div", { key: "label", style: LABEL_STYLE }, "Навыки"),
|
|
690
740
|
h("div", { key: "sub", style: SKILLS_SUB_STYLE }, totals
|
|
691
|
-
? pluralRuns(totals.runs) + " · " + totals.used + "
|
|
741
|
+
? pluralRuns(totals.runs) + " · " + totals.used + "/" + totals.skills + " · ≈" + compactTokens(totals.tokens)
|
|
692
742
|
: "загрузка статистики…")
|
|
693
743
|
]),
|
|
694
744
|
h("span", { key: "count", style: SKILLS_COUNT_STYLE }, String(count))
|
|
695
745
|
]),
|
|
696
746
|
active.length > 0
|
|
697
|
-
? h("div", { key: "active", style: { display: "flex", alignItems: "center", gap: "6px", marginTop: "6px", minWidth: 0 } }, [
|
|
747
|
+
? h("div", { key: "active", className: "gkb-skills-active", style: { display: "flex", alignItems: "center", gap: "6px", marginTop: "6px", minWidth: 0 } }, [
|
|
698
748
|
h("span", { key: "dot", className: "gkb-sk-pulse", style: { width: "6px", height: "6px", boxShadow: "0 0 0 3px color-mix(in srgb, var(--dsw-alias-brand-primary, #0E6E5E) 15%, transparent)" } }),
|
|
699
749
|
h("span", { key: "label", style: { fontSize: "9px", fontWeight: 750, letterSpacing: ".12em", textTransform: "uppercase", color: "var(--dsw-alias-brand-primary, #0E6E5E)", flex: "none" } }, "в работе"),
|
|
700
750
|
h("span", { key: "names", style: { fontSize: "10px", color: "var(--dsw-alias-label-secondary, #666)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, activeNames.join(", ") + activeMore)
|
|
701
751
|
])
|
|
702
752
|
: null,
|
|
703
|
-
h("div", { key: "chips", style: SKILLS_CHIPS_STYLE }, categories.map((category) =>
|
|
753
|
+
h("div", { key: "chips", className: "gkb-skills-chips", style: SKILLS_CHIPS_STYLE }, categories.slice(0, 4).map((category) =>
|
|
704
754
|
h("span", { key: category.id, style: SKILLS_CHIP_STYLE }, category.label + " " + (counts[category.id] || ""))
|
|
705
|
-
))
|
|
706
|
-
h("div", { key: "footer", style: { marginTop: "7px", display: "flex", alignItems: "center", justifyContent: "space-between", gap: "8px", fontSize: "10px", fontWeight: 650, letterSpacing: ".04em", color: "var(--dsw-alias-label-secondary, #777)" } }, [
|
|
707
|
-
h("span", { key: "text" }, "Открыть каталог"),
|
|
708
|
-
h("span", { key: "arrow", style: { opacity: .8 } }, "→")
|
|
709
|
-
])
|
|
755
|
+
))
|
|
710
756
|
]);
|
|
711
757
|
}
|
|
712
758
|
|
|
@@ -720,6 +766,81 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
720
766
|
const [detailLoading, setDetailLoading] = react.useState(false);
|
|
721
767
|
const [detailError, setDetailError] = react.useState("");
|
|
722
768
|
const [copied, setCopied] = react.useState(false);
|
|
769
|
+
const [editor, setEditor] = react.useState(null);
|
|
770
|
+
const [createDraft, setCreateDraft] = react.useState(null);
|
|
771
|
+
const [deleteConfirm, setDeleteConfirm] = react.useState(null);
|
|
772
|
+
const [notice, setNotice] = react.useState("");
|
|
773
|
+
const editorRef = react.useRef(null);
|
|
774
|
+
react.useEffect(() => {
|
|
775
|
+
if (editor && editorRef.current) editorRef.current.focus();
|
|
776
|
+
}, [editor && editor.name]);
|
|
777
|
+
|
|
778
|
+
const tokenEstimate = (text) => {
|
|
779
|
+
try { return Math.max(40, Math.round(new TextEncoder().encode(String(text)).length / 3.2)); } catch { return 0; }
|
|
780
|
+
};
|
|
781
|
+
const shortTokens = (value) => {
|
|
782
|
+
const n = Number(value) || 0;
|
|
783
|
+
if (n >= 1000) return (n / 1000).toFixed(1).replace(".0", "").replace(".", ",") + "k";
|
|
784
|
+
return String(n);
|
|
785
|
+
};
|
|
786
|
+
const flash = (message) => {
|
|
787
|
+
setNotice(message);
|
|
788
|
+
setTimeout(() => setNotice(""), 3200);
|
|
789
|
+
};
|
|
790
|
+
const reloadSkills = async () => {
|
|
791
|
+
await loadSkills(store.sessionId, true);
|
|
792
|
+
};
|
|
793
|
+
const openEditor = () => {
|
|
794
|
+
if (!detail || !detail.skill) return;
|
|
795
|
+
setEditor({ name: detail.skill.name, content: detail.skill.content, saving: false, error: "", baseTokens: detail.skill.tokens });
|
|
796
|
+
};
|
|
797
|
+
const saveEditor = async () => {
|
|
798
|
+
if (!editor || editor.saving) return;
|
|
799
|
+
setEditor(Object.assign({}, editor, { saving: true, error: "" }));
|
|
800
|
+
try {
|
|
801
|
+
const saved = await callSkills("skill-save", { name: editor.name, content: editor.content, sessionId: store.sessionId || undefined });
|
|
802
|
+
setDetail((previous) => previous ? Object.assign({}, previous, { skill: saved }) : previous);
|
|
803
|
+
setEditor(null);
|
|
804
|
+
await reloadSkills();
|
|
805
|
+
flash("Сохранено · резервная копия в .dsh-kit-backups");
|
|
806
|
+
} catch (error) {
|
|
807
|
+
setEditor((previous) => previous ? Object.assign({}, previous, { saving: false, error: String(error && error.message ? error.message : error) }) : previous);
|
|
808
|
+
}
|
|
809
|
+
};
|
|
810
|
+
const submitCreate = async () => {
|
|
811
|
+
if (!createDraft || createDraft.busy) return;
|
|
812
|
+
const name = createDraft.name.trim();
|
|
813
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name)) {
|
|
814
|
+
setCreateDraft(Object.assign({}, createDraft, { error: "Имя: kebab-case из латиницы и цифр, например my-skill" }));
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
setCreateDraft(Object.assign({}, createDraft, { busy: true, error: "" }));
|
|
818
|
+
try {
|
|
819
|
+
const result = createDraft.mode === "duplicate"
|
|
820
|
+
? await callSkills("skill-duplicate", { source: createDraft.source, name, sessionId: store.sessionId || undefined })
|
|
821
|
+
: await callSkills("skill-create", { name, description: createDraft.description.trim(), sessionId: store.sessionId || undefined });
|
|
822
|
+
setCreateDraft(null);
|
|
823
|
+
await reloadSkills();
|
|
824
|
+
setSelected(result.skill.name);
|
|
825
|
+
flash(createDraft.mode === "duplicate" ? "Копия создана" : "Навык создан");
|
|
826
|
+
} catch (error) {
|
|
827
|
+
setCreateDraft((previous) => previous ? Object.assign({}, previous, { busy: false, error: String(error && error.message ? error.message : error) }) : previous);
|
|
828
|
+
}
|
|
829
|
+
};
|
|
830
|
+
const submitDelete = async () => {
|
|
831
|
+
if (!deleteConfirm || deleteConfirm.busy) return;
|
|
832
|
+
setDeleteConfirm(Object.assign({}, deleteConfirm, { busy: true, error: "" }));
|
|
833
|
+
try {
|
|
834
|
+
await callSkills("skill-delete", { name: deleteConfirm.name, sessionId: store.sessionId || undefined });
|
|
835
|
+
setDeleteConfirm(null);
|
|
836
|
+
setSelected(null);
|
|
837
|
+
setDetail(null);
|
|
838
|
+
await reloadSkills();
|
|
839
|
+
flash("Навык перемещён в .dsh-kit-trash — данные не потеряны");
|
|
840
|
+
} catch (error) {
|
|
841
|
+
setDeleteConfirm((previous) => previous ? Object.assign({}, previous, { busy: false, error: String(error && error.message ? error.message : error) }) : previous);
|
|
842
|
+
}
|
|
843
|
+
};
|
|
723
844
|
|
|
724
845
|
react.useEffect(() => {
|
|
725
846
|
if (store.open) void loadSkills(store.sessionId, true);
|
|
@@ -732,14 +853,22 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
732
853
|
react.useEffect(() => {
|
|
733
854
|
if (!store.open) return undefined;
|
|
734
855
|
const onKey = (event) => {
|
|
856
|
+
if ((event.metaKey || event.ctrlKey) && String(event.key).toLowerCase() === "s" && editor) {
|
|
857
|
+
event.preventDefault();
|
|
858
|
+
void saveEditor();
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
735
861
|
if (event.key !== "Escape") return;
|
|
736
862
|
event.stopPropagation();
|
|
863
|
+
if (editor) { setEditor(null); return; }
|
|
864
|
+
if (deleteConfirm) { setDeleteConfirm(null); return; }
|
|
865
|
+
if (createDraft) { setCreateDraft(null); return; }
|
|
737
866
|
if (selected) { setSelected(null); setDetail(null); }
|
|
738
867
|
else skillsStore.set({ open: false });
|
|
739
868
|
};
|
|
740
869
|
document.addEventListener("keydown", onKey, true);
|
|
741
870
|
return () => document.removeEventListener("keydown", onKey, true);
|
|
742
|
-
}, [store.open, selected]);
|
|
871
|
+
}, [store.open, selected, editor, deleteConfirm, createDraft]);
|
|
743
872
|
react.useEffect(() => {
|
|
744
873
|
if (!selected) { setDetail(null); setDetailError(""); return; }
|
|
745
874
|
let alive = true;
|
|
@@ -804,7 +933,8 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
804
933
|
h("span", { className: "gkb-sk-card-foot", key: "foot" }, [
|
|
805
934
|
h("span", { className: "gkb-sk-meta", key: "meta" }, [
|
|
806
935
|
h("span", { key: "files" }, h("b", null, "обновлён "), shortDate(skill.updatedAt)),
|
|
807
|
-
h("span", { key: "used" }, h("b", null, skill.usage.count > 0 ? "последний раз " : "ещё не использовался"), skill.usage.count > 0 ? shortDate(skill.usage.lastAt) : "")
|
|
936
|
+
h("span", { key: "used" }, h("b", null, skill.usage.count > 0 ? "последний раз " : "ещё не использовался"), skill.usage.count > 0 ? shortDate(skill.usage.lastAt) : ""),
|
|
937
|
+
h("span", { key: "cost", className: "gkb-sk-cost", "data-heavy": skill.heavy ? "true" : "false" }, "ядро ≈" + shortTokens(skill.tokens))
|
|
808
938
|
]),
|
|
809
939
|
activeNames.has(skill.name) ? h("span", { className: "gkb-sk-now", key: "spark", style: { position: "static" } }, "в работе") : h("span", { className: "gkb-sk-spark", key: "spark" }, sparkline(skill.usage.series, skill.color))
|
|
810
940
|
]),
|
|
@@ -830,6 +960,7 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
830
960
|
h("div", { className: "gkb-sk-stat", key: "week" }, [h("span", { key: "l" }, "за неделю"), h("b", { key: "v" }, String(detail.usage ? detail.usage.week : 0))]),
|
|
831
961
|
h("div", { className: "gkb-sk-stat", key: "last" }, [h("span", { key: "l" }, "последний"), h("b", { key: "v" }, shortDate(detail.usage && detail.usage.lastAt)), h("small", { key: "s" }, detail.usage && detail.usage.lastAt ? new Date(detail.usage.lastAt).toLocaleTimeString("ru-RU", { hour: "2-digit", minute: "2-digit" }) : "")]),
|
|
832
962
|
h("div", { className: "gkb-sk-stat", key: "files" }, [h("span", { key: "l" }, "файлов"), h("b", { key: "v" }, String(detail.skill.files)), h("small", { key: "s" }, detail.skill.human)]),
|
|
963
|
+
h("div", { className: "gkb-sk-stat", key: "tokens" }, [h("span", { key: "l" }, "ядро"), h("b", { key: "v" }, "≈" + shortTokens(detail.skill.tokens)), h("small", { key: "s" }, detail.skill.heavy ? "выше бюджета 6k" : "в бюджете")]),
|
|
833
964
|
h("div", { className: "gkb-sk-stat", key: "updated" }, [h("span", { key: "l" }, "обновлён"), h("b", { key: "v" }, shortDate(detail.skill.updatedAt))])
|
|
834
965
|
])
|
|
835
966
|
: null,
|
|
@@ -837,11 +968,46 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
837
968
|
? h("div", { key: "when", className: "gkb-sk-when" }, [h("b", { key: "b" }, "Когда применять: "), detail.skill.whenToUse])
|
|
838
969
|
: null,
|
|
839
970
|
detail && detail.skill
|
|
840
|
-
?
|
|
971
|
+
? (editor
|
|
972
|
+
? h("div", { key: "editor", className: "gkb-sk-editor" }, [
|
|
973
|
+
h("div", { key: "head", className: "gkb-sk-editor-head" }, [
|
|
974
|
+
h("span", { key: "t" }, "Редактирование SKILL.md"),
|
|
975
|
+
h("span", { key: "c", className: "gkb-sk-editor-cost", "data-heavy": tokenEstimate(editor.content) > 6000 ? "true" : "false" }, "≈" + shortTokens(tokenEstimate(editor.content)) + " токенов")
|
|
976
|
+
]),
|
|
977
|
+
editor.error ? h("div", { key: "err", className: "gkb-sk-error" }, editor.error) : null,
|
|
978
|
+
h("textarea", {
|
|
979
|
+
key: "area",
|
|
980
|
+
ref: editorRef,
|
|
981
|
+
className: "gkb-sk-editor-area",
|
|
982
|
+
value: editor.content,
|
|
983
|
+
spellCheck: false,
|
|
984
|
+
"aria-label": "Содержимое SKILL.md",
|
|
985
|
+
onChange: (event) => setEditor(Object.assign({}, editor, { content: event.target.value })),
|
|
986
|
+
onKeyDown: (event) => {
|
|
987
|
+
if (event.key !== "Tab") return;
|
|
988
|
+
event.preventDefault();
|
|
989
|
+
const element = event.target;
|
|
990
|
+
const start = element.selectionStart;
|
|
991
|
+
const end = element.selectionEnd;
|
|
992
|
+
const next = editor.content.slice(0, start) + " " + editor.content.slice(end);
|
|
993
|
+
setEditor(Object.assign({}, editor, { content: next }));
|
|
994
|
+
requestAnimationFrame(() => { element.selectionStart = start + 2; element.selectionEnd = start + 2; });
|
|
995
|
+
}
|
|
996
|
+
}),
|
|
997
|
+
h("div", { key: "hint", className: "gkb-sk-editor-hint" }, "Ctrl+S — сохранить. Перед записью создаётся резервная копия в ~/.dsh/skills/.dsh-kit-backups. Держите ядро ≤ 6k токенов: детали — в references.")
|
|
998
|
+
])
|
|
999
|
+
: h("pre", { key: "content", className: "gkb-sk-content" }, detail.skill.content))
|
|
841
1000
|
: null
|
|
842
1001
|
]),
|
|
843
|
-
h("div", { className: "gkb-sk-detail-foot" }, [
|
|
844
|
-
h("button", { key: "
|
|
1002
|
+
h("div", { className: "gkb-sk-detail-foot" }, editor ? [
|
|
1003
|
+
h("button", { key: "save", type: "button", className: "gkb-sk-btn", "data-tone": "primary", disabled: editor.saving, onClick: () => { void saveEditor(); } }, editor.saving ? "Сохраняю…" : "Сохранить"),
|
|
1004
|
+
h("button", { key: "cancel", type: "button", className: "gkb-sk-btn", onClick: () => setEditor(null) }, "Отмена"),
|
|
1005
|
+
h("span", { key: "note", className: "gkb-sk-foot-note" }, "≈" + shortTokens(tokenEstimate(editor.content)) + " токенов · Ctrl+S")
|
|
1006
|
+
] : [
|
|
1007
|
+
h("button", { key: "edit", type: "button", className: "gkb-sk-btn", "data-tone": "primary", onClick: openEditor }, "Редактировать"),
|
|
1008
|
+
h("button", { key: "dup", type: "button", className: "gkb-sk-btn", onClick: () => setCreateDraft({ mode: "duplicate", name: selected + "-copy", source: selected, description: "", error: "" }) }, "Дублировать"),
|
|
1009
|
+
h("button", { key: "del", type: "button", className: "gkb-sk-btn", "data-tone": "danger", onClick: () => setDeleteConfirm({ name: selected, error: "" }) }, "Удалить"),
|
|
1010
|
+
h("button", { key: "open", type: "button", className: "gkb-sk-btn", onClick: () => { void callSkills("open-skill", { name: selected }).catch(() => {}); } }, "Открыть папку"),
|
|
845
1011
|
h("button", { key: "copy", type: "button", className: "gkb-sk-btn", onClick: () => { try { void navigator.clipboard.writeText(selected); setCopied(true); setTimeout(() => setCopied(false), 1500); } catch {} } }, copied ? "Скопировано" : "Скопировать имя"),
|
|
846
1012
|
h("button", { key: "close", type: "button", className: "gkb-sk-btn", onClick: () => { setSelected(null); setDetail(null); } }, "Закрыть"),
|
|
847
1013
|
h("span", { key: "path", className: "gkb-sk-foot-note" }, detail && detail.skill ? detail.skill.path : "")
|
|
@@ -850,6 +1016,47 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
850
1016
|
)
|
|
851
1017
|
: null;
|
|
852
1018
|
|
|
1019
|
+
const createModal = createDraft
|
|
1020
|
+
? h("div", { className: "gkb-sk-modal-backdrop", onMouseDown: (event) => { if (event.target === event.currentTarget) setCreateDraft(null); } },
|
|
1021
|
+
h("div", { className: "gkb-sk-modal", role: "dialog", "aria-modal": "true", "aria-label": createDraft.mode === "duplicate" ? "Дублировать навык" : "Новый навык" }, [
|
|
1022
|
+
h("h3", { key: "title", className: "gkb-sk-modal-title" }, createDraft.mode === "duplicate" ? "Дублировать навык" : "Новый навык"),
|
|
1023
|
+
createDraft.mode === "duplicate"
|
|
1024
|
+
? h("p", { key: "text", className: "gkb-sk-modal-text" }, "Копия навыка «" + createDraft.source + "» со всеми references.")
|
|
1025
|
+
: h("p", { key: "text", className: "gkb-sk-modal-text" }, "Появится папка в ~/.dsh/skills с компактным шаблоном: ядро ≤ 6k токенов, детали — в references."),
|
|
1026
|
+
h("label", { key: "name", className: "gkb-sk-field" }, [
|
|
1027
|
+
h("span", { key: "s" }, "Имя (kebab-case)"),
|
|
1028
|
+
h("input", { key: "i", className: "gkb-sk-input", value: createDraft.name, autoFocus: true, placeholder: "my-skill", onChange: (event) => setCreateDraft(Object.assign({}, createDraft, { name: event.target.value })) })
|
|
1029
|
+
]),
|
|
1030
|
+
createDraft.mode === "create"
|
|
1031
|
+
? h("label", { key: "desc", className: "gkb-sk-field" }, [
|
|
1032
|
+
h("span", { key: "s" }, "Короткое описание-триггер"),
|
|
1033
|
+
h("input", { key: "i", className: "gkb-sk-input", value: createDraft.description, placeholder: "Когда применять навык — одной строкой", onChange: (event) => setCreateDraft(Object.assign({}, createDraft, { description: event.target.value })) })
|
|
1034
|
+
])
|
|
1035
|
+
: null,
|
|
1036
|
+
createDraft.error ? h("div", { key: "error", className: "gkb-sk-error" }, createDraft.error) : null,
|
|
1037
|
+
h("div", { key: "foot", className: "gkb-sk-modal-foot" }, [
|
|
1038
|
+
h("button", { key: "ok", type: "button", className: "gkb-sk-btn", "data-tone": "primary", disabled: createDraft.busy, onClick: () => { void submitCreate(); } }, createDraft.busy ? "Создаю…" : (createDraft.mode === "duplicate" ? "Создать копию" : "Создать навык")),
|
|
1039
|
+
h("button", { key: "cancel", type: "button", className: "gkb-sk-btn", onClick: () => setCreateDraft(null) }, "Отмена")
|
|
1040
|
+
])
|
|
1041
|
+
])
|
|
1042
|
+
)
|
|
1043
|
+
: null;
|
|
1044
|
+
|
|
1045
|
+
const deleteModal = deleteConfirm
|
|
1046
|
+
? h("div", { className: "gkb-sk-modal-backdrop", onMouseDown: (event) => { if (event.target === event.currentTarget) setDeleteConfirm(null); } },
|
|
1047
|
+
h("div", { className: "gkb-sk-modal", role: "dialog", "aria-modal": "true", "aria-label": "Удалить навык" }, [
|
|
1048
|
+
h("h3", { key: "title", className: "gkb-sk-modal-title" }, "Удалить навык?"),
|
|
1049
|
+
h("p", { key: "text", className: "gkb-sk-modal-text" }, "Папка переместится в ~/.dsh/skills/.dsh-kit-trash — это обратимо, файлы не удаляются."),
|
|
1050
|
+
h("code", { key: "name", className: "gkb-sk-modal-code" }, deleteConfirm.name),
|
|
1051
|
+
deleteConfirm.error ? h("div", { key: "error", className: "gkb-sk-error" }, deleteConfirm.error) : null,
|
|
1052
|
+
h("div", { key: "foot", className: "gkb-sk-modal-foot" }, [
|
|
1053
|
+
h("button", { key: "ok", type: "button", className: "gkb-sk-btn", "data-tone": "danger", disabled: deleteConfirm.busy, onClick: () => { void submitDelete(); } }, deleteConfirm.busy ? "Переношу…" : "В корзину"),
|
|
1054
|
+
h("button", { key: "cancel", type: "button", className: "gkb-sk-btn", onClick: () => setDeleteConfirm(null) }, "Отмена")
|
|
1055
|
+
])
|
|
1056
|
+
])
|
|
1057
|
+
)
|
|
1058
|
+
: null;
|
|
1059
|
+
|
|
853
1060
|
return h("div", { className: "gkb-sk-backdrop", onMouseDown: (event) => { if (event.target === event.currentTarget) close(); } },
|
|
854
1061
|
h("div", { className: "gkb-sk-panel", role: "dialog", "aria-modal": "true", "aria-label": "Каталог навыков" },
|
|
855
1062
|
h("div", { className: "gkb-sk-head" }, [
|
|
@@ -858,10 +1065,14 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
858
1065
|
h("div", { key: "kicker", className: "gkb-sk-kicker" }, "DSH · навыки"),
|
|
859
1066
|
h("h2", { key: "title", className: "gkb-sk-title" }, "Мои навыки"),
|
|
860
1067
|
h("div", { key: "sub", className: "gkb-sk-sub" }, total
|
|
861
|
-
? [h("b", { key: "n" }, pluralSkills(total.skills)), " · ", h("b", { key: "r" }, pluralRuns(total.runs)), " · ", h("b", { key: "u" }, String(total.used)), " из ", String(total.skills), "
|
|
1068
|
+
? [h("b", { key: "n" }, pluralSkills(total.skills)), " · ", h("b", { key: "r" }, pluralRuns(total.runs)), " · ", h("b", { key: "u" }, String(total.used)), " из ", String(total.skills), " использовано", " · ≈", h("b", { key: "t" }, shortTokens(total.tokens)), " токенов ядер", total.active > 0 ? " · " + total.active + " в работе" : ""]
|
|
862
1069
|
: "Загружаю статистику…")
|
|
863
1070
|
]),
|
|
864
|
-
h("
|
|
1071
|
+
h("div", { key: "actions", className: "gkb-sk-head-actions" }, [
|
|
1072
|
+
h("button", { key: "tokens", type: "button", className: "gkb-sk-btn", onClick: () => { try { if (typeof window.__DSH_TOKEN_OPEN__ === "function") window.__DSH_TOKEN_OPEN__(store.sessionId || undefined); } catch {} } }, "Токены"),
|
|
1073
|
+
h("button", { key: "new", type: "button", className: "gkb-sk-btn", "data-tone": "primary", onClick: () => setCreateDraft({ mode: "create", name: "", description: "", error: "" }) }, "Новый навык"),
|
|
1074
|
+
h("button", { key: "close", type: "button", className: "gkb-sk-close", onClick: close, "aria-label": "Закрыть каталог навыков" }, "×")
|
|
1075
|
+
])
|
|
865
1076
|
]),
|
|
866
1077
|
h("div", { key: "search", className: "gkb-sk-search-row" }, [
|
|
867
1078
|
h("input", { key: "input", className: "gkb-sk-search", placeholder: "Поиск по названию, описанию или категории…", value: query, onChange: (event) => setQuery(event.target.value), autoFocus: true, "aria-label": "Поиск навыков" }),
|
|
@@ -873,6 +1084,7 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
873
1084
|
])
|
|
874
1085
|
]),
|
|
875
1086
|
activeBand,
|
|
1087
|
+
notice ? h("div", { key: "notice", className: "gkb-sk-notice", role: "status" }, notice) : null,
|
|
876
1088
|
h("div", { key: "filters", className: "gkb-sk-filters" }, [
|
|
877
1089
|
h("button", { key: "all", type: "button", className: "gkb-sk-chip", "data-active": category === "all" ? "true" : "false", onClick: () => setCategory("all") }, "Все " + skills.length),
|
|
878
1090
|
...categories.filter((item) => counts[item.id] > 0).map((item) => h("button", {
|
|
@@ -897,10 +1109,36 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
897
1109
|
: null
|
|
898
1110
|
]),
|
|
899
1111
|
detailPanel
|
|
900
|
-
)
|
|
1112
|
+
),
|
|
1113
|
+
createModal,
|
|
1114
|
+
deleteModal
|
|
901
1115
|
);
|
|
902
1116
|
}
|
|
903
1117
|
|
|
1118
|
+
/* Mobile rail cannot hold the tile stack; one pill opens the catalog. */
|
|
1119
|
+
function MobileSkillsFab() {
|
|
1120
|
+
const store = useSkillsStore();
|
|
1121
|
+
if (store.open) return null;
|
|
1122
|
+
const totals = store.data ? store.data.totals : null;
|
|
1123
|
+
const open = () => {
|
|
1124
|
+
skillsStore.set({ open: true, sessionId: store.sessionId || null });
|
|
1125
|
+
void loadSkills(store.sessionId, true);
|
|
1126
|
+
};
|
|
1127
|
+
return h("button", {
|
|
1128
|
+
type: "button",
|
|
1129
|
+
className: "gkb-fab",
|
|
1130
|
+
onClick: open,
|
|
1131
|
+
"aria-label": "Открыть каталог навыков"
|
|
1132
|
+
}, [
|
|
1133
|
+
h("svg", { key: "icon", width: 17, height: 17, viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true" }, [
|
|
1134
|
+
h("path", { key: "a", d: "M12 3l2.2 5.8L20 11l-5.8 2.2L12 19l-2.2-5.8L4 11l5.8-2.2L12 3z", stroke: "currentColor", strokeWidth: "1.6", strokeLinejoin: "round" }),
|
|
1135
|
+
h("circle", { key: "b", cx: 19, cy: 5, r: 1.4, fill: "currentColor" })
|
|
1136
|
+
]),
|
|
1137
|
+
h("span", { key: "label" }, "Навыки"),
|
|
1138
|
+
totals ? h("b", { key: "count" }, String(totals.skills)) : null
|
|
1139
|
+
]);
|
|
1140
|
+
}
|
|
1141
|
+
|
|
904
1142
|
function SpendRow(props) {
|
|
905
1143
|
if (props && props.wide === false) return null;
|
|
906
1144
|
const snap = useSnapshot();
|
|
@@ -926,13 +1164,16 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
926
1164
|
h("span", { key: "value", style: amountStyle }, value)
|
|
927
1165
|
]);
|
|
928
1166
|
}
|
|
929
|
-
/* One slot registration:
|
|
1167
|
+
/* One slot registration: AURORA tiles in a single full-width column.
|
|
1168
|
+
* Token Lab (a sibling client part) hands its tile over through a global;
|
|
1169
|
+
* rendering it here keeps the footer stack instead of a second slot row. */
|
|
930
1170
|
function SidebarTiles(props) {
|
|
1171
|
+
const TokenTile = typeof window !== "undefined" ? window.__DSH_TOKEN_TILE__ : null;
|
|
931
1172
|
return h("div", {
|
|
932
1173
|
style: {
|
|
933
1174
|
display: "flex",
|
|
934
1175
|
flexDirection: "column",
|
|
935
|
-
gap: "
|
|
1176
|
+
gap: "4px",
|
|
936
1177
|
width: "100%",
|
|
937
1178
|
boxSizing: "border-box",
|
|
938
1179
|
padding: "4px 8px 6px"
|
|
@@ -940,6 +1181,7 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
940
1181
|
"data-gkb-tiles": "1"
|
|
941
1182
|
}, [
|
|
942
1183
|
h(SkillsRow, Object.assign({ key: "skills" }, props)),
|
|
1184
|
+
TokenTile ? h(TokenTile, { key: "tokens", useSessions: props && props.useSessions }) : null,
|
|
943
1185
|
h(BalanceRow, { key: "balance" }),
|
|
944
1186
|
h(SpendRow, { key: "spend" })
|
|
945
1187
|
]);
|
|
@@ -1092,6 +1334,516 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
1092
1334
|
order: 640
|
|
1093
1335
|
}, SkillsCatalog)), "gkb-ru: skills catalog overlay");
|
|
1094
1336
|
|
|
1337
|
+
ctx.effect(() => ctx.slots.inject("shell.overlay", () => ctx.slots.register({
|
|
1338
|
+
name: "shell.overlay",
|
|
1339
|
+
id: "gkb-mobile-fab",
|
|
1340
|
+
order: 660
|
|
1341
|
+
}, MobileSkillsFab)), "gkb-ru: mobile skills fab");
|
|
1342
|
+
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
exports.apply = apply;
|
|
1346
|
+
exports.inject = inject;
|
|
1347
|
+
|
|
1348
|
+
return module.exports;
|
|
1349
|
+
})());
|
|
1350
|
+
|
|
1351
|
+
/* ---- dsh-token-lab ---- */
|
|
1352
|
+
__parts.push((function () {
|
|
1353
|
+
var module = { exports: {} };
|
|
1354
|
+
var exports = module.exports;
|
|
1355
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
1356
|
+
const react = require("react");
|
|
1357
|
+
const h = react.createElement;
|
|
1358
|
+
|
|
1359
|
+
const inject = ["slots", "connection"];
|
|
1360
|
+
|
|
1361
|
+
let pluginCtx;
|
|
1362
|
+
|
|
1363
|
+
/* ------------------------------------------------------------------ */
|
|
1364
|
+
/* session selection + store */
|
|
1365
|
+
/* ------------------------------------------------------------------ */
|
|
1366
|
+
|
|
1367
|
+
function currentSessionSelector(state) {
|
|
1368
|
+
try {
|
|
1369
|
+
const sessions = Object.values(state.byId || {});
|
|
1370
|
+
const main = sessions.find((session) => session && session.retainedBy && (session.retainedBy.mainView || 0) > 0);
|
|
1371
|
+
return main ? main.id : undefined;
|
|
1372
|
+
} catch { return undefined; }
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
const store = {
|
|
1376
|
+
state: { open: false, sessionId: null, report: null, loading: false, error: null, loadedAt: 0 },
|
|
1377
|
+
listeners: new Set(),
|
|
1378
|
+
get() { return this.state; },
|
|
1379
|
+
subscribe(listener) { this.listeners.add(listener); return () => { this.listeners.delete(listener); }; },
|
|
1380
|
+
set(patch) {
|
|
1381
|
+
this.state = Object.assign({}, this.state, patch);
|
|
1382
|
+
for (const listener of Array.from(this.listeners)) { try { listener(); } catch {} }
|
|
1383
|
+
}
|
|
1384
|
+
};
|
|
1385
|
+
function useStore() {
|
|
1386
|
+
return react.useSyncExternalStore(store.subscribe.bind(store), store.get.bind(store), store.get.bind(store));
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
let inflight = null;
|
|
1390
|
+
async function callToken(operation, payload) {
|
|
1391
|
+
if (!pluginCtx || !pluginCtx.connection) throw new Error("RPC недоступен");
|
|
1392
|
+
const result = await pluginCtx.connection.rpc.call("/api", "dsh-token-lab/" + operation, payload || {}, AbortSignal.timeout(20000));
|
|
1393
|
+
if (!result || result.ok !== true) throw new Error((result && result.error && result.error.message) || "ошибка RPC");
|
|
1394
|
+
return result.value;
|
|
1395
|
+
}
|
|
1396
|
+
function loadReport(sessionId, force) {
|
|
1397
|
+
const state = store.get();
|
|
1398
|
+
const wanted = sessionId || state.sessionId || null;
|
|
1399
|
+
if (!force && state.report && state.sessionId === wanted && Date.now() - state.loadedAt < 8000) return Promise.resolve(state.report);
|
|
1400
|
+
if (inflight) return inflight;
|
|
1401
|
+
store.set({ loading: true, error: null, sessionId: wanted });
|
|
1402
|
+
inflight = callToken("report", wanted ? { sessionId: wanted } : {})
|
|
1403
|
+
.then((report) => {
|
|
1404
|
+
store.set({ report, loading: false, error: null, loadedAt: Date.now() });
|
|
1405
|
+
return report;
|
|
1406
|
+
})
|
|
1407
|
+
.catch((error) => {
|
|
1408
|
+
store.set({ loading: false, error: String(error && error.message ? error.message : error) });
|
|
1409
|
+
return null;
|
|
1410
|
+
})
|
|
1411
|
+
.finally(() => { inflight = null; });
|
|
1412
|
+
return inflight;
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
/* Cross-plugin opener: the skills catalog offers a "Токены" button that
|
|
1416
|
+
* works on mobile, where the sidebar tile rail is collapsed. */
|
|
1417
|
+
try {
|
|
1418
|
+
if (typeof window !== "undefined") {
|
|
1419
|
+
window.__DSH_TOKEN_OPEN__ = (sessionId) => {
|
|
1420
|
+
const wanted = sessionId || store.state.sessionId || null;
|
|
1421
|
+
store.set({ open: true, sessionId: wanted });
|
|
1422
|
+
void loadReport(wanted, true);
|
|
1423
|
+
};
|
|
1424
|
+
}
|
|
1425
|
+
} catch {}
|
|
1426
|
+
|
|
1427
|
+
/* ------------------------------------------------------------------ */
|
|
1428
|
+
/* formatting */
|
|
1429
|
+
/* ------------------------------------------------------------------ */
|
|
1430
|
+
|
|
1431
|
+
function num(value) {
|
|
1432
|
+
const n = Number(value) || 0;
|
|
1433
|
+
return n.toLocaleString("ru-RU");
|
|
1434
|
+
}
|
|
1435
|
+
function compact(value) {
|
|
1436
|
+
const n = Number(value) || 0;
|
|
1437
|
+
if (n >= 1000000) return (n / 1000000).toFixed(1).replace(".0", "").replace(".", ",") + "M";
|
|
1438
|
+
if (n >= 1000) return (n / 1000).toFixed(1).replace(".0", "").replace(".", ",") + "k";
|
|
1439
|
+
return String(Math.round(n));
|
|
1440
|
+
}
|
|
1441
|
+
function percent(value, digits) {
|
|
1442
|
+
const n = Number(value) || 0;
|
|
1443
|
+
return (n * 100).toFixed(digits === undefined ? 0 : digits).replace(".", ",") + "%";
|
|
1444
|
+
}
|
|
1445
|
+
function pressureColor(pressure) {
|
|
1446
|
+
const p = Number(pressure) || 0;
|
|
1447
|
+
if (p >= 0.75) return "#8E2B18";
|
|
1448
|
+
if (p >= 0.5) return "#B07A2A";
|
|
1449
|
+
return "#0E6E5E";
|
|
1450
|
+
}
|
|
1451
|
+
function pressureLabel(pressure) {
|
|
1452
|
+
const p = Number(pressure) || 0;
|
|
1453
|
+
if (p >= 0.85) return "Почти переполнение — пора сжимать";
|
|
1454
|
+
if (p >= 0.65) return "Заполнено больше половины";
|
|
1455
|
+
if (p >= 0.35) return "Рабочий диапазон";
|
|
1456
|
+
return "Много свободного места";
|
|
1457
|
+
}
|
|
1458
|
+
function plural(count, one, few, many) {
|
|
1459
|
+
const n = Math.abs(Number(count) || 0) % 100;
|
|
1460
|
+
const last = n % 10;
|
|
1461
|
+
if (n > 10 && n < 20) return many;
|
|
1462
|
+
if (last === 1) return one;
|
|
1463
|
+
if (last >= 2 && last <= 4) return few;
|
|
1464
|
+
return many;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
/* ------------------------------------------------------------------ */
|
|
1468
|
+
/* primitives */
|
|
1469
|
+
/* ------------------------------------------------------------------ */
|
|
1470
|
+
|
|
1471
|
+
function Ring(props) {
|
|
1472
|
+
const pressure = Math.max(0, Math.min(1, Number(props.pressure) || 0));
|
|
1473
|
+
const color = pressureColor(pressure);
|
|
1474
|
+
const radius = 52;
|
|
1475
|
+
const circumference = 2 * Math.PI * radius;
|
|
1476
|
+
const dash = circumference * pressure;
|
|
1477
|
+
return h("svg", {
|
|
1478
|
+
className: "tl-ring",
|
|
1479
|
+
width: 132, height: 132, viewBox: "0 0 132 132",
|
|
1480
|
+
role: "img",
|
|
1481
|
+
"aria-label": "Заполнение контекстного окна " + percent(pressure)
|
|
1482
|
+
}, [
|
|
1483
|
+
h("circle", { key: "track", cx: 66, cy: 66, r: radius, fill: "none", stroke: "color-mix(in oklab, currentColor 12%, transparent)", strokeWidth: 9 }),
|
|
1484
|
+
h("circle", {
|
|
1485
|
+
key: "value", cx: 66, cy: 66, r: radius, fill: "none", stroke: color, strokeWidth: 9,
|
|
1486
|
+
strokeLinecap: "round", strokeDasharray: dash + " " + circumference,
|
|
1487
|
+
transform: "rotate(-90 66 66)",
|
|
1488
|
+
style: { transition: "stroke-dasharray 600ms cubic-bezier(.16,1,.3,1), stroke 300ms ease" }
|
|
1489
|
+
}),
|
|
1490
|
+
h("text", { key: "pct", x: 66, y: 62, textAnchor: "middle", fontSize: 25, fontWeight: 720, fill: "currentColor", style: { fontVariantNumeric: "tabular-nums", letterSpacing: "-.02em" } }, percent(pressure)),
|
|
1491
|
+
h("text", { key: "cap", x: 66, y: 82, textAnchor: "middle", fontSize: 9.5, fontWeight: 650, letterSpacing: ".12em", fill: "currentColor", opacity: 0.55 }, "КОНТЕКСТ")
|
|
1492
|
+
]);
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
function Sparkline(props) {
|
|
1496
|
+
const values = Array.isArray(props.series) ? props.series.filter((value) => Number.isFinite(value)) : [];
|
|
1497
|
+
if (values.length < 2) return null;
|
|
1498
|
+
const width = 560;
|
|
1499
|
+
const height = 74;
|
|
1500
|
+
const max = Math.max(1, ...values);
|
|
1501
|
+
const step = width / (values.length - 1);
|
|
1502
|
+
const points = values.map((value, index) => [index * step, height - 4 - (value / max) * (height - 12)]);
|
|
1503
|
+
const line = points.map((point, index) => (index === 0 ? "M" : "L") + point[0].toFixed(1) + " " + point[1].toFixed(1)).join(" ");
|
|
1504
|
+
const area = line + " L" + width + " " + height + " L0 " + height + " Z";
|
|
1505
|
+
const id = "tl-spark-" + (props.id || "main");
|
|
1506
|
+
return h("svg", { className: "tl-spark", viewBox: "0 0 " + width + " " + height, preserveAspectRatio: "none", "aria-hidden": "true" }, [
|
|
1507
|
+
h("defs", { key: "defs" }, h("linearGradient", { id, x1: 0, y1: 0, x2: 0, y2: 1 }, [
|
|
1508
|
+
h("stop", { key: "a", offset: "0%", stopColor: "var(--tl-accent, #0E6E5E)", stopOpacity: 0.28 }),
|
|
1509
|
+
h("stop", { key: "b", offset: "100%", stopColor: "var(--tl-accent, #0E6E5E)", stopOpacity: 0 })
|
|
1510
|
+
])),
|
|
1511
|
+
h("path", { key: "area", d: area, fill: "url(#" + id + ")" }),
|
|
1512
|
+
h("path", { key: "line", d: line, fill: "none", stroke: "var(--tl-accent, #0E6E5E)", strokeWidth: 2, strokeLinejoin: "round", strokeLinecap: "round", vectorEffect: "non-scaling-stroke" })
|
|
1513
|
+
]);
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
function River(props) {
|
|
1517
|
+
const groups = Array.isArray(props.groups) ? props.groups.filter((group) => group.tokens > 0) : [];
|
|
1518
|
+
const total = groups.reduce((sum, group) => sum + group.tokens, 0) || 1;
|
|
1519
|
+
return h("div", { className: "tl-river", role: "img", "aria-label": "Состав контекста по типам сообщений" },
|
|
1520
|
+
groups.map((group) => h("span", {
|
|
1521
|
+
key: group.id,
|
|
1522
|
+
className: "tl-river-seg",
|
|
1523
|
+
style: { width: Math.max(2, (group.tokens / total) * 100) + "%", background: group.color },
|
|
1524
|
+
title: group.label + ": " + num(group.tokens)
|
|
1525
|
+
}))
|
|
1526
|
+
);
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
/* ------------------------------------------------------------------ */
|
|
1530
|
+
/* tile */
|
|
1531
|
+
/* ------------------------------------------------------------------ */
|
|
1532
|
+
|
|
1533
|
+
function TokenTile(props) {
|
|
1534
|
+
const state = useStore();
|
|
1535
|
+
const useSessions = props && props.useSessions;
|
|
1536
|
+
const sessionId = useSessions ? useSessions(currentSessionSelector) : undefined;
|
|
1537
|
+
react.useEffect(() => {
|
|
1538
|
+
if (sessionId) void loadReport(sessionId);
|
|
1539
|
+
}, [sessionId]);
|
|
1540
|
+
react.useEffect(() => {
|
|
1541
|
+
const timer = setInterval(() => {
|
|
1542
|
+
if (!document.hidden) void loadReport(sessionId, true);
|
|
1543
|
+
}, 20000);
|
|
1544
|
+
return () => clearInterval(timer);
|
|
1545
|
+
}, [sessionId]);
|
|
1546
|
+
|
|
1547
|
+
const report = state.report && (!sessionId || state.report.sessionId === sessionId) ? state.report : null;
|
|
1548
|
+
const total = report ? report.totalTokens : 0;
|
|
1549
|
+
const pressure = report ? report.pressure : 0;
|
|
1550
|
+
const color = pressureColor(pressure);
|
|
1551
|
+
const delta = report && report.surfaceDeltaTokens > 0 ? report.surfaceDeltaTokens : 0;
|
|
1552
|
+
|
|
1553
|
+
const open = () => {
|
|
1554
|
+
store.set({ open: true, sessionId: sessionId || state.sessionId });
|
|
1555
|
+
void loadReport(sessionId || state.sessionId, true);
|
|
1556
|
+
};
|
|
1557
|
+
return h("button", {
|
|
1558
|
+
type: "button",
|
|
1559
|
+
className: "tl-tile",
|
|
1560
|
+
"aria-label": "Открыть токен-лабораторию: " + (total ? num(total) + " токенов в контексте" : "нет данных"),
|
|
1561
|
+
onClick: open
|
|
1562
|
+
}, [
|
|
1563
|
+
h("div", { className: "tl-tile-head", key: "head" }, [
|
|
1564
|
+
h("span", { className: "tl-tile-label", key: "label" }, "Токены"),
|
|
1565
|
+
h("span", { className: "tl-tile-pct", key: "pct", style: { color } }, report ? percent(pressure) : "—"),
|
|
1566
|
+
h("span", { className: "tl-tile-dot", key: "dot", style: { background: color } })
|
|
1567
|
+
]),
|
|
1568
|
+
h("div", { className: "tl-tile-row", key: "row" }, [
|
|
1569
|
+
h("b", { key: "b" }, report ? compact(total) : "—"),
|
|
1570
|
+
h("span", { className: "tl-tile-cap", key: "cap" }, report ? "из " + compact(report.contextWindow) : "нет сессии"),
|
|
1571
|
+
delta > 0 ? h("span", { className: "tl-tile-delta", key: "d" }, "+" + compact(delta)) : null
|
|
1572
|
+
]),
|
|
1573
|
+
h("div", { className: "tl-tile-bar", key: "bar" }, h("span", { style: { width: Math.max(2, Math.min(100, pressure * 100)) + "%", background: color } }))
|
|
1574
|
+
]);
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
/* The kit's sidebar tile stack owns the footer slot; hand it this tile so
|
|
1578
|
+
* both live in one column instead of competing for the slot row. */
|
|
1579
|
+
try {
|
|
1580
|
+
if (typeof window !== "undefined") window.__DSH_TOKEN_TILE__ = TokenTile;
|
|
1581
|
+
} catch {}
|
|
1582
|
+
|
|
1583
|
+
/* ------------------------------------------------------------------ */
|
|
1584
|
+
/* panel */
|
|
1585
|
+
/* ------------------------------------------------------------------ */
|
|
1586
|
+
|
|
1587
|
+
function TokenPanel() {
|
|
1588
|
+
const state = useStore();
|
|
1589
|
+
react.useEffect(() => {
|
|
1590
|
+
if (!state.open) return undefined;
|
|
1591
|
+
void loadReport(state.sessionId, true);
|
|
1592
|
+
const timer = setInterval(() => {
|
|
1593
|
+
if (!document.hidden) void loadReport(state.sessionId, true);
|
|
1594
|
+
}, 10000);
|
|
1595
|
+
const onKey = (event) => { if (event.key === "Escape") store.set({ open: false }); };
|
|
1596
|
+
document.addEventListener("keydown", onKey);
|
|
1597
|
+
return () => { clearInterval(timer); document.removeEventListener("keydown", onKey); };
|
|
1598
|
+
}, [state.open, state.sessionId]);
|
|
1599
|
+
|
|
1600
|
+
if (!state.open) return null;
|
|
1601
|
+
const report = state.report;
|
|
1602
|
+
const close = () => store.set({ open: false });
|
|
1603
|
+
const pressure = report ? report.pressure : 0;
|
|
1604
|
+
const color = pressureColor(pressure);
|
|
1605
|
+
const groups = report ? report.groups : [];
|
|
1606
|
+
const top = report ? report.top : [];
|
|
1607
|
+
const skills = report ? report.library : null;
|
|
1608
|
+
const savings = skills ? Math.max(0, skills.totalTokens - skills.typicalTokens) : 0;
|
|
1609
|
+
|
|
1610
|
+
return h("div", { className: "tl-backdrop", onClick: (event) => { if (event.target === event.currentTarget) close(); } },
|
|
1611
|
+
h("section", { className: "tl-panel", role: "dialog", "aria-modal": "true", "aria-label": "Токен-лаборатория", style: { "--tl-accent": color } }, [
|
|
1612
|
+
h("header", { className: "tl-head", key: "head" }, [
|
|
1613
|
+
h("div", { key: "titles" }, [
|
|
1614
|
+
h("div", { className: "tl-kicker", key: "k" }, "ТОКЕН-ЛАБОРАТОРИЯ"),
|
|
1615
|
+
h("h2", { className: "tl-title", key: "t" }, "Куда уходит контекст"),
|
|
1616
|
+
h("p", { className: "tl-sub", key: "s" }, report
|
|
1617
|
+
? "Текущая сессия · " + num(report.nodes) + " " + plural(report.nodes, "узел", "узла", "узлов") + " · обновляется вживую"
|
|
1618
|
+
: "Подключаемся к сессии…")
|
|
1619
|
+
]),
|
|
1620
|
+
h("button", { className: "tl-close", key: "c", type: "button", onClick: close, "aria-label": "Закрыть" }, "×")
|
|
1621
|
+
]),
|
|
1622
|
+
|
|
1623
|
+
state.error ? h("div", { className: "tl-error", key: "err" }, state.error) : null,
|
|
1624
|
+
|
|
1625
|
+
report ? h("div", { className: "tl-hero", key: "hero" }, [
|
|
1626
|
+
h("div", { className: "tl-hero-main", key: "main" }, [
|
|
1627
|
+
h("div", { className: "tl-big", key: "big" }, [
|
|
1628
|
+
h("b", { key: "n" }, num(report.totalTokens)),
|
|
1629
|
+
h("span", { key: "u" }, "токенов в контексте")
|
|
1630
|
+
]),
|
|
1631
|
+
h("div", { className: "tl-meta", key: "meta" }, [
|
|
1632
|
+
h("span", { key: "window" }, "окно " + num(report.contextWindow)),
|
|
1633
|
+
h("span", { key: "left" }, "свободно " + num(Math.max(0, report.contextWindow - report.totalTokens))),
|
|
1634
|
+
report.surfaceDeltaTokens > 0 ? h("span", { key: "d", className: "tl-up" }, "+" + num(report.surfaceDeltaTokens) + " за последний шаг") : null
|
|
1635
|
+
]),
|
|
1636
|
+
h("div", { className: "tl-status", key: "st", style: { color } }, [
|
|
1637
|
+
h("span", { className: "tl-status-dot", key: "d", style: { background: color } }),
|
|
1638
|
+
h("span", { key: "t" }, pressureLabel(pressure))
|
|
1639
|
+
])
|
|
1640
|
+
]),
|
|
1641
|
+
h("div", { className: "tl-hero-ring", key: "ring" }, [
|
|
1642
|
+
h(Ring, { key: "r", pressure }),
|
|
1643
|
+
h("div", { className: "tl-hero-hint", key: "h" }, report.baseline && report.baseline.kind === "usage" ? "по факту провайдера" : "оценка DSH")
|
|
1644
|
+
]),
|
|
1645
|
+
h("div", { className: "tl-spark-wrap", key: "spark" }, [
|
|
1646
|
+
h("div", { className: "tl-spark-head", key: "sh" }, [
|
|
1647
|
+
h("span", { key: "l" }, "Рост контекста"),
|
|
1648
|
+
h("span", { key: "v" }, compact(report.totalTokens))
|
|
1649
|
+
]),
|
|
1650
|
+
h(Sparkline, { key: "s", series: report.series })
|
|
1651
|
+
])
|
|
1652
|
+
]) : null,
|
|
1653
|
+
|
|
1654
|
+
report ? h("div", { className: "tl-grid", key: "grid" }, [
|
|
1655
|
+
h("div", { className: "tl-card tl-card-wide", key: "composition" }, [
|
|
1656
|
+
h("div", { className: "tl-card-head", key: "h" }, [
|
|
1657
|
+
h("h3", { key: "t" }, "Состав"),
|
|
1658
|
+
h("span", { key: "s" }, "по типам сообщений")
|
|
1659
|
+
]),
|
|
1660
|
+
h(River, { key: "river", groups }),
|
|
1661
|
+
h("div", { className: "tl-legend", key: "legend" }, groups.map((group) => h("div", { className: "tl-legend-row", key: group.id }, [
|
|
1662
|
+
h("span", { className: "tl-swatch", key: "sw", style: { background: group.color } }),
|
|
1663
|
+
h("span", { className: "tl-legend-label", key: "l" }, group.label),
|
|
1664
|
+
h("span", { className: "tl-legend-count", key: "c" }, group.count + " " + plural(group.count, "узел", "узла", "узлов")),
|
|
1665
|
+
h("span", { className: "tl-legend-tokens", key: "t" }, num(group.tokens)),
|
|
1666
|
+
h("span", { className: "tl-legend-pct", key: "p" }, report.totalTokens > 0 ? percent(group.tokens / report.totalTokens) : "0%")
|
|
1667
|
+
])))
|
|
1668
|
+
]),
|
|
1669
|
+
h("div", { className: "tl-card", key: "top" }, [
|
|
1670
|
+
h("div", { className: "tl-card-head", key: "h" }, [
|
|
1671
|
+
h("h3", { key: "t" }, "Самые тяжёлые"),
|
|
1672
|
+
h("span", { key: "s" }, "топ-6 узлов")
|
|
1673
|
+
]),
|
|
1674
|
+
h("div", { className: "tl-top", key: "list" }, top.length > 0 ? top.map((node, index) => {
|
|
1675
|
+
const max = Math.max(1, ...top.map((item) => item.tokens));
|
|
1676
|
+
return h("div", { className: "tl-top-row", key: node.seq }, [
|
|
1677
|
+
h("span", { className: "tl-rank", key: "r" }, String(index + 1).padStart(2, "0")),
|
|
1678
|
+
h("div", { className: "tl-top-body", key: "b" }, [
|
|
1679
|
+
h("div", { className: "tl-top-text", key: "t" }, node.label || "Системный блок"),
|
|
1680
|
+
h("div", { className: "tl-top-bar", key: "bar" }, h("span", { style: { width: Math.max(4, (node.tokens / max) * 100) + "%" } }))
|
|
1681
|
+
]),
|
|
1682
|
+
h("span", { className: "tl-top-tokens", key: "n" }, num(node.tokens))
|
|
1683
|
+
]);
|
|
1684
|
+
}) : h("div", { className: "tl-empty", key: "empty" }, "Пока пусто: в этой сессии ещё нет сообщений. Откройте рабочую сессию — состав появится здесь."))
|
|
1685
|
+
]),
|
|
1686
|
+
h("div", { className: "tl-card tl-savings", key: "savings" }, [
|
|
1687
|
+
h("div", { className: "tl-card-head", key: "h" }, [
|
|
1688
|
+
h("h3", { key: "t" }, "Экономия навыков"),
|
|
1689
|
+
h("span", { key: "s" }, "библиотека " + (skills ? skills.count : 0) + " шт.")
|
|
1690
|
+
]),
|
|
1691
|
+
h("div", { className: "tl-save-num", key: "n" }, [
|
|
1692
|
+
h("b", { key: "b" }, "−" + compact(savings)),
|
|
1693
|
+
h("span", { key: "s" }, "токенов против загрузки всей библиотеки")
|
|
1694
|
+
]),
|
|
1695
|
+
h("div", { className: "tl-save-compare", key: "cmp" }, [
|
|
1696
|
+
h("div", { key: "a" }, [h("span", { key: "l" }, "вся библиотека"), h("b", { key: "v" }, compact(skills ? skills.totalTokens : 0))]),
|
|
1697
|
+
h("div", { className: "tl-save-arrow", key: "ar" }, "→"),
|
|
1698
|
+
h("div", { key: "b" }, [h("span", { key: "l" }, "3 средних ядра"), h("b", { key: "v", style: { color } }, compact(skills ? skills.typicalTokens : 0))])
|
|
1699
|
+
]),
|
|
1700
|
+
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 }, [
|
|
1701
|
+
h("span", { key: "n" }, skill.title),
|
|
1702
|
+
h("span", { key: "t" }, compact(skill.tokens))
|
|
1703
|
+
]))),
|
|
1704
|
+
h("div", { className: "tl-save-foot", key: "foot" }, report.autopilot.mode === "on"
|
|
1705
|
+
? "Автопилот подключает нужные ядра сам — до " + report.autopilot.maxSkills + " навыков"
|
|
1706
|
+
: "Автопилот выключен: навыки грузятся вручную")
|
|
1707
|
+
])
|
|
1708
|
+
]) : h("div", { className: "tl-skeleton", key: "sk" }, "Считаем контекст…"),
|
|
1709
|
+
|
|
1710
|
+
h("footer", { className: "tl-foot", key: "foot" }, [
|
|
1711
|
+
h("span", { key: "l" }, report ? "Сессия " + report.sessionId.slice(0, 18) + "…" : ""),
|
|
1712
|
+
h("span", { key: "r" }, report ? "обновлено " + new Date(report.generatedAt).toLocaleTimeString("ru-RU") : "")
|
|
1713
|
+
])
|
|
1714
|
+
])
|
|
1715
|
+
);
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
/* ------------------------------------------------------------------ */
|
|
1719
|
+
/* styles */
|
|
1720
|
+
/* ------------------------------------------------------------------ */
|
|
1721
|
+
|
|
1722
|
+
const CSS = `
|
|
1723
|
+
.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}
|
|
1724
|
+
.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)}
|
|
1725
|
+
.tl-tile:active{transform:scale(.995)}
|
|
1726
|
+
.tl-tile:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#0E6E5E);outline-offset:2px}
|
|
1727
|
+
.tl-tile-head{display:flex;align-items:center;justify-content:space-between;gap:6px}
|
|
1728
|
+
.tl-tile-label{font-size:9.5px;font-weight:650;letter-spacing:.1em;text-transform:uppercase;opacity:.72;flex:1 1 auto}
|
|
1729
|
+
.tl-tile-pct{font-size:10px;font-weight:700;font-variant-numeric:tabular-nums;letter-spacing:.01em}
|
|
1730
|
+
.tl-tile-dot{width:6px;height:6px;border-radius:999px;flex:0 0 auto}
|
|
1731
|
+
.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}
|
|
1732
|
+
.tl-tile-row b{font-size:17px;font-weight:720;letter-spacing:-.02em}
|
|
1733
|
+
.tl-tile-cap{font-size:9.5px;font-weight:550;opacity:.55;white-space:nowrap}
|
|
1734
|
+
.tl-tile-bar{height:3px;border-radius:999px;background:color-mix(in oklab,currentColor 12%,transparent);overflow:hidden}
|
|
1735
|
+
.tl-tile-bar span{display:block;height:100%;border-radius:999px;transition:width .5s cubic-bezier(.16,1,.3,1)}
|
|
1736
|
+
.tl-tile-delta{font-size:9.5px;font-weight:700;color:#8E2B18;margin-left:auto}
|
|
1737
|
+
|
|
1738
|
+
.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}
|
|
1739
|
+
.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)}
|
|
1740
|
+
.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))}
|
|
1741
|
+
.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)}
|
|
1742
|
+
.tl-kicker::before{content:"";width:26px;height:1px;background:color-mix(in srgb,var(--tl-accent,#0E6E5E) 65%,transparent)}
|
|
1743
|
+
.tl-title{margin:7px 0 0;font-size:25px;line-height:1.15;font-weight:720;letter-spacing:-.025em}
|
|
1744
|
+
.tl-sub{margin:5px 0 0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#5b6472);font-variant-numeric:tabular-nums}
|
|
1745
|
+
.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}
|
|
1746
|
+
.tl-close:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06));color:var(--dsw-alias-label-primary,#111)}
|
|
1747
|
+
.tl-close:focus-visible,.tl-panel button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#0E6E5E);outline-offset:2px}
|
|
1748
|
+
.tl-hero{display:grid;grid-template-columns:minmax(0,1.25fr) auto minmax(0,1fr);gap:26px;align-items:center;padding:24px 26px 20px}
|
|
1749
|
+
.tl-big{display:flex;align-items:baseline;gap:10px;font-variant-numeric:tabular-nums}
|
|
1750
|
+
.tl-big b{font-size:44px;line-height:1;font-weight:730;letter-spacing:-.04em}
|
|
1751
|
+
.tl-big span{font-size:12px;font-weight:550;color:var(--dsw-alias-label-secondary,#5b6472)}
|
|
1752
|
+
.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}
|
|
1753
|
+
.tl-meta .tl-up{color:#8E2B18;font-weight:650}
|
|
1754
|
+
.tl-status{display:inline-flex;align-items:center;gap:8px;margin-top:14px;font-size:12px;font-weight:650}
|
|
1755
|
+
.tl-status-dot{width:8px;height:8px;border-radius:999px;box-shadow:0 0 0 4px color-mix(in srgb,currentColor 14%,transparent)}
|
|
1756
|
+
.tl-hero-ring{display:flex;flex-direction:column;align-items:center;gap:6px;color:var(--dsw-alias-label-primary,#111)}
|
|
1757
|
+
.tl-ring{display:block}
|
|
1758
|
+
.tl-hero-hint{font-size:9.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--dsw-alias-label-secondary,#7b8490)}
|
|
1759
|
+
.tl-spark-wrap{display:flex;flex-direction:column;gap:8px;min-width:0}
|
|
1760
|
+
.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}
|
|
1761
|
+
.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}
|
|
1762
|
+
.tl-spark{width:100%;height:74px;display:block}
|
|
1763
|
+
.tl-grid{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,.85fr);gap:14px;padding:4px 26px 22px}
|
|
1764
|
+
.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}
|
|
1765
|
+
.tl-card-wide{grid-column:1 / -1}
|
|
1766
|
+
.tl-card-head{display:flex;align-items:baseline;justify-content:space-between;gap:10px;margin-bottom:12px}
|
|
1767
|
+
.tl-card-head h3{margin:0;font-size:12px;font-weight:700;letter-spacing:.02em}
|
|
1768
|
+
.tl-card-head span{font-size:10px;letter-spacing:.08em;text-transform:uppercase;color:var(--dsw-alias-label-secondary,#7b8490);font-weight:650}
|
|
1769
|
+
.tl-river{display:flex;height:12px;border-radius:999px;overflow:hidden;background:color-mix(in oklab,currentColor 8%,transparent)}
|
|
1770
|
+
.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)}
|
|
1771
|
+
.tl-legend{display:flex;flex-direction:column;margin-top:12px}
|
|
1772
|
+
.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}
|
|
1773
|
+
.tl-legend-row:first-child{border-top:0}
|
|
1774
|
+
.tl-swatch{width:8px;height:8px;border-radius:2px}
|
|
1775
|
+
.tl-legend-label{color:var(--dsw-alias-label-primary,#111);font-weight:550;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
1776
|
+
.tl-legend-count{color:var(--dsw-alias-label-secondary,#7b8490);font-size:10.5px}
|
|
1777
|
+
.tl-legend-tokens{text-align:right;font-weight:650}
|
|
1778
|
+
.tl-legend-pct{text-align:right;color:var(--dsw-alias-label-secondary,#7b8490);font-size:10.5px}
|
|
1779
|
+
.tl-top{display:flex;flex-direction:column;gap:10px}
|
|
1780
|
+
.tl-top-row{display:grid;grid-template-columns:22px minmax(0,1fr) auto;gap:10px;align-items:center}
|
|
1781
|
+
.tl-rank{font-size:10px;font-weight:700;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-secondary,#7b8490)}
|
|
1782
|
+
.tl-top-body{min-width:0;display:flex;flex-direction:column;gap:5px}
|
|
1783
|
+
.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}
|
|
1784
|
+
.tl-top-bar{height:3px;border-radius:999px;background:color-mix(in oklab,currentColor 9%,transparent);overflow:hidden}
|
|
1785
|
+
.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)}
|
|
1786
|
+
.tl-top-tokens{font-size:11px;font-weight:650;font-variant-numeric:tabular-nums;color:var(--dsw-alias-label-secondary,#5b6472)}
|
|
1787
|
+
.tl-savings{display:flex;flex-direction:column}
|
|
1788
|
+
.tl-save-num{display:flex;align-items:baseline;gap:8px;margin-bottom:10px}
|
|
1789
|
+
.tl-save-num b{font-size:30px;line-height:1;font-weight:730;letter-spacing:-.035em;color:#0E6E5E;font-variant-numeric:tabular-nums}
|
|
1790
|
+
.tl-save-num span{font-size:11px;color:var(--dsw-alias-label-secondary,#5b6472);max-width:130px;line-height:14px}
|
|
1791
|
+
.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}
|
|
1792
|
+
.tl-save-compare>div{display:flex;flex-direction:column;gap:2px}
|
|
1793
|
+
.tl-save-compare span{font-size:9.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--dsw-alias-label-secondary,#7b8490)}
|
|
1794
|
+
.tl-save-compare b{font-size:16px;font-weight:700}
|
|
1795
|
+
.tl-save-arrow{font-size:15px;opacity:.5;display:flex;align-items:center}
|
|
1796
|
+
.tl-save-list{margin-top:10px;display:flex;flex-direction:column}
|
|
1797
|
+
.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)}
|
|
1798
|
+
.tl-save-row span:last-child{font-variant-numeric:tabular-nums;font-weight:600;color:var(--dsw-alias-label-primary,#111)}
|
|
1799
|
+
.tl-save-foot{margin-top:auto;padding-top:10px;font-size:10.5px;line-height:15px;color:var(--dsw-alias-label-secondary,#7b8490)}
|
|
1800
|
+
.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}
|
|
1801
|
+
.tl-skeleton{padding:60px 26px;text-align:center;color:var(--dsw-alias-label-secondary,#7b8490);font-size:13px}
|
|
1802
|
+
.tl-empty{padding:18px 2px;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#7b8490)}
|
|
1803
|
+
.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}
|
|
1804
|
+
@keyframes tl-fade{from{opacity:0}to{opacity:1}}
|
|
1805
|
+
@keyframes tl-in{from{opacity:0;transform:translateY(10px) scale(.985)}to{opacity:1;transform:none}}
|
|
1806
|
+
@media (max-width: 900px){
|
|
1807
|
+
.tl-hero{grid-template-columns:minmax(0,1fr) auto;gap:18px}
|
|
1808
|
+
.tl-spark-wrap{grid-column:1 / -1}
|
|
1809
|
+
.tl-grid{grid-template-columns:1fr}
|
|
1810
|
+
.tl-card-wide{grid-column:auto}
|
|
1811
|
+
}
|
|
1812
|
+
@media (max-width: 640px){
|
|
1813
|
+
.tl-backdrop{padding:0}
|
|
1814
|
+
.tl-panel{width:100%;max-height:100vh;height:100%;border-radius:0;border-left:0;border-right:0}
|
|
1815
|
+
.tl-head{padding:18px 16px 12px}
|
|
1816
|
+
.tl-title{font-size:21px}
|
|
1817
|
+
.tl-hero{grid-template-columns:1fr;padding:18px 16px 14px;gap:14px}
|
|
1818
|
+
.tl-hero-ring{flex-direction:row;justify-content:flex-start;gap:14px}
|
|
1819
|
+
.tl-big b{font-size:34px}
|
|
1820
|
+
.tl-grid{padding:4px 16px 18px}
|
|
1821
|
+
.tl-legend-row{grid-template-columns:8px minmax(0,1fr) auto}
|
|
1822
|
+
.tl-legend-count{display:none}
|
|
1823
|
+
.tl-legend-pct{display:none}
|
|
1824
|
+
.tl-foot{padding:10px 16px}
|
|
1825
|
+
}
|
|
1826
|
+
`;
|
|
1827
|
+
|
|
1828
|
+
/* ------------------------------------------------------------------ */
|
|
1829
|
+
/* apply */
|
|
1830
|
+
/* ------------------------------------------------------------------ */
|
|
1831
|
+
|
|
1832
|
+
function apply(ctx) {
|
|
1833
|
+
pluginCtx = ctx;
|
|
1834
|
+
ctx.effect(() => {
|
|
1835
|
+
const style = document.createElement("style");
|
|
1836
|
+
style.id = "dsh-token-lab";
|
|
1837
|
+
style.textContent = CSS;
|
|
1838
|
+
document.head.appendChild(style);
|
|
1839
|
+
return () => style.remove();
|
|
1840
|
+
}, "token-lab: styles");
|
|
1841
|
+
|
|
1842
|
+
ctx.effect(() => ctx.slots.inject("shell.overlay", () => ctx.slots.register({
|
|
1843
|
+
name: "shell.overlay",
|
|
1844
|
+
id: "token-lab-panel",
|
|
1845
|
+
order: 650
|
|
1846
|
+
}, TokenPanel)), "token-lab: panel");
|
|
1095
1847
|
}
|
|
1096
1848
|
|
|
1097
1849
|
exports.apply = apply;
|