dsh-skill-hub 0.2.2 → 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": "发现诊断",
@@ -487,9 +486,9 @@ window.__ModuleLoader__.load({
487
486
  "settings.announceToAgent": "向 Agent 公告",
488
487
  "settings.announceToAgentHint": "在系统提示中加入本插件说明,用户提到技能管理时 Agent 知道如何协作。",
489
488
  "settings.dotModelColor": "模型可调圆点颜色",
490
- "settings.dotModelColorHint": "技能名旁蓝色圆点的颜色(#rrggbb)。",
489
+ "settings.dotModelColorHint": "技能行/聊天 / 菜单中「模型可调」蓝色圆点的颜色(#rrggbb)。",
491
490
  "settings.dotUserColor": "用户可调圆点颜色",
492
- "settings.dotUserColorHint": "技能名旁绿色圆点的颜色(#rrggbb)。",
491
+ "settings.dotUserColorHint": "技能行/聊天 / 菜单中「仅用户可调」绿色圆点的颜色(#rrggbb)。",
493
492
  "settings.showUseCount": "显示调用次数",
494
493
  "settings.showUseCountHint": "在技能名旁显示琥珀色调用次数。",
495
494
  "settings.showUseTime": "显示最近调用时间",
@@ -526,7 +525,7 @@ window.__ModuleLoader__.load({
526
525
  "panel.search": "Search skills…",
527
526
  "legend.model": "Model-invocable — agents may call automatically",
528
527
  "legend.user": "User-invocable — explicit invocation only",
529
- "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.",
530
529
  "panel.enabled": "Enabled",
531
530
  "panel.disabled": "Disabled",
532
531
  "panel.diagnostics": "Discovery diagnostics",
@@ -695,9 +694,9 @@ window.__ModuleLoader__.load({
695
694
  "settings.announceToAgent": "Announce to agent",
696
695
  "settings.announceToAgentHint": "Adds a system-prompt section so agents know how to collaborate when users mention skill management.",
697
696
  "settings.dotModelColor": "Model-invocable dot color",
698
- "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).",
699
698
  "settings.dotUserColor": "User-invocable dot color",
700
- "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).",
701
700
  "settings.showUseCount": "Show invocation count",
702
701
  "settings.showUseCountHint": "Show the amber invocation count beside each skill name.",
703
702
  "settings.showUseTime": "Show last-used time",
@@ -769,6 +768,341 @@ window.__ModuleLoader__.load({
769
768
  };
770
769
  }
771
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
772
1106
  //#region \0dsh-css:/Users/huanyi/Documents/personal/tools/dsh-skill-hub/src/client/settings-card.module.css.mjs
773
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)}";
774
1108
  const tagId$1 = "dsh-skill-hub/settings-card.module.css";
@@ -856,7 +1190,7 @@ window.__ModuleLoader__.load({
856
1190
  className: settings_card_module_css_default.pending,
857
1191
  children: props.t("settings.unsaved")
858
1192
  }) : null,
859
- /* @__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 })
860
1194
  ]
861
1195
  });
862
1196
  if (!state.exposed) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
@@ -1216,12 +1550,6 @@ window.__ModuleLoader__.load({
1216
1550
  };
1217
1551
  //#endregion
1218
1552
  //#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
1553
  /** Bridges the hub's config scope onto the card's staged form. */
1226
1554
  var SkillHubSettingsCardController = class {
1227
1555
  form;
@@ -1378,139 +1706,6 @@ window.__ModuleLoader__.load({
1378
1706
  });
1379
1707
  }
1380
1708
  //#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
1709
  //#region \0dsh-css:/Users/huanyi/Documents/personal/tools/dsh-skill-hub/src/client/panel/panel.module.css.mjs
1515
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}";
1516
1711
  const tagId = "dsh-skill-hub/panel.module.css";
@@ -1708,19 +1903,15 @@ window.__ModuleLoader__.load({
1708
1903
  children: tt("detail.back")
1709
1904
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1710
1905
  className: panel_module_css_default.detailName,
1711
- children: [
1712
- detail.name,
1713
- detail.invocation.modelInvocable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1714
- className: panel_module_css_default.dot + " " + panel_module_css_default.dotModel,
1715
- style: dotStyle(hubConfig?.dotModelColor),
1716
- title: tt("legend.model")
1717
- }) : null,
1718
- detail.invocation.userInvocable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1719
- className: panel_module_css_default.dot + " " + panel_module_css_default.dotUser,
1720
- style: dotStyle(hubConfig?.dotUserColor),
1721
- title: tt("legend.user")
1722
- }) : null
1723
- ]
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]
1724
1915
  })]
1725
1916
  }),
1726
1917
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -1987,6 +2178,16 @@ window.__ModuleLoader__.load({
1987
2178
  const stat = hub.uses.get(skill.name);
1988
2179
  const count = stat?.count ?? 0;
1989
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;
1990
2191
  /** 键盘打开详情:Enter 或空格。仅当焦点在行本身(而非行内按钮)时生效。 */
1991
2192
  const onKeyDown = (event) => {
1992
2193
  if (event.target !== event.currentTarget) return;
@@ -2017,16 +2218,7 @@ window.__ModuleLoader__.load({
2017
2218
  className: panel_module_css_default.useCount,
2018
2219
  children: count
2019
2220
  }) : null,
2020
- skill.invocation.modelInvocable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
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,
2221
+ dot,
2030
2222
  lastUsed !== void 0 && hub.hubConfig?.showUseTime !== false ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2031
2223
  className: panel_module_css_default.useTime,
2032
2224
  children: relativeTimeText(lastUsed)
@@ -2046,7 +2238,7 @@ window.__ModuleLoader__.load({
2046
2238
  event.stopPropagation();
2047
2239
  hub.requestDeleteSkill(skill.name);
2048
2240
  },
2049
- 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 })
2050
2242
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
2051
2243
  type: "button",
2052
2244
  role: "switch",
@@ -4114,7 +4306,7 @@ window.__ModuleLoader__.load({
4114
4306
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("h2", {
4115
4307
  className: panel_module_css_default.title,
4116
4308
  children: [
4117
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconSkillOutline16, {
4309
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconSkillOutline16, {
4118
4310
  size: 16,
4119
4311
  className: panel_module_css_default.titleIcon
4120
4312
  }),
@@ -4612,7 +4804,8 @@ window.__ModuleLoader__.load({
4612
4804
  "locale",
4613
4805
  "connection",
4614
4806
  "remote",
4615
- "settingsScope"
4807
+ "settingsScope",
4808
+ "inputTriggers"
4616
4809
  ];
4617
4810
  /**
4618
4811
  * Mount the settings card and the skill hub section.
@@ -4626,6 +4819,7 @@ window.__ModuleLoader__.load({
4626
4819
  const t = ctx.locale.bind(NS);
4627
4820
  const api = new SkillHubApi();
4628
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");
4629
4823
  ctx.effect(() => ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
4630
4824
  name: "settings.plugin.item",
4631
4825
  key: NS,