glayguo's picture
Publish verified EvalArc 19ac9d8a5cb8
6c4b387 verified
Raw History Blame Contribute Delete
25.3 kB
window.__ModuleLoader__.load({
id: "dsh-skills-anywhere",
factory: (require) => {
var module = { exports: {} };
var exports = module.exports;
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
let react = require("react");
let react_jsx_runtime = require("react/jsx-runtime");
//#region src/web-protocol.ts
/** Settings namespace the host registers and the card edits. */
const SETTINGS_NAMESPACE = "skills-anywhere";
/**
* Exact Fetch route the host registers on the dsh connection's shared `/api`
* channel (the public seam for feature packages; it inherits the browser-trust
* fence and authentication) and the card POSTs to. Body: `ReportRequest` JSON;
* response: `RpcResult<ReportView>` JSON.
*/
const REPORT_PATH = "/api/skills-anywhere/report";
//#endregion
//#region src/client/locale.ts
/** Dictionaries for the settings card. Keys are shared by both languages. */
const LOCALE_NS = "skills-anywhere";
const en = {
title: "Skills Anywhere",
description: "Skills from other agents, Claude Code plugins and git sources: what the model sees and what stays one search away.",
expand: "Expand",
collapse: "Collapse",
readOnly: "This browser cannot change settings on this host; values are shown read-only.",
loading: "Reading the skill catalog…",
loadFailed: "Could not read the skill catalog",
retry: "Retry",
refresh: "Refresh",
summary: "{listed} of {total} skills listed for the model",
incomplete: "Some directories could not be read; the list may be incomplete.",
limitLabel: "Catalog budget",
limitHint: "Maximum skills listed for the model. 0 lists every skill.",
unlimited: "unlimited",
search: "Filter by name, description or origin",
noMatches: "No skills match.",
stateVisible: "listed",
stateHidden: "not listed",
stateDisabled: "author disabled",
pinned: "pinned",
renamedFrom: "renamed from {name}",
pin: "Pin",
unpin: "Unpin",
pinHint: "Always list this skill for the model",
hide: "Hide",
unhide: "Show",
hideHint: "Keep this skill out of the model catalog; /name and find_skills still work",
exclude: "Exclude",
excludeHint: "Drop this skill from the provider entirely",
excluded: "Excluded skills",
restore: "Restore",
droppedSummary: "{count} duplicates collapsed",
invalidSummary: "{count} files skipped",
saving: "Saving…",
saveFailed: "The change was refused; reload and try again.",
roots: "Scanned directories",
rootsSummary: "{present} of {total} directories exist"
};
const zh = {
title: "Skills Anywhere",
description: "来自其他 agent、Claude Code 插件和 git 源的技能:哪些进入模型目录,哪些留在搜索之外。",
expand: "展开",
collapse: "收起",
readOnly: "当前浏览器无法修改这台主机的设置,以下内容只读。",
loading: "正在读取技能目录…",
loadFailed: "无法读取技能目录",
retry: "重试",
refresh: "刷新",
summary: "{total} 个技能中有 {listed} 个列入模型目录",
incomplete: "部分目录无法读取,列表可能不完整。",
limitLabel: "目录预算",
limitHint: "列给模型的最大技能数。0 表示全部列出。",
unlimited: "不限",
search: "按名称、描述或来源筛选",
noMatches: "没有匹配的技能。",
stateVisible: "已列出",
stateHidden: "未列出",
stateDisabled: "作者禁用",
pinned: "已置顶",
renamedFrom: "原名 {name}",
pin: "置顶",
unpin: "取消置顶",
pinHint: "始终把这个技能列给模型",
hide: "隐藏",
unhide: "显示",
hideHint: "不进入模型目录;/名称 和 find_skills 仍然可用",
exclude: "排除",
excludeHint: "从本 provider 中彻底去掉这个技能",
excluded: "已排除的技能",
restore: "恢复",
droppedSummary: "合并了 {count} 个重复项",
invalidSummary: "跳过了 {count} 个文件",
saving: "保存中…",
saveFailed: "修改被拒绝,请刷新后重试。",
roots: "扫描的目录",
rootsSummary: "{total} 个目录中有 {present} 个存在"
};
/** `{name}`-style interpolation for the few messages that carry numbers or names. */
function fill(template, values) {
return template.replace(/\{(\w+)\}/g, (_match, key) => String(values[key] ?? `{${key}}`));
}
//#endregion
//#region src/client/styles.ts
const label = "var(--dsw-alias-label-primary)";
const secondary = "var(--dsw-alias-label-secondary)";
const tertiary = "var(--dsw-alias-label-tertiary)";
const border = "var(--dsw-alias-border-l2)";
const brand = "var(--dsw-alias-brand-primary)";
const styles = {
card: {
listStyle: "none",
border: `1px solid ${border}`,
borderRadius: 10,
marginBottom: 8,
background: "var(--dsw-alias-bg-layer-3)",
color: label
},
header: {
display: "flex",
alignItems: "center",
gap: 12,
width: "100%",
padding: "12px 14px",
background: "none",
border: "none",
color: "inherit",
cursor: "pointer",
textAlign: "left",
font: "inherit"
},
headText: {
display: "flex",
flexDirection: "column",
gap: 2,
flex: 1,
minWidth: 0
},
name: {
fontWeight: 600,
fontSize: 14
},
description: {
color: tertiary,
fontSize: 12,
lineHeight: 1.4
},
chevron: {
color: tertiary,
fontSize: 12,
transition: "transform .15s"
},
body: {
padding: "0 14px 14px",
display: "flex",
flexDirection: "column",
gap: 12
},
status: {
color: tertiary,
fontSize: 12,
margin: 0
},
errorText: {
color: "var(--dsw-alias-label-error)",
fontSize: 12,
margin: 0
},
row: {
display: "flex",
alignItems: "center",
gap: 8,
flexWrap: "wrap"
},
input: {
font: "inherit",
fontSize: 13,
color: label,
background: "transparent",
border: `1px solid ${border}`,
borderRadius: 6,
padding: "5px 8px",
minWidth: 0
},
search: {
flex: 1,
minWidth: 180
},
limitInput: {
width: 72,
textAlign: "right"
},
hint: {
color: tertiary,
fontSize: 12
},
group: {
margin: 0,
padding: 0,
listStyle: "none",
display: "flex",
flexDirection: "column",
gap: 4
},
groupTitle: {
color: secondary,
fontSize: 12,
fontWeight: 600,
margin: "8px 0 4px",
textTransform: "uppercase",
letterSpacing: ".04em"
},
skill: {
display: "grid",
gridTemplateColumns: "minmax(0, 1fr) auto",
gap: "2px 12px",
alignItems: "center",
padding: "6px 8px",
borderRadius: 6,
border: `1px solid transparent`
},
skillHidden: { opacity: .7 },
skillName: {
fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
fontSize: 13,
display: "flex",
gap: 6,
alignItems: "center",
flexWrap: "wrap"
},
skillMeta: {
gridColumn: "1 / 2",
color: tertiary,
fontSize: 12,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap"
},
actions: {
gridColumn: "2 / 3",
gridRow: "1 / 3",
display: "flex",
gap: 6
},
tag: {
fontSize: 11,
lineHeight: "16px",
padding: "0 6px",
borderRadius: 999,
border: `1px solid ${border}`,
color: secondary,
whiteSpace: "nowrap"
},
tagOn: {
borderColor: brand,
color: brand
},
tagOff: { color: tertiary },
button: {
font: "inherit",
fontSize: 12,
color: label,
background: "transparent",
border: `1px solid ${border}`,
borderRadius: 6,
padding: "3px 8px",
cursor: "pointer"
},
buttonActive: {
borderColor: brand,
color: brand
},
buttonDisabled: {
opacity: .5,
cursor: "default"
},
details: {
color: tertiary,
fontSize: 12
},
mono: { fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace" }
};
//#endregion
//#region src/client/index.tsx
/**
* dsh-skills-anywhere — browser half.
*
* Registers one card into the web Settings → Plugins → *Plugin configuration*
* tab (`settings.plugin.item`, keyed by the `skills-anywhere` settings
* namespace). The card lists every skill the provider found, grouped by where
* it lives, shows whether the model catalog lists it, and lets the user pin,
* hide or exclude skills. Edits write through the dsh settings scope, so they
* persist in the profile's settings document and take effect immediately: the
* host provider re-applies the namespace on every commit.
*
* Runtime services (Cordis `inject`): `slots`, `locale`, `settingsScope`. The
* report is fetched from the host's exact `/api` route with the page's own
* credentials. Bundled as a lazy-CJS factory for the dsh client module system.
*
* @module dsh-skills-anywhere/client
*/
/** Client services the bundle needs before `apply` runs. */
const inject = [
"slots",
"locale",
"settingsScope"
];
/** Register the dictionaries and the settings card. */
function apply(ctx) {
ctx.effect(() => ctx.locale.register(LOCALE_NS, {
en,
zh
}), "skills-anywhere: dictionaries");
const face = {
scope: ctx.settingsScope.bind({ namespace: SETTINGS_NAMESPACE }),
fetchReport: async (signal) => {
const response = await fetch(new URL(REPORT_PATH, location.href), {
method: "POST",
headers: { "content-type": "application/json" },
body: "{}",
credentials: "same-origin",
signal
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const result = await response.json();
if (!result.ok) throw new Error(`${result.error.code}: ${result.error.message}`);
return result.value;
}
};
ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
name: "settings.plugin.item",
key: SETTINGS_NAMESPACE,
locale: LOCALE_NS,
inject: () => ({ ...face })
}, SkillsAnywhereCard));
}
/** Settings card for the skills-anywhere provider. Exported for tests. */
function SkillsAnywhereCard(props) {
const { t, scope, fetchReport } = props;
const subscribe = (0, react.useCallback)((listener) => scope.subscribe(listener), [scope]);
const getSnapshot = (0, react.useCallback)(() => scope.getSnapshot(), [scope]);
const snapshot = (0, react.useSyncExternalStore)(subscribe, getSnapshot, getSnapshot);
const [open, setOpen] = (0, react.useState)(false);
const [report, setReport] = (0, react.useState)({ kind: "loading" });
const [reloadTick, setReloadTick] = (0, react.useState)(0);
const [saving, setSaving] = (0, react.useState)(false);
const [saveError, setSaveError] = (0, react.useState)(void 0);
const [query, setQuery] = (0, react.useState)("");
(0, react.useEffect)(() => {
if (!open) return;
const abort = new AbortController();
setReport((current) => current.kind === "ready" ? current : { kind: "loading" });
fetchReport(abort.signal).then((value) => {
if (!abort.signal.aborted) setReport({
kind: "ready",
report: value
});
}).catch((error) => {
if (!abort.signal.aborted) setReport({
kind: "failed",
message: error instanceof Error ? error.message : String(error)
});
});
return () => abort.abort();
}, [
open,
fetchReport,
snapshot.revision,
reloadTick
]);
const settings = snapshot.value;
const writable = snapshot.writable && !saving;
const write = (0, react.useCallback)(async (next) => {
setSaving(true);
setSaveError(void 0);
const ops = [];
if (next.catalog !== void 0) for (const [key, value] of Object.entries(next.catalog)) ops.push({
op: "set",
path: ["catalog", key],
value
});
if (next.excludeSkills !== void 0) ops.push({
op: "set",
path: ["excludeSkills"],
value: next.excludeSkills
});
try {
await scope.mutate(ops);
setReloadTick((tick) => tick + 1);
} catch (error) {
setSaveError(error instanceof Error ? error.message : String(error));
} finally {
setSaving(false);
}
}, [scope]);
const toggleList = (0, react.useCallback)((list, name) => {
if (settings === void 0) return;
const current = new Set(settings.catalog[list]);
if (current.has(name)) current.delete(name);
else current.add(name);
const pin = new Set(list === "pin" ? current : settings.catalog.pin);
const hide = new Set(list === "hide" ? current : settings.catalog.hide);
if (list === "pin" && current.has(name)) hide.delete(name);
if (list === "hide" && current.has(name)) pin.delete(name);
write({ catalog: {
limit: settings.catalog.limit,
pin: [...pin].toSorted(),
hide: [...hide].toSorted()
} });
}, [settings, write]);
const setExcluded = (0, react.useCallback)((name, excluded) => {
if (settings === void 0) return;
const current = new Set(settings.excludeSkills);
if (excluded) current.add(name);
else current.delete(name);
write({ excludeSkills: [...current].toSorted() });
}, [settings, write]);
const setLimit = (0, react.useCallback)((text) => {
if (settings === void 0) return;
const limit = text.trim() === "" ? 0 : Number(text);
if (!Number.isInteger(limit) || limit < 0 || limit === settings.catalog.limit) return;
write({ catalog: {
...settings.catalog,
limit
} });
}, [settings, write]);
const groups = (0, react.useMemo)(() => {
if (report.kind !== "ready") return [];
const terms = query.trim().toLowerCase().split(/\s+/).filter(Boolean);
const matches = (skill) => terms.every((term) => skill.name.toLowerCase().includes(term) || skill.description.toLowerCase().includes(term) || skill.origin.toLowerCase().includes(term));
const byGroup = /* @__PURE__ */ new Map();
for (const skill of report.report.skills) {
if (!matches(skill)) continue;
const list = byGroup.get(skill.group);
if (list === void 0) byGroup.set(skill.group, [skill]);
else list.push(skill);
}
return [...byGroup.entries()];
}, [report, query]);
if (snapshot.status !== "ready" || settings === void 0) return null;
const title = t("title");
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
style: styles.card,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
type: "button",
style: styles.header,
"aria-expanded": open,
"aria-label": `${t(open ? "collapse" : "expand")}: ${title}`,
onClick: () => setOpen(!open),
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
style: styles.headText,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
style: styles.name,
children: title
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
style: styles.description,
children: t("description")
})]
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
style: {
...styles.chevron,
transform: open ? "rotate(180deg)" : void 0
},
"aria-hidden": "true",
children: "▾"
})]
}), open ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: styles.body,
children: [
!snapshot.writable ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
style: styles.status,
role: "status",
children: t("readOnly")
}) : null,
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: styles.row,
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
style: styles.hint,
htmlFor: "skills-anywhere-limit",
children: t("limitLabel")
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
id: "skills-anywhere-limit",
style: {
...styles.input,
...styles.limitInput
},
type: "number",
min: 0,
step: 1,
defaultValue: settings.catalog.limit,
disabled: !writable,
onBlur: (event) => setLimit(event.currentTarget.value),
onKeyDown: (event) => {
if (event.key === "Enter") setLimit(event.currentTarget.value);
},
"aria-describedby": "skills-anywhere-limit-hint"
}, `limit-${settings.catalog.limit}`),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
id: "skills-anywhere-limit-hint",
style: styles.hint,
children: [
settings.catalog.limit === 0 ? t("unlimited") : "",
" ",
t("limitHint")
]
})
]
}),
report.kind === "loading" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
style: styles.status,
role: "status",
children: t("loading")
}) : null,
report.kind === "failed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
style: styles.errorText,
role: "alert",
children: [
t("loadFailed"),
": ",
report.message,
" ",
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
type: "button",
style: styles.button,
onClick: () => setReloadTick((tick) => tick + 1),
children: t("retry")
})
]
}) : null,
report.kind === "ready" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
style: styles.row,
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
style: styles.hint,
children: fill(t("summary"), {
listed: report.report.listed,
total: report.report.skills.length
})
}),
!report.report.complete ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
style: styles.errorText,
children: t("incomplete")
}) : null,
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: { flex: 1 } }),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
type: "button",
style: styles.button,
onClick: () => setReloadTick((tick) => tick + 1),
children: t("refresh")
})
]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
style: {
...styles.input,
...styles.search
},
type: "search",
placeholder: t("search"),
value: query,
onChange: (event) => setQuery(event.currentTarget.value),
"aria-label": t("search")
}),
saving ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
style: styles.status,
role: "status",
children: t("saving")
}) : null,
saveError !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
style: styles.errorText,
role: "alert",
children: [
t("saveFailed"),
" (",
saveError,
")"
]
}) : null,
groups.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
style: styles.status,
children: t("noMatches")
}) : null,
groups.map(([group, skills]) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
style: styles.groupTitle,
children: group
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
style: styles.group,
children: skills.map((skill) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillRow, {
skill,
t,
writable,
onPin: () => toggleList("pin", skill.name),
onHide: () => toggleList("hide", skill.name),
onExclude: () => setExcluded(skill.name, true)
}, skill.name))
})] }, group)),
settings.excludeSkills.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h4", {
style: styles.groupTitle,
children: t("excluded")
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
style: styles.group,
children: settings.excludeSkills.map((name) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
style: styles.skill,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
style: styles.skillName,
children: name
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
style: styles.actions,
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
type: "button",
style: {
...styles.button,
...writable ? {} : styles.buttonDisabled
},
disabled: !writable,
onClick: () => setExcluded(name, false),
children: t("restore")
})
})]
}, name))
})] }) : null,
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
style: styles.details,
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("summary", { children: [
fill(t("rootsSummary"), {
present: report.report.roots.filter((root) => root.exists).length,
total: report.report.roots.length
}),
report.report.dropped.length > 0 ? ` · ${fill(t("droppedSummary"), { count: report.report.dropped.length })}` : "",
report.report.invalid.length > 0 ? ` · ${fill(t("invalidSummary"), { count: report.report.invalid.length })}` : ""
] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ul", {
style: styles.group,
children: [report.report.roots.filter((root) => root.exists).map((root) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
style: styles.mono,
children: [
root.label,
" — ",
root.path,
" (",
root.count,
")"
]
}, root.path)), report.report.invalid.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
style: styles.mono,
children: [
entry.path,
": ",
entry.reason
]
}, entry.path))]
})]
})
] }) : null
]
}) : null]
});
}
function SkillRow({ skill, t, writable, onPin, onHide, onExclude }) {
const stateKey = skill.state === "visible" ? "stateVisible" : skill.state === "hidden" ? "stateHidden" : "stateDisabled";
const stateStyle = skill.state === "visible" ? styles.tagOn : styles.tagOff;
const disabledButton = writable && !skill.authorDisabled ? {} : styles.buttonDisabled;
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
style: {
...styles.skill,
...skill.state === "visible" ? {} : styles.skillHidden
},
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
style: styles.skillName,
children: [
skill.name,
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
style: {
...styles.tag,
...stateStyle
},
children: t(stateKey)
}),
skill.pinned ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
style: {
...styles.tag,
...styles.tagOn
},
children: t("pinned")
}) : null,
skill.renamedFrom !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
style: styles.tag,
children: fill(t("renamedFrom"), { name: skill.renamedFrom })
}) : null
]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
style: styles.skillMeta,
title: `${skill.path}\n${skill.description}`,
children: [
skill.origin,
" · ",
skill.description
]
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
style: styles.actions,
children: [
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
type: "button",
style: {
...styles.button,
...skill.pinned ? styles.buttonActive : {},
...disabledButton
},
disabled: !writable || skill.authorDisabled,
title: t("pinHint"),
onClick: onPin,
children: t(skill.pinned ? "unpin" : "pin")
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
type: "button",
style: {
...styles.button,
...skill.hidden ? styles.buttonActive : {},
...disabledButton
},
disabled: !writable || skill.authorDisabled,
title: t("hideHint"),
onClick: onHide,
children: t(skill.hidden ? "unhide" : "hide")
}),
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
type: "button",
style: {
...styles.button,
...writable ? {} : styles.buttonDisabled
},
disabled: !writable,
title: t("excludeHint"),
onClick: onExclude,
children: t("exclude")
})
]
})
]
});
}
//#endregion
exports.SkillsAnywhereCard = SkillsAnywhereCard;
exports.apply = apply;
exports.inject = inject;
return module.exports;
}
});