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
|
@@ -70,6 +70,11 @@ window.__ModuleLoader__.load({
|
|
|
70
70
|
if (value % 10 >= 2 && value % 10 <= 4 && (value % 100 < 12 || value % 100 > 14)) return value + " запуска";
|
|
71
71
|
return value + " запусков";
|
|
72
72
|
}
|
|
73
|
+
function compactTokens(value) {
|
|
74
|
+
const n = Number(value) || 0;
|
|
75
|
+
if (n >= 1000) return (n / 1000).toFixed(1).replace(".0", "").replace(".", ",") + "k";
|
|
76
|
+
return String(n);
|
|
77
|
+
}
|
|
73
78
|
function shortDate(iso) {
|
|
74
79
|
if (!iso) return "—";
|
|
75
80
|
try {
|
|
@@ -430,7 +435,7 @@ window.__ModuleLoader__.load({
|
|
|
430
435
|
{ id: "design", label: "Дизайн", color: "#0E6E5E", skills: ["wow-design", "design-critique", "ui-design-system"] },
|
|
431
436
|
{ id: "seo", label: "SEO", color: "#2C5F8A", skills: ["seo"] },
|
|
432
437
|
{ id: "content", label: "Контент", color: "#B36A43", skills: ["content-writing"] },
|
|
433
|
-
{ id: "process", label: "Процесс", color: "#6B7275", skills: ["dev-workflow", "requirements-and-planning", "skills-router"] },
|
|
438
|
+
{ id: "process", label: "Процесс", color: "#6B7275", skills: ["dev-workflow", "requirements-and-planning", "skills-router", "context-economy"] },
|
|
434
439
|
{ id: "quality", label: "Качество", color: "#1F7A57", skills: ["testing-strategy", "debugging-playbook", "code-review-checklist"] },
|
|
435
440
|
{ id: "safety", label: "Данные", color: "#94661C", skills: ["database-safety", "security-review", "pg-upgrade-verify"] },
|
|
436
441
|
{ id: "release", label: "Релиз", color: "#2C5F8A", skills: ["release-and-deploy", "git-and-ci"] },
|
|
@@ -438,7 +443,7 @@ window.__ModuleLoader__.load({
|
|
|
438
443
|
{ id: "memory", label: "Память", color: "#5B4B8A", skills: ["project-deep-dive", "project-memory-maintenance"] }
|
|
439
444
|
];
|
|
440
445
|
const SKILL_NAMES = SKILL_GROUPS.reduce((acc, group) => acc.concat(group.skills), []);
|
|
441
|
-
const SKILLS_TILE_STYLE = Object.assign({}, TILE_BASE, { display: "block", padding: "10px
|
|
446
|
+
const SKILLS_TILE_STYLE = Object.assign({}, TILE_BASE, { display: "block", padding: "8px 10px", cursor: "pointer" });
|
|
442
447
|
const SKILLS_HEAD_STYLE = { display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: "8px" };
|
|
443
448
|
const SKILLS_COUNT_STYLE = {
|
|
444
449
|
display: "inline-flex", alignItems: "center", justifyContent: "center",
|
|
@@ -451,14 +456,14 @@ window.__ModuleLoader__.load({
|
|
|
451
456
|
fontSize: "10px", fontWeight: 500, marginTop: "2px", letterSpacing: "0.02em",
|
|
452
457
|
color: "var(--dsw-alias-label-secondary, #777)"
|
|
453
458
|
};
|
|
454
|
-
const SKILLS_CHIPS_STYLE = { display: "flex", flexWrap: "
|
|
459
|
+
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%)" };
|
|
455
460
|
const SKILLS_CHIP_STYLE = {
|
|
456
|
-
fontSize: "
|
|
461
|
+
fontSize: "9.5px", fontWeight: 600, lineHeight: 1.45,
|
|
457
462
|
color: "var(--dsw-alias-label-secondary, #666)",
|
|
458
463
|
border: "1px solid var(--dsw-alias-border-l2, rgba(0,0,0,.10))",
|
|
459
464
|
borderRadius: "999px", padding: "1px 6px",
|
|
460
465
|
background: "var(--dsw-alias-settings-card-fill, rgba(0,0,0,.03))",
|
|
461
|
-
whiteSpace: "nowrap"
|
|
466
|
+
whiteSpace: "nowrap", flex: "0 0 auto"
|
|
462
467
|
};
|
|
463
468
|
const SKILLS_TOGGLE_STYLE = {
|
|
464
469
|
marginTop: "8px", display: "flex", alignItems: "center", justifyContent: "space-between", gap: "8px",
|
|
@@ -522,10 +527,20 @@ window.__ModuleLoader__.load({
|
|
|
522
527
|
.wtg9ga_root .GAkraW_groupSection.gkb-collapsed > .wnFoaW_projectRow::after { content: "▸"; }
|
|
523
528
|
.wtg9ga_root .GAkraW_groupSection.gkb-collapsed > span:not(:first-child) { display: none !important; }
|
|
524
529
|
.wtg9ga_root .wnFoaW_projectRow:hover::after { opacity: 1; color: var(--dsw-alias-label-primary, #111); }
|
|
530
|
+
/* Short windows: the sidebar must still show the account/settings foot. */
|
|
531
|
+
@media (max-height: 820px) {
|
|
532
|
+
.wtg9ga_root > .wtg9ga_regionArea { min-height: 140px; }
|
|
533
|
+
.gkb-skills-chips { display: none !important; }
|
|
534
|
+
.gkb-skills-active { margin-top: 4px !important; }
|
|
535
|
+
}
|
|
536
|
+
/* Mobile rail is 55px: tiles cannot fit, so they live behind the FAB. */
|
|
537
|
+
@media (max-width: 760px) {
|
|
538
|
+
[data-gkb-tiles] { display: none !important; }
|
|
539
|
+
}
|
|
525
540
|
`;
|
|
526
541
|
const SKILLS_CATALOG_CSS = `
|
|
527
542
|
.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}
|
|
528
|
-
.gkb-sk-panel{position:relative;display:flex;flex-direction:column;width:min(1120px,94vw);max-height:
|
|
543
|
+
.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)}
|
|
529
544
|
.gkb-sk-head{flex:none;display:flex;flex-direction:column;gap:12px;padding:22px 24px 14px;background:
|
|
530
545
|
radial-gradient(120% 160% at 100% 0%,color-mix(in srgb,var(--dsw-alias-brand-primary,#0E6E5E) 10%,transparent) 0%,transparent 55%),
|
|
531
546
|
linear-gradient(180deg,color-mix(in srgb,var(--dsw-alias-bg-layer-1,#f6f7f6) 70%,transparent) 0%,transparent 100%);
|
|
@@ -538,7 +553,8 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
538
553
|
.gkb-sk-sub b{color:var(--dsw-alias-label-primary,#111);font-weight:650}
|
|
539
554
|
.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}
|
|
540
555
|
.gkb-sk-close:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.06));color:var(--dsw-alias-label-primary,#111)}
|
|
541
|
-
.gkb-sk-close:focus-visible,.gkb-sk-card:focus-visible,.gkb-sk-chip:focus-visible,.gkb-sk-btn:focus-visible,.gkb-sk-
|
|
556
|
+
.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}
|
|
557
|
+
.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)}
|
|
542
558
|
.gkb-sk-search-row{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
|
|
543
559
|
.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}
|
|
544
560
|
.gkb-sk-search::placeholder{color:var(--dsw-alias-label-tertiary,#98a0a8)}
|
|
@@ -594,18 +610,52 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
594
610
|
.gkb-sk-btn:hover{background:var(--dsw-alias-interactive-bg-hover,rgba(0,0,0,.05))}
|
|
595
611
|
.gkb-sk-btn[data-tone="primary"]{border-color:transparent;background:var(--dsw-alias-label-primary,#111);color:var(--dsw-alias-bg-base,#fff)}
|
|
596
612
|
.gkb-sk-foot-note{margin-left:auto;font-size:10px;color:var(--dsw-alias-label-tertiary,#8a939b);align-self:center}
|
|
613
|
+
.gkb-sk-head-actions{display:flex;align-items:center;gap:8px;flex:none}
|
|
614
|
+
.gkb-sk-cost{color:var(--dsw-alias-label-secondary,#7b8490)}
|
|
615
|
+
.gkb-sk-cost[data-heavy="true"]{color:#8E2B18;font-weight:650}
|
|
616
|
+
.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}
|
|
617
|
+
.gkb-sk-btn[data-tone="danger"]{border-color:color-mix(in srgb,#8E2B18 40%,transparent);color:#8E2B18}
|
|
618
|
+
.gkb-sk-btn[data-tone="danger"]:hover{background:color-mix(in srgb,#8E2B18 8%,transparent)}
|
|
619
|
+
.gkb-sk-btn:disabled{opacity:.5;cursor:default}
|
|
620
|
+
.gkb-sk-editor{display:flex;flex-direction:column;gap:8px}
|
|
621
|
+
.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)}
|
|
622
|
+
.gkb-sk-editor-cost{font-size:10.5px;font-weight:650;color:var(--dsw-alias-label-secondary,#7b8490);font-variant-numeric:tabular-nums}
|
|
623
|
+
.gkb-sk-editor-cost[data-heavy="true"]{color:#8E2B18}
|
|
624
|
+
.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}
|
|
625
|
+
.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)}
|
|
626
|
+
.gkb-sk-editor-hint{font-size:10.5px;line-height:15px;color:var(--dsw-alias-label-secondary,#7b8490)}
|
|
627
|
+
.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}
|
|
628
|
+
.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)}
|
|
629
|
+
.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)}
|
|
630
|
+
.gkb-sk-modal-text{margin:0;font-size:12px;line-height:18px;color:var(--dsw-alias-label-secondary,#5b6472)}
|
|
631
|
+
.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)}
|
|
632
|
+
.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)}
|
|
633
|
+
.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}
|
|
634
|
+
.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)}
|
|
635
|
+
.gkb-sk-modal-foot{display:flex;justify-content:flex-end;gap:8px;margin-top:4px}
|
|
636
|
+
.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}
|
|
637
|
+
.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}
|
|
638
|
+
.gkb-fab:active{transform:scale(.98)}
|
|
639
|
+
@media (max-width: 760px){.gkb-fab{display:inline-flex}}
|
|
597
640
|
@media (max-width:760px){
|
|
598
641
|
.gkb-sk-backdrop{padding:0}
|
|
599
642
|
.gkb-sk-panel{width:100vw;height:100dvh;max-height:none;border-radius:0;border:none}
|
|
600
|
-
.gkb-sk-head{padding:
|
|
601
|
-
.gkb-sk-
|
|
602
|
-
.gkb-sk-
|
|
643
|
+
.gkb-sk-head{padding:14px 14px 10px;gap:9px}
|
|
644
|
+
.gkb-sk-head-row{flex-wrap:wrap;gap:8px}
|
|
645
|
+
.gkb-sk-head-row > div:first-child{flex:1 1 100%;min-width:0}
|
|
646
|
+
.gkb-sk-kicker{white-space:nowrap;font-size:9px;letter-spacing:.13em}
|
|
647
|
+
.gkb-sk-title{font-size:22px;line-height:27px;margin-top:4px}
|
|
648
|
+
.gkb-sk-sub{font-size:11px;line-height:16px}
|
|
649
|
+
.gkb-sk-head-actions{width:100%;justify-content:flex-start;gap:7px}
|
|
650
|
+
.gkb-sk-title{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
651
|
+
.gkb-sk-body{padding:12px 14px 20px}
|
|
603
652
|
.gkb-sk-grid,.gkb-sk-skeleton{grid-template-columns:1fr}
|
|
604
653
|
.gkb-sk-card-desc{min-height:0;-webkit-line-clamp:4}
|
|
605
654
|
.gkb-sk-detail{width:100%;max-height:100%}
|
|
606
655
|
.gkb-sk-detail-body{padding:14px 14px 16px}
|
|
607
656
|
.gkb-sk-detail-head{padding:14px 14px 12px}
|
|
608
657
|
.gkb-sk-detail-foot{padding:10px 14px 14px}
|
|
658
|
+
.gkb-sk-editor-area{min-height:240px;max-height:40vh}
|
|
609
659
|
}
|
|
610
660
|
@media (prefers-color-scheme:dark){
|
|
611
661
|
.gkb-sk-panel{box-shadow:0 48px 140px -40px rgba(0,0,0,.9),inset 0 1px 0 rgba(255,255,255,.05)}
|
|
@@ -670,25 +720,21 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
670
720
|
h("div", { key: "titles" }, [
|
|
671
721
|
h("div", { key: "label", style: LABEL_STYLE }, "Навыки"),
|
|
672
722
|
h("div", { key: "sub", style: SKILLS_SUB_STYLE }, totals
|
|
673
|
-
? pluralRuns(totals.runs) + " · " + totals.used + "
|
|
723
|
+
? pluralRuns(totals.runs) + " · " + totals.used + "/" + totals.skills + " · ≈" + compactTokens(totals.tokens)
|
|
674
724
|
: "загрузка статистики…")
|
|
675
725
|
]),
|
|
676
726
|
h("span", { key: "count", style: SKILLS_COUNT_STYLE }, String(count))
|
|
677
727
|
]),
|
|
678
728
|
active.length > 0
|
|
679
|
-
? h("div", { key: "active", style: { display: "flex", alignItems: "center", gap: "6px", marginTop: "6px", minWidth: 0 } }, [
|
|
729
|
+
? h("div", { key: "active", className: "gkb-skills-active", style: { display: "flex", alignItems: "center", gap: "6px", marginTop: "6px", minWidth: 0 } }, [
|
|
680
730
|
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)" } }),
|
|
681
731
|
h("span", { key: "label", style: { fontSize: "9px", fontWeight: 750, letterSpacing: ".12em", textTransform: "uppercase", color: "var(--dsw-alias-brand-primary, #0E6E5E)", flex: "none" } }, "в работе"),
|
|
682
732
|
h("span", { key: "names", style: { fontSize: "10px", color: "var(--dsw-alias-label-secondary, #666)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" } }, activeNames.join(", ") + activeMore)
|
|
683
733
|
])
|
|
684
734
|
: null,
|
|
685
|
-
h("div", { key: "chips", style: SKILLS_CHIPS_STYLE }, categories.map((category) =>
|
|
735
|
+
h("div", { key: "chips", className: "gkb-skills-chips", style: SKILLS_CHIPS_STYLE }, categories.slice(0, 4).map((category) =>
|
|
686
736
|
h("span", { key: category.id, style: SKILLS_CHIP_STYLE }, category.label + " " + (counts[category.id] || ""))
|
|
687
|
-
))
|
|
688
|
-
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)" } }, [
|
|
689
|
-
h("span", { key: "text" }, "Открыть каталог"),
|
|
690
|
-
h("span", { key: "arrow", style: { opacity: .8 } }, "→")
|
|
691
|
-
])
|
|
737
|
+
))
|
|
692
738
|
]);
|
|
693
739
|
}
|
|
694
740
|
|
|
@@ -702,6 +748,81 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
702
748
|
const [detailLoading, setDetailLoading] = react.useState(false);
|
|
703
749
|
const [detailError, setDetailError] = react.useState("");
|
|
704
750
|
const [copied, setCopied] = react.useState(false);
|
|
751
|
+
const [editor, setEditor] = react.useState(null);
|
|
752
|
+
const [createDraft, setCreateDraft] = react.useState(null);
|
|
753
|
+
const [deleteConfirm, setDeleteConfirm] = react.useState(null);
|
|
754
|
+
const [notice, setNotice] = react.useState("");
|
|
755
|
+
const editorRef = react.useRef(null);
|
|
756
|
+
react.useEffect(() => {
|
|
757
|
+
if (editor && editorRef.current) editorRef.current.focus();
|
|
758
|
+
}, [editor && editor.name]);
|
|
759
|
+
|
|
760
|
+
const tokenEstimate = (text) => {
|
|
761
|
+
try { return Math.max(40, Math.round(new TextEncoder().encode(String(text)).length / 3.2)); } catch { return 0; }
|
|
762
|
+
};
|
|
763
|
+
const shortTokens = (value) => {
|
|
764
|
+
const n = Number(value) || 0;
|
|
765
|
+
if (n >= 1000) return (n / 1000).toFixed(1).replace(".0", "").replace(".", ",") + "k";
|
|
766
|
+
return String(n);
|
|
767
|
+
};
|
|
768
|
+
const flash = (message) => {
|
|
769
|
+
setNotice(message);
|
|
770
|
+
setTimeout(() => setNotice(""), 3200);
|
|
771
|
+
};
|
|
772
|
+
const reloadSkills = async () => {
|
|
773
|
+
await loadSkills(store.sessionId, true);
|
|
774
|
+
};
|
|
775
|
+
const openEditor = () => {
|
|
776
|
+
if (!detail || !detail.skill) return;
|
|
777
|
+
setEditor({ name: detail.skill.name, content: detail.skill.content, saving: false, error: "", baseTokens: detail.skill.tokens });
|
|
778
|
+
};
|
|
779
|
+
const saveEditor = async () => {
|
|
780
|
+
if (!editor || editor.saving) return;
|
|
781
|
+
setEditor(Object.assign({}, editor, { saving: true, error: "" }));
|
|
782
|
+
try {
|
|
783
|
+
const saved = await callSkills("skill-save", { name: editor.name, content: editor.content, sessionId: store.sessionId || undefined });
|
|
784
|
+
setDetail((previous) => previous ? Object.assign({}, previous, { skill: saved }) : previous);
|
|
785
|
+
setEditor(null);
|
|
786
|
+
await reloadSkills();
|
|
787
|
+
flash("Сохранено · резервная копия в .dsh-kit-backups");
|
|
788
|
+
} catch (error) {
|
|
789
|
+
setEditor((previous) => previous ? Object.assign({}, previous, { saving: false, error: String(error && error.message ? error.message : error) }) : previous);
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
const submitCreate = async () => {
|
|
793
|
+
if (!createDraft || createDraft.busy) return;
|
|
794
|
+
const name = createDraft.name.trim();
|
|
795
|
+
if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name)) {
|
|
796
|
+
setCreateDraft(Object.assign({}, createDraft, { error: "Имя: kebab-case из латиницы и цифр, например my-skill" }));
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
setCreateDraft(Object.assign({}, createDraft, { busy: true, error: "" }));
|
|
800
|
+
try {
|
|
801
|
+
const result = createDraft.mode === "duplicate"
|
|
802
|
+
? await callSkills("skill-duplicate", { source: createDraft.source, name, sessionId: store.sessionId || undefined })
|
|
803
|
+
: await callSkills("skill-create", { name, description: createDraft.description.trim(), sessionId: store.sessionId || undefined });
|
|
804
|
+
setCreateDraft(null);
|
|
805
|
+
await reloadSkills();
|
|
806
|
+
setSelected(result.skill.name);
|
|
807
|
+
flash(createDraft.mode === "duplicate" ? "Копия создана" : "Навык создан");
|
|
808
|
+
} catch (error) {
|
|
809
|
+
setCreateDraft((previous) => previous ? Object.assign({}, previous, { busy: false, error: String(error && error.message ? error.message : error) }) : previous);
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
const submitDelete = async () => {
|
|
813
|
+
if (!deleteConfirm || deleteConfirm.busy) return;
|
|
814
|
+
setDeleteConfirm(Object.assign({}, deleteConfirm, { busy: true, error: "" }));
|
|
815
|
+
try {
|
|
816
|
+
await callSkills("skill-delete", { name: deleteConfirm.name, sessionId: store.sessionId || undefined });
|
|
817
|
+
setDeleteConfirm(null);
|
|
818
|
+
setSelected(null);
|
|
819
|
+
setDetail(null);
|
|
820
|
+
await reloadSkills();
|
|
821
|
+
flash("Навык перемещён в .dsh-kit-trash — данные не потеряны");
|
|
822
|
+
} catch (error) {
|
|
823
|
+
setDeleteConfirm((previous) => previous ? Object.assign({}, previous, { busy: false, error: String(error && error.message ? error.message : error) }) : previous);
|
|
824
|
+
}
|
|
825
|
+
};
|
|
705
826
|
|
|
706
827
|
react.useEffect(() => {
|
|
707
828
|
if (store.open) void loadSkills(store.sessionId, true);
|
|
@@ -714,14 +835,22 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
714
835
|
react.useEffect(() => {
|
|
715
836
|
if (!store.open) return undefined;
|
|
716
837
|
const onKey = (event) => {
|
|
838
|
+
if ((event.metaKey || event.ctrlKey) && String(event.key).toLowerCase() === "s" && editor) {
|
|
839
|
+
event.preventDefault();
|
|
840
|
+
void saveEditor();
|
|
841
|
+
return;
|
|
842
|
+
}
|
|
717
843
|
if (event.key !== "Escape") return;
|
|
718
844
|
event.stopPropagation();
|
|
845
|
+
if (editor) { setEditor(null); return; }
|
|
846
|
+
if (deleteConfirm) { setDeleteConfirm(null); return; }
|
|
847
|
+
if (createDraft) { setCreateDraft(null); return; }
|
|
719
848
|
if (selected) { setSelected(null); setDetail(null); }
|
|
720
849
|
else skillsStore.set({ open: false });
|
|
721
850
|
};
|
|
722
851
|
document.addEventListener("keydown", onKey, true);
|
|
723
852
|
return () => document.removeEventListener("keydown", onKey, true);
|
|
724
|
-
}, [store.open, selected]);
|
|
853
|
+
}, [store.open, selected, editor, deleteConfirm, createDraft]);
|
|
725
854
|
react.useEffect(() => {
|
|
726
855
|
if (!selected) { setDetail(null); setDetailError(""); return; }
|
|
727
856
|
let alive = true;
|
|
@@ -786,7 +915,8 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
786
915
|
h("span", { className: "gkb-sk-card-foot", key: "foot" }, [
|
|
787
916
|
h("span", { className: "gkb-sk-meta", key: "meta" }, [
|
|
788
917
|
h("span", { key: "files" }, h("b", null, "обновлён "), shortDate(skill.updatedAt)),
|
|
789
|
-
h("span", { key: "used" }, h("b", null, skill.usage.count > 0 ? "последний раз " : "ещё не использовался"), skill.usage.count > 0 ? shortDate(skill.usage.lastAt) : "")
|
|
918
|
+
h("span", { key: "used" }, h("b", null, skill.usage.count > 0 ? "последний раз " : "ещё не использовался"), skill.usage.count > 0 ? shortDate(skill.usage.lastAt) : ""),
|
|
919
|
+
h("span", { key: "cost", className: "gkb-sk-cost", "data-heavy": skill.heavy ? "true" : "false" }, "ядро ≈" + shortTokens(skill.tokens))
|
|
790
920
|
]),
|
|
791
921
|
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))
|
|
792
922
|
]),
|
|
@@ -812,6 +942,7 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
812
942
|
h("div", { className: "gkb-sk-stat", key: "week" }, [h("span", { key: "l" }, "за неделю"), h("b", { key: "v" }, String(detail.usage ? detail.usage.week : 0))]),
|
|
813
943
|
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" }) : "")]),
|
|
814
944
|
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)]),
|
|
945
|
+
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" : "в бюджете")]),
|
|
815
946
|
h("div", { className: "gkb-sk-stat", key: "updated" }, [h("span", { key: "l" }, "обновлён"), h("b", { key: "v" }, shortDate(detail.skill.updatedAt))])
|
|
816
947
|
])
|
|
817
948
|
: null,
|
|
@@ -819,11 +950,46 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
819
950
|
? h("div", { key: "when", className: "gkb-sk-when" }, [h("b", { key: "b" }, "Когда применять: "), detail.skill.whenToUse])
|
|
820
951
|
: null,
|
|
821
952
|
detail && detail.skill
|
|
822
|
-
?
|
|
953
|
+
? (editor
|
|
954
|
+
? h("div", { key: "editor", className: "gkb-sk-editor" }, [
|
|
955
|
+
h("div", { key: "head", className: "gkb-sk-editor-head" }, [
|
|
956
|
+
h("span", { key: "t" }, "Редактирование SKILL.md"),
|
|
957
|
+
h("span", { key: "c", className: "gkb-sk-editor-cost", "data-heavy": tokenEstimate(editor.content) > 6000 ? "true" : "false" }, "≈" + shortTokens(tokenEstimate(editor.content)) + " токенов")
|
|
958
|
+
]),
|
|
959
|
+
editor.error ? h("div", { key: "err", className: "gkb-sk-error" }, editor.error) : null,
|
|
960
|
+
h("textarea", {
|
|
961
|
+
key: "area",
|
|
962
|
+
ref: editorRef,
|
|
963
|
+
className: "gkb-sk-editor-area",
|
|
964
|
+
value: editor.content,
|
|
965
|
+
spellCheck: false,
|
|
966
|
+
"aria-label": "Содержимое SKILL.md",
|
|
967
|
+
onChange: (event) => setEditor(Object.assign({}, editor, { content: event.target.value })),
|
|
968
|
+
onKeyDown: (event) => {
|
|
969
|
+
if (event.key !== "Tab") return;
|
|
970
|
+
event.preventDefault();
|
|
971
|
+
const element = event.target;
|
|
972
|
+
const start = element.selectionStart;
|
|
973
|
+
const end = element.selectionEnd;
|
|
974
|
+
const next = editor.content.slice(0, start) + " " + editor.content.slice(end);
|
|
975
|
+
setEditor(Object.assign({}, editor, { content: next }));
|
|
976
|
+
requestAnimationFrame(() => { element.selectionStart = start + 2; element.selectionEnd = start + 2; });
|
|
977
|
+
}
|
|
978
|
+
}),
|
|
979
|
+
h("div", { key: "hint", className: "gkb-sk-editor-hint" }, "Ctrl+S — сохранить. Перед записью создаётся резервная копия в ~/.dsh/skills/.dsh-kit-backups. Держите ядро ≤ 6k токенов: детали — в references.")
|
|
980
|
+
])
|
|
981
|
+
: h("pre", { key: "content", className: "gkb-sk-content" }, detail.skill.content))
|
|
823
982
|
: null
|
|
824
983
|
]),
|
|
825
|
-
h("div", { className: "gkb-sk-detail-foot" }, [
|
|
826
|
-
h("button", { key: "
|
|
984
|
+
h("div", { className: "gkb-sk-detail-foot" }, editor ? [
|
|
985
|
+
h("button", { key: "save", type: "button", className: "gkb-sk-btn", "data-tone": "primary", disabled: editor.saving, onClick: () => { void saveEditor(); } }, editor.saving ? "Сохраняю…" : "Сохранить"),
|
|
986
|
+
h("button", { key: "cancel", type: "button", className: "gkb-sk-btn", onClick: () => setEditor(null) }, "Отмена"),
|
|
987
|
+
h("span", { key: "note", className: "gkb-sk-foot-note" }, "≈" + shortTokens(tokenEstimate(editor.content)) + " токенов · Ctrl+S")
|
|
988
|
+
] : [
|
|
989
|
+
h("button", { key: "edit", type: "button", className: "gkb-sk-btn", "data-tone": "primary", onClick: openEditor }, "Редактировать"),
|
|
990
|
+
h("button", { key: "dup", type: "button", className: "gkb-sk-btn", onClick: () => setCreateDraft({ mode: "duplicate", name: selected + "-copy", source: selected, description: "", error: "" }) }, "Дублировать"),
|
|
991
|
+
h("button", { key: "del", type: "button", className: "gkb-sk-btn", "data-tone": "danger", onClick: () => setDeleteConfirm({ name: selected, error: "" }) }, "Удалить"),
|
|
992
|
+
h("button", { key: "open", type: "button", className: "gkb-sk-btn", onClick: () => { void callSkills("open-skill", { name: selected }).catch(() => {}); } }, "Открыть папку"),
|
|
827
993
|
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 ? "Скопировано" : "Скопировать имя"),
|
|
828
994
|
h("button", { key: "close", type: "button", className: "gkb-sk-btn", onClick: () => { setSelected(null); setDetail(null); } }, "Закрыть"),
|
|
829
995
|
h("span", { key: "path", className: "gkb-sk-foot-note" }, detail && detail.skill ? detail.skill.path : "")
|
|
@@ -832,6 +998,47 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
832
998
|
)
|
|
833
999
|
: null;
|
|
834
1000
|
|
|
1001
|
+
const createModal = createDraft
|
|
1002
|
+
? h("div", { className: "gkb-sk-modal-backdrop", onMouseDown: (event) => { if (event.target === event.currentTarget) setCreateDraft(null); } },
|
|
1003
|
+
h("div", { className: "gkb-sk-modal", role: "dialog", "aria-modal": "true", "aria-label": createDraft.mode === "duplicate" ? "Дублировать навык" : "Новый навык" }, [
|
|
1004
|
+
h("h3", { key: "title", className: "gkb-sk-modal-title" }, createDraft.mode === "duplicate" ? "Дублировать навык" : "Новый навык"),
|
|
1005
|
+
createDraft.mode === "duplicate"
|
|
1006
|
+
? h("p", { key: "text", className: "gkb-sk-modal-text" }, "Копия навыка «" + createDraft.source + "» со всеми references.")
|
|
1007
|
+
: h("p", { key: "text", className: "gkb-sk-modal-text" }, "Появится папка в ~/.dsh/skills с компактным шаблоном: ядро ≤ 6k токенов, детали — в references."),
|
|
1008
|
+
h("label", { key: "name", className: "gkb-sk-field" }, [
|
|
1009
|
+
h("span", { key: "s" }, "Имя (kebab-case)"),
|
|
1010
|
+
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 })) })
|
|
1011
|
+
]),
|
|
1012
|
+
createDraft.mode === "create"
|
|
1013
|
+
? h("label", { key: "desc", className: "gkb-sk-field" }, [
|
|
1014
|
+
h("span", { key: "s" }, "Короткое описание-триггер"),
|
|
1015
|
+
h("input", { key: "i", className: "gkb-sk-input", value: createDraft.description, placeholder: "Когда применять навык — одной строкой", onChange: (event) => setCreateDraft(Object.assign({}, createDraft, { description: event.target.value })) })
|
|
1016
|
+
])
|
|
1017
|
+
: null,
|
|
1018
|
+
createDraft.error ? h("div", { key: "error", className: "gkb-sk-error" }, createDraft.error) : null,
|
|
1019
|
+
h("div", { key: "foot", className: "gkb-sk-modal-foot" }, [
|
|
1020
|
+
h("button", { key: "ok", type: "button", className: "gkb-sk-btn", "data-tone": "primary", disabled: createDraft.busy, onClick: () => { void submitCreate(); } }, createDraft.busy ? "Создаю…" : (createDraft.mode === "duplicate" ? "Создать копию" : "Создать навык")),
|
|
1021
|
+
h("button", { key: "cancel", type: "button", className: "gkb-sk-btn", onClick: () => setCreateDraft(null) }, "Отмена")
|
|
1022
|
+
])
|
|
1023
|
+
])
|
|
1024
|
+
)
|
|
1025
|
+
: null;
|
|
1026
|
+
|
|
1027
|
+
const deleteModal = deleteConfirm
|
|
1028
|
+
? h("div", { className: "gkb-sk-modal-backdrop", onMouseDown: (event) => { if (event.target === event.currentTarget) setDeleteConfirm(null); } },
|
|
1029
|
+
h("div", { className: "gkb-sk-modal", role: "dialog", "aria-modal": "true", "aria-label": "Удалить навык" }, [
|
|
1030
|
+
h("h3", { key: "title", className: "gkb-sk-modal-title" }, "Удалить навык?"),
|
|
1031
|
+
h("p", { key: "text", className: "gkb-sk-modal-text" }, "Папка переместится в ~/.dsh/skills/.dsh-kit-trash — это обратимо, файлы не удаляются."),
|
|
1032
|
+
h("code", { key: "name", className: "gkb-sk-modal-code" }, deleteConfirm.name),
|
|
1033
|
+
deleteConfirm.error ? h("div", { key: "error", className: "gkb-sk-error" }, deleteConfirm.error) : null,
|
|
1034
|
+
h("div", { key: "foot", className: "gkb-sk-modal-foot" }, [
|
|
1035
|
+
h("button", { key: "ok", type: "button", className: "gkb-sk-btn", "data-tone": "danger", disabled: deleteConfirm.busy, onClick: () => { void submitDelete(); } }, deleteConfirm.busy ? "Переношу…" : "В корзину"),
|
|
1036
|
+
h("button", { key: "cancel", type: "button", className: "gkb-sk-btn", onClick: () => setDeleteConfirm(null) }, "Отмена")
|
|
1037
|
+
])
|
|
1038
|
+
])
|
|
1039
|
+
)
|
|
1040
|
+
: null;
|
|
1041
|
+
|
|
835
1042
|
return h("div", { className: "gkb-sk-backdrop", onMouseDown: (event) => { if (event.target === event.currentTarget) close(); } },
|
|
836
1043
|
h("div", { className: "gkb-sk-panel", role: "dialog", "aria-modal": "true", "aria-label": "Каталог навыков" },
|
|
837
1044
|
h("div", { className: "gkb-sk-head" }, [
|
|
@@ -840,10 +1047,14 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
840
1047
|
h("div", { key: "kicker", className: "gkb-sk-kicker" }, "DSH · навыки"),
|
|
841
1048
|
h("h2", { key: "title", className: "gkb-sk-title" }, "Мои навыки"),
|
|
842
1049
|
h("div", { key: "sub", className: "gkb-sk-sub" }, total
|
|
843
|
-
? [h("b", { key: "n" }, pluralSkills(total.skills)), " · ", h("b", { key: "r" }, pluralRuns(total.runs)), " · ", h("b", { key: "u" }, String(total.used)), " из ", String(total.skills), "
|
|
1050
|
+
? [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 + " в работе" : ""]
|
|
844
1051
|
: "Загружаю статистику…")
|
|
845
1052
|
]),
|
|
846
|
-
h("
|
|
1053
|
+
h("div", { key: "actions", className: "gkb-sk-head-actions" }, [
|
|
1054
|
+
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 {} } }, "Токены"),
|
|
1055
|
+
h("button", { key: "new", type: "button", className: "gkb-sk-btn", "data-tone": "primary", onClick: () => setCreateDraft({ mode: "create", name: "", description: "", error: "" }) }, "Новый навык"),
|
|
1056
|
+
h("button", { key: "close", type: "button", className: "gkb-sk-close", onClick: close, "aria-label": "Закрыть каталог навыков" }, "×")
|
|
1057
|
+
])
|
|
847
1058
|
]),
|
|
848
1059
|
h("div", { key: "search", className: "gkb-sk-search-row" }, [
|
|
849
1060
|
h("input", { key: "input", className: "gkb-sk-search", placeholder: "Поиск по названию, описанию или категории…", value: query, onChange: (event) => setQuery(event.target.value), autoFocus: true, "aria-label": "Поиск навыков" }),
|
|
@@ -855,6 +1066,7 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
855
1066
|
])
|
|
856
1067
|
]),
|
|
857
1068
|
activeBand,
|
|
1069
|
+
notice ? h("div", { key: "notice", className: "gkb-sk-notice", role: "status" }, notice) : null,
|
|
858
1070
|
h("div", { key: "filters", className: "gkb-sk-filters" }, [
|
|
859
1071
|
h("button", { key: "all", type: "button", className: "gkb-sk-chip", "data-active": category === "all" ? "true" : "false", onClick: () => setCategory("all") }, "Все " + skills.length),
|
|
860
1072
|
...categories.filter((item) => counts[item.id] > 0).map((item) => h("button", {
|
|
@@ -879,10 +1091,36 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
879
1091
|
: null
|
|
880
1092
|
]),
|
|
881
1093
|
detailPanel
|
|
882
|
-
)
|
|
1094
|
+
),
|
|
1095
|
+
createModal,
|
|
1096
|
+
deleteModal
|
|
883
1097
|
);
|
|
884
1098
|
}
|
|
885
1099
|
|
|
1100
|
+
/* Mobile rail cannot hold the tile stack; one pill opens the catalog. */
|
|
1101
|
+
function MobileSkillsFab() {
|
|
1102
|
+
const store = useSkillsStore();
|
|
1103
|
+
if (store.open) return null;
|
|
1104
|
+
const totals = store.data ? store.data.totals : null;
|
|
1105
|
+
const open = () => {
|
|
1106
|
+
skillsStore.set({ open: true, sessionId: store.sessionId || null });
|
|
1107
|
+
void loadSkills(store.sessionId, true);
|
|
1108
|
+
};
|
|
1109
|
+
return h("button", {
|
|
1110
|
+
type: "button",
|
|
1111
|
+
className: "gkb-fab",
|
|
1112
|
+
onClick: open,
|
|
1113
|
+
"aria-label": "Открыть каталог навыков"
|
|
1114
|
+
}, [
|
|
1115
|
+
h("svg", { key: "icon", width: 17, height: 17, viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true" }, [
|
|
1116
|
+
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" }),
|
|
1117
|
+
h("circle", { key: "b", cx: 19, cy: 5, r: 1.4, fill: "currentColor" })
|
|
1118
|
+
]),
|
|
1119
|
+
h("span", { key: "label" }, "Навыки"),
|
|
1120
|
+
totals ? h("b", { key: "count" }, String(totals.skills)) : null
|
|
1121
|
+
]);
|
|
1122
|
+
}
|
|
1123
|
+
|
|
886
1124
|
function SpendRow(props) {
|
|
887
1125
|
if (props && props.wide === false) return null;
|
|
888
1126
|
const snap = useSnapshot();
|
|
@@ -908,13 +1146,16 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
908
1146
|
h("span", { key: "value", style: amountStyle }, value)
|
|
909
1147
|
]);
|
|
910
1148
|
}
|
|
911
|
-
/* One slot registration:
|
|
1149
|
+
/* One slot registration: AURORA tiles in a single full-width column.
|
|
1150
|
+
* Token Lab (a sibling client part) hands its tile over through a global;
|
|
1151
|
+
* rendering it here keeps the footer stack instead of a second slot row. */
|
|
912
1152
|
function SidebarTiles(props) {
|
|
1153
|
+
const TokenTile = typeof window !== "undefined" ? window.__DSH_TOKEN_TILE__ : null;
|
|
913
1154
|
return h("div", {
|
|
914
1155
|
style: {
|
|
915
1156
|
display: "flex",
|
|
916
1157
|
flexDirection: "column",
|
|
917
|
-
gap: "
|
|
1158
|
+
gap: "4px",
|
|
918
1159
|
width: "100%",
|
|
919
1160
|
boxSizing: "border-box",
|
|
920
1161
|
padding: "4px 8px 6px"
|
|
@@ -922,6 +1163,7 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
922
1163
|
"data-gkb-tiles": "1"
|
|
923
1164
|
}, [
|
|
924
1165
|
h(SkillsRow, Object.assign({ key: "skills" }, props)),
|
|
1166
|
+
TokenTile ? h(TokenTile, { key: "tokens", useSessions: props && props.useSessions }) : null,
|
|
925
1167
|
h(BalanceRow, { key: "balance" }),
|
|
926
1168
|
h(SpendRow, { key: "spend" })
|
|
927
1169
|
]);
|
|
@@ -1074,6 +1316,12 @@ border-bottom:1px solid var(--dsw-alias-border-l1,rgba(0,0,0,.07))}
|
|
|
1074
1316
|
order: 640
|
|
1075
1317
|
}, SkillsCatalog)), "gkb-ru: skills catalog overlay");
|
|
1076
1318
|
|
|
1319
|
+
ctx.effect(() => ctx.slots.inject("shell.overlay", () => ctx.slots.register({
|
|
1320
|
+
name: "shell.overlay",
|
|
1321
|
+
id: "gkb-mobile-fab",
|
|
1322
|
+
order: 660
|
|
1323
|
+
}, MobileSkillsFab)), "gkb-ru: mobile skills fab");
|
|
1324
|
+
|
|
1077
1325
|
}
|
|
1078
1326
|
|
|
1079
1327
|
exports.apply = apply;
|
|
@@ -11,7 +11,7 @@ import fs from "node:fs";
|
|
|
11
11
|
import os from "node:os";
|
|
12
12
|
import path from "node:path";
|
|
13
13
|
import { spawn } from "node:child_process";
|
|
14
|
-
import { collectSkillsData, listSkills, readSkillContent, skillsHome } from "./skills.js";
|
|
14
|
+
import { collectSkillsData, listSkills, readSkillContent, skillsHome, saveSkillContent, createSkill, duplicateSkill, deleteSkill } from "./skills.js";
|
|
15
15
|
|
|
16
16
|
const ROUTE_BASE = "/api/dsh-gkb-ru-ui";
|
|
17
17
|
const RPC_NAMESPACE = "dsh-gkb-ru-ui";
|
|
@@ -127,6 +127,50 @@ export function apply(ctx) {
|
|
|
127
127
|
return { skill, usage };
|
|
128
128
|
});
|
|
129
129
|
|
|
130
|
+
registerOperation(ctx, "skill-save", async (payload) => {
|
|
131
|
+
const name = typeof payload?.name === "string" ? payload.name : "";
|
|
132
|
+
const content = typeof payload?.content === "string" ? payload.content : "";
|
|
133
|
+
if (name.length === 0) {
|
|
134
|
+
const error = new Error("не указано имя навыка");
|
|
135
|
+
error.code = "skills/bad-request";
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
const saved = saveSkillContent(skillsHome(), name, content);
|
|
139
|
+
const data = collectSkillsData(skillsHome(), typeof payload?.sessionId === "string" ? payload.sessionId : null);
|
|
140
|
+
return { ...saved, totals: data.totals };
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
registerOperation(ctx, "skill-create", async (payload) => {
|
|
144
|
+
const skill = createSkill(skillsHome(), payload);
|
|
145
|
+
const data = collectSkillsData(skillsHome(), typeof payload?.sessionId === "string" ? payload.sessionId : null);
|
|
146
|
+
return { skill, totals: data.totals };
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
registerOperation(ctx, "skill-duplicate", async (payload) => {
|
|
150
|
+
const source = typeof payload?.source === "string" ? payload.source : "";
|
|
151
|
+
const name = typeof payload?.name === "string" ? payload.name : "";
|
|
152
|
+
if (source.length === 0) {
|
|
153
|
+
const error = new Error("не указан исходный навык");
|
|
154
|
+
error.code = "skills/bad-request";
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
const skill = duplicateSkill(skillsHome(), source, name);
|
|
158
|
+
const data = collectSkillsData(skillsHome(), typeof payload?.sessionId === "string" ? payload.sessionId : null);
|
|
159
|
+
return { skill, totals: data.totals };
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
registerOperation(ctx, "skill-delete", async (payload) => {
|
|
163
|
+
const name = typeof payload?.name === "string" ? payload.name : "";
|
|
164
|
+
if (name.length === 0) {
|
|
165
|
+
const error = new Error("не указано имя навыка");
|
|
166
|
+
error.code = "skills/bad-request";
|
|
167
|
+
throw error;
|
|
168
|
+
}
|
|
169
|
+
const result = deleteSkill(skillsHome(), name);
|
|
170
|
+
const data = collectSkillsData(skillsHome(), typeof payload?.sessionId === "string" ? payload.sessionId : null);
|
|
171
|
+
return { ...result, totals: data.totals };
|
|
172
|
+
});
|
|
173
|
+
|
|
130
174
|
registerOperation(ctx, "open-skill", async (payload) => {
|
|
131
175
|
const name = typeof payload?.name === "string" ? payload.name : "";
|
|
132
176
|
const home = skillsHome();
|