dsh-skill-hub 0.3.12 → 0.3.14
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/CONTRIBUTING.md +4 -2
- package/README.md +2 -1
- package/README.zh.md +2 -1
- package/lib/client.js +108 -88
- package/lib/client.js.map +1 -1
- package/lib/index.js +99 -5
- package/lib/types/client/SkillHubSettingsCard.d.ts +5 -3
- package/lib/types/client/grouping.d.ts +18 -1
- package/lib/types/client/icons.d.ts +4 -5
- package/lib/types/client/index.d.ts +4 -4
- package/lib/types/client/slash-dots.d.ts +17 -8
- package/lib/types/index.d.ts +4 -5
- package/lib/types/reconcile.d.ts +24 -0
- package/lib/types/skillfs/scan.d.ts +7 -0
- package/package.json +29 -39
- package/src/client/SkillHubSettingsCard.tsx +5 -3
- package/src/client/grouping.test.ts +35 -2
- package/src/client/grouping.ts +38 -1
- package/src/client/icons.tsx +4 -5
- package/src/client/index.tsx +18 -16
- package/src/client/panel/PanelDialogs.tsx +8 -2
- package/src/client/panel/SourcesView.tsx +12 -12
- package/src/client/panel/dialogs.tsx +45 -18
- package/src/client/slash-dots.test.ts +16 -33
- package/src/client/slash-dots.tsx +27 -57
- package/src/index.ts +20 -11
- package/src/reconcile.test.ts +76 -0
- package/src/reconcile.ts +62 -0
- package/src/skillfs/scan.ts +38 -0
package/CONTRIBUTING.md
CHANGED
|
@@ -39,7 +39,7 @@ src/client/locales/ dictionaries by view (common/skills/market/sources/detai
|
|
|
39
39
|
```bash
|
|
40
40
|
npm install
|
|
41
41
|
npm run typecheck # tsc --noEmit
|
|
42
|
-
npm test # vitest (
|
|
42
|
+
npm test # vitest (233 tests across 14 suites)
|
|
43
43
|
npm run build # tsc declarations + tsdown bundles (lib/index.js + lib/client.js)
|
|
44
44
|
```
|
|
45
45
|
|
|
@@ -68,10 +68,12 @@ npm run build # tsc declarations + tsdown bundles (lib/index.js + lib/clie
|
|
|
68
68
|
```bash
|
|
69
69
|
# after a change:
|
|
70
70
|
npm run build
|
|
71
|
-
cp lib/index.js lib/client.js ~/.dsh/profiles/web/node_modules/dsh-skill-hub/lib/
|
|
72
71
|
# restart the dsh web process, then verify Settings → 技能 and Settings → 插件 → Skill Hub
|
|
73
72
|
```
|
|
74
73
|
|
|
74
|
+
When the web profile installs this repo as a link (`"dsh-skill-hub": "link:/path/to/repo"`, the
|
|
75
|
+
usual local-dev setup), `lib/` is picked up on the next `dsh web` restart — no copy step needed.
|
|
76
|
+
|
|
75
77
|
## Issues
|
|
76
78
|
|
|
77
79
|
- **Bugs**: include the dsh version, Node version, the plugin version, and the exact steps.
|
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ dsh plugin --profile web add dsh-skill-hub
|
|
|
24
24
|
# restart dsh web → Settings → 技能 → Market → scan → import
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
Requires `Node ^22.19 || >=24` + dsh web
|
|
27
|
+
Requires `Node ^22.19 || >=24` + dsh web `>=0.1.6-alpha.2 <0.2`.
|
|
28
28
|
|
|
29
29
|
## Features
|
|
30
30
|
|
|
@@ -115,6 +115,7 @@ npm run build # tsc + tsdown → lib/index.js + lib/client.js
|
|
|
115
115
|
|
|
116
116
|
- `duplicate loader entry id: skill-hub` — remove the duplicate install (keep one `dsh plugin add` method).
|
|
117
117
|
- Skill missing — check the diagnostics section (frontmatter / name mismatch / short description).
|
|
118
|
+
- Empty source group — its skills were deleted, or their disabled records were lost (sidecar restored/hand-edited). Startup reconciles `.disabled` files on disk; source groups with no visible member are no longer rendered.
|
|
118
119
|
- Dots missing in `/` menu — dsh internals changed; catalog still works.
|
|
119
120
|
|
|
120
121
|
## Community
|
package/README.zh.md
CHANGED
|
@@ -24,7 +24,7 @@ dsh plugin --profile web add dsh-skill-hub
|
|
|
24
24
|
# 重启 dsh web → 设置 → 技能 → 市场 → 扫描 → 导入
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
要求 `Node ^22.19 || >=24` + dsh web
|
|
27
|
+
要求 `Node ^22.19 || >=24` + dsh web `>=0.1.6-alpha.2 <0.2`。
|
|
28
28
|
|
|
29
29
|
## 功能
|
|
30
30
|
|
|
@@ -115,6 +115,7 @@ npm run build # tsc + tsdown → lib/index.js + lib/client.js
|
|
|
115
115
|
|
|
116
116
|
- `duplicate loader entry id: skill-hub` — 删掉重复安装(只留一种 `dsh plugin add`)。
|
|
117
117
|
- 技能不出现 — 看诊断区(缺 frontmatter / 名称不一致 / 描述过短)。
|
|
118
|
+
- 来源组空壳 — 组内技能已被删除、或禁用记录丢失(状态文件被恢复/手改)。启动时自动对账磁盘上的 `.disabled` 文件补记录;无可见成员的来源组不再渲染。
|
|
118
119
|
- `/` 菜单圆点消失 — dsh 内部触发源变更,目录功能不受影响。
|
|
119
120
|
|
|
120
121
|
## 社区
|
package/lib/client.js
CHANGED
|
@@ -959,6 +959,27 @@ window.__ModuleLoader__.load({
|
|
|
959
959
|
});
|
|
960
960
|
}
|
|
961
961
|
/**
|
|
962
|
+
* Match origin collections against the currently visible enabled skills and
|
|
963
|
+
* disabled records, dropping collections with no visible member. Without
|
|
964
|
+
* this, a stale origin (skill deleted on disk, or a `.disabled` file whose
|
|
965
|
+
* sidecar record was lost) renders a group header with zero rows — an empty
|
|
966
|
+
* shell the sources tab otherwise never shows (project/personal groups
|
|
967
|
+
* already disappear when they have nothing to display).
|
|
968
|
+
*/
|
|
969
|
+
function visibleCollections(collections, visibleSkills, disabledRecords, normalized, sourceFilter, origins) {
|
|
970
|
+
const visible = [];
|
|
971
|
+
for (const collection of collections) {
|
|
972
|
+
const skills = visibleSkills.filter((skill) => collection.skillNames.includes(skill.name));
|
|
973
|
+
const disabledMembers = disabledRecords.filter((record) => collection.skillNames.includes(record.name) && (normalized.length === 0 || record.name.toLocaleLowerCase().includes(normalized) || record.description.toLocaleLowerCase().includes(normalized)) && (sourceFilter === "all" || (origins[record.name] ?? "private") === sourceFilter));
|
|
974
|
+
if (skills.length > 0 || disabledMembers.length > 0) visible.push({
|
|
975
|
+
collection,
|
|
976
|
+
skills,
|
|
977
|
+
disabledMembers
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
return visible;
|
|
981
|
+
}
|
|
982
|
+
/**
|
|
962
983
|
* Sort a skill list in place-safe copy order: name ascending, added
|
|
963
984
|
* descending (newest first, unknown addedAt last), or uses descending
|
|
964
985
|
* (most-called first). Unknown values always trail.
|
|
@@ -1143,36 +1164,11 @@ window.__ModuleLoader__.load({
|
|
|
1143
1164
|
}
|
|
1144
1165
|
}
|
|
1145
1166
|
/**
|
|
1146
|
-
*
|
|
1147
|
-
*
|
|
1148
|
-
* @param
|
|
1149
|
-
* @
|
|
1150
|
-
|
|
1151
|
-
function dotIcon(color) {
|
|
1152
|
-
return (0, react.createElement)("span", {
|
|
1153
|
-
"aria-hidden": true,
|
|
1154
|
-
style: {
|
|
1155
|
-
display: "inline-block",
|
|
1156
|
-
width: 6,
|
|
1157
|
-
height: 6,
|
|
1158
|
-
borderRadius: 3,
|
|
1159
|
-
background: color,
|
|
1160
|
-
flex: "none"
|
|
1161
|
-
}
|
|
1162
|
-
});
|
|
1163
|
-
}
|
|
1164
|
-
/**
|
|
1165
|
-
* Wrap the core skill source so every menu row carries the invocation dot.
|
|
1166
|
-
* Exported for unit tests; production wiring goes through setupSkillSlashDots.
|
|
1167
|
-
* @param source - the registered `/skill` source.
|
|
1168
|
-
* @param api - hub browser API for the modelInvocable lookup.
|
|
1169
|
-
* @param scope - hub settings scope for the dot colors.
|
|
1170
|
-
* @returns a disposer restoring the original candidates.
|
|
1171
|
-
*/
|
|
1172
|
-
/**
|
|
1173
|
-
* DOM 兜底:在 alpha.2 新版 MenuView(icon 仅枚举)下通过直接操作
|
|
1174
|
-
* 已渲染的 `[role="option"]` 列表注入彩色点,绕过 `icon` 限制。
|
|
1175
|
-
* 旧版仍走 `icon` 注入,此处仅为新版。
|
|
1167
|
+
* 通过直接操作已渲染的 `[role="option"]` 列表注入彩色点,
|
|
1168
|
+
* 绕过 MenuView 对 `icon` 的枚举限制。
|
|
1169
|
+
* @param modelByName - 技能名 → 模型可调用性;不在表内的候选不注点。
|
|
1170
|
+
* @param modelColor - 模型可调用技能的点色。
|
|
1171
|
+
* @param userColor - 仅用户可调用技能的点色。
|
|
1176
1172
|
*/
|
|
1177
1173
|
function injectDotsViaDOM(modelByName, modelColor, userColor) {
|
|
1178
1174
|
if (typeof document === "undefined" || typeof requestAnimationFrame === "undefined") return;
|
|
@@ -1210,25 +1206,24 @@ window.__ModuleLoader__.load({
|
|
|
1210
1206
|
};
|
|
1211
1207
|
requestAnimationFrame(() => setTimeout(run, 0));
|
|
1212
1208
|
}
|
|
1209
|
+
/**
|
|
1210
|
+
* Wrap the core skill source so every menu row carries the invocation dot.
|
|
1211
|
+
* Exported for unit tests; production wiring goes through setupSkillSlashDots.
|
|
1212
|
+
* @param source - the registered `/skill` source.
|
|
1213
|
+
* @param api - hub browser API for the modelInvocable lookup.
|
|
1214
|
+
* @param scope - hub settings scope for the dot colors.
|
|
1215
|
+
* @returns a disposer restoring the original candidates.
|
|
1216
|
+
*/
|
|
1213
1217
|
function wrapSkillSource(source, api, scope) {
|
|
1214
1218
|
const original = source.candidates;
|
|
1215
1219
|
source.candidates = async (session, req) => {
|
|
1216
1220
|
const items = await original(session, req);
|
|
1217
1221
|
if (req.signal.aborted) return items;
|
|
1218
|
-
const isNewHost = req !== null && typeof req === "object" && "drilled" in req;
|
|
1219
1222
|
const modelByName = await modelInvocableMap(api);
|
|
1220
1223
|
if (req.signal.aborted) return items;
|
|
1221
1224
|
const snapshot = scope.getSnapshot();
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
if (isNewHost) {
|
|
1225
|
-
injectDotsViaDOM(modelByName, modelColor, userColor);
|
|
1226
|
-
return items;
|
|
1227
|
-
}
|
|
1228
|
-
return items.map((item) => ({
|
|
1229
|
-
...item,
|
|
1230
|
-
icon: dotIcon(modelByName.get(item.name) ?? true ? modelColor : userColor)
|
|
1231
|
-
}));
|
|
1225
|
+
injectDotsViaDOM(modelByName, snapshot.value?.dotModelColor ?? "#2f81f7", snapshot.value?.dotUserColor ?? "#3fb950");
|
|
1226
|
+
return items;
|
|
1232
1227
|
};
|
|
1233
1228
|
return () => {
|
|
1234
1229
|
source.candidates = original;
|
|
@@ -2564,8 +2559,10 @@ window.__ModuleLoader__.load({
|
|
|
2564
2559
|
* The dsh-skill-hub plugin settings card: bridges the hub's settings
|
|
2565
2560
|
* namespace (bound through the official settings transport) onto the
|
|
2566
2561
|
* family-style staged card form (enabled master switch + agent announcement).
|
|
2567
|
-
* Registered into
|
|
2568
|
-
*
|
|
2562
|
+
* Registered into `plugins.bundle.config` under the bundle's package name, so
|
|
2563
|
+
* the Plugins manager renders it on dsh-skill-hub's own page. That slot is
|
|
2564
|
+
* dispatched with `view: 'page'` only, so the card always renders its full
|
|
2565
|
+
* form (no `summary` one-liner variant).
|
|
2569
2566
|
*/
|
|
2570
2567
|
/** Bridges the hub's config scope onto the card's staged form. */
|
|
2571
2568
|
var SkillHubSettingsCardController = class {
|
|
@@ -3833,10 +3830,12 @@ window.__ModuleLoader__.load({
|
|
|
3833
3830
|
const [topOverKey, setTopOverKey] = (0, react.useState)(null);
|
|
3834
3831
|
/** 重复技能名集合:整表只建一次,行内用 has 取代逐行线性 includes。 */
|
|
3835
3832
|
const duplicateNames = (0, react.useMemo)(() => new Set(catalog?.duplicateNames ?? []), [catalog]);
|
|
3836
|
-
const
|
|
3833
|
+
const sourceFiltered = filterBySource(sorted, sourceFilter, origins);
|
|
3834
|
+
const projectSkillsAll = sourceFiltered.filter((skill) => isProjectSource(skill.source));
|
|
3837
3835
|
const hasProject = projectSkillsAll.length > 0;
|
|
3838
|
-
const
|
|
3839
|
-
const
|
|
3836
|
+
const visible = visibleCollections(groupsState?.collections ?? [], sourceFiltered, catalog?.disabled ?? [], normalized, sourceFilter, origins);
|
|
3837
|
+
const collections = visible.map((entry) => entry.collection);
|
|
3838
|
+
const uncategorized = sourceFiltered.filter((skill) => origins[skill.name] === void 0 && !isProjectSource(skill.source));
|
|
3840
3839
|
const personalDisabled = (catalog?.disabled ?? []).filter((record) => origins[record.name] === void 0).filter((record) => normalized.length === 0 || record.name.toLocaleLowerCase().includes(normalized) || record.description.toLocaleLowerCase().includes(normalized)).filter((record) => sourceFilter === "all" || sourceFilter === "private");
|
|
3841
3840
|
const allPersonalNames = [...uncategorized.map((s) => s.name), ...personalDisabled.map((r) => r.name)];
|
|
3842
3841
|
const hasPersonal = allPersonalNames.length > 0;
|
|
@@ -3895,7 +3894,7 @@ window.__ModuleLoader__.load({
|
|
|
3895
3894
|
openDetail: hub.openDetail,
|
|
3896
3895
|
requestDeleteSkill: hub.requestDeleteSkill
|
|
3897
3896
|
};
|
|
3898
|
-
if (skillView === "flat") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children:
|
|
3897
|
+
if (skillView === "flat") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: sourceFiltered.map((skill) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillRow, {
|
|
3899
3898
|
skill,
|
|
3900
3899
|
...rowProps
|
|
3901
3900
|
}, skill.name)) });
|
|
@@ -3933,10 +3932,9 @@ window.__ModuleLoader__.load({
|
|
|
3933
3932
|
}, "project");
|
|
3934
3933
|
if (topKey.startsWith("col:")) {
|
|
3935
3934
|
const colName = topKey.slice(4);
|
|
3936
|
-
const
|
|
3937
|
-
if (
|
|
3938
|
-
const
|
|
3939
|
-
const disabledMembers = (catalog?.disabled ?? []).filter((record) => collection.skillNames.includes(record.name) && (normalized.length === 0 || record.name.toLocaleLowerCase().includes(normalized) || record.description.toLocaleLowerCase().includes(normalized)) && (sourceFilter === "all" || (origins[record.name] ?? "private") === sourceFilter));
|
|
3935
|
+
const entry = visible.find((item) => item.collection.name === colName);
|
|
3936
|
+
if (entry === void 0) return null;
|
|
3937
|
+
const { collection, skills, disabledMembers } = entry;
|
|
3940
3938
|
const collapsed = collapsedGroups.has("col:" + collection.name);
|
|
3941
3939
|
const view = groupSwitchView(collection.skillNames, viewNames);
|
|
3942
3940
|
const check = sourceCheck[collection.name];
|
|
@@ -4407,6 +4405,23 @@ window.__ModuleLoader__.load({
|
|
|
4407
4405
|
function VersionChoiceDialog(props) {
|
|
4408
4406
|
const { choice, busy, onSelect, onCustom, onCancel, onConfirm } = props;
|
|
4409
4407
|
const effective = choice.custom.trim() !== "" ? choice.custom.trim() : choice.selected;
|
|
4408
|
+
const branchOptions = choice.branches.filter((branch) => !choice.releases.includes(branch));
|
|
4409
|
+
const [refGroup, setRefGroup] = (0, react.useState)(choice.releases.includes(choice.selected) || choice.branches.length === 0 ? "releases" : "branches");
|
|
4410
|
+
(0, react.useEffect)(() => {
|
|
4411
|
+
if (choice.loading) return;
|
|
4412
|
+
if (choice.releases.includes(choice.selected)) setRefGroup("releases");
|
|
4413
|
+
else if (branchOptions.includes(choice.selected)) setRefGroup("branches");
|
|
4414
|
+
}, [choice.loading, choice.repo]);
|
|
4415
|
+
const activeRefs = refGroup === "releases" ? choice.releases : branchOptions;
|
|
4416
|
+
const listedRefs = [...choice.releases, ...branchOptions];
|
|
4417
|
+
const currentUnlistedRef = choice.selected !== "" && !listedRefs.includes(choice.selected) ? choice.selected : void 0;
|
|
4418
|
+
const selectRefs = currentUnlistedRef !== void 0 ? [currentUnlistedRef, ...activeRefs] : activeRefs;
|
|
4419
|
+
const selectValue = selectRefs.includes(choice.selected) ? choice.selected : selectRefs[0] ?? "";
|
|
4420
|
+
const selectGroup = (next) => {
|
|
4421
|
+
setRefGroup(next);
|
|
4422
|
+
const nextRefs = next === "releases" ? choice.releases : branchOptions;
|
|
4423
|
+
if (nextRefs.length > 0 && !nextRefs.includes(choice.selected)) onSelect(nextRefs[0]);
|
|
4424
|
+
};
|
|
4410
4425
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(DialogShell, {
|
|
4411
4426
|
onClose: onCancel,
|
|
4412
4427
|
children: [
|
|
@@ -4422,38 +4437,42 @@ window.__ModuleLoader__.load({
|
|
|
4422
4437
|
className: panel_module_css_default.dialogText,
|
|
4423
4438
|
children: tt("market.scanning")
|
|
4424
4439
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
4425
|
-
|
|
4426
|
-
className: panel_module_css_default.
|
|
4427
|
-
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
children:
|
|
4439
|
-
},
|
|
4440
|
-
|
|
4441
|
-
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4440
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4441
|
+
className: panel_module_css_default.segmented,
|
|
4442
|
+
role: "group",
|
|
4443
|
+
"aria-label": tt("market.versionTitle"),
|
|
4444
|
+
style: { marginBottom: 8 },
|
|
4445
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4446
|
+
type: "button",
|
|
4447
|
+
className: panel_module_css_default.segBtn + (refGroup === "releases" ? " " + panel_module_css_default.segBtnActive : ""),
|
|
4448
|
+
disabled: choice.releases.length === 0,
|
|
4449
|
+
"aria-pressed": refGroup === "releases",
|
|
4450
|
+
onClick: () => {
|
|
4451
|
+
selectGroup("releases");
|
|
4452
|
+
},
|
|
4453
|
+
children: tt("market.versionReleases")
|
|
4454
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4455
|
+
type: "button",
|
|
4456
|
+
className: panel_module_css_default.segBtn + (refGroup === "branches" ? " " + panel_module_css_default.segBtnActive : ""),
|
|
4457
|
+
disabled: branchOptions.length === 0,
|
|
4458
|
+
"aria-pressed": refGroup === "branches",
|
|
4459
|
+
onClick: () => {
|
|
4460
|
+
selectGroup("branches");
|
|
4461
|
+
},
|
|
4462
|
+
children: tt("market.versionBranches")
|
|
4463
|
+
})]
|
|
4464
|
+
}),
|
|
4465
|
+
selectValue !== "" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("select", {
|
|
4446
4466
|
className: panel_module_css_default.select + " " + panel_module_css_default.dialogSelect,
|
|
4447
|
-
value:
|
|
4467
|
+
value: selectValue,
|
|
4448
4468
|
onChange: (event) => {
|
|
4449
4469
|
onSelect(event.target.value);
|
|
4450
|
-
onCustom("");
|
|
4451
4470
|
},
|
|
4452
|
-
children:
|
|
4453
|
-
value:
|
|
4454
|
-
children:
|
|
4455
|
-
},
|
|
4456
|
-
})
|
|
4471
|
+
children: selectRefs.map((ref) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
4472
|
+
value: ref,
|
|
4473
|
+
children: ref
|
|
4474
|
+
}, ref))
|
|
4475
|
+
}) : null,
|
|
4457
4476
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4458
4477
|
className: panel_module_css_default.dialogText,
|
|
4459
4478
|
style: { marginBottom: 4 },
|
|
@@ -5322,14 +5341,15 @@ window.__ModuleLoader__.load({
|
|
|
5322
5341
|
choice: versionDialog,
|
|
5323
5342
|
busy: versionBusy,
|
|
5324
5343
|
onSelect: (selected) => {
|
|
5325
|
-
setVersionDialog({
|
|
5326
|
-
...
|
|
5327
|
-
selected
|
|
5344
|
+
setVersionDialog((previous) => previous === null ? previous : {
|
|
5345
|
+
...previous,
|
|
5346
|
+
selected,
|
|
5347
|
+
custom: ""
|
|
5328
5348
|
});
|
|
5329
5349
|
},
|
|
5330
5350
|
onCustom: (custom) => {
|
|
5331
|
-
setVersionDialog({
|
|
5332
|
-
...
|
|
5351
|
+
setVersionDialog((previous) => previous === null ? previous : {
|
|
5352
|
+
...previous,
|
|
5333
5353
|
custom
|
|
5334
5354
|
});
|
|
5335
5355
|
},
|
|
@@ -7570,12 +7590,12 @@ window.__ModuleLoader__.load({
|
|
|
7570
7590
|
const scope = ctx.settingsScope.bind({ namespace: NS });
|
|
7571
7591
|
const settingsCard = new SkillHubSettingsCardController(scope);
|
|
7572
7592
|
ctx.effect(() => setupSkillSlashDots(ctx, api, scope), "dsh-skill-hub: slash dots");
|
|
7573
|
-
ctx.effect(() => ctx.slots.inject("
|
|
7574
|
-
name: "
|
|
7575
|
-
key:
|
|
7593
|
+
ctx.effect(() => ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({
|
|
7594
|
+
name: "plugins.bundle.config",
|
|
7595
|
+
key: "dsh-skill-hub",
|
|
7576
7596
|
locale: NS,
|
|
7577
7597
|
inject: () => settingsCard.inject()
|
|
7578
|
-
}, SkillHubSettingsCard)), "dsh-skill-hub:
|
|
7598
|
+
}, SkillHubSettingsCard)), "dsh-skill-hub: plugin config");
|
|
7579
7599
|
ctx.effect(() => ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
7580
7600
|
name: "settings.section",
|
|
7581
7601
|
id: "skill-hub",
|