dsh-skill-hub 0.2.2 → 0.2.5
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 +10 -14
- package/README.zh.md +9 -12
- package/lib/client.js +493 -177
- package/lib/client.js.map +1 -1
- package/lib/index.js +213 -36
- package/lib/types/client/SkillHubSettingsCard.d.ts +4 -6
- package/lib/types/client/icons.d.ts +25 -0
- package/lib/types/client/locales.d.ts +7 -3
- package/lib/types/client/panel/format.d.ts +10 -0
- package/lib/types/client/settings-card.d.ts +18 -0
- package/lib/types/client/settings-form.d.ts +9 -0
- package/lib/types/client/slash-dots.d.ts +57 -0
- package/lib/types/index.d.ts +4 -0
- package/lib/types/protocol.d.ts +42 -1
- package/lib/types/stats.d.ts +53 -4
- package/lib/types/store.d.ts +7 -2
- package/package.json +68 -26
- package/src/client/SkillHubSettingsCard.tsx +35 -10
- package/src/client/icons.tsx +61 -0
- package/src/client/index.tsx +13 -1
- package/src/client/locales.ts +14 -6
- package/src/client/panel/SkillDetailView.tsx +6 -2
- package/src/client/panel/SkillHubPanel.tsx +2 -2
- package/src/client/panel/SkillRow.tsx +8 -3
- package/src/client/panel/format.ts +11 -0
- package/src/client/panel/panel.module.css +1 -0
- package/src/client/settings-card.tsx +49 -1
- package/src/client/settings-form.ts +21 -0
- package/src/client/slash-dots.test.ts +145 -0
- package/src/client/slash-dots.tsx +190 -0
- package/src/index.ts +34 -3
- package/src/protocol.ts +47 -1
- package/src/routes.test.ts +3 -1
- package/src/routes.ts +2 -2
- package/src/stats.test.ts +273 -2
- package/src/stats.ts +166 -23
- package/src/store.test.ts +32 -0
- package/src/store.ts +58 -3
package/lib/client.js
CHANGED
|
@@ -5,7 +5,6 @@ window.__ModuleLoader__.load({
|
|
|
5
5
|
var exports = module.exports;
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
let react = require("react");
|
|
8
|
-
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
9
8
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
10
9
|
let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
|
|
11
10
|
//#region src/protocol.ts
|
|
@@ -318,7 +317,7 @@ window.__ModuleLoader__.load({
|
|
|
318
317
|
"panel.search": "搜索技能…",
|
|
319
318
|
"legend.model": "模型可调 — Agent 可自动调用",
|
|
320
319
|
"legend.user": "用户可调 — 仅显式点名时触发",
|
|
321
|
-
"legend.hint": "
|
|
320
|
+
"legend.hint": "每个技能最多一个圆点;无圆点 = 不可被调用。",
|
|
322
321
|
"panel.enabled": "已启用",
|
|
323
322
|
"panel.disabled": "已禁用",
|
|
324
323
|
"panel.diagnostics": "发现诊断",
|
|
@@ -487,15 +486,19 @@ window.__ModuleLoader__.load({
|
|
|
487
486
|
"settings.announceToAgent": "向 Agent 公告",
|
|
488
487
|
"settings.announceToAgentHint": "在系统提示中加入本插件说明,用户提到技能管理时 Agent 知道如何协作。",
|
|
489
488
|
"settings.dotModelColor": "模型可调圆点颜色",
|
|
490
|
-
"settings.dotModelColorHint": "
|
|
489
|
+
"settings.dotModelColorHint": "技能行/聊天 / 菜单中「模型可调」蓝色圆点的颜色(#rrggbb)。",
|
|
491
490
|
"settings.dotUserColor": "用户可调圆点颜色",
|
|
492
|
-
"settings.dotUserColorHint": "
|
|
491
|
+
"settings.dotUserColorHint": "技能行/聊天 / 菜单中「仅用户可调」绿色圆点的颜色(#rrggbb)。",
|
|
493
492
|
"settings.showUseCount": "显示调用次数",
|
|
494
493
|
"settings.showUseCountHint": "在技能名旁显示琥珀色调用次数。",
|
|
495
494
|
"settings.showUseTime": "显示最近调用时间",
|
|
496
495
|
"settings.showUseTimeHint": "在技能名行右侧显示相对时间(如「3 天前」)。",
|
|
497
496
|
"settings.showGroupSummary": "显示分组汇总",
|
|
498
497
|
"settings.showGroupSummaryHint": "在分组标题后汇总调用次数与最近调用时间。",
|
|
498
|
+
"settings.statsWindowDays": "统计窗口(天)",
|
|
499
|
+
"settings.statsWindowDaysHint": "只统计最近 N 天的使用次数,默认 14 天;0 = 全部历史。改动立即生效。",
|
|
500
|
+
"settings.statsScanMinutes": "自动统计间隔(分钟)",
|
|
501
|
+
"settings.statsScanMinutesHint": "后台扫描会话日志的间隔,最小 1 分钟;扫描耗时会自动拉长间隔。",
|
|
499
502
|
"settings.inherit": "继承",
|
|
500
503
|
"settings.on": "开",
|
|
501
504
|
"settings.off": "关",
|
|
@@ -526,7 +529,7 @@ window.__ModuleLoader__.load({
|
|
|
526
529
|
"panel.search": "Search skills…",
|
|
527
530
|
"legend.model": "Model-invocable — agents may call automatically",
|
|
528
531
|
"legend.user": "User-invocable — explicit invocation only",
|
|
529
|
-
"legend.hint": "
|
|
532
|
+
"legend.hint": "At most one dot per skill; no dot = not invocable.",
|
|
530
533
|
"panel.enabled": "Enabled",
|
|
531
534
|
"panel.disabled": "Disabled",
|
|
532
535
|
"panel.diagnostics": "Discovery diagnostics",
|
|
@@ -695,15 +698,19 @@ window.__ModuleLoader__.load({
|
|
|
695
698
|
"settings.announceToAgent": "Announce to agent",
|
|
696
699
|
"settings.announceToAgentHint": "Adds a system-prompt section so agents know how to collaborate when users mention skill management.",
|
|
697
700
|
"settings.dotModelColor": "Model-invocable dot color",
|
|
698
|
-
"settings.dotModelColorHint": "Color of the blue dot
|
|
701
|
+
"settings.dotModelColorHint": "Color of the blue \"model-callable\" dot in the skill panel and the chat / menu (#rrggbb).",
|
|
699
702
|
"settings.dotUserColor": "User-invocable dot color",
|
|
700
|
-
"settings.dotUserColorHint": "Color of the green dot
|
|
703
|
+
"settings.dotUserColorHint": "Color of the green \"user-only\" dot in the skill panel and the chat / menu (#rrggbb).",
|
|
701
704
|
"settings.showUseCount": "Show invocation count",
|
|
702
705
|
"settings.showUseCountHint": "Show the amber invocation count beside each skill name.",
|
|
703
706
|
"settings.showUseTime": "Show last-used time",
|
|
704
707
|
"settings.showUseTimeHint": "Show relative last-used time on the name row (e.g. \"3 d ago\").",
|
|
705
708
|
"settings.showGroupSummary": "Show group summaries",
|
|
706
709
|
"settings.showGroupSummaryHint": "Summarize invocation count and last-used time after group titles.",
|
|
710
|
+
"settings.statsWindowDays": "Stats window (days)",
|
|
711
|
+
"settings.statsWindowDaysHint": "Count only usage within the last N days (default 14); 0 = full history. Changes apply immediately.",
|
|
712
|
+
"settings.statsScanMinutes": "Auto stats interval (minutes)",
|
|
713
|
+
"settings.statsScanMinutesHint": "How often the background session-log scan runs (min 1); heavy scans automatically stretch it.",
|
|
707
714
|
"settings.inherit": "Inherit",
|
|
708
715
|
"settings.on": "On",
|
|
709
716
|
"settings.off": "Off",
|
|
@@ -769,6 +776,341 @@ window.__ModuleLoader__.load({
|
|
|
769
776
|
};
|
|
770
777
|
}
|
|
771
778
|
//#endregion
|
|
779
|
+
//#region src/client/grouping.ts
|
|
780
|
+
/**
|
|
781
|
+
* Derive a group switch view from its member names and the set of currently
|
|
782
|
+
* enabled skill names (catalog.skills). Members not in the enabled set count
|
|
783
|
+
* as disabled (catalog.disabled or absent read-only rows).
|
|
784
|
+
*/
|
|
785
|
+
function groupSwitchView(members, enabledNames) {
|
|
786
|
+
const enabled = [];
|
|
787
|
+
const disabled = [];
|
|
788
|
+
for (const name of members) if (enabledNames.has(name)) enabled.push(name);
|
|
789
|
+
else disabled.push(name);
|
|
790
|
+
return {
|
|
791
|
+
state: disabled.length === 0 ? "on" : enabled.length === 0 ? "off" : "mixed",
|
|
792
|
+
enabled,
|
|
793
|
+
disabled
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
/** Names of every group a skill belongs to (tags + collections). */
|
|
797
|
+
function groupNamesOf(name, tags, collections) {
|
|
798
|
+
const names = [];
|
|
799
|
+
for (const tag of tags) if (tag.skillNames.includes(name)) names.push(tag.name);
|
|
800
|
+
for (const collection of collections) if (collection.skillNames.includes(name)) names.push(collection.name);
|
|
801
|
+
return names;
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* Members of a group that are currently enabled AND also belong to at least
|
|
805
|
+
* one other group — the set a "close" action must ask about.
|
|
806
|
+
*/
|
|
807
|
+
function conflictsOnClose(members, enabledNames, otherGroups) {
|
|
808
|
+
return members.filter((name) => {
|
|
809
|
+
if (!enabledNames.has(name)) return false;
|
|
810
|
+
return otherGroups.some((group) => group.members.includes(name));
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
/** Origin-repo filter value: skills with no source record (private skills). */
|
|
814
|
+
const PRIVATE_SOURCE = "private";
|
|
815
|
+
/** 项目级技能来源(它们有 workspace 归属,不属于「个人」组)。 */
|
|
816
|
+
function isProjectSource(source) {
|
|
817
|
+
return source === "project-dsh" || source === "project-agents";
|
|
818
|
+
}
|
|
819
|
+
/**
|
|
820
|
+
* Apply the origin filter ('all' or a specific origin repo; skills without a
|
|
821
|
+
* source record count as PRIVATE_SOURCE). The origins map is the store's
|
|
822
|
+
* skillName → repo derivation, so filtering follows the tracked source
|
|
823
|
+
* records instead of the filesystem root a skill happens to live under.
|
|
824
|
+
* 项目级技能(有 workspace 归属)永远不算「个人」。
|
|
825
|
+
*/
|
|
826
|
+
function filterBySource(skills, source, origins) {
|
|
827
|
+
if (source === "all") return [...skills];
|
|
828
|
+
return skills.filter((skill) => {
|
|
829
|
+
if (isProjectSource(skill.source)) return false;
|
|
830
|
+
return (origins[skill.name] ?? "private") === source;
|
|
831
|
+
});
|
|
832
|
+
}
|
|
833
|
+
/**
|
|
834
|
+
* Sort a skill list in place-safe copy order: name ascending, added
|
|
835
|
+
* descending (newest first, unknown addedAt last), or uses descending
|
|
836
|
+
* (most-called first). Unknown values always trail.
|
|
837
|
+
*/
|
|
838
|
+
function sortSkills(skills, key, getUses) {
|
|
839
|
+
const list = [...skills];
|
|
840
|
+
if (key === "name") list.sort((a, b) => a.name.localeCompare(b.name));
|
|
841
|
+
else if (key === "added") list.sort((a, b) => (b.addedAt ?? -Infinity) - (a.addedAt ?? -Infinity));
|
|
842
|
+
else if (key === "uses") list.sort((a, b) => (getUses?.(b.name) ?? 0) - (getUses?.(a.name) ?? 0));
|
|
843
|
+
return list;
|
|
844
|
+
}
|
|
845
|
+
/** Format an epoch-ms timestamp as a short relative time bucket. */
|
|
846
|
+
function formatRelativeTime(ms, now = Date.now()) {
|
|
847
|
+
const diff = Math.max(0, now - ms);
|
|
848
|
+
const minutes = Math.floor(diff / 6e4);
|
|
849
|
+
if (minutes < 1) return { key: "time.justNow" };
|
|
850
|
+
if (minutes < 60) return {
|
|
851
|
+
key: "time.minutesAgo",
|
|
852
|
+
value: minutes
|
|
853
|
+
};
|
|
854
|
+
const hours = Math.floor(minutes / 60);
|
|
855
|
+
if (hours < 24) return {
|
|
856
|
+
key: "time.hoursAgo",
|
|
857
|
+
value: hours
|
|
858
|
+
};
|
|
859
|
+
const days = Math.floor(hours / 24);
|
|
860
|
+
if (days < 7) return {
|
|
861
|
+
key: "time.daysAgo",
|
|
862
|
+
value: days
|
|
863
|
+
};
|
|
864
|
+
return {
|
|
865
|
+
key: "time.weeksAgo",
|
|
866
|
+
value: Math.floor(days / 7)
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
//#endregion
|
|
870
|
+
//#region src/client/helpers.ts
|
|
871
|
+
/**
|
|
872
|
+
* Shared panel helpers: the active-dictionary pick (document-language
|
|
873
|
+
* based, family precedent) plus a small error-message extractor.
|
|
874
|
+
*/
|
|
875
|
+
/** Active dictionary, picked by the document language at call time. */
|
|
876
|
+
function dictionary() {
|
|
877
|
+
return (typeof document !== "undefined" ? document.documentElement.lang : "zh").toLowerCase().startsWith("en") ? { ...en } : { ...zh };
|
|
878
|
+
}
|
|
879
|
+
/** Translate a key with optional {name} template params (current language). */
|
|
880
|
+
function tt(key, values) {
|
|
881
|
+
return t(dictionary(), key, values);
|
|
882
|
+
}
|
|
883
|
+
/** Human-readable error text from an unknown thrown value. */
|
|
884
|
+
function errorMessage(error) {
|
|
885
|
+
if (error instanceof Error) return error.message;
|
|
886
|
+
return String(error);
|
|
887
|
+
}
|
|
888
|
+
//#endregion
|
|
889
|
+
//#region src/client/panel/format.ts
|
|
890
|
+
/** Model-invocable dot color default. Single source for the TS side; the
|
|
891
|
+
* panel's CSS mirrors it via --hub-model (panel.module.css). */
|
|
892
|
+
const DEFAULT_DOT_MODEL_COLOR = "#2f81f7";
|
|
893
|
+
/** User-invocable dot color default. Single source for the TS side; the
|
|
894
|
+
* panel's CSS mirrors it via --hub-user (panel.module.css). */
|
|
895
|
+
const DEFAULT_DOT_USER_COLOR = "#3fb950";
|
|
896
|
+
/** Dot inline style from the user-chosen color (undefined keeps the CSS default). */
|
|
897
|
+
function dotStyle(color) {
|
|
898
|
+
if (color === void 0) return void 0;
|
|
899
|
+
return {
|
|
900
|
+
background: color,
|
|
901
|
+
borderColor: color
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
/** Localized relative-time text for a Unix-ms timestamp. */
|
|
905
|
+
function relativeTimeText(ms) {
|
|
906
|
+
const rt = formatRelativeTime(ms);
|
|
907
|
+
return tt(rt.key, rt.value !== void 0 ? { value: rt.value } : void 0);
|
|
908
|
+
}
|
|
909
|
+
/** Localized absolute time for the detail meta (e.g. "2026/8/16 11:00:00"). */
|
|
910
|
+
function formatDateTime(ms) {
|
|
911
|
+
return new Date(ms).toLocaleString();
|
|
912
|
+
}
|
|
913
|
+
/** Short form of a commit SHA for display. */
|
|
914
|
+
function shortSha(sha) {
|
|
915
|
+
return sha.length > 7 ? sha.slice(0, 7) : sha;
|
|
916
|
+
}
|
|
917
|
+
//#endregion
|
|
918
|
+
//#region src/client/slash-dots.tsx
|
|
919
|
+
/** The core plugin's skill source identity on the '/' trigger. */
|
|
920
|
+
const SKILL_SOURCE = {
|
|
921
|
+
trigger: "/",
|
|
922
|
+
name: "skill"
|
|
923
|
+
};
|
|
924
|
+
/** How long one catalog-derived modelInvocable map stays hot before refresh. */
|
|
925
|
+
const MODEL_TTL_MS = 6e4;
|
|
926
|
+
/**
|
|
927
|
+
* Find the core `/skill` source through the runtime registry, or undefined.
|
|
928
|
+
* The lookup never throws: a missing/reshaped registry just means no dots. Exported
|
|
929
|
+
* for unit tests; the apply path only calls it indirectly through setupSkillSlashDots.
|
|
930
|
+
* @param service - the ctx.inputTriggers service face.
|
|
931
|
+
* @returns the registered skill source, or undefined.
|
|
932
|
+
*/
|
|
933
|
+
function findSkillSource(service) {
|
|
934
|
+
const live = service.live;
|
|
935
|
+
if (live?.sources === void 0) return void 0;
|
|
936
|
+
return live.sources.find((source) => source.trigger === SKILL_SOURCE.trigger && source.name === SKILL_SOURCE.name);
|
|
937
|
+
}
|
|
938
|
+
/** One catalog-derived name → modelInvocable snapshot with a load timestamp. */
|
|
939
|
+
let modelCache;
|
|
940
|
+
/**
|
|
941
|
+
* Clear the modelInvocable cache. Called on connection/reset so a fresh
|
|
942
|
+
* catalog wins after reconnect; exported for deterministic unit tests.
|
|
943
|
+
*/
|
|
944
|
+
function resetModelCache() {
|
|
945
|
+
modelCache = void 0;
|
|
946
|
+
}
|
|
947
|
+
/**
|
|
948
|
+
* Resolve name → modelInvocable from the hub's catalog, cached for
|
|
949
|
+
* MODEL_TTL_MS. A failed load caches the failure briefly so a downed route
|
|
950
|
+
* doesn't hammer the host on every keystroke; the returned map is empty then
|
|
951
|
+
* (callers fall back to the model dot for unknown names).
|
|
952
|
+
* @param api - the hub browser API.
|
|
953
|
+
* @returns name → whether the model may call the skill.
|
|
954
|
+
*/
|
|
955
|
+
async function modelInvocableMap(api) {
|
|
956
|
+
const now = Date.now();
|
|
957
|
+
const cached = modelCache;
|
|
958
|
+
if (cached !== void 0 && now - cached.at < MODEL_TTL_MS) return "failed" in cached ? /* @__PURE__ */ new Map() : cached.map;
|
|
959
|
+
try {
|
|
960
|
+
const catalog = await api.catalog();
|
|
961
|
+
const map = new Map(catalog.skills.map((skill) => [skill.name, skill.invocation.modelInvocable]));
|
|
962
|
+
modelCache = {
|
|
963
|
+
at: now,
|
|
964
|
+
map
|
|
965
|
+
};
|
|
966
|
+
return map;
|
|
967
|
+
} catch (error) {
|
|
968
|
+
console.error("[dsh-skill-hub] slash dot color lookup failed:", error);
|
|
969
|
+
modelCache = {
|
|
970
|
+
at: now,
|
|
971
|
+
failed: true
|
|
972
|
+
};
|
|
973
|
+
return /* @__PURE__ */ new Map();
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
/**
|
|
977
|
+
* One menu-row dot element. Inline span so it needs no CSS module; the
|
|
978
|
+
* candidate menu centers it inside its 16×16 leading icon slot.
|
|
979
|
+
* @param color - the dot's background color.
|
|
980
|
+
* @returns a React node (memory-only, never crosses the Host boundary).
|
|
981
|
+
*/
|
|
982
|
+
function dotIcon(color) {
|
|
983
|
+
return (0, react.createElement)("span", {
|
|
984
|
+
"aria-hidden": true,
|
|
985
|
+
style: {
|
|
986
|
+
display: "inline-block",
|
|
987
|
+
width: 6,
|
|
988
|
+
height: 6,
|
|
989
|
+
borderRadius: 3,
|
|
990
|
+
background: color,
|
|
991
|
+
flex: "none"
|
|
992
|
+
}
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
/**
|
|
996
|
+
* Wrap the core skill source so every menu row carries the invocation dot.
|
|
997
|
+
* Exported for unit tests; production wiring goes through setupSkillSlashDots.
|
|
998
|
+
* @param source - the registered `/skill` source.
|
|
999
|
+
* @param api - hub browser API for the modelInvocable lookup.
|
|
1000
|
+
* @param scope - hub settings scope for the dot colors.
|
|
1001
|
+
* @returns a disposer restoring the original candidates.
|
|
1002
|
+
*/
|
|
1003
|
+
function wrapSkillSource(source, api, scope) {
|
|
1004
|
+
const original = source.candidates;
|
|
1005
|
+
source.candidates = async (session, req) => {
|
|
1006
|
+
const items = await original(session, req);
|
|
1007
|
+
if (req.signal.aborted) return items;
|
|
1008
|
+
const modelByName = await modelInvocableMap(api);
|
|
1009
|
+
if (req.signal.aborted) return items;
|
|
1010
|
+
const snapshot = scope.getSnapshot();
|
|
1011
|
+
const modelColor = snapshot.value?.dotModelColor ?? "#2f81f7";
|
|
1012
|
+
const userColor = snapshot.value?.dotUserColor ?? "#3fb950";
|
|
1013
|
+
return items.map((item) => ({
|
|
1014
|
+
...item,
|
|
1015
|
+
icon: dotIcon(modelByName.get(item.name) ?? true ? modelColor : userColor)
|
|
1016
|
+
}));
|
|
1017
|
+
};
|
|
1018
|
+
return () => {
|
|
1019
|
+
source.candidates = original;
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
/**
|
|
1023
|
+
* Mount the slash-menu dots on the registered `/skill` source. Idempotent and
|
|
1024
|
+
* defensive: if the core source isn't registered yet (or the registry shape
|
|
1025
|
+
* changes), it retries briefly and then gives up silently — the chat keeps
|
|
1026
|
+
* working, it simply shows no dots. The returned disposer restores the
|
|
1027
|
+
* original candidates and clears the model cache.
|
|
1028
|
+
* @param ctx - the client root context (inputTriggers + events).
|
|
1029
|
+
* @param api - hub browser API.
|
|
1030
|
+
* @param scope - hub settings scope for dot colors.
|
|
1031
|
+
* @returns a cleanup function for `ctx.effect`.
|
|
1032
|
+
*/
|
|
1033
|
+
function setupSkillSlashDots(ctx, api, scope) {
|
|
1034
|
+
const inputTriggers = ctx.get("inputTriggers");
|
|
1035
|
+
if (inputTriggers === void 0) return () => {};
|
|
1036
|
+
let disposed = false;
|
|
1037
|
+
let restore;
|
|
1038
|
+
let attempts = 0;
|
|
1039
|
+
const attempt = () => {
|
|
1040
|
+
if (disposed) return;
|
|
1041
|
+
const source = findSkillSource(inputTriggers);
|
|
1042
|
+
if (source === void 0) {
|
|
1043
|
+
if (attempts < 10) {
|
|
1044
|
+
attempts += 1;
|
|
1045
|
+
setTimeout(attempt, 100);
|
|
1046
|
+
}
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
restore = wrapSkillSource(source, api, scope);
|
|
1050
|
+
};
|
|
1051
|
+
attempt();
|
|
1052
|
+
const clearCache = () => {
|
|
1053
|
+
resetModelCache();
|
|
1054
|
+
};
|
|
1055
|
+
const offReset = ctx.on("connection/reset", clearCache);
|
|
1056
|
+
return () => {
|
|
1057
|
+
disposed = true;
|
|
1058
|
+
offReset();
|
|
1059
|
+
restore?.();
|
|
1060
|
+
restore = void 0;
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
//#endregion
|
|
1064
|
+
//#region src/client/icons.tsx
|
|
1065
|
+
/** ic_ds_chevron_down_outline_14 */
|
|
1066
|
+
function IconChevronDownOutline14({ size = 14, className }) {
|
|
1067
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
1068
|
+
width: size,
|
|
1069
|
+
height: size,
|
|
1070
|
+
className,
|
|
1071
|
+
viewBox: "0 0 14 14",
|
|
1072
|
+
fill: "none",
|
|
1073
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1074
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
1075
|
+
d: "M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z",
|
|
1076
|
+
fill: "currentColor"
|
|
1077
|
+
})
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
/** ic_ds_trash_outline_16 */
|
|
1081
|
+
function IconTrashOutline16({ size = 16, className }) {
|
|
1082
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
1083
|
+
width: size,
|
|
1084
|
+
height: size,
|
|
1085
|
+
className,
|
|
1086
|
+
viewBox: "0 0 16 16",
|
|
1087
|
+
fill: "none",
|
|
1088
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1089
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
1090
|
+
d: "M14.4782 4.84067L14.2138 10.1152C14.1102 12.1872 14.067 13.0115 13.3866 13.9607C13.1044 14.3546 12.7498 14.6912 12.3424 14.9535C11.8239 15.2872 11.2415 15.4316 10.5585 15.4998C9.88727 15.5668 9.04946 15.5656 7.99998 15.5656C6.95051 15.5656 6.1127 15.5668 5.44142 15.4998C4.75851 15.4316 4.17602 15.2872 3.65753 14.9535C3.25012 14.6912 2.89559 14.3546 2.61332 13.9607C1.93296 13.0115 1.88979 12.1872 1.78619 10.1152L1.52179 4.84067L2.89006 4.77277L3.15343 10.0463C3.26221 12.2218 3.32452 12.6015 3.72646 13.1624C3.90825 13.4161 4.13686 13.6334 4.39927 13.8023C4.66204 13.9714 5.00263 14.0792 5.57825 14.1367C6.16562 14.1953 6.92298 14.1963 7.99998 14.1963C9.07699 14.1963 9.83434 14.1953 10.4217 14.1367C10.9973 14.0792 11.3379 13.9714 11.6007 13.8023C11.8631 13.6334 12.0917 13.4161 12.2735 13.1624C12.6755 12.6015 12.7378 12.2218 12.8465 10.0463L13.1099 4.77277L14.4782 4.84067ZM5.43011 6.22849H6.7994V11.3909H5.43011V6.22849ZM9.20056 6.22849H10.5699V11.3909H9.20056V6.22849ZM8.53597 0.434431C9.17976 0.434431 9.6522 0.426926 10.0966 0.571258C10.2357 0.616451 10.3717 0.672554 10.502 0.738948C10.9182 0.951107 11.2464 1.29099 11.7015 1.74612L12.4978 2.54136H15.3742V3.91169H0.625732V2.54136H3.50218L4.29845 1.74612C4.75358 1.29099 5.08174 0.951107 5.49801 0.738948C5.62831 0.672554 5.76425 0.616451 5.90334 0.571258C6.34776 0.426926 6.82021 0.434431 7.46399 0.434431H8.53597ZM7.46399 1.80476C6.73208 1.80476 6.51641 1.81187 6.32617 1.87369C6.25545 1.89667 6.18668 1.92533 6.12041 1.95907C5.96398 2.03878 5.82348 2.16253 5.44142 2.54136H10.5585C10.1765 2.16253 10.036 2.03878 9.87955 1.95907C9.81329 1.92533 9.74452 1.89667 9.6738 1.87369C9.48356 1.81187 9.26789 1.80476 8.53597 1.80476H7.46399Z",
|
|
1091
|
+
fill: "currentColor"
|
|
1092
|
+
})
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
/** ic_ds_skill_outline_16 */
|
|
1096
|
+
function IconSkillOutline16({ size = 16, className }) {
|
|
1097
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
1098
|
+
width: size,
|
|
1099
|
+
height: size,
|
|
1100
|
+
className,
|
|
1101
|
+
viewBox: "0 0 16 16",
|
|
1102
|
+
fill: "none",
|
|
1103
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1104
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
1105
|
+
d: "M12.5113 15.4067C12.4395 15.6249 12.1308 15.6249 12.059 15.4067L11.643 14.1416C11.454 13.567 11.0033 13.1164 10.4288 12.9274L9.16369 12.5113C8.94544 12.4395 8.94544 12.1308 9.16369 12.059L10.4288 11.643C11.0033 11.454 11.454 11.0033 11.643 10.4288L12.059 9.16369C12.1308 8.94544 12.4395 8.94544 12.5113 9.16369L12.9274 10.4288C13.1164 11.0033 13.567 11.454 14.1416 11.643L15.4067 12.059C15.6249 12.1308 15.6249 12.4395 15.4067 12.5113L14.1416 12.9274C13.567 13.1164 13.1164 13.567 12.9274 14.1416L12.5113 15.4067Z",
|
|
1106
|
+
fill: "currentColor"
|
|
1107
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
1108
|
+
d: "M9.02246 0.546878C9.9822 0.546878 10.7564 0.545403 11.374 0.612307C12.0042 0.680586 12.5515 0.826244 13.0273 1.17188C13.3052 1.37376 13.5501 1.61868 13.752 1.89649C14.0975 2.37225 14.2432 2.91984 14.3115 3.54981C14.3784 4.16727 14.377 4.94206 14.377 5.90137V8.51367C13.9611 8.29533 13.5071 8.13985 13.0273 8.06055V5.90137C13.0273 4.9121 13.0259 4.22322 12.9688 3.69532C12.9129 3.18044 12.8098 2.89782 12.6592 2.69043C12.5406 2.52724 12.3966 2.38326 12.2334 2.26465C12.026 2.11404 11.7437 2.0109 11.2285 1.95508C10.7005 1.89789 10.0122 1.89649 9.02246 1.89649H6.55371C5.56395 1.89649 4.87569 1.89787 4.34766 1.95508C3.83242 2.01092 3.55022 2.11398 3.34278 2.26465C3.17953 2.38329 3.03564 2.52719 2.91699 2.69043C2.76642 2.89782 2.66325 3.18042 2.60742 3.69532C2.55027 4.22322 2.54883 4.9121 2.54883 5.90137V10.0986C2.54883 11.0878 2.55031 11.7768 2.60742 12.3047C2.66326 12.8196 2.76642 13.1032 2.91699 13.3105C3.03558 13.4736 3.17966 13.6178 3.34278 13.7363C3.5502 13.8869 3.83265 13.9901 4.34766 14.0459C4.87568 14.1031 5.56398 14.1035 6.55371 14.1035H8.08399C8.27443 14.6025 8.55077 15.0585 8.89551 15.4541H6.55371C5.59402 15.4541 4.81976 15.4546 4.20215 15.3877C3.57204 15.3194 3.02468 15.1738 2.54883 14.8281C2.27111 14.6263 2.02606 14.3813 1.82422 14.1035C1.47883 13.6278 1.33293 13.08 1.26465 12.4502C1.19783 11.8327 1.19922 11.0579 1.19922 10.0986V5.90137C1.19922 4.94206 1.1978 4.16727 1.26465 3.54981C1.33295 2.91984 1.47867 2.37225 1.82422 1.89649C2.02613 1.61864 2.27098 1.37379 2.54883 1.17188C3.02472 0.826181 3.57197 0.6806 4.20215 0.612307C4.81976 0.545393 5.594 0.546877 6.55371 0.546878H9.02246ZM9.19629 9.14649H4.5459V7.84571H9.19629V9.14649ZM11.0303 6.10645H4.5459V4.80567H11.0303V6.10645Z",
|
|
1109
|
+
fill: "currentColor"
|
|
1110
|
+
})]
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
//#endregion
|
|
772
1114
|
//#region \0dsh-css:/Users/huanyi/Documents/personal/tools/dsh-skill-hub/src/client/settings-card.module.css.mjs
|
|
773
1115
|
const css$1 = ".TqD_hG_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.TqD_hG_card:hover{border-color:var(--dsw-alias-label-dimmed)}.TqD_hG_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.TqD_hG_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.TqD_hG_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.TqD_hG_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.TqD_hG_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.TqD_hG_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.TqD_hG_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.TqD_hG_chevron,.TqD_hG_chevronOpen{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.TqD_hG_chevronOpen{transform:rotate(180deg)}.TqD_hG_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.TqD_hG_readOnly,.TqD_hG_notExposed{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.TqD_hG_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.TqD_hG_failed{min-width:0;color:var(--dsw-alias-label-error);flex:1;margin:0;font-size:12px;line-height:1.5}.TqD_hG_discard,.TqD_hG_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.TqD_hG_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.TqD_hG_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.TqD_hG_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.TqD_hG_discard:disabled,.TqD_hG_save:disabled{opacity:.4;cursor:default}.TqD_hG_discard:focus-visible,.TqD_hG_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.TqD_hG_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.TqD_hG_field+.TqD_hG_field{border-top:1px solid var(--dsw-alias-border-l2)}.TqD_hG_head{align-items:center;gap:8px;display:flex}.TqD_hG_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.TqD_hG_badges{align-items:center;gap:8px;display:inline-flex}.TqD_hG_badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.TqD_hG_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.TqD_hG_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.TqD_hG_reset:disabled{cursor:default}.TqD_hG_input{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.TqD_hG_input:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.TqD_hG_input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.TqD_hG_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}.TqD_hG_colorRow{align-items:center;gap:8px;display:flex}.TqD_hG_colorInput{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);cursor:pointer;border-radius:8px;width:34px;height:34px;padding:0}.TqD_hG_colorInput:disabled{opacity:.5;cursor:default}.TqD_hG_colorInput::-webkit-color-swatch-wrapper{padding:2px}.TqD_hG_colorInput::-webkit-color-swatch{border:none;border-radius:4px}.TqD_hG_colorText{flex:1;min-width:0}.TqD_hG_switchRow{align-items:center;gap:12px;display:flex}.TqD_hG_switchText{flex:1;min-width:0}.TqD_hG_switch,.TqD_hG_switchOn{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);cursor:pointer;border-radius:999px;flex:none;width:38px;height:22px;padding:2px;transition:background .15s,border-color .15s}.TqD_hG_switchOn{border-color:var(--dsw-alias-state-business-primary,#2f81f7);background:var(--dsw-alias-state-business-primary,#2f81f7)}.TqD_hG_switch:disabled,.TqD_hG_switchOn:disabled{opacity:.4;cursor:default}.TqD_hG_switchThumb{background:#fff;border-radius:50%;width:16px;height:16px;transition:transform .15s;display:block;transform:translate(0);box-shadow:0 1px 2px #00000040}.TqD_hG_switchOn .TqD_hG_switchThumb{transform:translate(16px)}";
|
|
774
1116
|
const tagId$1 = "dsh-skill-hub/settings-card.module.css";
|
|
@@ -856,7 +1198,7 @@ window.__ModuleLoader__.load({
|
|
|
856
1198
|
className: settings_card_module_css_default.pending,
|
|
857
1199
|
children: props.t("settings.unsaved")
|
|
858
1200
|
}) : null,
|
|
859
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
1201
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronDownOutline14, { className: open ? settings_card_module_css_default.chevronOpen : settings_card_module_css_default.chevron })
|
|
860
1202
|
]
|
|
861
1203
|
});
|
|
862
1204
|
if (!state.exposed) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
@@ -1008,6 +1350,50 @@ window.__ModuleLoader__.load({
|
|
|
1008
1350
|
]
|
|
1009
1351
|
});
|
|
1010
1352
|
}
|
|
1353
|
+
function NumberField(props) {
|
|
1354
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1355
|
+
className: settings_card_module_css_default.field,
|
|
1356
|
+
children: [
|
|
1357
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1358
|
+
className: settings_card_module_css_default.head,
|
|
1359
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
|
|
1360
|
+
className: settings_card_module_css_default.label,
|
|
1361
|
+
htmlFor: props.id,
|
|
1362
|
+
children: props.label
|
|
1363
|
+
}), props.overridden ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1364
|
+
className: settings_card_module_css_default.badges,
|
|
1365
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1366
|
+
className: settings_card_module_css_default.badge,
|
|
1367
|
+
children: props.overriddenLabel
|
|
1368
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1369
|
+
type: "button",
|
|
1370
|
+
className: settings_card_module_css_default.reset,
|
|
1371
|
+
disabled: props.disabled,
|
|
1372
|
+
onClick: props.onReset,
|
|
1373
|
+
children: props.resetLabel
|
|
1374
|
+
})]
|
|
1375
|
+
}) : null]
|
|
1376
|
+
}),
|
|
1377
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1378
|
+
id: props.id,
|
|
1379
|
+
type: "text",
|
|
1380
|
+
inputMode: "numeric",
|
|
1381
|
+
className: settings_card_module_css_default.input,
|
|
1382
|
+
value: props.text,
|
|
1383
|
+
disabled: props.disabled,
|
|
1384
|
+
placeholder: props.inheritLabel,
|
|
1385
|
+
"aria-invalid": props.invalid === true,
|
|
1386
|
+
onChange: (event) => {
|
|
1387
|
+
props.onEdit(event.target.value);
|
|
1388
|
+
}
|
|
1389
|
+
}),
|
|
1390
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1391
|
+
className: settings_card_module_css_default.hint,
|
|
1392
|
+
children: props.hint
|
|
1393
|
+
})
|
|
1394
|
+
]
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1011
1397
|
//#endregion
|
|
1012
1398
|
//#region src/client/settings-form.ts
|
|
1013
1399
|
/**
|
|
@@ -1050,6 +1436,29 @@ window.__ModuleLoader__.load({
|
|
|
1050
1436
|
}
|
|
1051
1437
|
};
|
|
1052
1438
|
}
|
|
1439
|
+
/**
|
|
1440
|
+
* A numeric field with range clamping. Integer by default: a fractional draft
|
|
1441
|
+
* is truncated so "5.5 分钟" cannot sneak past an integer-only schema.
|
|
1442
|
+
*/
|
|
1443
|
+
function numberField(field, options = {}) {
|
|
1444
|
+
const min = options.min ?? Number.NEGATIVE_INFINITY;
|
|
1445
|
+
const max = options.max ?? Number.POSITIVE_INFINITY;
|
|
1446
|
+
const integer = options.integer ?? true;
|
|
1447
|
+
return {
|
|
1448
|
+
field,
|
|
1449
|
+
format: (value) => typeof value === "number" && Number.isFinite(value) ? String(value) : "",
|
|
1450
|
+
parse: (text) => {
|
|
1451
|
+
const trimmed = text.trim();
|
|
1452
|
+
if (!/^-?\d+(\.\d+)?$/.test(trimmed)) return void 0;
|
|
1453
|
+
const parsed = integer ? Math.trunc(Number(trimmed)) : Number(trimmed);
|
|
1454
|
+
if (!Number.isFinite(parsed) || parsed < min || parsed > max) return void 0;
|
|
1455
|
+
return {
|
|
1456
|
+
kind: "set",
|
|
1457
|
+
value: parsed
|
|
1458
|
+
};
|
|
1459
|
+
}
|
|
1460
|
+
};
|
|
1461
|
+
}
|
|
1053
1462
|
/** Stages one card's edits and writes them through the settings transport. */
|
|
1054
1463
|
var CardForm = class {
|
|
1055
1464
|
scope;
|
|
@@ -1216,12 +1625,6 @@ window.__ModuleLoader__.load({
|
|
|
1216
1625
|
};
|
|
1217
1626
|
//#endregion
|
|
1218
1627
|
//#region src/client/SkillHubSettingsCard.tsx
|
|
1219
|
-
/** Model-invocable dot color default. Single source for the TS side; the
|
|
1220
|
-
* panel's CSS mirrors it via --hub-model (panel.module.css). */
|
|
1221
|
-
const DEFAULT_DOT_MODEL_COLOR = "#2f81f7";
|
|
1222
|
-
/** User-invocable dot color default. Single source for the TS side; the
|
|
1223
|
-
* panel's CSS mirrors it via --hub-user (panel.module.css). */
|
|
1224
|
-
const DEFAULT_DOT_USER_COLOR = "#3fb950";
|
|
1225
1628
|
/** Bridges the hub's config scope onto the card's staged form. */
|
|
1226
1629
|
var SkillHubSettingsCardController = class {
|
|
1227
1630
|
form;
|
|
@@ -1235,7 +1638,15 @@ window.__ModuleLoader__.load({
|
|
|
1235
1638
|
colorField("dotUserColor"),
|
|
1236
1639
|
booleanField("showUseCount"),
|
|
1237
1640
|
booleanField("showUseTime"),
|
|
1238
|
-
booleanField("showGroupSummary")
|
|
1641
|
+
booleanField("showGroupSummary"),
|
|
1642
|
+
numberField("statsWindowDays", {
|
|
1643
|
+
min: 0,
|
|
1644
|
+
max: 3650
|
|
1645
|
+
}),
|
|
1646
|
+
numberField("statsScanMinutes", {
|
|
1647
|
+
min: 1,
|
|
1648
|
+
max: 1440
|
|
1649
|
+
})
|
|
1239
1650
|
]);
|
|
1240
1651
|
this.store = this.form.bind(() => this.projection());
|
|
1241
1652
|
}
|
|
@@ -1248,7 +1659,9 @@ window.__ModuleLoader__.load({
|
|
|
1248
1659
|
dotUserColor: this.form.field("dotUserColor"),
|
|
1249
1660
|
showUseCount: this.form.field("showUseCount"),
|
|
1250
1661
|
showUseTime: this.form.field("showUseTime"),
|
|
1251
|
-
showGroupSummary: this.form.field("showGroupSummary")
|
|
1662
|
+
showGroupSummary: this.form.field("showGroupSummary"),
|
|
1663
|
+
statsWindowDays: this.form.field("statsWindowDays"),
|
|
1664
|
+
statsScanMinutes: this.form.field("statsScanMinutes")
|
|
1252
1665
|
};
|
|
1253
1666
|
}
|
|
1254
1667
|
/**
|
|
@@ -1373,146 +1786,45 @@ window.__ModuleLoader__.load({
|
|
|
1373
1786
|
onReset: () => {
|
|
1374
1787
|
props.resetField("showGroupSummary");
|
|
1375
1788
|
}
|
|
1789
|
+
}),
|
|
1790
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(NumberField, {
|
|
1791
|
+
id: "skill-hub-stats-window-days",
|
|
1792
|
+
label: t("settings.statsWindowDays"),
|
|
1793
|
+
hint: t("settings.statsWindowDaysHint"),
|
|
1794
|
+
inheritLabel: t("settings.inherit"),
|
|
1795
|
+
...fieldProps,
|
|
1796
|
+
text: state.statsWindowDays.text,
|
|
1797
|
+
overridden: state.statsWindowDays.overridden,
|
|
1798
|
+
invalid: state.statsWindowDays.invalid,
|
|
1799
|
+
onEdit: (text) => {
|
|
1800
|
+
props.edit("statsWindowDays", text);
|
|
1801
|
+
},
|
|
1802
|
+
onReset: () => {
|
|
1803
|
+
props.resetField("statsWindowDays");
|
|
1804
|
+
}
|
|
1805
|
+
}),
|
|
1806
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(NumberField, {
|
|
1807
|
+
id: "skill-hub-stats-scan-minutes",
|
|
1808
|
+
label: t("settings.statsScanMinutes"),
|
|
1809
|
+
hint: t("settings.statsScanMinutesHint"),
|
|
1810
|
+
inheritLabel: t("settings.inherit"),
|
|
1811
|
+
...fieldProps,
|
|
1812
|
+
text: state.statsScanMinutes.text,
|
|
1813
|
+
overridden: state.statsScanMinutes.overridden,
|
|
1814
|
+
invalid: state.statsScanMinutes.invalid,
|
|
1815
|
+
onEdit: (text) => {
|
|
1816
|
+
props.edit("statsScanMinutes", text);
|
|
1817
|
+
},
|
|
1818
|
+
onReset: () => {
|
|
1819
|
+
props.resetField("statsScanMinutes");
|
|
1820
|
+
}
|
|
1376
1821
|
})
|
|
1377
1822
|
]
|
|
1378
1823
|
});
|
|
1379
1824
|
}
|
|
1380
1825
|
//#endregion
|
|
1381
|
-
//#region src/client/helpers.ts
|
|
1382
|
-
/**
|
|
1383
|
-
* Shared panel helpers: the active-dictionary pick (document-language
|
|
1384
|
-
* based, family precedent) plus a small error-message extractor.
|
|
1385
|
-
*/
|
|
1386
|
-
/** Active dictionary, picked by the document language at call time. */
|
|
1387
|
-
function dictionary() {
|
|
1388
|
-
return (typeof document !== "undefined" ? document.documentElement.lang : "zh").toLowerCase().startsWith("en") ? { ...en } : { ...zh };
|
|
1389
|
-
}
|
|
1390
|
-
/** Translate a key with optional {name} template params (current language). */
|
|
1391
|
-
function tt(key, values) {
|
|
1392
|
-
return t(dictionary(), key, values);
|
|
1393
|
-
}
|
|
1394
|
-
/** Human-readable error text from an unknown thrown value. */
|
|
1395
|
-
function errorMessage(error) {
|
|
1396
|
-
if (error instanceof Error) return error.message;
|
|
1397
|
-
return String(error);
|
|
1398
|
-
}
|
|
1399
|
-
//#endregion
|
|
1400
|
-
//#region src/client/grouping.ts
|
|
1401
|
-
/**
|
|
1402
|
-
* Derive a group switch view from its member names and the set of currently
|
|
1403
|
-
* enabled skill names (catalog.skills). Members not in the enabled set count
|
|
1404
|
-
* as disabled (catalog.disabled or absent read-only rows).
|
|
1405
|
-
*/
|
|
1406
|
-
function groupSwitchView(members, enabledNames) {
|
|
1407
|
-
const enabled = [];
|
|
1408
|
-
const disabled = [];
|
|
1409
|
-
for (const name of members) if (enabledNames.has(name)) enabled.push(name);
|
|
1410
|
-
else disabled.push(name);
|
|
1411
|
-
return {
|
|
1412
|
-
state: disabled.length === 0 ? "on" : enabled.length === 0 ? "off" : "mixed",
|
|
1413
|
-
enabled,
|
|
1414
|
-
disabled
|
|
1415
|
-
};
|
|
1416
|
-
}
|
|
1417
|
-
/** Names of every group a skill belongs to (tags + collections). */
|
|
1418
|
-
function groupNamesOf(name, tags, collections) {
|
|
1419
|
-
const names = [];
|
|
1420
|
-
for (const tag of tags) if (tag.skillNames.includes(name)) names.push(tag.name);
|
|
1421
|
-
for (const collection of collections) if (collection.skillNames.includes(name)) names.push(collection.name);
|
|
1422
|
-
return names;
|
|
1423
|
-
}
|
|
1424
|
-
/**
|
|
1425
|
-
* Members of a group that are currently enabled AND also belong to at least
|
|
1426
|
-
* one other group — the set a "close" action must ask about.
|
|
1427
|
-
*/
|
|
1428
|
-
function conflictsOnClose(members, enabledNames, otherGroups) {
|
|
1429
|
-
return members.filter((name) => {
|
|
1430
|
-
if (!enabledNames.has(name)) return false;
|
|
1431
|
-
return otherGroups.some((group) => group.members.includes(name));
|
|
1432
|
-
});
|
|
1433
|
-
}
|
|
1434
|
-
/** Origin-repo filter value: skills with no source record (private skills). */
|
|
1435
|
-
const PRIVATE_SOURCE = "private";
|
|
1436
|
-
/** 项目级技能来源(它们有 workspace 归属,不属于「个人」组)。 */
|
|
1437
|
-
function isProjectSource(source) {
|
|
1438
|
-
return source === "project-dsh" || source === "project-agents";
|
|
1439
|
-
}
|
|
1440
|
-
/**
|
|
1441
|
-
* Apply the origin filter ('all' or a specific origin repo; skills without a
|
|
1442
|
-
* source record count as PRIVATE_SOURCE). The origins map is the store's
|
|
1443
|
-
* skillName → repo derivation, so filtering follows the tracked source
|
|
1444
|
-
* records instead of the filesystem root a skill happens to live under.
|
|
1445
|
-
* 项目级技能(有 workspace 归属)永远不算「个人」。
|
|
1446
|
-
*/
|
|
1447
|
-
function filterBySource(skills, source, origins) {
|
|
1448
|
-
if (source === "all") return [...skills];
|
|
1449
|
-
return skills.filter((skill) => {
|
|
1450
|
-
if (isProjectSource(skill.source)) return false;
|
|
1451
|
-
return (origins[skill.name] ?? "private") === source;
|
|
1452
|
-
});
|
|
1453
|
-
}
|
|
1454
|
-
/**
|
|
1455
|
-
* Sort a skill list in place-safe copy order: name ascending, added
|
|
1456
|
-
* descending (newest first, unknown addedAt last), or uses descending
|
|
1457
|
-
* (most-called first). Unknown values always trail.
|
|
1458
|
-
*/
|
|
1459
|
-
function sortSkills(skills, key, getUses) {
|
|
1460
|
-
const list = [...skills];
|
|
1461
|
-
if (key === "name") list.sort((a, b) => a.name.localeCompare(b.name));
|
|
1462
|
-
else if (key === "added") list.sort((a, b) => (b.addedAt ?? -Infinity) - (a.addedAt ?? -Infinity));
|
|
1463
|
-
else if (key === "uses") list.sort((a, b) => (getUses?.(b.name) ?? 0) - (getUses?.(a.name) ?? 0));
|
|
1464
|
-
return list;
|
|
1465
|
-
}
|
|
1466
|
-
/** Format an epoch-ms timestamp as a short relative time bucket. */
|
|
1467
|
-
function formatRelativeTime(ms, now = Date.now()) {
|
|
1468
|
-
const diff = Math.max(0, now - ms);
|
|
1469
|
-
const minutes = Math.floor(diff / 6e4);
|
|
1470
|
-
if (minutes < 1) return { key: "time.justNow" };
|
|
1471
|
-
if (minutes < 60) return {
|
|
1472
|
-
key: "time.minutesAgo",
|
|
1473
|
-
value: minutes
|
|
1474
|
-
};
|
|
1475
|
-
const hours = Math.floor(minutes / 60);
|
|
1476
|
-
if (hours < 24) return {
|
|
1477
|
-
key: "time.hoursAgo",
|
|
1478
|
-
value: hours
|
|
1479
|
-
};
|
|
1480
|
-
const days = Math.floor(hours / 24);
|
|
1481
|
-
if (days < 7) return {
|
|
1482
|
-
key: "time.daysAgo",
|
|
1483
|
-
value: days
|
|
1484
|
-
};
|
|
1485
|
-
return {
|
|
1486
|
-
key: "time.weeksAgo",
|
|
1487
|
-
value: Math.floor(days / 7)
|
|
1488
|
-
};
|
|
1489
|
-
}
|
|
1490
|
-
//#endregion
|
|
1491
|
-
//#region src/client/panel/format.ts
|
|
1492
|
-
/** Dot inline style from the user-chosen color (undefined keeps the CSS default). */
|
|
1493
|
-
function dotStyle(color) {
|
|
1494
|
-
if (color === void 0) return void 0;
|
|
1495
|
-
return {
|
|
1496
|
-
background: color,
|
|
1497
|
-
borderColor: color
|
|
1498
|
-
};
|
|
1499
|
-
}
|
|
1500
|
-
/** Localized relative-time text for a Unix-ms timestamp. */
|
|
1501
|
-
function relativeTimeText(ms) {
|
|
1502
|
-
const rt = formatRelativeTime(ms);
|
|
1503
|
-
return tt(rt.key, rt.value !== void 0 ? { value: rt.value } : void 0);
|
|
1504
|
-
}
|
|
1505
|
-
/** Localized absolute time for the detail meta (e.g. "2026/8/16 11:00:00"). */
|
|
1506
|
-
function formatDateTime(ms) {
|
|
1507
|
-
return new Date(ms).toLocaleString();
|
|
1508
|
-
}
|
|
1509
|
-
/** Short form of a commit SHA for display. */
|
|
1510
|
-
function shortSha(sha) {
|
|
1511
|
-
return sha.length > 7 ? sha.slice(0, 7) : sha;
|
|
1512
|
-
}
|
|
1513
|
-
//#endregion
|
|
1514
1826
|
//#region \0dsh-css:/Users/huanyi/Documents/personal/tools/dsh-skill-hub/src/client/panel/panel.module.css.mjs
|
|
1515
|
-
const css = "._6VtqdG_panel{-webkit-font-smoothing:antialiased;color-scheme:light dark;--hub-model:#2f81f7;--hub-user:#3fb950;flex-direction:column;gap:14px;max-width:720px;margin:0 auto;padding:28px 20px 44px;font-family:-apple-system,BlinkMacSystemFont,SF Pro Text,Helvetica Neue,sans-serif;display:flex}._6VtqdG_header{flex-wrap:wrap;align-items:baseline;gap:10px;display:flex}._6VtqdG_title{letter-spacing:-.02em;margin:0;font-size:24px;font-weight:700}._6VtqdG_hint{opacity:.5;font-size:12px}._6VtqdG_actions{gap:8px;margin-left:auto;display:flex}._6VtqdG_segmented{background:#80808024;border-radius:9px;gap:2px;padding:2px;display:flex}._6VtqdG_segBtn{color:inherit;opacity:.62;cursor:pointer;background:0 0;border:none;border-radius:7px;padding:5px 13px;font-size:12px;transition:background .15s,opacity .15s}._6VtqdG_segBtn:hover{opacity:.9}._6VtqdG_segBtnActive{opacity:1;background:#80808047;font-weight:600}._6VtqdG_search{width:100%;color:inherit;background:#8080801f;border:none;border-radius:10px;outline:none;padding:10px 14px;font-size:13px;transition:background .15s}._6VtqdG_search::placeholder{opacity:.45}._6VtqdG_search:focus{background:#8080802e}._6VtqdG_section{background:#8080800f;border-radius:12px;flex-direction:column;margin-top:4px;display:flex;overflow:hidden;box-shadow:0 1px 2px #00000008,0 4px 12px #0000000a}._6VtqdG_sectionTitle{margin:14px 16px 2px;font-size:13px;font-weight:700}._6VtqdG_groupHead{align-items:center;gap:8px;padding:12px 14px 4px;display:flex}._6VtqdG_groupTitle{opacity:.92;flex:1;min-width:0;font-size:13px;font-weight:600}._6VtqdG_groupOps{flex:none;gap:6px;display:inline-flex}._6VtqdG_opBtn{color:inherit;cursor:pointer;background:#80808021;border:none;border-radius:999px;padding:3px 11px;font-size:11px;transition:background .15s}._6VtqdG_opBtn:hover{background:#80808038}._6VtqdG_opBtn:disabled{opacity:.4;cursor:default}._6VtqdG_row{cursor:pointer;background:0 0;align-items:center;gap:12px;padding:10px 14px;transition:background .12s;display:flex}._6VtqdG_row:hover{background:#80808014}._6VtqdG_row:focus-visible{outline:2px solid var(--hub-model);outline-offset:-2px}._6VtqdG_rowStatic{cursor:default}._6VtqdG_rowStatic:hover{background:0 0}._6VtqdG_row+._6VtqdG_row{border-top:.5px solid #80808021}._6VtqdG_rowMain{flex:1;min-width:0}._6VtqdG_rowName{align-items:center;gap:5px;font-size:13.5px;display:flex}._6VtqdG_rowNameText{font-weight:600}._6VtqdG_rowDesc{opacity:.52;white-space:nowrap;text-overflow:ellipsis;margin-top:1px;font-size:12.5px;overflow:hidden}._6VtqdG_rowMeta{opacity:.45;white-space:nowrap;flex:none;font-size:11.5px}._6VtqdG_badges{flex:none;align-items:center;gap:6px;display:flex}._6VtqdG_badge{opacity:.8;white-space:nowrap;border:.5px solid #8080804d;border-radius:999px;padding:1px 7px;font-size:10.5px}._6VtqdG_badgeModel{color:var(--hub-model);border-color:currentColor}._6VtqdG_badgeUser{color:var(--hub-user);border-color:currentColor}._6VtqdG_badgeUses{color:#d29922;border-color:currentColor}._6VtqdG_badgeReadonly{opacity:.5}._6VtqdG_switch{cursor:pointer;background:#8080804d;border:none;border-radius:999px;flex:none;width:36px;height:21px;padding:2px;transition:background .18s}._6VtqdG_switch:disabled{opacity:.5;cursor:default}._6VtqdG_switchOn{background:#34c759}._6VtqdG_switchThumb{background:#fff;border-radius:50%;width:17px;height:17px;transition:transform .18s;display:block;transform:translate(0);box-shadow:0 1px 2px #00000040}._6VtqdG_switchOn ._6VtqdG_switchThumb{transform:translate(15px)}._6VtqdG_empty{opacity:.5;text-align:center;padding:20px 0;font-size:13px}._6VtqdG_diagRow{background:#d299220f;border-left:3px solid #d29922;padding:9px 14px;font-size:12px}._6VtqdG_diagPath{opacity:.75;word-break:break-all;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}._6VtqdG_diagReason{opacity:.65;margin-top:1px}._6VtqdG_updateLink{color:inherit;font-weight:600;text-decoration:underline}._6VtqdG_errorBanner{color:#d1242f;white-space:pre-wrap;word-break:break-all;background:#d1242f12;border-radius:10px;align-items:flex-start;gap:10px;padding:10px 14px;font-size:12.5px;display:flex}._6VtqdG_successBanner{color:#3fb950;white-space:pre-wrap;word-break:break-all;background:#3fb95014;border-radius:10px;align-items:flex-start;gap:10px;padding:10px 14px;font-size:12.5px;display:flex}._6VtqdG_detailHead{flex-wrap:wrap;align-items:center;gap:10px;display:flex}._6VtqdG_back{color:inherit;cursor:pointer;background:#8080801f;border:none;border-radius:8px;padding:6px 12px;font-size:12.5px}._6VtqdG_back:hover{background:#80808033}._6VtqdG_detailName{letter-spacing:-.01em;font-size:19px;font-weight:700}._6VtqdG_detailMeta{opacity:.6;flex-direction:column;gap:4px;font-size:12px;display:flex}._6VtqdG_detailMetaLine{word-break:break-all}._6VtqdG_detailContent{white-space:pre-wrap;word-break:break-word;background:#8080800d;border-radius:12px;max-height:62vh;padding:14px 16px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.6;overflow:auto}._6VtqdG_form{background:#8080800d;border-radius:12px;flex-direction:column;gap:10px;padding:14px 16px;display:flex;box-shadow:0 1px 2px #00000008,0 4px 12px #0000000a}._6VtqdG_formRow{flex-direction:column;gap:5px;display:flex}._6VtqdG_formLabel{opacity:.6;font-size:12px}._6VtqdG_input,._6VtqdG_select{color:inherit;background:#8080801f;border:none;border-radius:9px;outline:none;padding:9px 12px;font-size:13px}._6VtqdG_input:focus,._6VtqdG_select:focus{background:#8080802e}._6VtqdG_buttons{align-items:center;gap:8px;display:flex}._6VtqdG_button{cursor:pointer;color:inherit;background:#8080801f;border:none;border-radius:8px;padding:6px 13px;font-size:12.5px;transition:background .15s}._6VtqdG_button:hover{background:#80808033}._6VtqdG_primary{background:var(--hub-model);color:#fff}._6VtqdG_primary:hover{opacity:.92;background:#2f81f7}._6VtqdG_primary:disabled{opacity:.45;cursor:default}._6VtqdG_danger{color:#fff;background:#d1242f}._6VtqdG_danger:hover{opacity:.92;background:#d1242f}._6VtqdG_danger:disabled{opacity:.45;cursor:default}._6VtqdG_formError{color:#d1242f}._6VtqdG_formSuccess{color:#3fb950}._6VtqdG_muted{opacity:.55}._6VtqdG_dot{vertical-align:middle;border-radius:50%;width:7px;height:7px;margin-left:5px;display:inline-block}._6VtqdG_dotModel{background:var(--hub-model)}._6VtqdG_dotUser{background:var(--hub-user)}._6VtqdG_legend{opacity:.55;flex-wrap:wrap;align-items:center;gap:12px;padding:2px 2px 0;font-size:11.5px;display:flex}._6VtqdG_legendItem{align-items:center;gap:4px;display:inline-flex}._6VtqdG_legendItem ._6VtqdG_dot{margin-left:0}._6VtqdG_legendHint{opacity:.8}._6VtqdG_badgeSource,._6VtqdG_badgeCount{opacity:.72;background:#8080801a;border:none}._6VtqdG_badgeDisabled{opacity:.58;background:#8080801a;border:none}._6VtqdG_disclosure{min-width:0;color:inherit;cursor:pointer;text-align:left;background:0 0;border:none;flex:1;align-items:center;gap:7px;padding:2px 0;display:flex}._6VtqdG_disclosure:hover ._6VtqdG_groupTitle{opacity:1}._6VtqdG_chevron{opacity:.55;border-bottom:1.5px solid;border-right:1.5px solid;flex:none;width:8px;height:8px;margin-right:2px;transition:transform .15s;transform:rotate(45deg)}._6VtqdG_chevronCollapsed{transform:rotate(-45deg)}._6VtqdG_headerCount{opacity:.5;white-space:nowrap;margin-left:2px;font-size:12px}._6VtqdG_subbar{flex-wrap:wrap;align-items:center;gap:8px;display:flex}._6VtqdG_legendToggle{width:22px;height:22px;color:inherit;cursor:pointer;opacity:.55;background:#80808024;border:none;border-radius:50%;flex:none;font-size:12px;line-height:1;transition:opacity .15s,background .15s}._6VtqdG_legendToggle:hover{opacity:1}._6VtqdG_legendToggleActive{opacity:1;background:#80808047}._6VtqdG_filterBar{flex-wrap:wrap;align-items:center;gap:8px;display:flex}._6VtqdG_filterBar ._6VtqdG_formLabel{margin:0}._6VtqdG_useCount{color:#d29922;vertical-align:1px;background:#d299221f;border-radius:999px;margin-left:6px;padding:0 6px;font-size:11px;font-weight:700;line-height:1.6;display:inline-block}._6VtqdG_useTime{opacity:.45;white-space:nowrap;margin-left:auto;font-size:11px}._6VtqdG_switchMixed{background:#2f81f773}._6VtqdG_switchMixed ._6VtqdG_switchThumb{transform:translate(7.5px);box-shadow:0 1px 2px #00000040}._6VtqdG_groupTitleInner{align-items:baseline;display:inline-flex}._6VtqdG_groupOps{flex-wrap:wrap;flex:none;align-items:center;gap:6px;display:inline-flex}._6VtqdG_sourceLink{color:inherit;opacity:.9;border-bottom:1px dotted;font-weight:600;text-decoration:none}._6VtqdG_sourceLink:hover{opacity:1}._6VtqdG_statusBadges{flex:none;align-items:center;gap:4px;display:inline-flex}._6VtqdG_statusBadge{opacity:.8;white-space:nowrap;background:#8080801a;border:.5px solid #8080804d;border-radius:999px;padding:1px 7px;font-size:10.5px}._6VtqdG_statusOk{color:#3fb950;background:0 0;border-color:currentColor}._6VtqdG_statusUpdated{color:#d29922;background:#d2992214;border-color:currentColor}._6VtqdG_statusError{color:#d1242f;background:#d1242f14;border-color:currentColor}._6VtqdG_statusWrap{cursor:pointer;background:0 0;border:none;align-items:center;gap:4px;padding:0;font-family:inherit;display:inline-flex}._6VtqdG_statusWrap:hover ._6VtqdG_statusBadge{opacity:1}._6VtqdG_statusButton{cursor:pointer;font-family:inherit}._6VtqdG_statusButton:hover{opacity:1}._6VtqdG_opDanger{color:#d1242f;background:#d1242f1a}._6VtqdG_opDanger:hover{background:#d1242f2e}._6VtqdG_iconBtn{border-radius:999px;justify-content:center;align-items:center;width:24px;height:24px;padding:0;display:inline-flex}._6VtqdG_sourceCard{background:#8080800d;border-radius:12px;flex-direction:column;gap:4px;padding:12px 14px;font-size:12px;display:flex}._6VtqdG_sourceCardTitle{flex-wrap:wrap;align-items:center;gap:8px;display:flex}._6VtqdG_dialogOverlay{z-index:50;-webkit-backdrop-filter:blur(2px);background:#00000059;justify-content:center;align-items:center;padding:24px;display:flex;position:fixed;inset:0}._6VtqdG_dialog{background:var(--dsw-surface,#f5f5f7);min-width:min(320px,100%);max-width:380px;color:var(--dsw-text,#1d1d1f);border-radius:14px;padding:18px 18px 14px;box-shadow:0 12px 40px #00000047,0 2px 8px #00000029}@media (prefers-color-scheme:dark){._6VtqdG_dialog{color:#f5f5f7;background:#2c2c2e}}._6VtqdG_dialogTitle{letter-spacing:-.01em;margin:0 0 8px;font-size:15px;font-weight:700}._6VtqdG_dialogText{opacity:.65;margin:0 0 10px;font-size:12.5px;line-height:1.5}._6VtqdG_dialogList{flex-direction:column;gap:5px;max-height:200px;margin:0 0 14px;padding:0;list-style:none;display:flex;overflow:auto}._6VtqdG_dialogList li{opacity:.85;word-break:break-all;font-size:12.5px}._6VtqdG_dialogActions{justify-content:flex-end;gap:8px;display:flex}._6VtqdG_filterBar ._6VtqdG_search{flex:1;min-width:160px}._6VtqdG_filterBar ._6VtqdG_formLabel{flex:none}._6VtqdG_filterBar ._6VtqdG_select{flex:none;max-width:150px}._6VtqdG_filterBar ._6VtqdG_segmented{flex:none}._6VtqdG_titleIcon{vertical-align:-2px;opacity:.8;margin-right:2px;display:inline-block}._6VtqdG_grow{flex:1}._6VtqdG_hintLine{opacity:.55;margin:0;font-size:11.5px}._6VtqdG_hintPadded{margin:6px 12px 2px}._6VtqdG_hintInline{margin:4px 2px}._6VtqdG_rowMuted{cursor:default;opacity:.55}._6VtqdG_actionsPadded{padding:8px 12px}._6VtqdG_actionsTop{margin-top:8px}._6VtqdG_actionsBottom{margin-bottom:8px}._6VtqdG_groupTime{margin-left:6px}._6VtqdG_sectionHeadRow{align-items:center;gap:8px;display:flex}._6VtqdG_sectionTitleFill{flex:1}._6VtqdG_dialogSelect{width:100%;margin-bottom:12px}._6VtqdG_dialogRow{align-items:center;gap:8px;display:flex}._6VtqdG_workspaceBox{align-items:center;gap:6px;display:inline-flex}._6VtqdG_workspaceInput{width:200px;padding:6px 10px;font-size:12px}._6VtqdG_projectNest{border-left:1px solid #80808024;flex-direction:column;margin:2px 0 0 14px;display:flex}";
|
|
1827
|
+
const css = "._6VtqdG_panel{-webkit-font-smoothing:antialiased;color-scheme:light dark;--hub-model:#2f81f7;--hub-user:#3fb950;flex-direction:column;gap:14px;max-width:720px;margin:0 auto;padding:28px 20px 44px;font-family:-apple-system,BlinkMacSystemFont,SF Pro Text,Helvetica Neue,sans-serif;display:flex}._6VtqdG_header{flex-wrap:wrap;align-items:baseline;gap:10px;display:flex}._6VtqdG_title{letter-spacing:-.02em;margin:0;font-size:24px;font-weight:700}._6VtqdG_hint{opacity:.5;font-size:12px}._6VtqdG_actions{gap:8px;margin-left:auto;display:flex}._6VtqdG_segmented{background:#80808024;border-radius:9px;gap:2px;padding:2px;display:flex}._6VtqdG_segBtn{color:inherit;opacity:.62;cursor:pointer;background:0 0;border:none;border-radius:7px;padding:5px 13px;font-size:12px;transition:background .15s,opacity .15s}._6VtqdG_segBtn:hover{opacity:.9}._6VtqdG_segBtnActive{opacity:1;background:#80808047;font-weight:600}._6VtqdG_search{width:100%;color:inherit;background:#8080801f;border:none;border-radius:10px;outline:none;padding:10px 14px;font-size:13px;transition:background .15s}._6VtqdG_search::placeholder{opacity:.45}._6VtqdG_search:focus{background:#8080802e}._6VtqdG_section{background:#8080800f;border-radius:12px;flex-direction:column;margin-top:4px;display:flex;overflow:hidden;box-shadow:0 1px 2px #00000008,0 4px 12px #0000000a}._6VtqdG_sectionTitle{margin:14px 16px 2px;font-size:13px;font-weight:700}._6VtqdG_groupHead{align-items:center;gap:8px;padding:12px 14px 4px;display:flex}._6VtqdG_groupTitle{opacity:.92;flex:1;min-width:0;font-size:13px;font-weight:600}._6VtqdG_groupOps{flex:none;gap:6px;display:inline-flex}._6VtqdG_opBtn{color:inherit;cursor:pointer;background:#80808021;border:none;border-radius:999px;padding:3px 11px;font-size:11px;transition:background .15s}._6VtqdG_opBtn:hover{background:#80808038}._6VtqdG_opBtn:disabled{opacity:.4;cursor:default}._6VtqdG_row{cursor:pointer;background:0 0;align-items:center;gap:12px;padding:10px 14px;transition:background .12s;display:flex}._6VtqdG_row:hover{background:#80808014}._6VtqdG_row:focus-visible{outline:2px solid var(--hub-model);outline-offset:-2px}._6VtqdG_rowStatic{cursor:default}._6VtqdG_rowStatic:hover{background:0 0}._6VtqdG_row+._6VtqdG_row{border-top:.5px solid #80808021}._6VtqdG_rowMain{flex:1;min-width:0}._6VtqdG_rowName{align-items:center;gap:5px;font-size:13.5px;display:flex}._6VtqdG_rowNameText{font-weight:600}._6VtqdG_rowDesc{opacity:.52;white-space:nowrap;text-overflow:ellipsis;margin-top:1px;font-size:12.5px;overflow:hidden}._6VtqdG_rowMeta{opacity:.45;white-space:nowrap;flex:none;font-size:11.5px}._6VtqdG_badges{flex:none;align-items:center;gap:6px;display:flex}._6VtqdG_badge{opacity:.8;white-space:nowrap;border:.5px solid #8080804d;border-radius:999px;padding:1px 7px;font-size:10.5px}._6VtqdG_badgeModel{color:var(--hub-model);border-color:currentColor}._6VtqdG_badgeUser{color:var(--hub-user);border-color:currentColor}._6VtqdG_badgeUses{color:#d29922;border-color:currentColor}._6VtqdG_badgeReadonly{opacity:.5}._6VtqdG_switch{cursor:pointer;background:#8080804d;border:none;border-radius:999px;flex:none;width:36px;height:21px;padding:2px;transition:background .18s}._6VtqdG_switch:disabled{opacity:.5;cursor:default}._6VtqdG_switchOn{background:#34c759}._6VtqdG_switchThumb{background:#fff;border-radius:50%;width:17px;height:17px;transition:transform .18s;display:block;transform:translate(0);box-shadow:0 1px 2px #00000040}._6VtqdG_switchOn ._6VtqdG_switchThumb{transform:translate(15px)}._6VtqdG_empty{opacity:.5;text-align:center;padding:20px 0;font-size:13px}._6VtqdG_diagRow{background:#d299220f;border-left:3px solid #d29922;padding:9px 14px;font-size:12px}._6VtqdG_diagPath{opacity:.75;word-break:break-all;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}._6VtqdG_diagReason{opacity:.65;margin-top:1px}._6VtqdG_updateLink{color:inherit;font-weight:600;text-decoration:underline}._6VtqdG_errorBanner{color:#d1242f;white-space:pre-wrap;word-break:break-all;background:#d1242f12;border-radius:10px;align-items:flex-start;gap:10px;padding:10px 14px;font-size:12.5px;display:flex}._6VtqdG_successBanner{color:#3fb950;white-space:pre-wrap;word-break:break-all;background:#3fb95014;border-radius:10px;align-items:flex-start;gap:10px;padding:10px 14px;font-size:12.5px;display:flex}._6VtqdG_detailHead{flex-wrap:wrap;align-items:center;gap:10px;display:flex}._6VtqdG_back{color:inherit;cursor:pointer;background:#8080801f;border:none;border-radius:8px;padding:6px 12px;font-size:12.5px}._6VtqdG_back:hover{background:#80808033}._6VtqdG_detailName{letter-spacing:-.01em;font-size:19px;font-weight:700}._6VtqdG_detailMeta{opacity:.6;flex-direction:column;gap:4px;font-size:12px;display:flex}._6VtqdG_detailMetaLine{word-break:break-all}._6VtqdG_detailContent{white-space:pre-wrap;word-break:break-word;background:#8080800d;border-radius:12px;max-height:62vh;padding:14px 16px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.6;overflow:auto}._6VtqdG_form{background:#8080800d;border-radius:12px;flex-direction:column;gap:10px;padding:14px 16px;display:flex;box-shadow:0 1px 2px #00000008,0 4px 12px #0000000a}._6VtqdG_formRow{flex-direction:column;gap:5px;display:flex}._6VtqdG_formLabel{opacity:.6;font-size:12px}._6VtqdG_input,._6VtqdG_select{color:inherit;background:#8080801f;border:none;border-radius:9px;outline:none;padding:9px 12px;font-size:13px}._6VtqdG_input:focus,._6VtqdG_select:focus{background:#8080802e}._6VtqdG_buttons{align-items:center;gap:8px;display:flex}._6VtqdG_button{cursor:pointer;color:inherit;background:#8080801f;border:none;border-radius:8px;padding:6px 13px;font-size:12.5px;transition:background .15s}._6VtqdG_button:hover{background:#80808033}._6VtqdG_primary{background:var(--hub-model);color:#fff}._6VtqdG_primary:hover{opacity:.92;background:#2f81f7}._6VtqdG_primary:disabled{opacity:.45;cursor:default}._6VtqdG_danger{color:#fff;background:#d1242f}._6VtqdG_danger:hover{opacity:.92;background:#d1242f}._6VtqdG_danger:disabled{opacity:.45;cursor:default}._6VtqdG_formError{color:#d1242f}._6VtqdG_formSuccess{color:#3fb950}._6VtqdG_muted{opacity:.55}._6VtqdG_dot{vertical-align:middle;border-radius:50%;width:7px;height:7px;margin-left:5px;display:inline-block}._6VtqdG_dotModel{background:var(--hub-model)}._6VtqdG_dotUser{background:var(--hub-user)}._6VtqdG_legend{opacity:.55;flex-wrap:wrap;align-items:center;gap:12px;padding:2px 2px 0;font-size:11.5px;display:flex}._6VtqdG_legendItem{align-items:center;gap:4px;display:inline-flex}._6VtqdG_legendItem ._6VtqdG_dot{margin-left:0}._6VtqdG_legendHint{opacity:.8}._6VtqdG_badgeSource,._6VtqdG_badgeCount{opacity:.72;background:#8080801a;border:none}._6VtqdG_badgeDisabled{opacity:.58;background:#8080801a;border:none}._6VtqdG_disclosure{min-width:0;color:inherit;cursor:pointer;text-align:left;background:0 0;border:none;flex:1;align-items:center;gap:7px;padding:2px 0;display:flex}._6VtqdG_disclosure:hover ._6VtqdG_groupTitle{opacity:1}._6VtqdG_chevron{opacity:.55;border-bottom:1.5px solid;border-right:1.5px solid;flex:none;width:8px;height:8px;margin-right:2px;transition:transform .15s;transform:rotate(45deg)}._6VtqdG_chevronCollapsed{transform:rotate(-45deg)}._6VtqdG_headerCount{opacity:.5;white-space:nowrap;margin-left:2px;font-size:12px}._6VtqdG_pluginVersion{opacity:.45;white-space:nowrap;font-variant-numeric:tabular-nums;margin-left:6px;font-size:12px;font-weight:500}._6VtqdG_subbar{flex-wrap:wrap;align-items:center;gap:8px;display:flex}._6VtqdG_legendToggle{width:22px;height:22px;color:inherit;cursor:pointer;opacity:.55;background:#80808024;border:none;border-radius:50%;flex:none;font-size:12px;line-height:1;transition:opacity .15s,background .15s}._6VtqdG_legendToggle:hover{opacity:1}._6VtqdG_legendToggleActive{opacity:1;background:#80808047}._6VtqdG_filterBar{flex-wrap:wrap;align-items:center;gap:8px;display:flex}._6VtqdG_filterBar ._6VtqdG_formLabel{margin:0}._6VtqdG_useCount{color:#d29922;vertical-align:1px;background:#d299221f;border-radius:999px;margin-left:6px;padding:0 6px;font-size:11px;font-weight:700;line-height:1.6;display:inline-block}._6VtqdG_useTime{opacity:.45;white-space:nowrap;margin-left:auto;font-size:11px}._6VtqdG_switchMixed{background:#2f81f773}._6VtqdG_switchMixed ._6VtqdG_switchThumb{transform:translate(7.5px);box-shadow:0 1px 2px #00000040}._6VtqdG_groupTitleInner{align-items:baseline;display:inline-flex}._6VtqdG_groupOps{flex-wrap:wrap;flex:none;align-items:center;gap:6px;display:inline-flex}._6VtqdG_sourceLink{color:inherit;opacity:.9;border-bottom:1px dotted;font-weight:600;text-decoration:none}._6VtqdG_sourceLink:hover{opacity:1}._6VtqdG_statusBadges{flex:none;align-items:center;gap:4px;display:inline-flex}._6VtqdG_statusBadge{opacity:.8;white-space:nowrap;background:#8080801a;border:.5px solid #8080804d;border-radius:999px;padding:1px 7px;font-size:10.5px}._6VtqdG_statusOk{color:#3fb950;background:0 0;border-color:currentColor}._6VtqdG_statusUpdated{color:#d29922;background:#d2992214;border-color:currentColor}._6VtqdG_statusError{color:#d1242f;background:#d1242f14;border-color:currentColor}._6VtqdG_statusWrap{cursor:pointer;background:0 0;border:none;align-items:center;gap:4px;padding:0;font-family:inherit;display:inline-flex}._6VtqdG_statusWrap:hover ._6VtqdG_statusBadge{opacity:1}._6VtqdG_statusButton{cursor:pointer;font-family:inherit}._6VtqdG_statusButton:hover{opacity:1}._6VtqdG_opDanger{color:#d1242f;background:#d1242f1a}._6VtqdG_opDanger:hover{background:#d1242f2e}._6VtqdG_iconBtn{border-radius:999px;justify-content:center;align-items:center;width:24px;height:24px;padding:0;display:inline-flex}._6VtqdG_sourceCard{background:#8080800d;border-radius:12px;flex-direction:column;gap:4px;padding:12px 14px;font-size:12px;display:flex}._6VtqdG_sourceCardTitle{flex-wrap:wrap;align-items:center;gap:8px;display:flex}._6VtqdG_dialogOverlay{z-index:50;-webkit-backdrop-filter:blur(2px);background:#00000059;justify-content:center;align-items:center;padding:24px;display:flex;position:fixed;inset:0}._6VtqdG_dialog{background:var(--dsw-surface,#f5f5f7);min-width:min(320px,100%);max-width:380px;color:var(--dsw-text,#1d1d1f);border-radius:14px;padding:18px 18px 14px;box-shadow:0 12px 40px #00000047,0 2px 8px #00000029}@media (prefers-color-scheme:dark){._6VtqdG_dialog{color:#f5f5f7;background:#2c2c2e}}._6VtqdG_dialogTitle{letter-spacing:-.01em;margin:0 0 8px;font-size:15px;font-weight:700}._6VtqdG_dialogText{opacity:.65;margin:0 0 10px;font-size:12.5px;line-height:1.5}._6VtqdG_dialogList{flex-direction:column;gap:5px;max-height:200px;margin:0 0 14px;padding:0;list-style:none;display:flex;overflow:auto}._6VtqdG_dialogList li{opacity:.85;word-break:break-all;font-size:12.5px}._6VtqdG_dialogActions{justify-content:flex-end;gap:8px;display:flex}._6VtqdG_filterBar ._6VtqdG_search{flex:1;min-width:160px}._6VtqdG_filterBar ._6VtqdG_formLabel{flex:none}._6VtqdG_filterBar ._6VtqdG_select{flex:none;max-width:150px}._6VtqdG_filterBar ._6VtqdG_segmented{flex:none}._6VtqdG_titleIcon{vertical-align:-2px;opacity:.8;margin-right:2px;display:inline-block}._6VtqdG_grow{flex:1}._6VtqdG_hintLine{opacity:.55;margin:0;font-size:11.5px}._6VtqdG_hintPadded{margin:6px 12px 2px}._6VtqdG_hintInline{margin:4px 2px}._6VtqdG_rowMuted{cursor:default;opacity:.55}._6VtqdG_actionsPadded{padding:8px 12px}._6VtqdG_actionsTop{margin-top:8px}._6VtqdG_actionsBottom{margin-bottom:8px}._6VtqdG_groupTime{margin-left:6px}._6VtqdG_sectionHeadRow{align-items:center;gap:8px;display:flex}._6VtqdG_sectionTitleFill{flex:1}._6VtqdG_dialogSelect{width:100%;margin-bottom:12px}._6VtqdG_dialogRow{align-items:center;gap:8px;display:flex}._6VtqdG_workspaceBox{align-items:center;gap:6px;display:inline-flex}._6VtqdG_workspaceInput{width:200px;padding:6px 10px;font-size:12px}._6VtqdG_projectNest{border-left:1px solid #80808024;flex-direction:column;margin:2px 0 0 14px;display:flex}";
|
|
1516
1828
|
const tagId = "dsh-skill-hub/panel.module.css";
|
|
1517
1829
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
1518
1830
|
const tag = document.createElement("style");
|
|
@@ -1592,6 +1904,7 @@ window.__ModuleLoader__.load({
|
|
|
1592
1904
|
"opBtn": "_6VtqdG_opBtn",
|
|
1593
1905
|
"opDanger": "_6VtqdG_opDanger",
|
|
1594
1906
|
"panel": "_6VtqdG_panel",
|
|
1907
|
+
"pluginVersion": "_6VtqdG_pluginVersion",
|
|
1595
1908
|
"primary": "_6VtqdG_primary",
|
|
1596
1909
|
"projectNest": "_6VtqdG_projectNest",
|
|
1597
1910
|
"row": "_6VtqdG_row",
|
|
@@ -1708,19 +2021,15 @@ window.__ModuleLoader__.load({
|
|
|
1708
2021
|
children: tt("detail.back")
|
|
1709
2022
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1710
2023
|
className: panel_module_css_default.detailName,
|
|
1711
|
-
children: [
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
style: dotStyle(hubConfig?.dotUserColor),
|
|
1721
|
-
title: tt("legend.user")
|
|
1722
|
-
}) : null
|
|
1723
|
-
]
|
|
2024
|
+
children: [detail.name, detail.invocation.modelInvocable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2025
|
+
className: panel_module_css_default.dot + " " + panel_module_css_default.dotModel,
|
|
2026
|
+
style: dotStyle(hubConfig?.dotModelColor),
|
|
2027
|
+
title: tt("legend.model")
|
|
2028
|
+
}) : detail.invocation.userInvocable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2029
|
+
className: panel_module_css_default.dot + " " + panel_module_css_default.dotUser,
|
|
2030
|
+
style: dotStyle(hubConfig?.dotUserColor),
|
|
2031
|
+
title: tt("legend.user")
|
|
2032
|
+
}) : null]
|
|
1724
2033
|
})]
|
|
1725
2034
|
}),
|
|
1726
2035
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -1987,6 +2296,16 @@ window.__ModuleLoader__.load({
|
|
|
1987
2296
|
const stat = hub.uses.get(skill.name);
|
|
1988
2297
|
const count = stat?.count ?? 0;
|
|
1989
2298
|
const lastUsed = stat?.lastUsed;
|
|
2299
|
+
/** 单一状态圆点:模型可调 → 蓝;否则用户可调 → 绿。与聊天 / 菜单同规则。 */
|
|
2300
|
+
const dot = skill.invocation.modelInvocable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2301
|
+
className: panel_module_css_default.dot + " " + panel_module_css_default.dotModel,
|
|
2302
|
+
style: dotStyle(hub.hubConfig?.dotModelColor),
|
|
2303
|
+
title: tt("legend.model")
|
|
2304
|
+
}) : skill.invocation.userInvocable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2305
|
+
className: panel_module_css_default.dot + " " + panel_module_css_default.dotUser,
|
|
2306
|
+
style: dotStyle(hub.hubConfig?.dotUserColor),
|
|
2307
|
+
title: tt("legend.user")
|
|
2308
|
+
}) : null;
|
|
1990
2309
|
/** 键盘打开详情:Enter 或空格。仅当焦点在行本身(而非行内按钮)时生效。 */
|
|
1991
2310
|
const onKeyDown = (event) => {
|
|
1992
2311
|
if (event.target !== event.currentTarget) return;
|
|
@@ -2017,16 +2336,7 @@ window.__ModuleLoader__.load({
|
|
|
2017
2336
|
className: panel_module_css_default.useCount,
|
|
2018
2337
|
children: count
|
|
2019
2338
|
}) : null,
|
|
2020
|
-
|
|
2021
|
-
className: panel_module_css_default.dot + " " + panel_module_css_default.dotModel,
|
|
2022
|
-
style: dotStyle(hub.hubConfig?.dotModelColor),
|
|
2023
|
-
title: tt("legend.model")
|
|
2024
|
-
}) : null,
|
|
2025
|
-
skill.invocation.userInvocable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2026
|
-
className: panel_module_css_default.dot + " " + panel_module_css_default.dotUser,
|
|
2027
|
-
style: dotStyle(hub.hubConfig?.dotUserColor),
|
|
2028
|
-
title: tt("legend.user")
|
|
2029
|
-
}) : null,
|
|
2339
|
+
dot,
|
|
2030
2340
|
lastUsed !== void 0 && hub.hubConfig?.showUseTime !== false ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2031
2341
|
className: panel_module_css_default.useTime,
|
|
2032
2342
|
children: relativeTimeText(lastUsed)
|
|
@@ -2046,7 +2356,7 @@ window.__ModuleLoader__.load({
|
|
|
2046
2356
|
event.stopPropagation();
|
|
2047
2357
|
hub.requestDeleteSkill(skill.name);
|
|
2048
2358
|
},
|
|
2049
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
2359
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconTrashOutline16, { size: 14 })
|
|
2050
2360
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2051
2361
|
type: "button",
|
|
2052
2362
|
role: "switch",
|
|
@@ -4114,12 +4424,16 @@ window.__ModuleLoader__.load({
|
|
|
4114
4424
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h2", {
|
|
4115
4425
|
className: panel_module_css_default.title,
|
|
4116
4426
|
children: [
|
|
4117
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
4427
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconSkillOutline16, {
|
|
4118
4428
|
size: 16,
|
|
4119
4429
|
className: panel_module_css_default.titleIcon
|
|
4120
4430
|
}),
|
|
4121
4431
|
" ",
|
|
4122
|
-
tt("panel.title")
|
|
4432
|
+
tt("panel.title"),
|
|
4433
|
+
catalog !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4434
|
+
className: panel_module_css_default.pluginVersion,
|
|
4435
|
+
children: ["v", catalog.pluginVersion]
|
|
4436
|
+
}) : null
|
|
4123
4437
|
]
|
|
4124
4438
|
}),
|
|
4125
4439
|
catalog !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
@@ -4612,7 +4926,8 @@ window.__ModuleLoader__.load({
|
|
|
4612
4926
|
"locale",
|
|
4613
4927
|
"connection",
|
|
4614
4928
|
"remote",
|
|
4615
|
-
"settingsScope"
|
|
4929
|
+
"settingsScope",
|
|
4930
|
+
"inputTriggers"
|
|
4616
4931
|
];
|
|
4617
4932
|
/**
|
|
4618
4933
|
* Mount the settings card and the skill hub section.
|
|
@@ -4626,6 +4941,7 @@ window.__ModuleLoader__.load({
|
|
|
4626
4941
|
const t = ctx.locale.bind(NS);
|
|
4627
4942
|
const api = new SkillHubApi();
|
|
4628
4943
|
const settingsCard = new SkillHubSettingsCardController(ctx.settingsScope.bind({ namespace: NS }));
|
|
4944
|
+
ctx.effect(() => setupSkillSlashDots(ctx, api, ctx.settingsScope.bind({ namespace: NS })), "dsh-skill-hub: slash dots");
|
|
4629
4945
|
ctx.effect(() => ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
|
|
4630
4946
|
name: "settings.plugin.item",
|
|
4631
4947
|
key: NS,
|