dsh-skill-hub 0.2.1 → 0.2.4

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/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": "发现诊断",
@@ -432,6 +431,7 @@ window.__ModuleLoader__.load({
432
431
  "row.disable": "禁用",
433
432
  "row.enable": "启用",
434
433
  "row.delete": "移入回收站",
434
+ "row.open": "查看 {name} 详情(回车)",
435
435
  "delete.confirmTitle": "移入回收站?",
436
436
  "delete.confirmText": "确定把「{name}」移入回收站吗?可随时恢复。",
437
437
  "delete.confirm": "移入回收站",
@@ -486,9 +486,9 @@ window.__ModuleLoader__.load({
486
486
  "settings.announceToAgent": "向 Agent 公告",
487
487
  "settings.announceToAgentHint": "在系统提示中加入本插件说明,用户提到技能管理时 Agent 知道如何协作。",
488
488
  "settings.dotModelColor": "模型可调圆点颜色",
489
- "settings.dotModelColorHint": "技能名旁蓝色圆点的颜色(#rrggbb)。",
489
+ "settings.dotModelColorHint": "技能行/聊天 / 菜单中「模型可调」蓝色圆点的颜色(#rrggbb)。",
490
490
  "settings.dotUserColor": "用户可调圆点颜色",
491
- "settings.dotUserColorHint": "技能名旁绿色圆点的颜色(#rrggbb)。",
491
+ "settings.dotUserColorHint": "技能行/聊天 / 菜单中「仅用户可调」绿色圆点的颜色(#rrggbb)。",
492
492
  "settings.showUseCount": "显示调用次数",
493
493
  "settings.showUseCountHint": "在技能名旁显示琥珀色调用次数。",
494
494
  "settings.showUseTime": "显示最近调用时间",
@@ -525,7 +525,7 @@ window.__ModuleLoader__.load({
525
525
  "panel.search": "Search skills…",
526
526
  "legend.model": "Model-invocable — agents may call automatically",
527
527
  "legend.user": "User-invocable — explicit invocation only",
528
- "legend.hint": "Dots sit beside the skill name; no dot means the skill is not invocable.",
528
+ "legend.hint": "At most one dot per skill; no dot = not invocable.",
529
529
  "panel.enabled": "Enabled",
530
530
  "panel.disabled": "Disabled",
531
531
  "panel.diagnostics": "Discovery diagnostics",
@@ -639,6 +639,7 @@ window.__ModuleLoader__.load({
639
639
  "row.disable": "Disable",
640
640
  "row.enable": "Enable",
641
641
  "row.delete": "Move to trash",
642
+ "row.open": "View {name} details (Enter)",
642
643
  "delete.confirmTitle": "Move to trash?",
643
644
  "delete.confirmText": "Move \"{name}\" to trash? It can be restored anytime.",
644
645
  "delete.confirm": "Move to trash",
@@ -693,9 +694,9 @@ window.__ModuleLoader__.load({
693
694
  "settings.announceToAgent": "Announce to agent",
694
695
  "settings.announceToAgentHint": "Adds a system-prompt section so agents know how to collaborate when users mention skill management.",
695
696
  "settings.dotModelColor": "Model-invocable dot color",
696
- "settings.dotModelColorHint": "Color of the blue dot beside skill names (#rrggbb).",
697
+ "settings.dotModelColorHint": "Color of the blue \"model-callable\" dot in the skill panel and the chat / menu (#rrggbb).",
697
698
  "settings.dotUserColor": "User-invocable dot color",
698
- "settings.dotUserColorHint": "Color of the green dot beside skill names (#rrggbb).",
699
+ "settings.dotUserColorHint": "Color of the green \"user-only\" dot in the skill panel and the chat / menu (#rrggbb).",
699
700
  "settings.showUseCount": "Show invocation count",
700
701
  "settings.showUseCountHint": "Show the amber invocation count beside each skill name.",
701
702
  "settings.showUseTime": "Show last-used time",
@@ -767,6 +768,341 @@ window.__ModuleLoader__.load({
767
768
  };
768
769
  }
769
770
  //#endregion
771
+ //#region src/client/grouping.ts
772
+ /**
773
+ * Derive a group switch view from its member names and the set of currently
774
+ * enabled skill names (catalog.skills). Members not in the enabled set count
775
+ * as disabled (catalog.disabled or absent read-only rows).
776
+ */
777
+ function groupSwitchView(members, enabledNames) {
778
+ const enabled = [];
779
+ const disabled = [];
780
+ for (const name of members) if (enabledNames.has(name)) enabled.push(name);
781
+ else disabled.push(name);
782
+ return {
783
+ state: disabled.length === 0 ? "on" : enabled.length === 0 ? "off" : "mixed",
784
+ enabled,
785
+ disabled
786
+ };
787
+ }
788
+ /** Names of every group a skill belongs to (tags + collections). */
789
+ function groupNamesOf(name, tags, collections) {
790
+ const names = [];
791
+ for (const tag of tags) if (tag.skillNames.includes(name)) names.push(tag.name);
792
+ for (const collection of collections) if (collection.skillNames.includes(name)) names.push(collection.name);
793
+ return names;
794
+ }
795
+ /**
796
+ * Members of a group that are currently enabled AND also belong to at least
797
+ * one other group — the set a "close" action must ask about.
798
+ */
799
+ function conflictsOnClose(members, enabledNames, otherGroups) {
800
+ return members.filter((name) => {
801
+ if (!enabledNames.has(name)) return false;
802
+ return otherGroups.some((group) => group.members.includes(name));
803
+ });
804
+ }
805
+ /** Origin-repo filter value: skills with no source record (private skills). */
806
+ const PRIVATE_SOURCE = "private";
807
+ /** 项目级技能来源(它们有 workspace 归属,不属于「个人」组)。 */
808
+ function isProjectSource(source) {
809
+ return source === "project-dsh" || source === "project-agents";
810
+ }
811
+ /**
812
+ * Apply the origin filter ('all' or a specific origin repo; skills without a
813
+ * source record count as PRIVATE_SOURCE). The origins map is the store's
814
+ * skillName → repo derivation, so filtering follows the tracked source
815
+ * records instead of the filesystem root a skill happens to live under.
816
+ * 项目级技能(有 workspace 归属)永远不算「个人」。
817
+ */
818
+ function filterBySource(skills, source, origins) {
819
+ if (source === "all") return [...skills];
820
+ return skills.filter((skill) => {
821
+ if (isProjectSource(skill.source)) return false;
822
+ return (origins[skill.name] ?? "private") === source;
823
+ });
824
+ }
825
+ /**
826
+ * Sort a skill list in place-safe copy order: name ascending, added
827
+ * descending (newest first, unknown addedAt last), or uses descending
828
+ * (most-called first). Unknown values always trail.
829
+ */
830
+ function sortSkills(skills, key, getUses) {
831
+ const list = [...skills];
832
+ if (key === "name") list.sort((a, b) => a.name.localeCompare(b.name));
833
+ else if (key === "added") list.sort((a, b) => (b.addedAt ?? -Infinity) - (a.addedAt ?? -Infinity));
834
+ else if (key === "uses") list.sort((a, b) => (getUses?.(b.name) ?? 0) - (getUses?.(a.name) ?? 0));
835
+ return list;
836
+ }
837
+ /** Format an epoch-ms timestamp as a short relative time bucket. */
838
+ function formatRelativeTime(ms, now = Date.now()) {
839
+ const diff = Math.max(0, now - ms);
840
+ const minutes = Math.floor(diff / 6e4);
841
+ if (minutes < 1) return { key: "time.justNow" };
842
+ if (minutes < 60) return {
843
+ key: "time.minutesAgo",
844
+ value: minutes
845
+ };
846
+ const hours = Math.floor(minutes / 60);
847
+ if (hours < 24) return {
848
+ key: "time.hoursAgo",
849
+ value: hours
850
+ };
851
+ const days = Math.floor(hours / 24);
852
+ if (days < 7) return {
853
+ key: "time.daysAgo",
854
+ value: days
855
+ };
856
+ return {
857
+ key: "time.weeksAgo",
858
+ value: Math.floor(days / 7)
859
+ };
860
+ }
861
+ //#endregion
862
+ //#region src/client/helpers.ts
863
+ /**
864
+ * Shared panel helpers: the active-dictionary pick (document-language
865
+ * based, family precedent) plus a small error-message extractor.
866
+ */
867
+ /** Active dictionary, picked by the document language at call time. */
868
+ function dictionary() {
869
+ return (typeof document !== "undefined" ? document.documentElement.lang : "zh").toLowerCase().startsWith("en") ? { ...en } : { ...zh };
870
+ }
871
+ /** Translate a key with optional {name} template params (current language). */
872
+ function tt(key, values) {
873
+ return t(dictionary(), key, values);
874
+ }
875
+ /** Human-readable error text from an unknown thrown value. */
876
+ function errorMessage(error) {
877
+ if (error instanceof Error) return error.message;
878
+ return String(error);
879
+ }
880
+ //#endregion
881
+ //#region src/client/panel/format.ts
882
+ /** Model-invocable dot color default. Single source for the TS side; the
883
+ * panel's CSS mirrors it via --hub-model (panel.module.css). */
884
+ const DEFAULT_DOT_MODEL_COLOR = "#2f81f7";
885
+ /** User-invocable dot color default. Single source for the TS side; the
886
+ * panel's CSS mirrors it via --hub-user (panel.module.css). */
887
+ const DEFAULT_DOT_USER_COLOR = "#3fb950";
888
+ /** Dot inline style from the user-chosen color (undefined keeps the CSS default). */
889
+ function dotStyle(color) {
890
+ if (color === void 0) return void 0;
891
+ return {
892
+ background: color,
893
+ borderColor: color
894
+ };
895
+ }
896
+ /** Localized relative-time text for a Unix-ms timestamp. */
897
+ function relativeTimeText(ms) {
898
+ const rt = formatRelativeTime(ms);
899
+ return tt(rt.key, rt.value !== void 0 ? { value: rt.value } : void 0);
900
+ }
901
+ /** Localized absolute time for the detail meta (e.g. "2026/8/16 11:00:00"). */
902
+ function formatDateTime(ms) {
903
+ return new Date(ms).toLocaleString();
904
+ }
905
+ /** Short form of a commit SHA for display. */
906
+ function shortSha(sha) {
907
+ return sha.length > 7 ? sha.slice(0, 7) : sha;
908
+ }
909
+ //#endregion
910
+ //#region src/client/slash-dots.tsx
911
+ /** The core plugin's skill source identity on the '/' trigger. */
912
+ const SKILL_SOURCE = {
913
+ trigger: "/",
914
+ name: "skill"
915
+ };
916
+ /** How long one catalog-derived modelInvocable map stays hot before refresh. */
917
+ const MODEL_TTL_MS = 6e4;
918
+ /**
919
+ * Find the core `/skill` source through the runtime registry, or undefined.
920
+ * The lookup never throws: a missing/reshaped registry just means no dots. Exported
921
+ * for unit tests; the apply path only calls it indirectly through setupSkillSlashDots.
922
+ * @param service - the ctx.inputTriggers service face.
923
+ * @returns the registered skill source, or undefined.
924
+ */
925
+ function findSkillSource(service) {
926
+ const live = service.live;
927
+ if (live?.sources === void 0) return void 0;
928
+ return live.sources.find((source) => source.trigger === SKILL_SOURCE.trigger && source.name === SKILL_SOURCE.name);
929
+ }
930
+ /** One catalog-derived name → modelInvocable snapshot with a load timestamp. */
931
+ let modelCache;
932
+ /**
933
+ * Clear the modelInvocable cache. Called on connection/reset so a fresh
934
+ * catalog wins after reconnect; exported for deterministic unit tests.
935
+ */
936
+ function resetModelCache() {
937
+ modelCache = void 0;
938
+ }
939
+ /**
940
+ * Resolve name → modelInvocable from the hub's catalog, cached for
941
+ * MODEL_TTL_MS. A failed load caches the failure briefly so a downed route
942
+ * doesn't hammer the host on every keystroke; the returned map is empty then
943
+ * (callers fall back to the model dot for unknown names).
944
+ * @param api - the hub browser API.
945
+ * @returns name → whether the model may call the skill.
946
+ */
947
+ async function modelInvocableMap(api) {
948
+ const now = Date.now();
949
+ const cached = modelCache;
950
+ if (cached !== void 0 && now - cached.at < MODEL_TTL_MS) return "failed" in cached ? /* @__PURE__ */ new Map() : cached.map;
951
+ try {
952
+ const catalog = await api.catalog();
953
+ const map = new Map(catalog.skills.map((skill) => [skill.name, skill.invocation.modelInvocable]));
954
+ modelCache = {
955
+ at: now,
956
+ map
957
+ };
958
+ return map;
959
+ } catch (error) {
960
+ console.error("[dsh-skill-hub] slash dot color lookup failed:", error);
961
+ modelCache = {
962
+ at: now,
963
+ failed: true
964
+ };
965
+ return /* @__PURE__ */ new Map();
966
+ }
967
+ }
968
+ /**
969
+ * One menu-row dot element. Inline span so it needs no CSS module; the
970
+ * candidate menu centers it inside its 16×16 leading icon slot.
971
+ * @param color - the dot's background color.
972
+ * @returns a React node (memory-only, never crosses the Host boundary).
973
+ */
974
+ function dotIcon(color) {
975
+ return (0, react.createElement)("span", {
976
+ "aria-hidden": true,
977
+ style: {
978
+ display: "inline-block",
979
+ width: 6,
980
+ height: 6,
981
+ borderRadius: 3,
982
+ background: color,
983
+ flex: "none"
984
+ }
985
+ });
986
+ }
987
+ /**
988
+ * Wrap the core skill source so every menu row carries the invocation dot.
989
+ * Exported for unit tests; production wiring goes through setupSkillSlashDots.
990
+ * @param source - the registered `/skill` source.
991
+ * @param api - hub browser API for the modelInvocable lookup.
992
+ * @param scope - hub settings scope for the dot colors.
993
+ * @returns a disposer restoring the original candidates.
994
+ */
995
+ function wrapSkillSource(source, api, scope) {
996
+ const original = source.candidates;
997
+ source.candidates = async (session, req) => {
998
+ const items = await original(session, req);
999
+ if (req.signal.aborted) return items;
1000
+ const modelByName = await modelInvocableMap(api);
1001
+ if (req.signal.aborted) return items;
1002
+ const snapshot = scope.getSnapshot();
1003
+ const modelColor = snapshot.value?.dotModelColor ?? "#2f81f7";
1004
+ const userColor = snapshot.value?.dotUserColor ?? "#3fb950";
1005
+ return items.map((item) => ({
1006
+ ...item,
1007
+ icon: dotIcon(modelByName.get(item.name) ?? true ? modelColor : userColor)
1008
+ }));
1009
+ };
1010
+ return () => {
1011
+ source.candidates = original;
1012
+ };
1013
+ }
1014
+ /**
1015
+ * Mount the slash-menu dots on the registered `/skill` source. Idempotent and
1016
+ * defensive: if the core source isn't registered yet (or the registry shape
1017
+ * changes), it retries briefly and then gives up silently — the chat keeps
1018
+ * working, it simply shows no dots. The returned disposer restores the
1019
+ * original candidates and clears the model cache.
1020
+ * @param ctx - the client root context (inputTriggers + events).
1021
+ * @param api - hub browser API.
1022
+ * @param scope - hub settings scope for dot colors.
1023
+ * @returns a cleanup function for `ctx.effect`.
1024
+ */
1025
+ function setupSkillSlashDots(ctx, api, scope) {
1026
+ const inputTriggers = ctx.get("inputTriggers");
1027
+ if (inputTriggers === void 0) return () => {};
1028
+ let disposed = false;
1029
+ let restore;
1030
+ let attempts = 0;
1031
+ const attempt = () => {
1032
+ if (disposed) return;
1033
+ const source = findSkillSource(inputTriggers);
1034
+ if (source === void 0) {
1035
+ if (attempts < 10) {
1036
+ attempts += 1;
1037
+ setTimeout(attempt, 100);
1038
+ }
1039
+ return;
1040
+ }
1041
+ restore = wrapSkillSource(source, api, scope);
1042
+ };
1043
+ attempt();
1044
+ const clearCache = () => {
1045
+ resetModelCache();
1046
+ };
1047
+ const offReset = ctx.on("connection/reset", clearCache);
1048
+ return () => {
1049
+ disposed = true;
1050
+ offReset();
1051
+ restore?.();
1052
+ restore = void 0;
1053
+ };
1054
+ }
1055
+ //#endregion
1056
+ //#region src/client/icons.tsx
1057
+ /** ic_ds_chevron_down_outline_14 */
1058
+ function IconChevronDownOutline14({ size = 14, className }) {
1059
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1060
+ width: size,
1061
+ height: size,
1062
+ className,
1063
+ viewBox: "0 0 14 14",
1064
+ fill: "none",
1065
+ xmlns: "http://www.w3.org/2000/svg",
1066
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1067
+ 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",
1068
+ fill: "currentColor"
1069
+ })
1070
+ });
1071
+ }
1072
+ /** ic_ds_trash_outline_16 */
1073
+ function IconTrashOutline16({ size = 16, className }) {
1074
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1075
+ width: size,
1076
+ height: size,
1077
+ className,
1078
+ viewBox: "0 0 16 16",
1079
+ fill: "none",
1080
+ xmlns: "http://www.w3.org/2000/svg",
1081
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1082
+ 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",
1083
+ fill: "currentColor"
1084
+ })
1085
+ });
1086
+ }
1087
+ /** ic_ds_skill_outline_16 */
1088
+ function IconSkillOutline16({ size = 16, className }) {
1089
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
1090
+ width: size,
1091
+ height: size,
1092
+ className,
1093
+ viewBox: "0 0 16 16",
1094
+ fill: "none",
1095
+ xmlns: "http://www.w3.org/2000/svg",
1096
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1097
+ 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",
1098
+ fill: "currentColor"
1099
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
1100
+ 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",
1101
+ fill: "currentColor"
1102
+ })]
1103
+ });
1104
+ }
1105
+ //#endregion
770
1106
  //#region \0dsh-css:/Users/huanyi/Documents/personal/tools/dsh-skill-hub/src/client/settings-card.module.css.mjs
771
1107
  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)}";
772
1108
  const tagId$1 = "dsh-skill-hub/settings-card.module.css";
@@ -854,7 +1190,7 @@ window.__ModuleLoader__.load({
854
1190
  className: settings_card_module_css_default.pending,
855
1191
  children: props.t("settings.unsaved")
856
1192
  }) : null,
857
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconChevronDownOutline14, { className: open ? settings_card_module_css_default.chevronOpen : settings_card_module_css_default.chevron })
1193
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconChevronDownOutline14, { className: open ? settings_card_module_css_default.chevronOpen : settings_card_module_css_default.chevron })
858
1194
  ]
859
1195
  });
860
1196
  if (!state.exposed) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
@@ -1214,12 +1550,6 @@ window.__ModuleLoader__.load({
1214
1550
  };
1215
1551
  //#endregion
1216
1552
  //#region src/client/SkillHubSettingsCard.tsx
1217
- /** Model-invocable dot color default. Single source for the TS side; the
1218
- * panel's CSS mirrors it via --hub-model (panel.module.css). */
1219
- const DEFAULT_DOT_MODEL_COLOR = "#2f81f7";
1220
- /** User-invocable dot color default. Single source for the TS side; the
1221
- * panel's CSS mirrors it via --hub-user (panel.module.css). */
1222
- const DEFAULT_DOT_USER_COLOR = "#3fb950";
1223
1553
  /** Bridges the hub's config scope onto the card's staged form. */
1224
1554
  var SkillHubSettingsCardController = class {
1225
1555
  form;
@@ -1376,141 +1706,8 @@ window.__ModuleLoader__.load({
1376
1706
  });
1377
1707
  }
1378
1708
  //#endregion
1379
- //#region src/client/helpers.ts
1380
- /**
1381
- * Shared panel helpers: the active-dictionary pick (document-language
1382
- * based, family precedent) plus a small error-message extractor.
1383
- */
1384
- /** Active dictionary, picked by the document language at call time. */
1385
- function dictionary() {
1386
- return (typeof document !== "undefined" ? document.documentElement.lang : "zh").toLowerCase().startsWith("en") ? { ...en } : { ...zh };
1387
- }
1388
- /** Translate a key with optional {name} template params (current language). */
1389
- function tt(key, values) {
1390
- return t(dictionary(), key, values);
1391
- }
1392
- /** Human-readable error text from an unknown thrown value. */
1393
- function errorMessage(error) {
1394
- if (error instanceof Error) return error.message;
1395
- return String(error);
1396
- }
1397
- //#endregion
1398
- //#region src/client/grouping.ts
1399
- /**
1400
- * Derive a group switch view from its member names and the set of currently
1401
- * enabled skill names (catalog.skills). Members not in the enabled set count
1402
- * as disabled (catalog.disabled or absent read-only rows).
1403
- */
1404
- function groupSwitchView(members, enabledNames) {
1405
- const enabled = [];
1406
- const disabled = [];
1407
- for (const name of members) if (enabledNames.has(name)) enabled.push(name);
1408
- else disabled.push(name);
1409
- return {
1410
- state: disabled.length === 0 ? "on" : enabled.length === 0 ? "off" : "mixed",
1411
- enabled,
1412
- disabled
1413
- };
1414
- }
1415
- /** Names of every group a skill belongs to (tags + collections). */
1416
- function groupNamesOf(name, tags, collections) {
1417
- const names = [];
1418
- for (const tag of tags) if (tag.skillNames.includes(name)) names.push(tag.name);
1419
- for (const collection of collections) if (collection.skillNames.includes(name)) names.push(collection.name);
1420
- return names;
1421
- }
1422
- /**
1423
- * Members of a group that are currently enabled AND also belong to at least
1424
- * one other group — the set a "close" action must ask about.
1425
- */
1426
- function conflictsOnClose(members, enabledNames, otherGroups) {
1427
- return members.filter((name) => {
1428
- if (!enabledNames.has(name)) return false;
1429
- return otherGroups.some((group) => group.members.includes(name));
1430
- });
1431
- }
1432
- /** Origin-repo filter value: skills with no source record (private skills). */
1433
- const PRIVATE_SOURCE = "private";
1434
- /** 项目级技能来源(它们有 workspace 归属,不属于「个人」组)。 */
1435
- function isProjectSource(source) {
1436
- return source === "project-dsh" || source === "project-agents";
1437
- }
1438
- /**
1439
- * Apply the origin filter ('all' or a specific origin repo; skills without a
1440
- * source record count as PRIVATE_SOURCE). The origins map is the store's
1441
- * skillName → repo derivation, so filtering follows the tracked source
1442
- * records instead of the filesystem root a skill happens to live under.
1443
- * 项目级技能(有 workspace 归属)永远不算「个人」。
1444
- */
1445
- function filterBySource(skills, source, origins) {
1446
- if (source === "all") return [...skills];
1447
- return skills.filter((skill) => {
1448
- if (isProjectSource(skill.source)) return false;
1449
- return (origins[skill.name] ?? "private") === source;
1450
- });
1451
- }
1452
- /**
1453
- * Sort a skill list in place-safe copy order: name ascending, added
1454
- * descending (newest first, unknown addedAt last), or uses descending
1455
- * (most-called first). Unknown values always trail.
1456
- */
1457
- function sortSkills(skills, key, getUses) {
1458
- const list = [...skills];
1459
- if (key === "name") list.sort((a, b) => a.name.localeCompare(b.name));
1460
- else if (key === "added") list.sort((a, b) => (b.addedAt ?? -Infinity) - (a.addedAt ?? -Infinity));
1461
- else if (key === "uses") list.sort((a, b) => (getUses?.(b.name) ?? 0) - (getUses?.(a.name) ?? 0));
1462
- return list;
1463
- }
1464
- /** Format an epoch-ms timestamp as a short relative time bucket. */
1465
- function formatRelativeTime(ms, now = Date.now()) {
1466
- const diff = Math.max(0, now - ms);
1467
- const minutes = Math.floor(diff / 6e4);
1468
- if (minutes < 1) return { key: "time.justNow" };
1469
- if (minutes < 60) return {
1470
- key: "time.minutesAgo",
1471
- value: minutes
1472
- };
1473
- const hours = Math.floor(minutes / 60);
1474
- if (hours < 24) return {
1475
- key: "time.hoursAgo",
1476
- value: hours
1477
- };
1478
- const days = Math.floor(hours / 24);
1479
- if (days < 7) return {
1480
- key: "time.daysAgo",
1481
- value: days
1482
- };
1483
- return {
1484
- key: "time.weeksAgo",
1485
- value: Math.floor(days / 7)
1486
- };
1487
- }
1488
- //#endregion
1489
- //#region src/client/panel/format.ts
1490
- /** Dot inline style from the user-chosen color (undefined keeps the CSS default). */
1491
- function dotStyle(color) {
1492
- if (color === void 0) return void 0;
1493
- return {
1494
- background: color,
1495
- borderColor: color
1496
- };
1497
- }
1498
- /** Localized relative-time text for a Unix-ms timestamp. */
1499
- function relativeTimeText(ms) {
1500
- const rt = formatRelativeTime(ms);
1501
- return tt(rt.key, rt.value !== void 0 ? { value: rt.value } : void 0);
1502
- }
1503
- /** Localized absolute time for the detail meta (e.g. "2026/8/16 11:00:00"). */
1504
- function formatDateTime(ms) {
1505
- return new Date(ms).toLocaleString();
1506
- }
1507
- /** Short form of a commit SHA for display. */
1508
- function shortSha(sha) {
1509
- return sha.length > 7 ? sha.slice(0, 7) : sha;
1510
- }
1511
- //#endregion
1512
1709
  //#region \0dsh-css:/Users/huanyi/Documents/personal/tools/dsh-skill-hub/src/client/panel/panel.module.css.mjs
1513
- 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_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{margin-left:14px}";
1710
+ 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}";
1514
1711
  const tagId = "dsh-skill-hub/panel.module.css";
1515
1712
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
1516
1713
  const tag = document.createElement("style");
@@ -1706,19 +1903,15 @@ window.__ModuleLoader__.load({
1706
1903
  children: tt("detail.back")
1707
1904
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1708
1905
  className: panel_module_css_default.detailName,
1709
- children: [
1710
- detail.name,
1711
- detail.invocation.modelInvocable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1712
- className: panel_module_css_default.dot + " " + panel_module_css_default.dotModel,
1713
- style: dotStyle(hubConfig?.dotModelColor),
1714
- title: tt("legend.model")
1715
- }) : null,
1716
- detail.invocation.userInvocable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1717
- className: panel_module_css_default.dot + " " + panel_module_css_default.dotUser,
1718
- style: dotStyle(hubConfig?.dotUserColor),
1719
- title: tt("legend.user")
1720
- }) : null
1721
- ]
1906
+ children: [detail.name, detail.invocation.modelInvocable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1907
+ className: panel_module_css_default.dot + " " + panel_module_css_default.dotModel,
1908
+ style: dotStyle(hubConfig?.dotModelColor),
1909
+ title: tt("legend.model")
1910
+ }) : detail.invocation.userInvocable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1911
+ className: panel_module_css_default.dot + " " + panel_module_css_default.dotUser,
1912
+ style: dotStyle(hubConfig?.dotUserColor),
1913
+ title: tt("legend.user")
1914
+ }) : null]
1722
1915
  })]
1723
1916
  }),
1724
1917
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -1985,11 +2178,33 @@ window.__ModuleLoader__.load({
1985
2178
  const stat = hub.uses.get(skill.name);
1986
2179
  const count = stat?.count ?? 0;
1987
2180
  const lastUsed = stat?.lastUsed;
2181
+ /** 单一状态圆点:模型可调 → 蓝;否则用户可调 → 绿。与聊天 / 菜单同规则。 */
2182
+ const dot = skill.invocation.modelInvocable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2183
+ className: panel_module_css_default.dot + " " + panel_module_css_default.dotModel,
2184
+ style: dotStyle(hub.hubConfig?.dotModelColor),
2185
+ title: tt("legend.model")
2186
+ }) : skill.invocation.userInvocable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2187
+ className: panel_module_css_default.dot + " " + panel_module_css_default.dotUser,
2188
+ style: dotStyle(hub.hubConfig?.dotUserColor),
2189
+ title: tt("legend.user")
2190
+ }) : null;
2191
+ /** 键盘打开详情:Enter 或空格。仅当焦点在行本身(而非行内按钮)时生效。 */
2192
+ const onKeyDown = (event) => {
2193
+ if (event.target !== event.currentTarget) return;
2194
+ if (event.key === "Enter" || event.key === " ") {
2195
+ event.preventDefault();
2196
+ hub.openDetail(skill.name);
2197
+ }
2198
+ };
1988
2199
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1989
2200
  className: panel_module_css_default.row,
2201
+ role: "button",
2202
+ tabIndex: 0,
2203
+ "aria-label": tt("row.open", { name: skill.name }),
1990
2204
  onClick: () => {
1991
2205
  hub.openDetail(skill.name);
1992
2206
  },
2207
+ onKeyDown,
1993
2208
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1994
2209
  className: panel_module_css_default.rowMain,
1995
2210
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -2003,16 +2218,7 @@ window.__ModuleLoader__.load({
2003
2218
  className: panel_module_css_default.useCount,
2004
2219
  children: count
2005
2220
  }) : null,
2006
- skill.invocation.modelInvocable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2007
- className: panel_module_css_default.dot + " " + panel_module_css_default.dotModel,
2008
- style: dotStyle(hub.hubConfig?.dotModelColor),
2009
- title: tt("legend.model")
2010
- }) : null,
2011
- skill.invocation.userInvocable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2012
- className: panel_module_css_default.dot + " " + panel_module_css_default.dotUser,
2013
- style: dotStyle(hub.hubConfig?.dotUserColor),
2014
- title: tt("legend.user")
2015
- }) : null,
2221
+ dot,
2016
2222
  lastUsed !== void 0 && hub.hubConfig?.showUseTime !== false ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2017
2223
  className: panel_module_css_default.useTime,
2018
2224
  children: relativeTimeText(lastUsed)
@@ -2032,7 +2238,7 @@ window.__ModuleLoader__.load({
2032
2238
  event.stopPropagation();
2033
2239
  hub.requestDeleteSkill(skill.name);
2034
2240
  },
2035
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconTrashOutline16, { size: 14 })
2241
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconTrashOutline16, { size: 14 })
2036
2242
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2037
2243
  type: "button",
2038
2244
  role: "switch",
@@ -2159,7 +2365,7 @@ window.__ModuleLoader__.load({
2159
2365
  const projCollapsed = collapsedGroups.has(projKey);
2160
2366
  const subdivided = subdividedProjects.has(key);
2161
2367
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2162
- className: panel_module_css_default.section + " " + panel_module_css_default.projectNest,
2368
+ className: panel_module_css_default.projectNest,
2163
2369
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2164
2370
  className: panel_module_css_default.groupHead,
2165
2371
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
@@ -2201,7 +2407,7 @@ window.__ModuleLoader__.load({
2201
2407
  const srcKey = projKey + ":" + source;
2202
2408
  const srcCollapsed = collapsedGroups.has(srcKey);
2203
2409
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2204
- className: panel_module_css_default.section + " " + panel_module_css_default.projectNest,
2410
+ className: panel_module_css_default.projectNest,
2205
2411
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2206
2412
  className: panel_module_css_default.groupHead,
2207
2413
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
@@ -4100,7 +4306,7 @@ window.__ModuleLoader__.load({
4100
4306
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h2", {
4101
4307
  className: panel_module_css_default.title,
4102
4308
  children: [
4103
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSkillOutline16, {
4309
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconSkillOutline16, {
4104
4310
  size: 16,
4105
4311
  className: panel_module_css_default.titleIcon
4106
4312
  }),
@@ -4598,7 +4804,8 @@ window.__ModuleLoader__.load({
4598
4804
  "locale",
4599
4805
  "connection",
4600
4806
  "remote",
4601
- "settingsScope"
4807
+ "settingsScope",
4808
+ "inputTriggers"
4602
4809
  ];
4603
4810
  /**
4604
4811
  * Mount the settings card and the skill hub section.
@@ -4612,6 +4819,7 @@ window.__ModuleLoader__.load({
4612
4819
  const t = ctx.locale.bind(NS);
4613
4820
  const api = new SkillHubApi();
4614
4821
  const settingsCard = new SkillHubSettingsCardController(ctx.settingsScope.bind({ namespace: NS }));
4822
+ ctx.effect(() => setupSkillSlashDots(ctx, api, ctx.settingsScope.bind({ namespace: NS })), "dsh-skill-hub: slash dots");
4615
4823
  ctx.effect(() => ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
4616
4824
  name: "settings.plugin.item",
4617
4825
  key: NS,