dsh-skill-hub 0.2.6 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -229
- package/README.zh.md +67 -203
- package/lib/client.js +1955 -413
- package/lib/client.js.map +1 -1
- package/lib/index.js +441 -84
- package/lib/types/client/SkillHubSettingsCard.d.ts +1 -1
- package/lib/types/client/api.d.ts +12 -2
- package/lib/types/client/index.d.ts +6 -1
- package/lib/types/client/locales.d.ts +13 -0
- package/lib/types/client/panel/ScenesView.d.ts +1 -1
- package/lib/types/client/panel/SourcesView.d.ts +1 -1
- package/lib/types/client/panel/useSkillHub.d.ts +20 -2
- package/lib/types/client/settings-form.d.ts +1 -1
- package/lib/types/client/slash-dots.d.ts +2 -1
- package/lib/types/index.d.ts +2 -1
- package/lib/types/protocol.d.ts +93 -6
- package/lib/types/repo.d.ts +18 -11
- package/lib/types/store.d.ts +14 -2
- package/package.json +38 -29
- package/src/client/SkillHubSettingsCard.tsx +1 -1
- package/src/client/api.ts +61 -1
- package/src/client/index.tsx +10 -1
- package/src/client/locales.ts +26 -0
- package/src/client/panel/MarketView.tsx +201 -47
- package/src/client/panel/ScenesView.tsx +27 -2
- package/src/client/panel/SkillHubPanel.tsx +18 -19
- package/src/client/panel/SkillRow.tsx +8 -8
- package/src/client/panel/SourcesView.tsx +203 -26
- package/src/client/panel/panel.module.css +17 -6
- package/src/client/panel/useSkillHub.ts +219 -12
- package/src/client/settings-form.ts +2 -1
- package/src/client/slash-dots.test.ts +24 -6
- package/src/client/slash-dots.tsx +10 -1
- package/src/index.ts +20 -2
- package/src/protocol.ts +89 -6
- package/src/repo.test.ts +61 -19
- package/src/repo.ts +86 -23
- package/src/routes.test.ts +18 -4
- package/src/routes.ts +269 -47
- package/src/skillfs.ts +17 -4
- package/src/store.ts +72 -4
package/lib/client.js
CHANGED
|
@@ -6,7 +6,6 @@ window.__ModuleLoader__.load({
|
|
|
6
6
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
7
|
let react = require("react");
|
|
8
8
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
9
|
-
let _deepseek_ai_dsh_client_runtime_client = require("@deepseek-ai/dsh-client-runtime/client");
|
|
10
9
|
//#region src/protocol.ts
|
|
11
10
|
/**
|
|
12
11
|
* Shared API contract for dsh-skill-hub: the route paths and payload shapes
|
|
@@ -32,11 +31,16 @@ window.__ModuleLoader__.load({
|
|
|
32
31
|
marketSync: "/api/skill-hub/market/source/sync",
|
|
33
32
|
repo: "/api/skill-hub/repo",
|
|
34
33
|
repoImport: "/api/skill-hub/repo/import",
|
|
34
|
+
repoImportProgress: "/api/skill-hub/repo/import/progress",
|
|
35
|
+
repoImportCancel: "/api/skill-hub/repo/import/cancel",
|
|
35
36
|
update: "/api/skill-hub/update",
|
|
36
37
|
groups: "/api/skill-hub/groups",
|
|
37
38
|
tag: "/api/skill-hub/tag",
|
|
38
39
|
tagDelete: "/api/skill-hub/tag/delete",
|
|
39
40
|
tagMembers: "/api/skill-hub/tag/members",
|
|
41
|
+
tagReorder: "/api/skill-hub/tag/reorder",
|
|
42
|
+
collectionReorder: "/api/skill-hub/collections/reorder",
|
|
43
|
+
sourceGroupReorder: "/api/skill-hub/source-groups/reorder",
|
|
40
44
|
sources: "/api/skill-hub/sources",
|
|
41
45
|
sourceCheck: "/api/skill-hub/sources/check",
|
|
42
46
|
sourceSync: "/api/skill-hub/sources/sync",
|
|
@@ -188,7 +192,7 @@ window.__ModuleLoader__.load({
|
|
|
188
192
|
async repoDiscover(repo) {
|
|
189
193
|
return readJson(await fetchWithTimeout(SKILL_HUB_API.repo + "?repo=" + encodeURIComponent(repo)));
|
|
190
194
|
}
|
|
191
|
-
/** Install selected repo skills into the user-dsh root (records the source). */
|
|
195
|
+
/** Install selected repo skills into the user-dsh root (records the source). Returns jobId for polling. */
|
|
192
196
|
async repoImport(repo, paths, ref) {
|
|
193
197
|
const payload = {
|
|
194
198
|
repo,
|
|
@@ -201,6 +205,18 @@ window.__ModuleLoader__.load({
|
|
|
201
205
|
body: JSON.stringify(payload)
|
|
202
206
|
}));
|
|
203
207
|
}
|
|
208
|
+
/** Poll import job progress (B方案轮询) */
|
|
209
|
+
async repoImportProgress(jobId) {
|
|
210
|
+
return readJson(await fetchWithTimeout(SKILL_HUB_API.repoImportProgress + "?jobId=" + encodeURIComponent(jobId)));
|
|
211
|
+
}
|
|
212
|
+
/** Cancel a running import job (选项2:唯有取消才停) */
|
|
213
|
+
async repoImportCancel(jobId) {
|
|
214
|
+
return readJson(await fetchWithTimeout(SKILL_HUB_API.repoImportCancel, {
|
|
215
|
+
method: "POST",
|
|
216
|
+
headers: { "content-type": "application/json" },
|
|
217
|
+
body: JSON.stringify({ jobId })
|
|
218
|
+
}));
|
|
219
|
+
}
|
|
204
220
|
/** Read the hub's runtime config (effective values + saved overrides). */
|
|
205
221
|
async config() {
|
|
206
222
|
return readJson(await fetchWithTimeout(SKILL_HUB_API.config));
|
|
@@ -244,6 +260,30 @@ window.__ModuleLoader__.load({
|
|
|
244
260
|
})
|
|
245
261
|
}))).tags;
|
|
246
262
|
}
|
|
263
|
+
/** 拖拽重排场景分组 */
|
|
264
|
+
async reorderTags(orderedIds) {
|
|
265
|
+
return (await readJson(await fetchWithTimeout(SKILL_HUB_API.tagReorder, {
|
|
266
|
+
method: "POST",
|
|
267
|
+
headers: { "content-type": "application/json" },
|
|
268
|
+
body: JSON.stringify({ orderedIds })
|
|
269
|
+
}))).tags;
|
|
270
|
+
}
|
|
271
|
+
/** 拖拽重排来源集合 */
|
|
272
|
+
async reorderCollections(orderedNames) {
|
|
273
|
+
return (await readJson(await fetchWithTimeout(SKILL_HUB_API.collectionReorder, {
|
|
274
|
+
method: "POST",
|
|
275
|
+
headers: { "content-type": "application/json" },
|
|
276
|
+
body: JSON.stringify({ orderedNames })
|
|
277
|
+
}))).collections;
|
|
278
|
+
}
|
|
279
|
+
/** 拖拽重排来源顶层分组(project / collections / personal) */
|
|
280
|
+
async reorderSourceGroups(orderedKeys) {
|
|
281
|
+
return (await readJson(await fetchWithTimeout(SKILL_HUB_API.sourceGroupReorder, {
|
|
282
|
+
method: "POST",
|
|
283
|
+
headers: { "content-type": "application/json" },
|
|
284
|
+
body: JSON.stringify({ orderedKeys })
|
|
285
|
+
}))).order;
|
|
286
|
+
}
|
|
247
287
|
/** 来源列表 + 派生 origin 映射 + 集合组 + 回收站。 */
|
|
248
288
|
async sources() {
|
|
249
289
|
return readJson(await fetchWithTimeout(SKILL_HUB_API.sources));
|
|
@@ -351,6 +391,11 @@ window.__ModuleLoader__.load({
|
|
|
351
391
|
"market.scan": "扫描",
|
|
352
392
|
"market.scanning": "扫描中…",
|
|
353
393
|
"market.check": "检查",
|
|
394
|
+
"market.checkUpdate": "检查更新",
|
|
395
|
+
"market.checking": "检查中…",
|
|
396
|
+
"market.upToDate": "已是最新",
|
|
397
|
+
"market.update": "更新",
|
|
398
|
+
"market.updateCount": "更新 {count} 个",
|
|
354
399
|
"market.sync": "同步",
|
|
355
400
|
"market.syncing": "同步中…",
|
|
356
401
|
"market.updated": "有更新",
|
|
@@ -374,8 +419,12 @@ window.__ModuleLoader__.load({
|
|
|
374
419
|
"repo.imported": "导入成功 {count} 个",
|
|
375
420
|
"repo.skippedExisting": "跳过已存在 {count} 个",
|
|
376
421
|
"repo.failed": "失败 {count} 个",
|
|
422
|
+
"repo.cancel": "取消",
|
|
423
|
+
"repo.cancelled": "已取消 · 已导入 {imported}/{total} 已保留,临时文件已清理",
|
|
424
|
+
"repo.importingCurrent": "正在导入: {name} ({done}/{total})",
|
|
377
425
|
"repo.root.skills": "技能 skills",
|
|
378
426
|
"repo.root.designTemplates": "模板 design-templates",
|
|
427
|
+
"repo.root.generic": "{root}",
|
|
379
428
|
"update.check": "检查更新",
|
|
380
429
|
"update.checking": "检查中…",
|
|
381
430
|
"update.upToDate": "已是最新版本 v{version}",
|
|
@@ -462,6 +511,10 @@ window.__ModuleLoader__.load({
|
|
|
462
511
|
"source.followDelete": "跟进删除",
|
|
463
512
|
"source.deleteConfirmTitle": "跟进上游删除?",
|
|
464
513
|
"source.deleteConfirmText": "以下技能已从上游仓库移除,本地目录将移入回收站(可恢复):",
|
|
514
|
+
"source.deleteGroup": "删除整组",
|
|
515
|
+
"source.deleteGroupHint": "删除该分组下的全部 {count} 个技能",
|
|
516
|
+
"source.deleteGroupTitle": "删除整组?",
|
|
517
|
+
"source.deleteGroupText": "将把分组“{name}”下的全部 {count} 个技能移入回收站(可恢复),只读技能会跳过:",
|
|
465
518
|
"source.trash": "回收站",
|
|
466
519
|
"source.restore": "恢复",
|
|
467
520
|
"source.clearTrash": "清空回收站",
|
|
@@ -563,6 +616,11 @@ window.__ModuleLoader__.load({
|
|
|
563
616
|
"market.scan": "Scan",
|
|
564
617
|
"market.scanning": "Scanning…",
|
|
565
618
|
"market.check": "Check",
|
|
619
|
+
"market.checkUpdate": "Check for updates",
|
|
620
|
+
"market.checking": "Checking…",
|
|
621
|
+
"market.upToDate": "Up to date",
|
|
622
|
+
"market.update": "Update",
|
|
623
|
+
"market.updateCount": "Update {count}",
|
|
566
624
|
"market.sync": "Sync",
|
|
567
625
|
"market.syncing": "Syncing…",
|
|
568
626
|
"market.updated": "Updates available",
|
|
@@ -586,8 +644,12 @@ window.__ModuleLoader__.load({
|
|
|
586
644
|
"repo.imported": "Imported {count}",
|
|
587
645
|
"repo.skippedExisting": "Skipped existing {count}",
|
|
588
646
|
"repo.failed": "Failed {count}",
|
|
647
|
+
"repo.cancel": "Cancel",
|
|
648
|
+
"repo.cancelled": "Cancelled · {imported}/{total} kept, temp files cleaned",
|
|
649
|
+
"repo.importingCurrent": "Importing: {name} ({done}/{total})",
|
|
589
650
|
"repo.root.skills": "Skills",
|
|
590
651
|
"repo.root.designTemplates": "Templates design-templates",
|
|
652
|
+
"repo.root.generic": "{root}",
|
|
591
653
|
"update.check": "Check for updates",
|
|
592
654
|
"update.checking": "Checking…",
|
|
593
655
|
"update.upToDate": "Up to date: v{version}",
|
|
@@ -674,6 +736,10 @@ window.__ModuleLoader__.load({
|
|
|
674
736
|
"source.followDelete": "Follow deletion",
|
|
675
737
|
"source.deleteConfirmTitle": "Follow upstream deletion?",
|
|
676
738
|
"source.deleteConfirmText": "These skills were removed upstream; their local directories move to trash (restorable):",
|
|
739
|
+
"source.deleteGroup": "Delete group",
|
|
740
|
+
"source.deleteGroupHint": "Delete all {count} skills in this group",
|
|
741
|
+
"source.deleteGroupTitle": "Delete group?",
|
|
742
|
+
"source.deleteGroupText": "All {count} skills in group \"{name}\" will be moved to trash (restorable); read-only skills will be skipped:",
|
|
677
743
|
"source.trash": "Trash",
|
|
678
744
|
"source.restore": "Restore",
|
|
679
745
|
"source.clearTrash": "Empty trash",
|
|
@@ -1005,6 +1071,7 @@ window.__ModuleLoader__.load({
|
|
|
1005
1071
|
source.candidates = async (session, req) => {
|
|
1006
1072
|
const items = await original(session, req);
|
|
1007
1073
|
if (req.signal.aborted) return items;
|
|
1074
|
+
if (req !== null && typeof req === "object" && "drilled" in req) return items;
|
|
1008
1075
|
const modelByName = await modelInvocableMap(api);
|
|
1009
1076
|
if (req.signal.aborted) return items;
|
|
1010
1077
|
const snapshot = scope.getSnapshot();
|
|
@@ -1399,6 +1466,706 @@ window.__ModuleLoader__.load({
|
|
|
1399
1466
|
});
|
|
1400
1467
|
}
|
|
1401
1468
|
//#endregion
|
|
1469
|
+
//#region node_modules/zustand/esm/vanilla.mjs
|
|
1470
|
+
const createStoreImpl = (createState) => {
|
|
1471
|
+
let state;
|
|
1472
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
1473
|
+
const setState = (partial, replace) => {
|
|
1474
|
+
const nextState = typeof partial === "function" ? partial(state) : partial;
|
|
1475
|
+
if (!Object.is(nextState, state)) {
|
|
1476
|
+
const previousState = state;
|
|
1477
|
+
state = (replace != null ? replace : typeof nextState !== "object" || nextState === null) ? nextState : Object.assign({}, state, nextState);
|
|
1478
|
+
listeners.forEach((listener) => listener(state, previousState));
|
|
1479
|
+
}
|
|
1480
|
+
};
|
|
1481
|
+
const getState = () => state;
|
|
1482
|
+
const subscribe = (listener) => {
|
|
1483
|
+
listeners.add(listener);
|
|
1484
|
+
return () => listeners.delete(listener);
|
|
1485
|
+
};
|
|
1486
|
+
const destroy = () => {
|
|
1487
|
+
listeners.clear();
|
|
1488
|
+
};
|
|
1489
|
+
const api = {
|
|
1490
|
+
setState,
|
|
1491
|
+
getState,
|
|
1492
|
+
subscribe,
|
|
1493
|
+
destroy
|
|
1494
|
+
};
|
|
1495
|
+
state = createState(setState, getState, api);
|
|
1496
|
+
return api;
|
|
1497
|
+
};
|
|
1498
|
+
const createStore = (createState) => createState ? createStoreImpl(createState) : createStoreImpl;
|
|
1499
|
+
//#endregion
|
|
1500
|
+
//#region node_modules/zustand/esm/middleware.mjs
|
|
1501
|
+
const subscribeWithSelectorImpl = (fn) => (set, get, api) => {
|
|
1502
|
+
const origSubscribe = api.subscribe;
|
|
1503
|
+
api.subscribe = (selector, optListener, options) => {
|
|
1504
|
+
let listener = selector;
|
|
1505
|
+
if (optListener) {
|
|
1506
|
+
const equalityFn = (options == null ? void 0 : options.equalityFn) || Object.is;
|
|
1507
|
+
let currentSlice = selector(api.getState());
|
|
1508
|
+
listener = (state) => {
|
|
1509
|
+
const nextSlice = selector(state);
|
|
1510
|
+
if (!equalityFn(currentSlice, nextSlice)) {
|
|
1511
|
+
const previousSlice = currentSlice;
|
|
1512
|
+
optListener(currentSlice = nextSlice, previousSlice);
|
|
1513
|
+
}
|
|
1514
|
+
};
|
|
1515
|
+
if (options == null ? void 0 : options.fireImmediately) optListener(currentSlice, currentSlice);
|
|
1516
|
+
}
|
|
1517
|
+
return origSubscribe(listener);
|
|
1518
|
+
};
|
|
1519
|
+
return fn(set, get, api);
|
|
1520
|
+
};
|
|
1521
|
+
const subscribeWithSelector = subscribeWithSelectorImpl;
|
|
1522
|
+
//#endregion
|
|
1523
|
+
//#region node_modules/immer/dist/immer.mjs
|
|
1524
|
+
var NOTHING = Symbol.for("immer-nothing");
|
|
1525
|
+
var DRAFTABLE = Symbol.for("immer-draftable");
|
|
1526
|
+
var DRAFT_STATE = Symbol.for("immer-state");
|
|
1527
|
+
function die(error, ...args) {
|
|
1528
|
+
throw new Error(`[Immer] minified error nr: ${error}. Full error at: https://bit.ly/3cXEKWf`);
|
|
1529
|
+
}
|
|
1530
|
+
var getPrototypeOf = Object.getPrototypeOf;
|
|
1531
|
+
function isDraft(value) {
|
|
1532
|
+
return !!value && !!value[DRAFT_STATE];
|
|
1533
|
+
}
|
|
1534
|
+
function isDraftable(value) {
|
|
1535
|
+
if (!value) return false;
|
|
1536
|
+
return isPlainObject(value) || Array.isArray(value) || !!value[DRAFTABLE] || !!value.constructor?.[DRAFTABLE] || isMap(value) || isSet(value);
|
|
1537
|
+
}
|
|
1538
|
+
var objectCtorString = Object.prototype.constructor.toString();
|
|
1539
|
+
var cachedCtorStrings = /* @__PURE__ */ new WeakMap();
|
|
1540
|
+
function isPlainObject(value) {
|
|
1541
|
+
if (!value || typeof value !== "object") return false;
|
|
1542
|
+
const proto = Object.getPrototypeOf(value);
|
|
1543
|
+
if (proto === null || proto === Object.prototype) return true;
|
|
1544
|
+
const Ctor = Object.hasOwnProperty.call(proto, "constructor") && proto.constructor;
|
|
1545
|
+
if (Ctor === Object) return true;
|
|
1546
|
+
if (typeof Ctor !== "function") return false;
|
|
1547
|
+
let ctorString = cachedCtorStrings.get(Ctor);
|
|
1548
|
+
if (ctorString === void 0) {
|
|
1549
|
+
ctorString = Function.toString.call(Ctor);
|
|
1550
|
+
cachedCtorStrings.set(Ctor, ctorString);
|
|
1551
|
+
}
|
|
1552
|
+
return ctorString === objectCtorString;
|
|
1553
|
+
}
|
|
1554
|
+
function each(obj, iter, strict = true) {
|
|
1555
|
+
if (getArchtype(obj) === 0) (strict ? Reflect.ownKeys(obj) : Object.keys(obj)).forEach((key) => {
|
|
1556
|
+
iter(key, obj[key], obj);
|
|
1557
|
+
});
|
|
1558
|
+
else obj.forEach((entry, index) => iter(index, entry, obj));
|
|
1559
|
+
}
|
|
1560
|
+
function getArchtype(thing) {
|
|
1561
|
+
const state = thing[DRAFT_STATE];
|
|
1562
|
+
return state ? state.type_ : Array.isArray(thing) ? 1 : isMap(thing) ? 2 : isSet(thing) ? 3 : 0;
|
|
1563
|
+
}
|
|
1564
|
+
function has(thing, prop) {
|
|
1565
|
+
return getArchtype(thing) === 2 ? thing.has(prop) : Object.prototype.hasOwnProperty.call(thing, prop);
|
|
1566
|
+
}
|
|
1567
|
+
function set(thing, propOrOldValue, value) {
|
|
1568
|
+
const t = getArchtype(thing);
|
|
1569
|
+
if (t === 2) thing.set(propOrOldValue, value);
|
|
1570
|
+
else if (t === 3) thing.add(value);
|
|
1571
|
+
else thing[propOrOldValue] = value;
|
|
1572
|
+
}
|
|
1573
|
+
function is(x, y) {
|
|
1574
|
+
if (x === y) return x !== 0 || 1 / x === 1 / y;
|
|
1575
|
+
else return x !== x && y !== y;
|
|
1576
|
+
}
|
|
1577
|
+
function isMap(target) {
|
|
1578
|
+
return target instanceof Map;
|
|
1579
|
+
}
|
|
1580
|
+
function isSet(target) {
|
|
1581
|
+
return target instanceof Set;
|
|
1582
|
+
}
|
|
1583
|
+
function latest(state) {
|
|
1584
|
+
return state.copy_ || state.base_;
|
|
1585
|
+
}
|
|
1586
|
+
function shallowCopy(base, strict) {
|
|
1587
|
+
if (isMap(base)) return new Map(base);
|
|
1588
|
+
if (isSet(base)) return new Set(base);
|
|
1589
|
+
if (Array.isArray(base)) return Array.prototype.slice.call(base);
|
|
1590
|
+
const isPlain = isPlainObject(base);
|
|
1591
|
+
if (strict === true || strict === "class_only" && !isPlain) {
|
|
1592
|
+
const descriptors = Object.getOwnPropertyDescriptors(base);
|
|
1593
|
+
delete descriptors[DRAFT_STATE];
|
|
1594
|
+
let keys = Reflect.ownKeys(descriptors);
|
|
1595
|
+
for (let i = 0; i < keys.length; i++) {
|
|
1596
|
+
const key = keys[i];
|
|
1597
|
+
const desc = descriptors[key];
|
|
1598
|
+
if (desc.writable === false) {
|
|
1599
|
+
desc.writable = true;
|
|
1600
|
+
desc.configurable = true;
|
|
1601
|
+
}
|
|
1602
|
+
if (desc.get || desc.set) descriptors[key] = {
|
|
1603
|
+
configurable: true,
|
|
1604
|
+
writable: true,
|
|
1605
|
+
enumerable: desc.enumerable,
|
|
1606
|
+
value: base[key]
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
return Object.create(getPrototypeOf(base), descriptors);
|
|
1610
|
+
} else {
|
|
1611
|
+
const proto = getPrototypeOf(base);
|
|
1612
|
+
if (proto !== null && isPlain) return { ...base };
|
|
1613
|
+
const obj = Object.create(proto);
|
|
1614
|
+
return Object.assign(obj, base);
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
function freeze(obj, deep = false) {
|
|
1618
|
+
if (isFrozen(obj) || isDraft(obj) || !isDraftable(obj)) return obj;
|
|
1619
|
+
if (getArchtype(obj) > 1) Object.defineProperties(obj, {
|
|
1620
|
+
set: dontMutateMethodOverride,
|
|
1621
|
+
add: dontMutateMethodOverride,
|
|
1622
|
+
clear: dontMutateMethodOverride,
|
|
1623
|
+
delete: dontMutateMethodOverride
|
|
1624
|
+
});
|
|
1625
|
+
Object.freeze(obj);
|
|
1626
|
+
if (deep) Object.values(obj).forEach((value) => freeze(value, true));
|
|
1627
|
+
return obj;
|
|
1628
|
+
}
|
|
1629
|
+
function dontMutateFrozenCollections() {
|
|
1630
|
+
die(2);
|
|
1631
|
+
}
|
|
1632
|
+
var dontMutateMethodOverride = { value: dontMutateFrozenCollections };
|
|
1633
|
+
function isFrozen(obj) {
|
|
1634
|
+
if (obj === null || typeof obj !== "object") return true;
|
|
1635
|
+
return Object.isFrozen(obj);
|
|
1636
|
+
}
|
|
1637
|
+
var plugins = {};
|
|
1638
|
+
function getPlugin(pluginKey) {
|
|
1639
|
+
const plugin = plugins[pluginKey];
|
|
1640
|
+
if (!plugin) die(0, pluginKey);
|
|
1641
|
+
return plugin;
|
|
1642
|
+
}
|
|
1643
|
+
var currentScope;
|
|
1644
|
+
function getCurrentScope() {
|
|
1645
|
+
return currentScope;
|
|
1646
|
+
}
|
|
1647
|
+
function createScope(parent_, immer_) {
|
|
1648
|
+
return {
|
|
1649
|
+
drafts_: [],
|
|
1650
|
+
parent_,
|
|
1651
|
+
immer_,
|
|
1652
|
+
canAutoFreeze_: true,
|
|
1653
|
+
unfinalizedDrafts_: 0
|
|
1654
|
+
};
|
|
1655
|
+
}
|
|
1656
|
+
function usePatchesInScope(scope, patchListener) {
|
|
1657
|
+
if (patchListener) {
|
|
1658
|
+
getPlugin("Patches");
|
|
1659
|
+
scope.patches_ = [];
|
|
1660
|
+
scope.inversePatches_ = [];
|
|
1661
|
+
scope.patchListener_ = patchListener;
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
function revokeScope(scope) {
|
|
1665
|
+
leaveScope(scope);
|
|
1666
|
+
scope.drafts_.forEach(revokeDraft);
|
|
1667
|
+
scope.drafts_ = null;
|
|
1668
|
+
}
|
|
1669
|
+
function leaveScope(scope) {
|
|
1670
|
+
if (scope === currentScope) currentScope = scope.parent_;
|
|
1671
|
+
}
|
|
1672
|
+
function enterScope(immer2) {
|
|
1673
|
+
return currentScope = createScope(currentScope, immer2);
|
|
1674
|
+
}
|
|
1675
|
+
function revokeDraft(draft) {
|
|
1676
|
+
const state = draft[DRAFT_STATE];
|
|
1677
|
+
if (state.type_ === 0 || state.type_ === 1) state.revoke_();
|
|
1678
|
+
else state.revoked_ = true;
|
|
1679
|
+
}
|
|
1680
|
+
function processResult(result, scope) {
|
|
1681
|
+
scope.unfinalizedDrafts_ = scope.drafts_.length;
|
|
1682
|
+
const baseDraft = scope.drafts_[0];
|
|
1683
|
+
if (result !== void 0 && result !== baseDraft) {
|
|
1684
|
+
if (baseDraft[DRAFT_STATE].modified_) {
|
|
1685
|
+
revokeScope(scope);
|
|
1686
|
+
die(4);
|
|
1687
|
+
}
|
|
1688
|
+
if (isDraftable(result)) {
|
|
1689
|
+
result = finalize(scope, result);
|
|
1690
|
+
if (!scope.parent_) maybeFreeze(scope, result);
|
|
1691
|
+
}
|
|
1692
|
+
if (scope.patches_) getPlugin("Patches").generateReplacementPatches_(baseDraft[DRAFT_STATE].base_, result, scope.patches_, scope.inversePatches_);
|
|
1693
|
+
} else result = finalize(scope, baseDraft, []);
|
|
1694
|
+
revokeScope(scope);
|
|
1695
|
+
if (scope.patches_) scope.patchListener_(scope.patches_, scope.inversePatches_);
|
|
1696
|
+
return result !== NOTHING ? result : void 0;
|
|
1697
|
+
}
|
|
1698
|
+
function finalize(rootScope, value, path) {
|
|
1699
|
+
if (isFrozen(value)) return value;
|
|
1700
|
+
const useStrictIteration = rootScope.immer_.shouldUseStrictIteration();
|
|
1701
|
+
const state = value[DRAFT_STATE];
|
|
1702
|
+
if (!state) {
|
|
1703
|
+
each(value, (key, childValue) => finalizeProperty(rootScope, state, value, key, childValue, path), useStrictIteration);
|
|
1704
|
+
return value;
|
|
1705
|
+
}
|
|
1706
|
+
if (state.scope_ !== rootScope) return value;
|
|
1707
|
+
if (!state.modified_) {
|
|
1708
|
+
maybeFreeze(rootScope, state.base_, true);
|
|
1709
|
+
return state.base_;
|
|
1710
|
+
}
|
|
1711
|
+
if (!state.finalized_) {
|
|
1712
|
+
state.finalized_ = true;
|
|
1713
|
+
state.scope_.unfinalizedDrafts_--;
|
|
1714
|
+
const result = state.copy_;
|
|
1715
|
+
let resultEach = result;
|
|
1716
|
+
let isSet2 = false;
|
|
1717
|
+
if (state.type_ === 3) {
|
|
1718
|
+
resultEach = new Set(result);
|
|
1719
|
+
result.clear();
|
|
1720
|
+
isSet2 = true;
|
|
1721
|
+
}
|
|
1722
|
+
each(resultEach, (key, childValue) => finalizeProperty(rootScope, state, result, key, childValue, path, isSet2), useStrictIteration);
|
|
1723
|
+
maybeFreeze(rootScope, result, false);
|
|
1724
|
+
if (path && rootScope.patches_) getPlugin("Patches").generatePatches_(state, path, rootScope.patches_, rootScope.inversePatches_);
|
|
1725
|
+
}
|
|
1726
|
+
return state.copy_;
|
|
1727
|
+
}
|
|
1728
|
+
function finalizeProperty(rootScope, parentState, targetObject, prop, childValue, rootPath, targetIsSet) {
|
|
1729
|
+
if (childValue == null) return;
|
|
1730
|
+
if (typeof childValue !== "object" && !targetIsSet) return;
|
|
1731
|
+
const childIsFrozen = isFrozen(childValue);
|
|
1732
|
+
if (childIsFrozen && !targetIsSet) return;
|
|
1733
|
+
if (isDraft(childValue)) {
|
|
1734
|
+
const res = finalize(rootScope, childValue, rootPath && parentState && parentState.type_ !== 3 && !has(parentState.assigned_, prop) ? rootPath.concat(prop) : void 0);
|
|
1735
|
+
set(targetObject, prop, res);
|
|
1736
|
+
if (isDraft(res)) rootScope.canAutoFreeze_ = false;
|
|
1737
|
+
else return;
|
|
1738
|
+
} else if (targetIsSet) targetObject.add(childValue);
|
|
1739
|
+
if (isDraftable(childValue) && !childIsFrozen) {
|
|
1740
|
+
if (!rootScope.immer_.autoFreeze_ && rootScope.unfinalizedDrafts_ < 1) return;
|
|
1741
|
+
if (parentState && parentState.base_ && parentState.base_[prop] === childValue && childIsFrozen) return;
|
|
1742
|
+
finalize(rootScope, childValue);
|
|
1743
|
+
if ((!parentState || !parentState.scope_.parent_) && typeof prop !== "symbol" && (isMap(targetObject) ? targetObject.has(prop) : Object.prototype.propertyIsEnumerable.call(targetObject, prop))) maybeFreeze(rootScope, childValue);
|
|
1744
|
+
}
|
|
1745
|
+
}
|
|
1746
|
+
function maybeFreeze(scope, value, deep = false) {
|
|
1747
|
+
if (!scope.parent_ && scope.immer_.autoFreeze_ && scope.canAutoFreeze_) freeze(value, deep);
|
|
1748
|
+
}
|
|
1749
|
+
function createProxyProxy(base, parent) {
|
|
1750
|
+
const isArray = Array.isArray(base);
|
|
1751
|
+
const state = {
|
|
1752
|
+
type_: isArray ? 1 : 0,
|
|
1753
|
+
scope_: parent ? parent.scope_ : getCurrentScope(),
|
|
1754
|
+
modified_: false,
|
|
1755
|
+
finalized_: false,
|
|
1756
|
+
assigned_: {},
|
|
1757
|
+
parent_: parent,
|
|
1758
|
+
base_: base,
|
|
1759
|
+
draft_: null,
|
|
1760
|
+
copy_: null,
|
|
1761
|
+
revoke_: null,
|
|
1762
|
+
isManual_: false
|
|
1763
|
+
};
|
|
1764
|
+
let target = state;
|
|
1765
|
+
let traps = objectTraps;
|
|
1766
|
+
if (isArray) {
|
|
1767
|
+
target = [state];
|
|
1768
|
+
traps = arrayTraps;
|
|
1769
|
+
}
|
|
1770
|
+
const { revoke, proxy } = Proxy.revocable(target, traps);
|
|
1771
|
+
state.draft_ = proxy;
|
|
1772
|
+
state.revoke_ = revoke;
|
|
1773
|
+
return proxy;
|
|
1774
|
+
}
|
|
1775
|
+
var objectTraps = {
|
|
1776
|
+
get(state, prop) {
|
|
1777
|
+
if (prop === DRAFT_STATE) return state;
|
|
1778
|
+
const source = latest(state);
|
|
1779
|
+
if (!has(source, prop)) return readPropFromProto(state, source, prop);
|
|
1780
|
+
const value = source[prop];
|
|
1781
|
+
if (state.finalized_ || !isDraftable(value)) return value;
|
|
1782
|
+
if (value === peek(state.base_, prop)) {
|
|
1783
|
+
prepareCopy(state);
|
|
1784
|
+
return state.copy_[prop] = createProxy(value, state);
|
|
1785
|
+
}
|
|
1786
|
+
return value;
|
|
1787
|
+
},
|
|
1788
|
+
has(state, prop) {
|
|
1789
|
+
return prop in latest(state);
|
|
1790
|
+
},
|
|
1791
|
+
ownKeys(state) {
|
|
1792
|
+
return Reflect.ownKeys(latest(state));
|
|
1793
|
+
},
|
|
1794
|
+
set(state, prop, value) {
|
|
1795
|
+
const desc = getDescriptorFromProto(latest(state), prop);
|
|
1796
|
+
if (desc?.set) {
|
|
1797
|
+
desc.set.call(state.draft_, value);
|
|
1798
|
+
return true;
|
|
1799
|
+
}
|
|
1800
|
+
if (!state.modified_) {
|
|
1801
|
+
const current2 = peek(latest(state), prop);
|
|
1802
|
+
const currentState = current2?.[DRAFT_STATE];
|
|
1803
|
+
if (currentState && currentState.base_ === value) {
|
|
1804
|
+
state.copy_[prop] = value;
|
|
1805
|
+
state.assigned_[prop] = false;
|
|
1806
|
+
return true;
|
|
1807
|
+
}
|
|
1808
|
+
if (is(value, current2) && (value !== void 0 || has(state.base_, prop))) return true;
|
|
1809
|
+
prepareCopy(state);
|
|
1810
|
+
markChanged(state);
|
|
1811
|
+
}
|
|
1812
|
+
if (state.copy_[prop] === value && (value !== void 0 || prop in state.copy_) || Number.isNaN(value) && Number.isNaN(state.copy_[prop])) return true;
|
|
1813
|
+
state.copy_[prop] = value;
|
|
1814
|
+
state.assigned_[prop] = true;
|
|
1815
|
+
return true;
|
|
1816
|
+
},
|
|
1817
|
+
deleteProperty(state, prop) {
|
|
1818
|
+
if (peek(state.base_, prop) !== void 0 || prop in state.base_) {
|
|
1819
|
+
state.assigned_[prop] = false;
|
|
1820
|
+
prepareCopy(state);
|
|
1821
|
+
markChanged(state);
|
|
1822
|
+
} else delete state.assigned_[prop];
|
|
1823
|
+
if (state.copy_) delete state.copy_[prop];
|
|
1824
|
+
return true;
|
|
1825
|
+
},
|
|
1826
|
+
getOwnPropertyDescriptor(state, prop) {
|
|
1827
|
+
const owner = latest(state);
|
|
1828
|
+
const desc = Reflect.getOwnPropertyDescriptor(owner, prop);
|
|
1829
|
+
if (!desc) return desc;
|
|
1830
|
+
return {
|
|
1831
|
+
writable: true,
|
|
1832
|
+
configurable: state.type_ !== 1 || prop !== "length",
|
|
1833
|
+
enumerable: desc.enumerable,
|
|
1834
|
+
value: owner[prop]
|
|
1835
|
+
};
|
|
1836
|
+
},
|
|
1837
|
+
defineProperty() {
|
|
1838
|
+
die(11);
|
|
1839
|
+
},
|
|
1840
|
+
getPrototypeOf(state) {
|
|
1841
|
+
return getPrototypeOf(state.base_);
|
|
1842
|
+
},
|
|
1843
|
+
setPrototypeOf() {
|
|
1844
|
+
die(12);
|
|
1845
|
+
}
|
|
1846
|
+
};
|
|
1847
|
+
var arrayTraps = {};
|
|
1848
|
+
each(objectTraps, (key, fn) => {
|
|
1849
|
+
arrayTraps[key] = function() {
|
|
1850
|
+
arguments[0] = arguments[0][0];
|
|
1851
|
+
return fn.apply(this, arguments);
|
|
1852
|
+
};
|
|
1853
|
+
});
|
|
1854
|
+
arrayTraps.deleteProperty = function(state, prop) {
|
|
1855
|
+
return arrayTraps.set.call(this, state, prop, void 0);
|
|
1856
|
+
};
|
|
1857
|
+
arrayTraps.set = function(state, prop, value) {
|
|
1858
|
+
return objectTraps.set.call(this, state[0], prop, value, state[0]);
|
|
1859
|
+
};
|
|
1860
|
+
function peek(draft, prop) {
|
|
1861
|
+
const state = draft[DRAFT_STATE];
|
|
1862
|
+
return (state ? latest(state) : draft)[prop];
|
|
1863
|
+
}
|
|
1864
|
+
function readPropFromProto(state, source, prop) {
|
|
1865
|
+
const desc = getDescriptorFromProto(source, prop);
|
|
1866
|
+
return desc ? `value` in desc ? desc.value : desc.get?.call(state.draft_) : void 0;
|
|
1867
|
+
}
|
|
1868
|
+
function getDescriptorFromProto(source, prop) {
|
|
1869
|
+
if (!(prop in source)) return void 0;
|
|
1870
|
+
let proto = getPrototypeOf(source);
|
|
1871
|
+
while (proto) {
|
|
1872
|
+
const desc = Object.getOwnPropertyDescriptor(proto, prop);
|
|
1873
|
+
if (desc) return desc;
|
|
1874
|
+
proto = getPrototypeOf(proto);
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
function markChanged(state) {
|
|
1878
|
+
if (!state.modified_) {
|
|
1879
|
+
state.modified_ = true;
|
|
1880
|
+
if (state.parent_) markChanged(state.parent_);
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
function prepareCopy(state) {
|
|
1884
|
+
if (!state.copy_) state.copy_ = shallowCopy(state.base_, state.scope_.immer_.useStrictShallowCopy_);
|
|
1885
|
+
}
|
|
1886
|
+
var Immer2 = class {
|
|
1887
|
+
constructor(config) {
|
|
1888
|
+
this.autoFreeze_ = true;
|
|
1889
|
+
this.useStrictShallowCopy_ = false;
|
|
1890
|
+
this.useStrictIteration_ = true;
|
|
1891
|
+
/**
|
|
1892
|
+
* The `produce` function takes a value and a "recipe function" (whose
|
|
1893
|
+
* return value often depends on the base state). The recipe function is
|
|
1894
|
+
* free to mutate its first argument however it wants. All mutations are
|
|
1895
|
+
* only ever applied to a __copy__ of the base state.
|
|
1896
|
+
*
|
|
1897
|
+
* Pass only a function to create a "curried producer" which relieves you
|
|
1898
|
+
* from passing the recipe function every time.
|
|
1899
|
+
*
|
|
1900
|
+
* Only plain objects and arrays are made mutable. All other objects are
|
|
1901
|
+
* considered uncopyable.
|
|
1902
|
+
*
|
|
1903
|
+
* Note: This function is __bound__ to its `Immer` instance.
|
|
1904
|
+
*
|
|
1905
|
+
* @param {any} base - the initial state
|
|
1906
|
+
* @param {Function} recipe - function that receives a proxy of the base state as first argument and which can be freely modified
|
|
1907
|
+
* @param {Function} patchListener - optional function that will be called with all the patches produced here
|
|
1908
|
+
* @returns {any} a new state, or the initial state if nothing was modified
|
|
1909
|
+
*/
|
|
1910
|
+
this.produce = (base, recipe, patchListener) => {
|
|
1911
|
+
if (typeof base === "function" && typeof recipe !== "function") {
|
|
1912
|
+
const defaultBase = recipe;
|
|
1913
|
+
recipe = base;
|
|
1914
|
+
const self = this;
|
|
1915
|
+
return function curriedProduce(base2 = defaultBase, ...args) {
|
|
1916
|
+
return self.produce(base2, (draft) => recipe.call(this, draft, ...args));
|
|
1917
|
+
};
|
|
1918
|
+
}
|
|
1919
|
+
if (typeof recipe !== "function") die(6);
|
|
1920
|
+
if (patchListener !== void 0 && typeof patchListener !== "function") die(7);
|
|
1921
|
+
let result;
|
|
1922
|
+
if (isDraftable(base)) {
|
|
1923
|
+
const scope = enterScope(this);
|
|
1924
|
+
const proxy = createProxy(base, void 0);
|
|
1925
|
+
let hasError = true;
|
|
1926
|
+
try {
|
|
1927
|
+
result = recipe(proxy);
|
|
1928
|
+
hasError = false;
|
|
1929
|
+
} finally {
|
|
1930
|
+
if (hasError) revokeScope(scope);
|
|
1931
|
+
else leaveScope(scope);
|
|
1932
|
+
}
|
|
1933
|
+
usePatchesInScope(scope, patchListener);
|
|
1934
|
+
return processResult(result, scope);
|
|
1935
|
+
} else if (!base || typeof base !== "object") {
|
|
1936
|
+
result = recipe(base);
|
|
1937
|
+
if (result === void 0) result = base;
|
|
1938
|
+
if (result === NOTHING) result = void 0;
|
|
1939
|
+
if (this.autoFreeze_) freeze(result, true);
|
|
1940
|
+
if (patchListener) {
|
|
1941
|
+
const p = [];
|
|
1942
|
+
const ip = [];
|
|
1943
|
+
getPlugin("Patches").generateReplacementPatches_(base, result, p, ip);
|
|
1944
|
+
patchListener(p, ip);
|
|
1945
|
+
}
|
|
1946
|
+
return result;
|
|
1947
|
+
} else die(1, base);
|
|
1948
|
+
};
|
|
1949
|
+
this.produceWithPatches = (base, recipe) => {
|
|
1950
|
+
if (typeof base === "function") return (state, ...args) => this.produceWithPatches(state, (draft) => base(draft, ...args));
|
|
1951
|
+
let patches, inversePatches;
|
|
1952
|
+
return [
|
|
1953
|
+
this.produce(base, recipe, (p, ip) => {
|
|
1954
|
+
patches = p;
|
|
1955
|
+
inversePatches = ip;
|
|
1956
|
+
}),
|
|
1957
|
+
patches,
|
|
1958
|
+
inversePatches
|
|
1959
|
+
];
|
|
1960
|
+
};
|
|
1961
|
+
if (typeof config?.autoFreeze === "boolean") this.setAutoFreeze(config.autoFreeze);
|
|
1962
|
+
if (typeof config?.useStrictShallowCopy === "boolean") this.setUseStrictShallowCopy(config.useStrictShallowCopy);
|
|
1963
|
+
if (typeof config?.useStrictIteration === "boolean") this.setUseStrictIteration(config.useStrictIteration);
|
|
1964
|
+
}
|
|
1965
|
+
createDraft(base) {
|
|
1966
|
+
if (!isDraftable(base)) die(8);
|
|
1967
|
+
if (isDraft(base)) base = current(base);
|
|
1968
|
+
const scope = enterScope(this);
|
|
1969
|
+
const proxy = createProxy(base, void 0);
|
|
1970
|
+
proxy[DRAFT_STATE].isManual_ = true;
|
|
1971
|
+
leaveScope(scope);
|
|
1972
|
+
return proxy;
|
|
1973
|
+
}
|
|
1974
|
+
finishDraft(draft, patchListener) {
|
|
1975
|
+
const state = draft && draft[DRAFT_STATE];
|
|
1976
|
+
if (!state || !state.isManual_) die(9);
|
|
1977
|
+
const { scope_: scope } = state;
|
|
1978
|
+
usePatchesInScope(scope, patchListener);
|
|
1979
|
+
return processResult(void 0, scope);
|
|
1980
|
+
}
|
|
1981
|
+
/**
|
|
1982
|
+
* Pass true to automatically freeze all copies created by Immer.
|
|
1983
|
+
*
|
|
1984
|
+
* By default, auto-freezing is enabled.
|
|
1985
|
+
*/
|
|
1986
|
+
setAutoFreeze(value) {
|
|
1987
|
+
this.autoFreeze_ = value;
|
|
1988
|
+
}
|
|
1989
|
+
/**
|
|
1990
|
+
* Pass true to enable strict shallow copy.
|
|
1991
|
+
*
|
|
1992
|
+
* By default, immer does not copy the object descriptors such as getter, setter and non-enumrable properties.
|
|
1993
|
+
*/
|
|
1994
|
+
setUseStrictShallowCopy(value) {
|
|
1995
|
+
this.useStrictShallowCopy_ = value;
|
|
1996
|
+
}
|
|
1997
|
+
/**
|
|
1998
|
+
* Pass false to use faster iteration that skips non-enumerable properties
|
|
1999
|
+
* but still handles symbols for compatibility.
|
|
2000
|
+
*
|
|
2001
|
+
* By default, strict iteration is enabled (includes all own properties).
|
|
2002
|
+
*/
|
|
2003
|
+
setUseStrictIteration(value) {
|
|
2004
|
+
this.useStrictIteration_ = value;
|
|
2005
|
+
}
|
|
2006
|
+
shouldUseStrictIteration() {
|
|
2007
|
+
return this.useStrictIteration_;
|
|
2008
|
+
}
|
|
2009
|
+
applyPatches(base, patches) {
|
|
2010
|
+
let i;
|
|
2011
|
+
for (i = patches.length - 1; i >= 0; i--) {
|
|
2012
|
+
const patch = patches[i];
|
|
2013
|
+
if (patch.path.length === 0 && patch.op === "replace") {
|
|
2014
|
+
base = patch.value;
|
|
2015
|
+
break;
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
if (i > -1) patches = patches.slice(i + 1);
|
|
2019
|
+
const applyPatchesImpl = getPlugin("Patches").applyPatches_;
|
|
2020
|
+
if (isDraft(base)) return applyPatchesImpl(base, patches);
|
|
2021
|
+
return this.produce(base, (draft) => applyPatchesImpl(draft, patches));
|
|
2022
|
+
}
|
|
2023
|
+
};
|
|
2024
|
+
function createProxy(value, parent) {
|
|
2025
|
+
const draft = isMap(value) ? getPlugin("MapSet").proxyMap_(value, parent) : isSet(value) ? getPlugin("MapSet").proxySet_(value, parent) : createProxyProxy(value, parent);
|
|
2026
|
+
(parent ? parent.scope_ : getCurrentScope()).drafts_.push(draft);
|
|
2027
|
+
return draft;
|
|
2028
|
+
}
|
|
2029
|
+
function current(value) {
|
|
2030
|
+
if (!isDraft(value)) die(10, value);
|
|
2031
|
+
return currentImpl(value);
|
|
2032
|
+
}
|
|
2033
|
+
function currentImpl(value) {
|
|
2034
|
+
if (!isDraftable(value) || isFrozen(value)) return value;
|
|
2035
|
+
const state = value[DRAFT_STATE];
|
|
2036
|
+
let copy;
|
|
2037
|
+
let strict = true;
|
|
2038
|
+
if (state) {
|
|
2039
|
+
if (!state.modified_) return state.base_;
|
|
2040
|
+
state.finalized_ = true;
|
|
2041
|
+
copy = shallowCopy(value, state.scope_.immer_.useStrictShallowCopy_);
|
|
2042
|
+
strict = state.scope_.immer_.shouldUseStrictIteration();
|
|
2043
|
+
} else copy = shallowCopy(value, true);
|
|
2044
|
+
each(copy, (key, childValue) => {
|
|
2045
|
+
set(copy, key, currentImpl(childValue));
|
|
2046
|
+
}, strict);
|
|
2047
|
+
if (state) state.finalized_ = false;
|
|
2048
|
+
return copy;
|
|
2049
|
+
}
|
|
2050
|
+
var produce = new Immer2().produce;
|
|
2051
|
+
//#endregion
|
|
2052
|
+
//#region node_modules/@deepseek-ai/dsh-client-store/lib/index.js
|
|
2053
|
+
/**
|
|
2054
|
+
* React-free snapshot store engine (zustand vanilla + immer + subscribeWithSelector +
|
|
2055
|
+
* rafFlush middleware + opt-in persist + dev freeze) plus the declarative
|
|
2056
|
+
* shell over it: {@link defineStore} bakes an init/persist/actions literal
|
|
2057
|
+
* into a {@link StoreHandle}, the registration-side store seat of slot
|
|
2058
|
+
* terminals. Engine products are bare observables — subscribe/getSnapshot/
|
|
2059
|
+
* update/set, NO selector hook. Hook synthesis is ui-renderer's (the one
|
|
2060
|
+
* uSES bridge, cached per source at the binding site).
|
|
2061
|
+
*/
|
|
2062
|
+
/**
|
|
2063
|
+
* Notify an observer set without allowing one callback to starve the rest.
|
|
2064
|
+
* @param listeners - current observer callbacks; copied before dispatch.
|
|
2065
|
+
* @param label - diagnostic owner prefix.
|
|
2066
|
+
* @param args - callback arguments.
|
|
2067
|
+
*/
|
|
2068
|
+
function notifySubscribers(listeners, label, ...args) {
|
|
2069
|
+
for (const listener of [...listeners]) try {
|
|
2070
|
+
listener(...args);
|
|
2071
|
+
} catch (error) {
|
|
2072
|
+
console.error(`${label} subscriber failed:`, error);
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
/** Batches subscriber notification into one flush per animation frame. */
|
|
2076
|
+
function rafBatch(notify) {
|
|
2077
|
+
const schedule = typeof requestAnimationFrame === "function" ? (fn) => {
|
|
2078
|
+
requestAnimationFrame(() => {
|
|
2079
|
+
fn();
|
|
2080
|
+
});
|
|
2081
|
+
} : (fn) => {
|
|
2082
|
+
queueMicrotask(fn);
|
|
2083
|
+
};
|
|
2084
|
+
let scheduled = false;
|
|
2085
|
+
return () => {
|
|
2086
|
+
if (scheduled) return;
|
|
2087
|
+
scheduled = true;
|
|
2088
|
+
schedule(() => {
|
|
2089
|
+
scheduled = false;
|
|
2090
|
+
notify();
|
|
2091
|
+
});
|
|
2092
|
+
};
|
|
2093
|
+
}
|
|
2094
|
+
/**
|
|
2095
|
+
* Create a snapshot store.
|
|
2096
|
+
*
|
|
2097
|
+
* Flush default is 'sync' (controlled inputs need same-tick echo); frame-driven
|
|
2098
|
+
* stores opt into 'raf', where a frame's worth of updates coalesces into one
|
|
2099
|
+
* notification. Known raf-mode tradeoff: a component mounting mid-frame reads
|
|
2100
|
+
* fresh state while existing subscribers hear it next flush — transient
|
|
2101
|
+
* frame-level skew, same nature as the object layer's microtask batching.
|
|
2102
|
+
*
|
|
2103
|
+
* @param init - initial state.
|
|
2104
|
+
* @param opts - flush mode and opt-in persistence (localStorage, keyed by name).
|
|
2105
|
+
* @returns the store.
|
|
2106
|
+
*/
|
|
2107
|
+
function createSnapshotStore(init, opts) {
|
|
2108
|
+
const withSelector = subscribeWithSelector(() => init);
|
|
2109
|
+
const api = createStore()(withSelector);
|
|
2110
|
+
if (opts?.persist) attachPersistence(api, opts.persist.name);
|
|
2111
|
+
let subscribe = (fn) => api.subscribe(() => {
|
|
2112
|
+
notifySubscribers([fn], "[client-store]");
|
|
2113
|
+
});
|
|
2114
|
+
if (opts?.flush === "raf") {
|
|
2115
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
2116
|
+
const flush = rafBatch(() => {
|
|
2117
|
+
notifySubscribers(listeners, "[client-store]");
|
|
2118
|
+
});
|
|
2119
|
+
api.subscribe(flush);
|
|
2120
|
+
subscribe = (fn) => {
|
|
2121
|
+
listeners.add(fn);
|
|
2122
|
+
return () => {
|
|
2123
|
+
listeners.delete(fn);
|
|
2124
|
+
};
|
|
2125
|
+
};
|
|
2126
|
+
}
|
|
2127
|
+
return {
|
|
2128
|
+
getSnapshot: () => api.getState(),
|
|
2129
|
+
subscribe: (fn) => subscribe(fn),
|
|
2130
|
+
update: (mutator) => {
|
|
2131
|
+
api.setState(produce(api.getState(), (draft) => {
|
|
2132
|
+
mutator(draft);
|
|
2133
|
+
}), true);
|
|
2134
|
+
},
|
|
2135
|
+
set: (next) => {
|
|
2136
|
+
api.setState(devFreeze(next), true);
|
|
2137
|
+
}
|
|
2138
|
+
};
|
|
2139
|
+
}
|
|
2140
|
+
/**
|
|
2141
|
+
* Whole-value JSON persistence to localStorage. Hand-rolled instead of the
|
|
2142
|
+
* zustand persist middleware: its write path spreads state into an object
|
|
2143
|
+
* (`partialize({ ...get() })`), exploding primitive state (a persisted string
|
|
2144
|
+
* draft becomes {0:'h',1:'e',...}) — not fixable via merge/deserialize options
|
|
2145
|
+
* because the corruption happens before serialization. Storage failures
|
|
2146
|
+
* (quota, private mode) only disable persistence, never break the store.
|
|
2147
|
+
*/
|
|
2148
|
+
function attachPersistence(api, name) {
|
|
2149
|
+
if (typeof localStorage === "undefined") return;
|
|
2150
|
+
try {
|
|
2151
|
+
const raw = localStorage.getItem(name);
|
|
2152
|
+
if (raw !== null) api.setState(devFreeze(JSON.parse(raw)), true);
|
|
2153
|
+
} catch (error) {
|
|
2154
|
+
console.error(`snapshot store '${name}' rehydration failed:`, error);
|
|
2155
|
+
}
|
|
2156
|
+
api.subscribe((state) => {
|
|
2157
|
+
try {
|
|
2158
|
+
localStorage.setItem(name, JSON.stringify(state));
|
|
2159
|
+
} catch (error) {
|
|
2160
|
+
console.error(`snapshot store '${name}' persistence failed:`, error);
|
|
2161
|
+
}
|
|
2162
|
+
});
|
|
2163
|
+
}
|
|
2164
|
+
/** Deep-freeze draftable wholesale-set state outside production: set() bypasses immer's freeze. */
|
|
2165
|
+
function devFreeze(value) {
|
|
2166
|
+
return freeze(value, true);
|
|
2167
|
+
}
|
|
2168
|
+
//#endregion
|
|
1402
2169
|
//#region src/client/settings-form.ts
|
|
1403
2170
|
/**
|
|
1404
2171
|
* Settings-card form machinery, vendored from the dsh-web-ui family bucket
|
|
@@ -1481,7 +2248,7 @@ window.__ModuleLoader__.load({
|
|
|
1481
2248
|
}
|
|
1482
2249
|
/** Publish a projection of this form, rebuilt whenever the scope or a draft changes. */
|
|
1483
2250
|
bind(project) {
|
|
1484
|
-
const store =
|
|
2251
|
+
const store = createSnapshotStore(project());
|
|
1485
2252
|
this.listeners.add(() => {
|
|
1486
2253
|
store.set(project());
|
|
1487
2254
|
});
|
|
@@ -1846,7 +2613,7 @@ window.__ModuleLoader__.load({
|
|
|
1846
2613
|
}
|
|
1847
2614
|
//#endregion
|
|
1848
2615
|
//#region \0dsh-css:/Users/huanyi/Documents/personal/tools/dsh-skill-hub/src/client/panel/panel.module.css.mjs
|
|
1849
|
-
const css = "._6VtqdG_panel{-webkit-font-smoothing:antialiased;color-scheme:light dark;--hub-model:#2f81f7;--hub-user:#3fb950;flex-direction:column;gap:14px;max-width:720px;margin:0 auto;padding:28px 20px 44px;font-family:-apple-system,BlinkMacSystemFont,SF Pro Text,Helvetica Neue,sans-serif;display:flex}._6VtqdG_header{flex-wrap:wrap;align-items:baseline;gap:10px;display:flex}._6VtqdG_title{letter-spacing:-.02em;margin:0;font-size:24px;font-weight:700}._6VtqdG_hint{opacity:.5;font-size:12px}._6VtqdG_actions{gap:8px;margin-left:auto;display:flex}._6VtqdG_segmented{background:#80808024;border-radius:9px;gap:2px;padding:2px;display:flex}._6VtqdG_segBtn{color:inherit;opacity:.62;cursor:pointer;background:0 0;border:none;border-radius:7px;padding:5px 13px;font-size:12px;transition:background .15s,opacity .15s}._6VtqdG_segBtn:hover{opacity:.9}._6VtqdG_segBtnActive{opacity:1;background:#80808047;font-weight:600}._6VtqdG_search{width:100%;color:inherit;background:#8080801f;border:none;border-radius:10px;outline:none;padding:10px 14px;font-size:13px;transition:background .15s}._6VtqdG_search::placeholder{opacity:.45}._6VtqdG_search:focus{background:#8080802e}._6VtqdG_section{background:#8080800f;border-radius:12px;flex-direction:column;margin-top:4px;display:flex;overflow:hidden;box-shadow:0 1px 2px #00000008,0 4px 12px #0000000a}._6VtqdG_sectionTitle{margin:14px 16px 2px;font-size:13px;font-weight:700}._6VtqdG_groupHead{align-items:center;gap:8px;padding:12px 14px 4px;display:flex}._6VtqdG_groupTitle{opacity:.92;flex:1;min-width:0;font-size:13px;font-weight:600}._6VtqdG_groupOps{flex:none;gap:6px;display:inline-flex}._6VtqdG_opBtn{color:inherit;cursor:pointer;background:#80808021;border:none;border-radius:999px;padding:3px 11px;font-size:11px;transition:background .15s}._6VtqdG_opBtn:hover{background:#80808038}._6VtqdG_opBtn:disabled{opacity:.4;cursor:default}._6VtqdG_row{cursor:pointer;background:0 0;align-items:center;gap:12px;padding:10px 14px;transition:background .12s;display:flex}._6VtqdG_row:hover{background:#80808014}._6VtqdG_row:focus-visible{outline:2px solid var(--hub-model);outline-offset:-2px}._6VtqdG_rowStatic{cursor:default}._6VtqdG_rowStatic:hover{background:0 0}._6VtqdG_row+._6VtqdG_row{border-top:.5px solid #80808021}._6VtqdG_rowMain{flex:1;min-width:0}._6VtqdG_rowName{align-items:center;gap:5px;font-size:13.5px;display:flex}._6VtqdG_rowNameText{font-weight:600}._6VtqdG_rowDesc{opacity:.52;white-space:nowrap;text-overflow:ellipsis;margin-top:1px;font-size:12.5px;overflow:hidden}._6VtqdG_rowMeta{opacity:.45;white-space:nowrap;flex:none;font-size:11.5px}._6VtqdG_badges{flex:none;align-items:center;gap:6px;display:flex}._6VtqdG_badge{opacity:.8;white-space:nowrap;border:.5px solid #8080804d;border-radius:999px;padding:1px 7px;font-size:10.5px}._6VtqdG_badgeModel{color:var(--hub-model);border-color:currentColor}._6VtqdG_badgeUser{color:var(--hub-user);border-color:currentColor}._6VtqdG_badgeUses{color:#d29922;border-color:currentColor}._6VtqdG_badgeReadonly{opacity:.5}._6VtqdG_switch{cursor:pointer;background:#8080804d;border:none;border-radius:999px;flex:none;width:36px;height:21px;padding:2px;transition:background .18s}._6VtqdG_switch:disabled{opacity:.5;cursor:default}._6VtqdG_switchOn{background:#34c759}._6VtqdG_switchThumb{background:#fff;border-radius:50%;width:17px;height:17px;transition:transform .18s;display:block;transform:translate(0);box-shadow:0 1px 2px #00000040}._6VtqdG_switchOn ._6VtqdG_switchThumb{transform:translate(15px)}._6VtqdG_empty{opacity:.5;text-align:center;padding:20px 0;font-size:13px}._6VtqdG_diagRow{background:#d299220f;border-left:3px solid #d29922;padding:9px 14px;font-size:12px}._6VtqdG_diagPath{opacity:.75;word-break:break-all;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px}._6VtqdG_diagReason{opacity:.65;margin-top:1px}._6VtqdG_updateLink{color:inherit;font-weight:600;text-decoration:underline}._6VtqdG_errorBanner{color:#d1242f;white-space:pre-wrap;word-break:break-all;background:#d1242f12;border-radius:10px;align-items:flex-start;gap:10px;padding:10px 14px;font-size:12.5px;display:flex}._6VtqdG_successBanner{color:#3fb950;white-space:pre-wrap;word-break:break-all;background:#3fb95014;border-radius:10px;align-items:flex-start;gap:10px;padding:10px 14px;font-size:12.5px;display:flex}._6VtqdG_detailHead{flex-wrap:wrap;align-items:center;gap:10px;display:flex}._6VtqdG_back{color:inherit;cursor:pointer;background:#8080801f;border:none;border-radius:8px;padding:6px 12px;font-size:12.5px}._6VtqdG_back:hover{background:#80808033}._6VtqdG_detailName{letter-spacing:-.01em;font-size:19px;font-weight:700}._6VtqdG_detailMeta{opacity:.6;flex-direction:column;gap:4px;font-size:12px;display:flex}._6VtqdG_detailMetaLine{word-break:break-all}._6VtqdG_detailContent{white-space:pre-wrap;word-break:break-word;background:#8080800d;border-radius:12px;max-height:62vh;padding:14px 16px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;line-height:1.6;overflow:auto}._6VtqdG_form{background:#8080800d;border-radius:12px;flex-direction:column;gap:10px;padding:14px 16px;display:flex;box-shadow:0 1px 2px #00000008,0 4px 12px #0000000a}._6VtqdG_formRow{flex-direction:column;gap:5px;display:flex}._6VtqdG_formLabel{opacity:.6;font-size:12px}._6VtqdG_input,._6VtqdG_select{color:inherit;background:#8080801f;border:none;border-radius:9px;outline:none;padding:9px 12px;font-size:13px}._6VtqdG_input:focus,._6VtqdG_select:focus{background:#8080802e}._6VtqdG_buttons{align-items:center;gap:8px;display:flex}._6VtqdG_button{cursor:pointer;color:inherit;background:#8080801f;border:none;border-radius:8px;padding:6px 13px;font-size:12.5px;transition:background .15s}._6VtqdG_button:hover{background:#80808033}._6VtqdG_primary{background:var(--hub-model);color:#fff}._6VtqdG_primary:hover{opacity:.92;background:#2f81f7}._6VtqdG_primary:disabled{opacity:.45;cursor:default}._6VtqdG_danger{color:#fff;background:#d1242f}._6VtqdG_danger:hover{opacity:.92;background:#d1242f}._6VtqdG_danger:disabled{opacity:.45;cursor:default}._6VtqdG_formError{color:#d1242f}._6VtqdG_formSuccess{color:#3fb950}._6VtqdG_muted{opacity:.55}._6VtqdG_dot{vertical-align:middle;border-radius:50%;width:7px;height:7px;margin-left:5px;display:inline-block}._6VtqdG_dotModel{background:var(--hub-model)}._6VtqdG_dotUser{background:var(--hub-user)}._6VtqdG_legend{opacity:.55;flex-wrap:wrap;align-items:center;gap:12px;padding:2px 2px 0;font-size:11.5px;display:flex}._6VtqdG_legendItem{align-items:center;gap:4px;display:inline-flex}._6VtqdG_legendItem ._6VtqdG_dot{margin-left:0}._6VtqdG_legendHint{opacity:.8}._6VtqdG_badgeSource,._6VtqdG_badgeCount{opacity:.72;background:#8080801a;border:none}._6VtqdG_badgeDisabled{opacity:.58;background:#8080801a;border:none}._6VtqdG_disclosure{min-width:0;color:inherit;cursor:pointer;text-align:left;background:0 0;border:none;flex:1;align-items:center;gap:7px;padding:2px 0;display:flex}._6VtqdG_disclosure:hover ._6VtqdG_groupTitle{opacity:1}._6VtqdG_chevron{opacity:.55;border-bottom:1.5px solid;border-right:1.5px solid;flex:none;width:8px;height:8px;margin-right:2px;transition:transform .15s;transform:rotate(45deg)}._6VtqdG_chevronCollapsed{transform:rotate(-45deg)}._6VtqdG_headerCount{opacity:.5;white-space:nowrap;margin-left:2px;font-size:12px}._6VtqdG_pluginVersion{opacity:.45;white-space:nowrap;font-variant-numeric:tabular-nums;margin-left:6px;font-size:12px;font-weight:500}._6VtqdG_subbar{flex-wrap:wrap;align-items:center;gap:8px;display:flex}._6VtqdG_legendToggle{width:22px;height:22px;color:inherit;cursor:pointer;opacity:.55;background:#80808024;border:none;border-radius:50%;flex:none;font-size:12px;line-height:1;transition:opacity .15s,background .15s}._6VtqdG_legendToggle:hover{opacity:1}._6VtqdG_legendToggleActive{opacity:1;background:#80808047}._6VtqdG_filterBar{flex-wrap:wrap;align-items:center;gap:8px;display:flex}._6VtqdG_filterBar ._6VtqdG_formLabel{margin:0}._6VtqdG_useCount{color:#d29922;vertical-align:1px;background:#d299221f;border-radius:999px;margin-left:6px;padding:0 6px;font-size:11px;font-weight:700;line-height:1.6;display:inline-block}._6VtqdG_useTime{opacity:.45;white-space:nowrap;margin-left:auto;font-size:11px}._6VtqdG_switchMixed{background:#2f81f773}._6VtqdG_switchMixed ._6VtqdG_switchThumb{transform:translate(7.5px);box-shadow:0 1px 2px #00000040}._6VtqdG_groupTitleInner{align-items:baseline;display:inline-flex}._6VtqdG_groupOps{flex-wrap:wrap;flex:none;align-items:center;gap:6px;display:inline-flex}._6VtqdG_sourceLink{color:inherit;opacity:.9;border-bottom:1px dotted;font-weight:600;text-decoration:none}._6VtqdG_sourceLink:hover{opacity:1}._6VtqdG_statusBadges{flex:none;align-items:center;gap:4px;display:inline-flex}._6VtqdG_statusBadge{opacity:.8;white-space:nowrap;background:#8080801a;border:.5px solid #8080804d;border-radius:999px;padding:1px 7px;font-size:10.5px}._6VtqdG_statusOk{color:#3fb950;background:0 0;border-color:currentColor}._6VtqdG_statusUpdated{color:#d29922;background:#d2992214;border-color:currentColor}._6VtqdG_statusError{color:#d1242f;background:#d1242f14;border-color:currentColor}._6VtqdG_statusWrap{cursor:pointer;background:0 0;border:none;align-items:center;gap:4px;padding:0;font-family:inherit;display:inline-flex}._6VtqdG_statusWrap:hover ._6VtqdG_statusBadge{opacity:1}._6VtqdG_statusButton{cursor:pointer;font-family:inherit}._6VtqdG_statusButton:hover{opacity:1}._6VtqdG_opDanger{color:#d1242f;background:#d1242f1a}._6VtqdG_opDanger:hover{background:#d1242f2e}._6VtqdG_iconBtn{border-radius:999px;justify-content:center;align-items:center;width:24px;height:24px;padding:0;display:inline-flex}._6VtqdG_sourceCard{background:#8080800d;border-radius:12px;flex-direction:column;gap:4px;padding:12px 14px;font-size:12px;display:flex}._6VtqdG_sourceCardTitle{flex-wrap:wrap;align-items:center;gap:8px;display:flex}._6VtqdG_dialogOverlay{z-index:50;-webkit-backdrop-filter:blur(2px);background:#00000059;justify-content:center;align-items:center;padding:24px;display:flex;position:fixed;inset:0}._6VtqdG_dialog{background:var(--dsw-surface,#f5f5f7);min-width:min(320px,100%);max-width:380px;color:var(--dsw-text,#1d1d1f);border-radius:14px;padding:18px 18px 14px;box-shadow:0 12px 40px #00000047,0 2px 8px #00000029}@media (prefers-color-scheme:dark){._6VtqdG_dialog{color:#f5f5f7;background:#2c2c2e}}._6VtqdG_dialogTitle{letter-spacing:-.01em;margin:0 0 8px;font-size:15px;font-weight:700}._6VtqdG_dialogText{opacity:.65;margin:0 0 10px;font-size:12.5px;line-height:1.5}._6VtqdG_dialogList{flex-direction:column;gap:5px;max-height:200px;margin:0 0 14px;padding:0;list-style:none;display:flex;overflow:auto}._6VtqdG_dialogList li{opacity:.85;word-break:break-all;font-size:12.5px}._6VtqdG_dialogActions{justify-content:flex-end;gap:8px;display:flex}._6VtqdG_filterBar ._6VtqdG_search{flex:1;min-width:160px}._6VtqdG_filterBar ._6VtqdG_formLabel{flex:none}._6VtqdG_filterBar ._6VtqdG_select{flex:none;max-width:150px}._6VtqdG_filterBar ._6VtqdG_segmented{flex:none}._6VtqdG_titleIcon{vertical-align:-2px;opacity:.8;margin-right:2px;display:inline-block}._6VtqdG_grow{flex:1}._6VtqdG_hintLine{opacity:.55;margin:0;font-size:11.5px}._6VtqdG_hintPadded{margin:6px 12px 2px}._6VtqdG_hintInline{margin:4px 2px}._6VtqdG_rowMuted{cursor:default;opacity:.55}._6VtqdG_actionsPadded{padding:8px 12px}._6VtqdG_actionsTop{margin-top:8px}._6VtqdG_actionsBottom{margin-bottom:8px}._6VtqdG_groupTime{margin-left:6px}._6VtqdG_sectionHeadRow{align-items:center;gap:8px;display:flex}._6VtqdG_sectionTitleFill{flex:1}._6VtqdG_dialogSelect{width:100%;margin-bottom:12px}._6VtqdG_dialogRow{align-items:center;gap:8px;display:flex}._6VtqdG_workspaceBox{align-items:center;gap:6px;display:inline-flex}._6VtqdG_workspaceInput{width:200px;padding:6px 10px;font-size:12px}._6VtqdG_projectNest{border-left:1px solid #80808024;flex-direction:column;margin:2px 0 0 14px;display:flex}";
|
|
2616
|
+
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{box-sizing:border-box;align-items:center;gap:8px;min-height:44px;padding:10px 14px;display:flex}._6VtqdG_groupTitle{opacity:.92;flex:1;align-items:center;gap:6px;min-width:0;font-size:13px;font-weight:600;line-height:1;display:inline-flex}._6VtqdG_groupOps{flex:none;align-items:center;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;min-height:24px;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;align-self:center;width:8px;height:8px;margin-right:2px;transition:transform .15s;transform:rotate(45deg)translateY(-1px)}._6VtqdG_chevronCollapsed{transform:rotate(-45deg)translateY(-1px)}._6VtqdG_headerCount{opacity:.5;white-space:nowrap;margin-left:2px;font-size:12px}._6VtqdG_pluginVersion{opacity:.45;white-space:nowrap;font-variant-numeric:tabular-nums;margin-left:6px;font-size:12px;font-weight:500}._6VtqdG_subbar{flex-wrap:wrap;align-items:center;gap:8px;display:flex}._6VtqdG_legendToggle{width:22px;height:22px;color:inherit;cursor:pointer;opacity:.55;background:#80808024;border:none;border-radius:50%;flex:none;font-size:12px;line-height:1;transition:opacity .15s,background .15s}._6VtqdG_legendToggle:hover{opacity:1}._6VtqdG_legendToggleActive{opacity:1;background:#80808047}._6VtqdG_filterBar{flex-wrap:wrap;align-items:center;gap:8px;display:flex}._6VtqdG_filterBar ._6VtqdG_formLabel{margin:0}._6VtqdG_useCount{color:#d29922;vertical-align:1px;background:#d299221f;border-radius:999px;margin-left:6px;padding:0 6px;font-size:11px;font-weight:700;line-height:1.6;display:inline-block}._6VtqdG_useTime{opacity:.45;white-space:nowrap;margin-left:auto;font-size:11px}._6VtqdG_switchMixed{background:#2f81f773}._6VtqdG_switchMixed ._6VtqdG_switchThumb{transform:translate(7.5px);box-shadow:0 1px 2px #00000040}._6VtqdG_groupTitleInner{align-items:baseline;display:inline-flex}._6VtqdG_groupOps{flex-wrap:wrap;flex:none;align-items:center;gap:6px;display:inline-flex}._6VtqdG_sourceLink{color:inherit;opacity:.9;border-bottom:1px dotted;font-weight:600;text-decoration:none}._6VtqdG_sourceLink:hover{opacity:1}._6VtqdG_statusBadges{flex:none;align-items:center;gap:4px;display:inline-flex}._6VtqdG_statusBadge{opacity:.8;white-space:nowrap;background:#8080801a;border:.5px solid #8080804d;border-radius:999px;padding:1px 7px;font-size:10.5px}._6VtqdG_statusOk{color:#3fb950;background:0 0;border-color:currentColor}._6VtqdG_statusUpdated{color:#d29922;background:#d2992214;border-color:currentColor}._6VtqdG_statusError{color:#d1242f;background:#d1242f14;border-color:currentColor}._6VtqdG_statusWrap{cursor:pointer;background:0 0;border:none;align-items:center;gap:4px;padding:0;font-family:inherit;display:inline-flex}._6VtqdG_statusWrap:hover ._6VtqdG_statusBadge{opacity:1}._6VtqdG_statusButton{cursor:pointer;font-family:inherit}._6VtqdG_statusButton:hover{opacity:1}._6VtqdG_opDanger{color:#d1242f;background:#d1242f1a}._6VtqdG_opDanger:hover{background:#d1242f2e}._6VtqdG_iconBtn{border-radius:999px;justify-content:center;align-items:center;width:24px;height:24px;padding:0;display:inline-flex}._6VtqdG_sourceCard{background:#8080800d;border-radius:12px;flex-direction:column;gap:4px;padding:12px 14px;font-size:12px;display:flex}._6VtqdG_sourceCardTitle{flex-wrap:wrap;align-items:center;gap:8px;display:flex}._6VtqdG_dialogOverlay{z-index:50;-webkit-backdrop-filter:blur(2px);background:#00000059;justify-content:center;align-items:center;padding:24px;display:flex;position:fixed;inset:0}._6VtqdG_dialog{background:var(--dsw-surface,#f5f5f7);min-width:min(320px,100%);max-width:380px;color:var(--dsw-text,#1d1d1f);border-radius:14px;padding:18px 18px 14px;box-shadow:0 12px 40px #00000047,0 2px 8px #00000029}@media (prefers-color-scheme:dark){._6VtqdG_dialog{color:#f5f5f7;background:#2c2c2e}}._6VtqdG_dialogTitle{letter-spacing:-.01em;margin:0 0 8px;font-size:15px;font-weight:700}._6VtqdG_dialogText{opacity:.65;margin:0 0 10px;font-size:12.5px;line-height:1.5}._6VtqdG_dialogList{flex-direction:column;gap:5px;max-height:200px;margin:0 0 14px;padding:0;list-style:none;display:flex;overflow:auto}._6VtqdG_dialogList li{opacity:.85;word-break:break-all;font-size:12.5px}._6VtqdG_dialogActions{justify-content:flex-end;gap:8px;display:flex}._6VtqdG_filterBar ._6VtqdG_search{flex:1;min-width:160px}._6VtqdG_filterBar ._6VtqdG_formLabel{flex:none}._6VtqdG_filterBar ._6VtqdG_select{flex:none;max-width:150px}._6VtqdG_filterBar ._6VtqdG_segmented{flex:none}._6VtqdG_titleIcon{vertical-align:-2px;opacity:.8;margin-right:2px;display:inline-block}._6VtqdG_grow{flex:1}._6VtqdG_hintLine{opacity:.55;margin:0;font-size:11.5px}._6VtqdG_hintPadded{margin:6px 12px 2px}._6VtqdG_hintInline{margin:4px 2px}._6VtqdG_rowMuted{cursor:default;opacity:.55}._6VtqdG_actionsPadded{padding:8px 12px}._6VtqdG_actionsTop{margin-top:8px}._6VtqdG_actionsBottom{margin-bottom:8px}._6VtqdG_groupTime{margin-left:6px}._6VtqdG_sectionHeadRow{align-items:center;gap:8px;display:flex}._6VtqdG_sectionTitleFill{flex:1}._6VtqdG_dialogSelect{width:100%;margin-bottom:12px}._6VtqdG_dialogRow{align-items:center;gap:8px;display:flex}._6VtqdG_workspaceBox{align-items:center;gap:6px;display:inline-flex}._6VtqdG_workspaceInput{width:200px;padding:6px 10px;font-size:12px}._6VtqdG_projectNest{border-left:1px solid #80808024;flex-direction:column;margin:2px 0 0 14px;display:flex}._6VtqdG_dragHandle{cursor:grab;opacity:.32;letter-spacing:1px;user-select:none;flex:none;justify-content:center;align-self:center;align-items:center;height:20px;padding:0 4px;font-size:11px;display:inline-flex}._6VtqdG_dragHandle:active{cursor:grabbing}._6VtqdG_dragging{opacity:.45}._6VtqdG_dragOver{outline:2px dashed var(--hub-model,#2f81f7);outline-offset:-2px;background:#2f81f712}._6VtqdG_scanList{background:#8080800f;border:.5px solid #80808021;border-radius:12px;max-height:360px;margin-top:8px;overflow:auto}._6VtqdG_scanProgressTrack{background:#80808021;border-radius:4px;flex:1;height:6px;overflow:hidden}._6VtqdG_scanProgressFill{background:var(--hub-model,#2f81f7);height:100%;transition:width .2s}";
|
|
1850
2617
|
const tagId = "dsh-skill-hub/panel.module.css";
|
|
1851
2618
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
1852
2619
|
const tag = document.createElement("style");
|
|
@@ -1895,6 +2662,9 @@ window.__ModuleLoader__.load({
|
|
|
1895
2662
|
"dot": "_6VtqdG_dot",
|
|
1896
2663
|
"dotModel": "_6VtqdG_dotModel",
|
|
1897
2664
|
"dotUser": "_6VtqdG_dotUser",
|
|
2665
|
+
"dragHandle": "_6VtqdG_dragHandle",
|
|
2666
|
+
"dragOver": "_6VtqdG_dragOver",
|
|
2667
|
+
"dragging": "_6VtqdG_dragging",
|
|
1898
2668
|
"empty": "_6VtqdG_empty",
|
|
1899
2669
|
"errorBanner": "_6VtqdG_errorBanner",
|
|
1900
2670
|
"filterBar": "_6VtqdG_filterBar",
|
|
@@ -1937,6 +2707,9 @@ window.__ModuleLoader__.load({
|
|
|
1937
2707
|
"rowName": "_6VtqdG_rowName",
|
|
1938
2708
|
"rowNameText": "_6VtqdG_rowNameText",
|
|
1939
2709
|
"rowStatic": "_6VtqdG_rowStatic",
|
|
2710
|
+
"scanList": "_6VtqdG_scanList",
|
|
2711
|
+
"scanProgressFill": "_6VtqdG_scanProgressFill",
|
|
2712
|
+
"scanProgressTrack": "_6VtqdG_scanProgressTrack",
|
|
1940
2713
|
"search": "_6VtqdG_search",
|
|
1941
2714
|
"section": "_6VtqdG_section",
|
|
1942
2715
|
"sectionHeadRow": "_6VtqdG_sectionHeadRow",
|
|
@@ -2369,17 +3142,6 @@ window.__ModuleLoader__.load({
|
|
|
2369
3142
|
children: skill.description
|
|
2370
3143
|
})]
|
|
2371
3144
|
}), skill.writable ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2372
|
-
type: "button",
|
|
2373
|
-
className: panel_module_css_default.opBtn + " " + panel_module_css_default.opDanger + " " + panel_module_css_default.iconBtn,
|
|
2374
|
-
disabled: hub.busyNames.has(skill.name) || hub.tagBusy,
|
|
2375
|
-
title: tt("row.delete"),
|
|
2376
|
-
"aria-label": tt("row.delete"),
|
|
2377
|
-
onClick: (event) => {
|
|
2378
|
-
event.stopPropagation();
|
|
2379
|
-
hub.requestDeleteSkill(skill.name);
|
|
2380
|
-
},
|
|
2381
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconTrashOutline16, { size: 14 })
|
|
2382
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2383
3145
|
type: "button",
|
|
2384
3146
|
role: "switch",
|
|
2385
3147
|
"aria-checked": true,
|
|
@@ -2391,7 +3153,18 @@ window.__ModuleLoader__.load({
|
|
|
2391
3153
|
hub.toggle(skill, false);
|
|
2392
3154
|
},
|
|
2393
3155
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: panel_module_css_default.switchThumb })
|
|
2394
|
-
})
|
|
3156
|
+
}), hub.editMode ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3157
|
+
type: "button",
|
|
3158
|
+
className: panel_module_css_default.opBtn + " " + panel_module_css_default.opDanger + " " + panel_module_css_default.iconBtn,
|
|
3159
|
+
disabled: hub.busyNames.has(skill.name) || hub.tagBusy,
|
|
3160
|
+
title: tt("row.delete"),
|
|
3161
|
+
"aria-label": tt("row.delete"),
|
|
3162
|
+
onClick: (event) => {
|
|
3163
|
+
event.stopPropagation();
|
|
3164
|
+
hub.requestDeleteSkill(skill.name);
|
|
3165
|
+
},
|
|
3166
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(IconTrashOutline16, { size: 14 })
|
|
3167
|
+
}) : null] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2395
3168
|
className: panel_module_css_default.badge + " " + panel_module_css_default.badgeReadonly,
|
|
2396
3169
|
children: tt("row.readonly")
|
|
2397
3170
|
})]
|
|
@@ -2463,219 +3236,326 @@ window.__ModuleLoader__.load({
|
|
|
2463
3236
|
}
|
|
2464
3237
|
//#endregion
|
|
2465
3238
|
//#region src/client/panel/SourcesView.tsx
|
|
2466
|
-
/**
|
|
2467
|
-
|
|
3239
|
+
/**
|
|
3240
|
+
* Sources tab: the flat skill list or the grouped view — a project-level
|
|
3241
|
+
* three-tier tree (workspaces from workspace.json, each optionally split by
|
|
3242
|
+
* .dsh/.agents), one card per upstream collection with check/sync/
|
|
3243
|
+
* follow-delete actions and the tri-state switch, plus the uncategorized
|
|
3244
|
+
* "personal" card (project skills never count as personal).
|
|
3245
|
+
*/
|
|
3246
|
+
function SourcesView(props) {
|
|
2468
3247
|
const { hub } = props;
|
|
2469
|
-
const {
|
|
2470
|
-
const
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
const
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
3248
|
+
const { catalog, groupsState, skillView, sourceFilter, origins, sorted, normalized, collapsedGroups, viewNames, sourceCheck, actionNames, checkingSource, syncingSource, batchBusy, busyNames, toggleGroupCollapse, checkSources, requestSync, requestDelete, requestDeleteGroup, toggleGroup, enableDisabled } = hub;
|
|
3249
|
+
const [topDragKey, setTopDragKey] = (0, react.useState)(null);
|
|
3250
|
+
const [topOverKey, setTopOverKey] = (0, react.useState)(null);
|
|
3251
|
+
if (skillView === "flat") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: filterBySource(sorted, sourceFilter, origins).map((skill) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillRow, {
|
|
3252
|
+
skill,
|
|
3253
|
+
hub
|
|
3254
|
+
}, skill.name)) });
|
|
3255
|
+
const projectSkillsAll = filterBySource(sorted, sourceFilter, origins).filter((skill) => isProjectSource(skill.source));
|
|
3256
|
+
const hasProject = projectSkillsAll.length > 0;
|
|
3257
|
+
const collections = groupsState?.collections ?? [];
|
|
3258
|
+
const uncategorized = filterBySource(sorted, sourceFilter, origins).filter((skill) => origins[skill.name] === void 0 && !isProjectSource(skill.source));
|
|
3259
|
+
const personalDisabledAll = (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");
|
|
3260
|
+
const hasPersonal = [...uncategorized.map((s) => s.name), ...personalDisabledAll.map((r) => r.name)].length > 0;
|
|
3261
|
+
const defaultTopKeys = [
|
|
3262
|
+
...hasProject ? ["project"] : [],
|
|
3263
|
+
...collections.map((c) => "col:" + c.name),
|
|
3264
|
+
...hasPersonal ? ["uncategorized-source"] : []
|
|
3265
|
+
];
|
|
3266
|
+
const storedTopOrder = groupsState?.sourceGroupOrder ?? [];
|
|
3267
|
+
const topOrderedKeys = (() => {
|
|
3268
|
+
if (storedTopOrder.length === 0) return defaultTopKeys;
|
|
3269
|
+
const set = new Set(storedTopOrder);
|
|
3270
|
+
const result = storedTopOrder.filter((k) => defaultTopKeys.includes(k));
|
|
3271
|
+
for (const k of defaultTopKeys) if (!set.has(k)) result.push(k);
|
|
3272
|
+
if (result.length === 0) return defaultTopKeys;
|
|
3273
|
+
return result;
|
|
3274
|
+
})();
|
|
3275
|
+
const handleTopDrop = (targetKey) => {
|
|
3276
|
+
if (topDragKey === null || topDragKey === targetKey) return;
|
|
3277
|
+
const from = topOrderedKeys.indexOf(topDragKey);
|
|
3278
|
+
const to = topOrderedKeys.indexOf(targetKey);
|
|
3279
|
+
if (from === -1 || to === -1) return;
|
|
3280
|
+
const next = [...topOrderedKeys];
|
|
3281
|
+
const [moved] = next.splice(from, 1);
|
|
3282
|
+
next.splice(to, 0, moved);
|
|
3283
|
+
hub.reorderSourceGroups(next);
|
|
3284
|
+
};
|
|
3285
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [!hasProject && collections.length === 0 && !hasPersonal ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3286
|
+
className: panel_module_css_default.empty,
|
|
3287
|
+
children: tt("groups.noCollections")
|
|
3288
|
+
}) : null, topOrderedKeys.map((topKey) => {
|
|
3289
|
+
if (topKey === "project" && hasProject) {
|
|
3290
|
+
const topCollapsed = collapsedGroups.has("project");
|
|
3291
|
+
const isDragging = topDragKey === "project";
|
|
3292
|
+
const isOver = topOverKey === "project" && topDragKey !== "project";
|
|
3293
|
+
const byProject = /* @__PURE__ */ new Map();
|
|
3294
|
+
for (const skill of projectSkillsAll) {
|
|
3295
|
+
const key = skill.workspace ?? skill.source;
|
|
3296
|
+
const entry = byProject.get(key);
|
|
3297
|
+
if (entry === void 0) byProject.set(key, {
|
|
3298
|
+
title: skill.workspaceTitle ?? skill.workspace ?? tt("groups.project"),
|
|
3299
|
+
skills: [skill]
|
|
3300
|
+
});
|
|
3301
|
+
else entry.skills.push(skill);
|
|
3302
|
+
}
|
|
3303
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
3304
|
+
className: panel_module_css_default.section + (isDragging ? " " + panel_module_css_default.dragging : "") + (isOver ? " " + panel_module_css_default.dragOver : ""),
|
|
3305
|
+
draggable: true,
|
|
3306
|
+
onDragStart: (e) => {
|
|
3307
|
+
setTopDragKey("project");
|
|
3308
|
+
e.dataTransfer.effectAllowed = "move";
|
|
3309
|
+
e.dataTransfer.setData("text/plain", "project");
|
|
3310
|
+
},
|
|
3311
|
+
onDragOver: (e) => {
|
|
3312
|
+
e.preventDefault();
|
|
3313
|
+
if (topOverKey !== "project") setTopOverKey("project");
|
|
3314
|
+
},
|
|
3315
|
+
onDragLeave: () => {
|
|
3316
|
+
if (topOverKey === "project") setTopOverKey(null);
|
|
3317
|
+
},
|
|
3318
|
+
onDrop: (e) => {
|
|
3319
|
+
e.preventDefault();
|
|
3320
|
+
handleTopDrop("project");
|
|
3321
|
+
setTopOverKey(null);
|
|
3322
|
+
},
|
|
3323
|
+
onDragEnd: () => {
|
|
3324
|
+
setTopDragKey(null);
|
|
3325
|
+
setTopOverKey(null);
|
|
2493
3326
|
},
|
|
2494
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: panel_module_css_default.chevron + (topCollapsed ? " " + panel_module_css_default.chevronCollapsed : "") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2495
|
-
className: panel_module_css_default.groupTitle,
|
|
2496
|
-
children: [
|
|
2497
|
-
tt("groups.project"),
|
|
2498
|
-
" · ",
|
|
2499
|
-
byProject.size
|
|
2500
|
-
]
|
|
2501
|
-
})]
|
|
2502
|
-
})
|
|
2503
|
-
}), !topCollapsed ? [...byProject.entries()].map(([key, proj]) => {
|
|
2504
|
-
const projKey = "project:" + key;
|
|
2505
|
-
const projCollapsed = collapsedGroups.has(projKey);
|
|
2506
|
-
const subdivided = subdividedProjects.has(key);
|
|
2507
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2508
|
-
className: panel_module_css_default.projectNest,
|
|
2509
3327
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2510
3328
|
className: panel_module_css_default.groupHead,
|
|
2511
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.
|
|
3329
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3330
|
+
className: panel_module_css_default.dragHandle,
|
|
3331
|
+
"aria-hidden": true,
|
|
3332
|
+
title: "拖拽调整顺序",
|
|
3333
|
+
children: "⋮⋮"
|
|
3334
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2512
3335
|
type: "button",
|
|
2513
3336
|
className: panel_module_css_default.disclosure,
|
|
2514
|
-
"aria-expanded": !
|
|
3337
|
+
"aria-expanded": !topCollapsed,
|
|
2515
3338
|
onClick: () => {
|
|
2516
|
-
toggleGroupCollapse(
|
|
3339
|
+
toggleGroupCollapse("project");
|
|
2517
3340
|
},
|
|
2518
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: panel_module_css_default.chevron + (
|
|
3341
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: panel_module_css_default.chevron + (topCollapsed ? " " + panel_module_css_default.chevronCollapsed : "") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2519
3342
|
className: panel_module_css_default.groupTitle,
|
|
2520
3343
|
children: [
|
|
2521
|
-
|
|
3344
|
+
tt("groups.project"),
|
|
2522
3345
|
" · ",
|
|
2523
|
-
|
|
2524
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupSummary, {
|
|
2525
|
-
members: proj.skills.map((skill) => skill.name),
|
|
2526
|
-
hub
|
|
2527
|
-
})
|
|
3346
|
+
byProject.size
|
|
2528
3347
|
]
|
|
2529
3348
|
})]
|
|
2530
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2531
|
-
className: panel_module_css_default.groupOps,
|
|
2532
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2533
|
-
type: "button",
|
|
2534
|
-
className: panel_module_css_default.opBtn,
|
|
2535
|
-
onClick: (event) => {
|
|
2536
|
-
event.stopPropagation();
|
|
2537
|
-
toggleSubdivide(key);
|
|
2538
|
-
},
|
|
2539
|
-
children: subdivided ? tt("groups.merge") : tt("groups.subdivide")
|
|
2540
|
-
})
|
|
2541
3349
|
})]
|
|
2542
|
-
}), !
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
className: panel_module_css_default.
|
|
2553
|
-
|
|
3350
|
+
}), !topCollapsed ? [...byProject.entries()].map(([key, proj]) => {
|
|
3351
|
+
const projKey = "project:" + key;
|
|
3352
|
+
const projCollapsed = collapsedGroups.has(projKey);
|
|
3353
|
+
const subdivided = hub.subdividedProjects.has(key);
|
|
3354
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3355
|
+
className: panel_module_css_default.projectNest,
|
|
3356
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3357
|
+
className: panel_module_css_default.groupHead,
|
|
3358
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3359
|
+
type: "button",
|
|
3360
|
+
className: panel_module_css_default.disclosure,
|
|
3361
|
+
"aria-expanded": !projCollapsed,
|
|
3362
|
+
onClick: () => {
|
|
3363
|
+
toggleGroupCollapse(projKey);
|
|
3364
|
+
},
|
|
3365
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: panel_module_css_default.chevron + (projCollapsed ? " " + panel_module_css_default.chevronCollapsed : "") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3366
|
+
className: panel_module_css_default.groupTitle,
|
|
3367
|
+
children: [
|
|
3368
|
+
proj.title,
|
|
3369
|
+
" · ",
|
|
3370
|
+
proj.skills.length,
|
|
3371
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupSummary, {
|
|
3372
|
+
members: proj.skills.map((s) => s.name),
|
|
3373
|
+
hub
|
|
3374
|
+
})
|
|
3375
|
+
]
|
|
3376
|
+
})]
|
|
3377
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3378
|
+
className: panel_module_css_default.groupOps,
|
|
3379
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2554
3380
|
type: "button",
|
|
2555
|
-
className: panel_module_css_default.
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
3381
|
+
className: panel_module_css_default.opBtn,
|
|
3382
|
+
onClick: (event) => {
|
|
3383
|
+
event.stopPropagation();
|
|
3384
|
+
hub.toggleSubdivide(key);
|
|
2559
3385
|
},
|
|
2560
|
-
children:
|
|
2561
|
-
className: panel_module_css_default.groupTitle,
|
|
2562
|
-
children: [
|
|
2563
|
-
tt("badge.source." + source),
|
|
2564
|
-
" · ",
|
|
2565
|
-
list.length
|
|
2566
|
-
]
|
|
2567
|
-
})]
|
|
3386
|
+
children: subdivided ? tt("groups.merge") : tt("groups.subdivide")
|
|
2568
3387
|
})
|
|
2569
|
-
})
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
3388
|
+
})]
|
|
3389
|
+
}), !projCollapsed ? subdivided ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3390
|
+
className: panel_module_css_default.projectNest,
|
|
3391
|
+
children: ["project-dsh", "project-agents"].map((source) => {
|
|
3392
|
+
const list = proj.skills.filter((skill) => skill.source === source);
|
|
3393
|
+
if (list.length === 0) return null;
|
|
3394
|
+
const srcKey = projKey + ":" + source;
|
|
3395
|
+
const srcCollapsed = collapsedGroups.has(srcKey);
|
|
3396
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3397
|
+
className: panel_module_css_default.projectNest,
|
|
3398
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3399
|
+
className: panel_module_css_default.groupHead,
|
|
3400
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3401
|
+
type: "button",
|
|
3402
|
+
className: panel_module_css_default.disclosure,
|
|
3403
|
+
"aria-expanded": !srcCollapsed,
|
|
3404
|
+
onClick: () => {
|
|
3405
|
+
toggleGroupCollapse(srcKey);
|
|
3406
|
+
},
|
|
3407
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: panel_module_css_default.chevron + (srcCollapsed ? " " + panel_module_css_default.chevronCollapsed : "") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3408
|
+
className: panel_module_css_default.groupTitle,
|
|
3409
|
+
children: [
|
|
3410
|
+
tt("badge.source." + source),
|
|
3411
|
+
" · ",
|
|
3412
|
+
list.length
|
|
3413
|
+
]
|
|
3414
|
+
})]
|
|
3415
|
+
})
|
|
3416
|
+
}), !srcCollapsed ? list.map((skill) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillRow, {
|
|
3417
|
+
skill,
|
|
3418
|
+
hub
|
|
3419
|
+
}, skill.name)) : null]
|
|
3420
|
+
}, srcKey);
|
|
3421
|
+
})
|
|
3422
|
+
}) : proj.skills.map((skill) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillRow, {
|
|
3423
|
+
skill,
|
|
3424
|
+
hub
|
|
3425
|
+
}, skill.name)) : null]
|
|
3426
|
+
}, projKey);
|
|
3427
|
+
}) : null]
|
|
3428
|
+
}, "project");
|
|
3429
|
+
}
|
|
3430
|
+
if (topKey.startsWith("col:")) {
|
|
3431
|
+
const colName = topKey.slice(4);
|
|
3432
|
+
const collection = collections.find((c) => c.name === colName);
|
|
3433
|
+
if (collection === void 0) return null;
|
|
3434
|
+
const skills = filterBySource(sorted, sourceFilter, origins).filter((skill) => collection.skillNames.includes(skill.name));
|
|
3435
|
+
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));
|
|
3436
|
+
const collapsed = collapsedGroups.has("col:" + collection.name);
|
|
3437
|
+
const view = groupSwitchView(collection.skillNames, viewNames);
|
|
2601
3438
|
const check = sourceCheck[collection.name];
|
|
2602
3439
|
const hasWritable = collection.skillNames.some((name) => actionNames.has(name));
|
|
3440
|
+
const isDragging = topDragKey === topKey;
|
|
3441
|
+
const isOver = topOverKey === topKey && topDragKey !== topKey;
|
|
2603
3442
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2604
|
-
className: panel_module_css_default.section,
|
|
3443
|
+
className: panel_module_css_default.section + (isDragging ? " " + panel_module_css_default.dragging : "") + (isOver ? " " + panel_module_css_default.dragOver : ""),
|
|
3444
|
+
draggable: true,
|
|
3445
|
+
onDragStart: (e) => {
|
|
3446
|
+
setTopDragKey(topKey);
|
|
3447
|
+
e.dataTransfer.effectAllowed = "move";
|
|
3448
|
+
e.dataTransfer.setData("text/plain", topKey);
|
|
3449
|
+
},
|
|
3450
|
+
onDragOver: (e) => {
|
|
3451
|
+
e.preventDefault();
|
|
3452
|
+
if (topOverKey !== topKey) setTopOverKey(topKey);
|
|
3453
|
+
},
|
|
3454
|
+
onDragLeave: () => {
|
|
3455
|
+
if (topOverKey === topKey) setTopOverKey(null);
|
|
3456
|
+
},
|
|
3457
|
+
onDrop: (e) => {
|
|
3458
|
+
e.preventDefault();
|
|
3459
|
+
handleTopDrop(topKey);
|
|
3460
|
+
setTopOverKey(null);
|
|
3461
|
+
},
|
|
3462
|
+
onDragEnd: () => {
|
|
3463
|
+
setTopDragKey(null);
|
|
3464
|
+
setTopOverKey(null);
|
|
3465
|
+
},
|
|
2605
3466
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2606
3467
|
className: panel_module_css_default.groupHead,
|
|
2607
|
-
children: [
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
},
|
|
2614
|
-
|
|
2615
|
-
|
|
3468
|
+
children: [
|
|
3469
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3470
|
+
className: panel_module_css_default.dragHandle,
|
|
3471
|
+
"aria-hidden": true,
|
|
3472
|
+
title: "拖拽调整顺序",
|
|
3473
|
+
children: "⋮⋮"
|
|
3474
|
+
}),
|
|
3475
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3476
|
+
type: "button",
|
|
3477
|
+
className: panel_module_css_default.disclosure,
|
|
3478
|
+
"aria-expanded": !collapsed,
|
|
3479
|
+
onClick: () => {
|
|
3480
|
+
toggleGroupCollapse("col:" + collection.name);
|
|
3481
|
+
},
|
|
3482
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: panel_module_css_default.chevron + (collapsed ? " " + panel_module_css_default.chevronCollapsed : "") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3483
|
+
className: panel_module_css_default.groupTitle,
|
|
3484
|
+
children: [
|
|
3485
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
3486
|
+
className: panel_module_css_default.sourceLink,
|
|
3487
|
+
href: "https://github.com/" + collection.name,
|
|
3488
|
+
target: "_blank",
|
|
3489
|
+
rel: "noreferrer",
|
|
3490
|
+
onClick: (event) => {
|
|
3491
|
+
event.stopPropagation();
|
|
3492
|
+
},
|
|
3493
|
+
children: collection.name
|
|
3494
|
+
}),
|
|
3495
|
+
" · " + collection.skillNames.length,
|
|
3496
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupSummary, {
|
|
3497
|
+
members: collection.skillNames,
|
|
3498
|
+
hub
|
|
3499
|
+
})
|
|
3500
|
+
]
|
|
3501
|
+
})]
|
|
3502
|
+
}),
|
|
3503
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3504
|
+
className: panel_module_css_default.groupOps,
|
|
2616
3505
|
children: [
|
|
2617
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
3506
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SourceStatusBadge, {
|
|
3507
|
+
check,
|
|
3508
|
+
checking: checkingSource === collection.name,
|
|
3509
|
+
onCheck: () => {
|
|
3510
|
+
checkSources(collection.name);
|
|
3511
|
+
}
|
|
3512
|
+
}),
|
|
3513
|
+
check !== void 0 && check.changed && check.updated.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3514
|
+
type: "button",
|
|
3515
|
+
className: panel_module_css_default.opBtn,
|
|
3516
|
+
disabled: syncingSource !== null,
|
|
2622
3517
|
onClick: (event) => {
|
|
2623
3518
|
event.stopPropagation();
|
|
3519
|
+
requestSync(collection.name, check.updated);
|
|
2624
3520
|
},
|
|
2625
|
-
children: collection.name
|
|
3521
|
+
children: syncingSource === collection.name ? tt("source.syncing") : tt("source.sync")
|
|
3522
|
+
}) : null,
|
|
3523
|
+
check !== void 0 && check.deleted.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3524
|
+
type: "button",
|
|
3525
|
+
className: panel_module_css_default.opBtn + " " + panel_module_css_default.opDanger,
|
|
3526
|
+
onClick: (event) => {
|
|
3527
|
+
event.stopPropagation();
|
|
3528
|
+
requestDelete(collection.name, check.deleted);
|
|
3529
|
+
},
|
|
3530
|
+
children: tt("source.followDelete")
|
|
3531
|
+
}) : null,
|
|
3532
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3533
|
+
type: "button",
|
|
3534
|
+
role: "switch",
|
|
3535
|
+
"aria-checked": view.state !== "off",
|
|
3536
|
+
"aria-label": collection.name,
|
|
3537
|
+
className: panel_module_css_default.switch + (view.state === "on" ? " " + panel_module_css_default.switchOn : view.state === "mixed" ? " " + panel_module_css_default.switchMixed : ""),
|
|
3538
|
+
disabled: batchBusy || collection.skillNames.length === 0 || view.state !== "off" && !hasWritable,
|
|
3539
|
+
title: view.state !== "off" && !hasWritable ? tt("groups.noWritable") : void 0,
|
|
3540
|
+
onClick: (event) => {
|
|
3541
|
+
event.stopPropagation();
|
|
3542
|
+
toggleGroup("col:" + collection.name, collection.name, view.state);
|
|
3543
|
+
},
|
|
3544
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: panel_module_css_default.switchThumb })
|
|
2626
3545
|
}),
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
3546
|
+
hub.editMode ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3547
|
+
type: "button",
|
|
3548
|
+
className: panel_module_css_default.opBtn + " " + panel_module_css_default.opDanger,
|
|
3549
|
+
title: tt("source.deleteGroupHint", { count: collection.skillNames.length }),
|
|
3550
|
+
onClick: (event) => {
|
|
3551
|
+
event.stopPropagation();
|
|
3552
|
+
requestDeleteGroup(collection.name, collection.skillNames);
|
|
3553
|
+
},
|
|
3554
|
+
children: tt("source.deleteGroup")
|
|
3555
|
+
}) : null
|
|
2632
3556
|
]
|
|
2633
|
-
})
|
|
2634
|
-
|
|
2635
|
-
className: panel_module_css_default.groupOps,
|
|
2636
|
-
children: [
|
|
2637
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SourceStatusBadge, {
|
|
2638
|
-
check,
|
|
2639
|
-
checking: checkingSource === collection.name,
|
|
2640
|
-
onCheck: () => {
|
|
2641
|
-
checkSources(collection.name);
|
|
2642
|
-
}
|
|
2643
|
-
}),
|
|
2644
|
-
check !== void 0 && check.changed && check.updated.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2645
|
-
type: "button",
|
|
2646
|
-
className: panel_module_css_default.opBtn,
|
|
2647
|
-
disabled: syncingSource !== null,
|
|
2648
|
-
onClick: (event) => {
|
|
2649
|
-
event.stopPropagation();
|
|
2650
|
-
requestSync(collection.name, check.updated);
|
|
2651
|
-
},
|
|
2652
|
-
children: syncingSource === collection.name ? tt("source.syncing") : tt("source.sync")
|
|
2653
|
-
}) : null,
|
|
2654
|
-
check !== void 0 && check.deleted.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2655
|
-
type: "button",
|
|
2656
|
-
className: panel_module_css_default.opBtn + " " + panel_module_css_default.opDanger,
|
|
2657
|
-
onClick: (event) => {
|
|
2658
|
-
event.stopPropagation();
|
|
2659
|
-
requestDelete(collection.name, check.deleted);
|
|
2660
|
-
},
|
|
2661
|
-
children: tt("source.followDelete")
|
|
2662
|
-
}) : null,
|
|
2663
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2664
|
-
type: "button",
|
|
2665
|
-
role: "switch",
|
|
2666
|
-
"aria-checked": view.state !== "off",
|
|
2667
|
-
"aria-label": collection.name,
|
|
2668
|
-
className: panel_module_css_default.switch + (view.state === "on" ? " " + panel_module_css_default.switchOn : view.state === "mixed" ? " " + panel_module_css_default.switchMixed : ""),
|
|
2669
|
-
disabled: batchBusy || collection.skillNames.length === 0 || view.state !== "off" && !hasWritable,
|
|
2670
|
-
title: view.state !== "off" && !hasWritable ? tt("groups.noWritable") : void 0,
|
|
2671
|
-
onClick: (event) => {
|
|
2672
|
-
event.stopPropagation();
|
|
2673
|
-
toggleGroup("col:" + collection.name, collection.name, view.state);
|
|
2674
|
-
},
|
|
2675
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: panel_module_css_default.switchThumb })
|
|
2676
|
-
})
|
|
2677
|
-
]
|
|
2678
|
-
})]
|
|
3557
|
+
})
|
|
3558
|
+
]
|
|
2679
3559
|
}), !collapsed ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [skills.map((skill) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillRow, {
|
|
2680
3560
|
skill,
|
|
2681
3561
|
hub
|
|
@@ -2687,48 +3567,121 @@ window.__ModuleLoader__.load({
|
|
|
2687
3567
|
}
|
|
2688
3568
|
}, record.name))] }) : null]
|
|
2689
3569
|
}, "col:" + collection.name);
|
|
2690
|
-
}
|
|
2691
|
-
(
|
|
3570
|
+
}
|
|
3571
|
+
if (topKey === "uncategorized-source" && hasPersonal) {
|
|
2692
3572
|
const uncategorized = filterBySource(sorted, sourceFilter, origins).filter((skill) => origins[skill.name] === void 0 && !isProjectSource(skill.source));
|
|
2693
|
-
|
|
3573
|
+
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");
|
|
3574
|
+
const allPersonalNames = [...uncategorized.map((s) => s.name), ...personalDisabled.map((r) => r.name)];
|
|
3575
|
+
if (allPersonalNames.length === 0) return null;
|
|
2694
3576
|
const collapsed = collapsedGroups.has("uncategorized-source");
|
|
3577
|
+
const isDragging = topDragKey === "uncategorized-source";
|
|
3578
|
+
const isOver = topOverKey === "uncategorized-source" && topDragKey !== "uncategorized-source";
|
|
2695
3579
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2696
|
-
className: panel_module_css_default.section,
|
|
2697
|
-
|
|
3580
|
+
className: panel_module_css_default.section + (isDragging ? " " + panel_module_css_default.dragging : "") + (isOver ? " " + panel_module_css_default.dragOver : ""),
|
|
3581
|
+
draggable: true,
|
|
3582
|
+
onDragStart: (e) => {
|
|
3583
|
+
setTopDragKey("uncategorized-source");
|
|
3584
|
+
e.dataTransfer.effectAllowed = "move";
|
|
3585
|
+
e.dataTransfer.setData("text/plain", "uncategorized-source");
|
|
3586
|
+
},
|
|
3587
|
+
onDragOver: (e) => {
|
|
3588
|
+
e.preventDefault();
|
|
3589
|
+
if (topOverKey !== "uncategorized-source") setTopOverKey("uncategorized-source");
|
|
3590
|
+
},
|
|
3591
|
+
onDragLeave: () => {
|
|
3592
|
+
if (topOverKey === "uncategorized-source") setTopOverKey(null);
|
|
3593
|
+
},
|
|
3594
|
+
onDrop: (e) => {
|
|
3595
|
+
e.preventDefault();
|
|
3596
|
+
handleTopDrop("uncategorized-source");
|
|
3597
|
+
setTopOverKey(null);
|
|
3598
|
+
},
|
|
3599
|
+
onDragEnd: () => {
|
|
3600
|
+
setTopDragKey(null);
|
|
3601
|
+
setTopOverKey(null);
|
|
3602
|
+
},
|
|
3603
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2698
3604
|
className: panel_module_css_default.groupHead,
|
|
2699
|
-
children:
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
},
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
uncategorized
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
3605
|
+
children: [
|
|
3606
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3607
|
+
className: panel_module_css_default.dragHandle,
|
|
3608
|
+
"aria-hidden": true,
|
|
3609
|
+
title: "拖拽调整顺序",
|
|
3610
|
+
children: "⋮⋮"
|
|
3611
|
+
}),
|
|
3612
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
3613
|
+
type: "button",
|
|
3614
|
+
className: panel_module_css_default.disclosure,
|
|
3615
|
+
"aria-expanded": !collapsed,
|
|
3616
|
+
onClick: () => {
|
|
3617
|
+
toggleGroupCollapse("uncategorized-source");
|
|
3618
|
+
},
|
|
3619
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: panel_module_css_default.chevron + (collapsed ? " " + panel_module_css_default.chevronCollapsed : "") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3620
|
+
className: panel_module_css_default.groupTitle,
|
|
3621
|
+
children: [
|
|
3622
|
+
tt("groups.personal"),
|
|
3623
|
+
" · ",
|
|
3624
|
+
allPersonalNames.length,
|
|
3625
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupSummary, {
|
|
3626
|
+
members: allPersonalNames,
|
|
3627
|
+
hub
|
|
3628
|
+
})
|
|
3629
|
+
]
|
|
3630
|
+
})]
|
|
3631
|
+
}),
|
|
3632
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3633
|
+
className: panel_module_css_default.groupOps,
|
|
3634
|
+
children: hub.editMode ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3635
|
+
type: "button",
|
|
3636
|
+
className: panel_module_css_default.opBtn + " " + panel_module_css_default.opDanger,
|
|
3637
|
+
title: tt("source.deleteGroupHint", { count: allPersonalNames.length }),
|
|
3638
|
+
onClick: (event) => {
|
|
3639
|
+
event.stopPropagation();
|
|
3640
|
+
requestDeleteGroup(tt("groups.personal"), allPersonalNames);
|
|
3641
|
+
},
|
|
3642
|
+
children: tt("source.deleteGroup")
|
|
3643
|
+
}) : null
|
|
3644
|
+
})
|
|
3645
|
+
]
|
|
3646
|
+
}), !collapsed ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [uncategorized.map((skill) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillRow, {
|
|
2720
3647
|
skill,
|
|
2721
3648
|
hub
|
|
2722
|
-
}, skill.name))
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
3649
|
+
}, skill.name)), personalDisabled.map((record) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DisabledRow, {
|
|
3650
|
+
record,
|
|
3651
|
+
busy: busyNames.has(record.name),
|
|
3652
|
+
onEnable: () => {
|
|
3653
|
+
enableDisabled(record);
|
|
3654
|
+
}
|
|
3655
|
+
}, record.name))] }) : null]
|
|
3656
|
+
}, "uncategorized-source");
|
|
3657
|
+
}
|
|
3658
|
+
return null;
|
|
3659
|
+
})] });
|
|
2726
3660
|
}
|
|
2727
3661
|
//#endregion
|
|
2728
3662
|
//#region src/client/panel/ScenesView.tsx
|
|
3663
|
+
/**
|
|
3664
|
+
* Scenes tab: user tag groups (one card per scene with the tri-state switch
|
|
3665
|
+
* and edit entry) plus the new-scene form. Scenes are the user's own
|
|
3666
|
+
* enable/disable units (e.g. a Godot scene vs a Java scene); upstream repos
|
|
3667
|
+
* are managed in the sources tab.
|
|
3668
|
+
*/
|
|
2729
3669
|
function ScenesView(props) {
|
|
2730
3670
|
const { hub } = props;
|
|
2731
3671
|
const { catalog, groupsState, sorted, normalized, collapsedGroups, viewNames, actionNames, batchBusy, busyNames, newTagName, setNewTagName, tagBusy, createTag, toggleGroupCollapse, toggleGroup, setEditingTag, setEditName, setMembersDraft, setEditSearch, enableDisabled } = hub;
|
|
3672
|
+
const [dragId, setDragId] = (0, react.useState)(null);
|
|
3673
|
+
const [overId, setOverId] = (0, react.useState)(null);
|
|
3674
|
+
const handleDrop = (targetId) => {
|
|
3675
|
+
if (dragId === null || dragId === targetId || groupsState === null) return;
|
|
3676
|
+
const ids = groupsState.tags.map((t) => t.id);
|
|
3677
|
+
const from = ids.indexOf(dragId);
|
|
3678
|
+
const to = ids.indexOf(targetId);
|
|
3679
|
+
if (from === -1 || to === -1) return;
|
|
3680
|
+
const next = [...ids];
|
|
3681
|
+
const [moved] = next.splice(from, 1);
|
|
3682
|
+
next.splice(to, 0, moved);
|
|
3683
|
+
hub.reorderTags(next);
|
|
3684
|
+
};
|
|
2732
3685
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2733
3686
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("form", {
|
|
2734
3687
|
className: panel_module_css_default.form,
|
|
@@ -2762,56 +3715,89 @@ window.__ModuleLoader__.load({
|
|
|
2762
3715
|
const collapsed = collapsedGroups.has("tag:" + tag.id);
|
|
2763
3716
|
const view = groupSwitchView(tag.skillNames, viewNames);
|
|
2764
3717
|
const hasWritable = tag.skillNames.some((name) => actionNames.has(name));
|
|
3718
|
+
const isDragging = dragId === tag.id;
|
|
3719
|
+
const isOver = overId === tag.id && dragId !== tag.id;
|
|
2765
3720
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2766
|
-
className: panel_module_css_default.section,
|
|
3721
|
+
className: panel_module_css_default.section + (isDragging ? " " + panel_module_css_default.dragging : "") + (isOver ? " " + panel_module_css_default.dragOver : ""),
|
|
3722
|
+
draggable: true,
|
|
3723
|
+
onDragStart: (e) => {
|
|
3724
|
+
setDragId(tag.id);
|
|
3725
|
+
e.dataTransfer.effectAllowed = "move";
|
|
3726
|
+
e.dataTransfer.setData("text/plain", tag.id);
|
|
3727
|
+
},
|
|
3728
|
+
onDragOver: (e) => {
|
|
3729
|
+
e.preventDefault();
|
|
3730
|
+
if (overId !== tag.id) setOverId(tag.id);
|
|
3731
|
+
},
|
|
3732
|
+
onDragLeave: () => {
|
|
3733
|
+
if (overId === tag.id) setOverId(null);
|
|
3734
|
+
},
|
|
3735
|
+
onDrop: (e) => {
|
|
3736
|
+
e.preventDefault();
|
|
3737
|
+
handleDrop(tag.id);
|
|
3738
|
+
setOverId(null);
|
|
3739
|
+
},
|
|
3740
|
+
onDragEnd: () => {
|
|
3741
|
+
setDragId(null);
|
|
3742
|
+
setOverId(null);
|
|
3743
|
+
},
|
|
2767
3744
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2768
3745
|
className: panel_module_css_default.groupHead,
|
|
2769
|
-
children: [
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
},
|
|
2776
|
-
|
|
2777
|
-
className: panel_module_css_default.groupTitle,
|
|
2778
|
-
children: [
|
|
2779
|
-
tag.name,
|
|
2780
|
-
" · ",
|
|
2781
|
-
tag.skillNames.length,
|
|
2782
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupSummary, {
|
|
2783
|
-
members: tag.skillNames,
|
|
2784
|
-
hub
|
|
2785
|
-
})
|
|
2786
|
-
]
|
|
2787
|
-
})]
|
|
2788
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2789
|
-
className: panel_module_css_default.groupOps,
|
|
2790
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2791
|
-
type: "button",
|
|
2792
|
-
role: "switch",
|
|
2793
|
-
"aria-checked": view.state !== "off",
|
|
2794
|
-
"aria-label": tag.name,
|
|
2795
|
-
className: panel_module_css_default.switch + (view.state === "on" ? " " + panel_module_css_default.switchOn : view.state === "mixed" ? " " + panel_module_css_default.switchMixed : ""),
|
|
2796
|
-
disabled: batchBusy || tag.skillNames.length === 0 || view.state !== "off" && !hasWritable,
|
|
2797
|
-
title: view.state !== "off" && !hasWritable ? tt("groups.noWritable") : void 0,
|
|
2798
|
-
onClick: (event) => {
|
|
2799
|
-
event.stopPropagation();
|
|
2800
|
-
toggleGroup("tag:" + tag.id, tag.name, view.state);
|
|
2801
|
-
},
|
|
2802
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: panel_module_css_default.switchThumb })
|
|
2803
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3746
|
+
children: [
|
|
3747
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3748
|
+
className: panel_module_css_default.dragHandle,
|
|
3749
|
+
"aria-hidden": true,
|
|
3750
|
+
title: "拖拽调整顺序",
|
|
3751
|
+
children: "⋮⋮"
|
|
3752
|
+
}),
|
|
3753
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
2804
3754
|
type: "button",
|
|
2805
|
-
className: panel_module_css_default.
|
|
3755
|
+
className: panel_module_css_default.disclosure,
|
|
3756
|
+
"aria-expanded": !collapsed,
|
|
2806
3757
|
onClick: () => {
|
|
2807
|
-
|
|
2808
|
-
setEditName(tag.name);
|
|
2809
|
-
setMembersDraft(new Set(tag.skillNames));
|
|
2810
|
-
setEditSearch("");
|
|
3758
|
+
toggleGroupCollapse("tag:" + tag.id);
|
|
2811
3759
|
},
|
|
2812
|
-
children:
|
|
2813
|
-
|
|
2814
|
-
|
|
3760
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: panel_module_css_default.chevron + (collapsed ? " " + panel_module_css_default.chevronCollapsed : "") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3761
|
+
className: panel_module_css_default.groupTitle,
|
|
3762
|
+
children: [
|
|
3763
|
+
tag.name,
|
|
3764
|
+
" · ",
|
|
3765
|
+
tag.skillNames.length,
|
|
3766
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupSummary, {
|
|
3767
|
+
members: tag.skillNames,
|
|
3768
|
+
hub
|
|
3769
|
+
})
|
|
3770
|
+
]
|
|
3771
|
+
})]
|
|
3772
|
+
}),
|
|
3773
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
3774
|
+
className: panel_module_css_default.groupOps,
|
|
3775
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3776
|
+
type: "button",
|
|
3777
|
+
role: "switch",
|
|
3778
|
+
"aria-checked": view.state !== "off",
|
|
3779
|
+
"aria-label": tag.name,
|
|
3780
|
+
className: panel_module_css_default.switch + (view.state === "on" ? " " + panel_module_css_default.switchOn : view.state === "mixed" ? " " + panel_module_css_default.switchMixed : ""),
|
|
3781
|
+
disabled: batchBusy || tag.skillNames.length === 0 || view.state !== "off" && !hasWritable,
|
|
3782
|
+
title: view.state !== "off" && !hasWritable ? tt("groups.noWritable") : void 0,
|
|
3783
|
+
onClick: (event) => {
|
|
3784
|
+
event.stopPropagation();
|
|
3785
|
+
toggleGroup("tag:" + tag.id, tag.name, view.state);
|
|
3786
|
+
},
|
|
3787
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: panel_module_css_default.switchThumb })
|
|
3788
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3789
|
+
type: "button",
|
|
3790
|
+
className: panel_module_css_default.opBtn,
|
|
3791
|
+
onClick: () => {
|
|
3792
|
+
setEditingTag(tag);
|
|
3793
|
+
setEditName(tag.name);
|
|
3794
|
+
setMembersDraft(new Set(tag.skillNames));
|
|
3795
|
+
setEditSearch("");
|
|
3796
|
+
},
|
|
3797
|
+
children: tt("groups.edit")
|
|
3798
|
+
})]
|
|
3799
|
+
})
|
|
3800
|
+
]
|
|
2815
3801
|
}), !collapsed ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [skills.map((skill) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillRow, {
|
|
2816
3802
|
skill,
|
|
2817
3803
|
hub
|
|
@@ -3064,17 +4050,66 @@ window.__ModuleLoader__.load({
|
|
|
3064
4050
|
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + " KB";
|
|
3065
4051
|
return (bytes / 1024 / 1024).toFixed(1) + " MB";
|
|
3066
4052
|
}
|
|
4053
|
+
function formatSpeed(bps) {
|
|
4054
|
+
if (bps === void 0 || bps <= 0) return "";
|
|
4055
|
+
if (bps < 1024) return bps + " B/s";
|
|
4056
|
+
if (bps < 1024 * 1024) return (bps / 1024).toFixed(1) + " KB/s";
|
|
4057
|
+
return (bps / 1024 / 1024).toFixed(1) + " MB/s";
|
|
4058
|
+
}
|
|
3067
4059
|
function MarketView(props) {
|
|
3068
4060
|
const { hub } = props;
|
|
3069
|
-
const { marketState, marketCheck, sourceCheck, sourcesState, repoDiscoverState, scanningRepo, repoSelected, setRepoSelected, repoImporting, repoResult, newSourceName, setNewSourceName, syncingMarket, tagBusy, addSource, addMarketSource, removeMarketSource, scanRepo, checkMarket, checkSources, syncMarketSource, toggleRepoSelected, importRepo, updateAllDialog, setUpdateAllDialog, updateAll } = hub;
|
|
4061
|
+
const { marketState, marketCheck, sourceCheck, sourcesState, repoDiscoverState, scanningRepo, repoSelected, setRepoSelected, repoImporting, repoResult, newSourceName, setNewSourceName, syncingMarket, tagBusy, checkingSource, addSource, addMarketSource, removeMarketSource, scanRepo, checkMarket, checkSources, syncMarketSource, toggleRepoSelected, importRepo, cancelImport, updateAllDialog, setUpdateAllDialog, updateAll } = hub;
|
|
3070
4062
|
/** 有可更新技能的来源(全部更新按钮与确认列表共用)。 */
|
|
3071
4063
|
const updatableRepos = Object.entries(sourceCheck).filter(([, check]) => check.changed && check.updated.length > 0);
|
|
3072
|
-
|
|
4064
|
+
const [repoSearch, setRepoSearch] = (0, react.useState)("");
|
|
4065
|
+
const [repoFilter, setRepoFilter] = (0, react.useState)("all");
|
|
4066
|
+
const [visibleCount, setVisibleCount] = (0, react.useState)(50);
|
|
4067
|
+
(0, react.useEffect)(() => {
|
|
4068
|
+
setVisibleCount(50);
|
|
4069
|
+
}, [
|
|
4070
|
+
repoDiscoverState,
|
|
4071
|
+
repoSearch,
|
|
4072
|
+
repoFilter
|
|
4073
|
+
]);
|
|
4074
|
+
/** 一个已添加来源的完整行:状态徽章 + 操作按钮。B 方案:扫描主按钮 + 智能更新按钮(检查/更新二合一)+ 溢出移除 */
|
|
3073
4075
|
const sourceRow = (record) => {
|
|
3074
4076
|
const releaseCheck = marketCheck[record.repo];
|
|
3075
4077
|
const skillCheck = sourceCheck[record.repo];
|
|
3076
4078
|
const installedCount = sourcesState?.sources.find((source) => source.repo === record.repo)?.skills.length ?? 0;
|
|
3077
4079
|
const scanning = scanningRepo === record.repo;
|
|
4080
|
+
const syncing = syncingMarket === record.repo;
|
|
4081
|
+
const checking = checkingSource === record.repo;
|
|
4082
|
+
const hasSkillUpdate = skillCheck?.changed === true && skillCheck.updated.length > 0;
|
|
4083
|
+
const hasReleaseUpdate = releaseCheck?.updateAvailable === true;
|
|
4084
|
+
const hasUpdate = hasSkillUpdate || hasReleaseUpdate;
|
|
4085
|
+
const isChecked = releaseCheck !== void 0 || skillCheck !== void 0;
|
|
4086
|
+
let updateLabel;
|
|
4087
|
+
let updateDisabled = false;
|
|
4088
|
+
let updateDanger = false;
|
|
4089
|
+
let updateAction = () => {};
|
|
4090
|
+
if (syncing) {
|
|
4091
|
+
updateLabel = tt("market.syncing");
|
|
4092
|
+
updateDisabled = true;
|
|
4093
|
+
} else if (checking) {
|
|
4094
|
+
updateLabel = tt("market.checking");
|
|
4095
|
+
updateDisabled = true;
|
|
4096
|
+
} else if (!isChecked) {
|
|
4097
|
+
updateLabel = tt("market.checkUpdate");
|
|
4098
|
+
updateAction = () => {
|
|
4099
|
+
checkSources(record.repo);
|
|
4100
|
+
checkMarket();
|
|
4101
|
+
};
|
|
4102
|
+
} else if (hasUpdate) {
|
|
4103
|
+
const count = hasSkillUpdate ? skillCheck.updated.length : 1;
|
|
4104
|
+
updateLabel = count > 1 || hasSkillUpdate ? tt("market.updateCount", { count }) : tt("market.update");
|
|
4105
|
+
updateDanger = true;
|
|
4106
|
+
updateAction = () => {
|
|
4107
|
+
syncMarketSource(record.repo);
|
|
4108
|
+
};
|
|
4109
|
+
} else {
|
|
4110
|
+
updateLabel = tt("market.upToDate");
|
|
4111
|
+
updateDisabled = true;
|
|
4112
|
+
}
|
|
3078
4113
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3079
4114
|
className: panel_module_css_default.row + " " + panel_module_css_default.rowStatic,
|
|
3080
4115
|
children: [
|
|
@@ -3098,7 +4133,7 @@ window.__ModuleLoader__.load({
|
|
|
3098
4133
|
className: panel_module_css_default.badge + " " + panel_module_css_default.badgeCount,
|
|
3099
4134
|
children: tt("market.installed", { count: installedCount })
|
|
3100
4135
|
}) : null,
|
|
3101
|
-
|
|
4136
|
+
hasSkillUpdate ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3102
4137
|
className: panel_module_css_default.badge + " " + panel_module_css_default.statusUpdated,
|
|
3103
4138
|
children: tt("market.updatable", { count: skillCheck.updated.length })
|
|
3104
4139
|
}) : null,
|
|
@@ -3106,7 +4141,7 @@ window.__ModuleLoader__.load({
|
|
|
3106
4141
|
className: panel_module_css_default.badge + " " + panel_module_css_default.statusError,
|
|
3107
4142
|
children: tt("market.deletedUpstream", { count: skillCheck.deleted.length })
|
|
3108
4143
|
}) : null,
|
|
3109
|
-
|
|
4144
|
+
hasReleaseUpdate ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3110
4145
|
className: panel_module_css_default.badge + " " + panel_module_css_default.statusUpdated,
|
|
3111
4146
|
children: releaseCheck.latestTag !== void 0 ? tt("market.newRelease", { version: releaseCheck.latestTag }) : tt("market.updated")
|
|
3112
4147
|
}) : null
|
|
@@ -3115,40 +4150,39 @@ window.__ModuleLoader__.load({
|
|
|
3115
4150
|
}),
|
|
3116
4151
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3117
4152
|
type: "button",
|
|
3118
|
-
className: panel_module_css_default.
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
},
|
|
3123
|
-
children: tt("market.check")
|
|
3124
|
-
}),
|
|
3125
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3126
|
-
type: "button",
|
|
3127
|
-
className: panel_module_css_default.opBtn + (releaseCheck?.updateAvailable === true ? " " + panel_module_css_default.opDanger : ""),
|
|
3128
|
-
disabled: syncingMarket === record.repo,
|
|
3129
|
-
onClick: () => {
|
|
3130
|
-
syncMarketSource(record.repo);
|
|
4153
|
+
className: panel_module_css_default.button + " " + panel_module_css_default.primary,
|
|
4154
|
+
style: {
|
|
4155
|
+
padding: "4px 10px",
|
|
4156
|
+
fontSize: 12
|
|
3131
4157
|
},
|
|
3132
|
-
children: syncingMarket === record.repo ? tt("market.syncing") : tt("market.sync")
|
|
3133
|
-
}),
|
|
3134
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3135
|
-
type: "button",
|
|
3136
|
-
className: panel_module_css_default.opBtn,
|
|
3137
4158
|
disabled: scanning,
|
|
3138
4159
|
onClick: () => {
|
|
3139
4160
|
scanRepo(record.repo);
|
|
3140
4161
|
},
|
|
3141
4162
|
children: scanning ? tt("market.scanning") : tt("market.scan")
|
|
3142
4163
|
}),
|
|
4164
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4165
|
+
type: "button",
|
|
4166
|
+
className: panel_module_css_default.opBtn + (updateDanger ? " " + panel_module_css_default.opDanger : ""),
|
|
4167
|
+
disabled: updateDisabled,
|
|
4168
|
+
title: hasUpdate ? "将同步到上游最新版并提示可更新的本地技能" : void 0,
|
|
4169
|
+
onClick: updateAction,
|
|
4170
|
+
children: updateLabel
|
|
4171
|
+
}),
|
|
3143
4172
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3144
4173
|
type: "button",
|
|
3145
4174
|
className: panel_module_css_default.opBtn,
|
|
3146
4175
|
disabled: tagBusy,
|
|
3147
4176
|
title: tt("market.removeHint"),
|
|
4177
|
+
"aria-label": tt("market.deleteSource"),
|
|
3148
4178
|
onClick: () => {
|
|
3149
4179
|
removeMarketSource(record.repo);
|
|
3150
4180
|
},
|
|
3151
|
-
|
|
4181
|
+
style: {
|
|
4182
|
+
padding: "4px 8px",
|
|
4183
|
+
minWidth: 28
|
|
4184
|
+
},
|
|
4185
|
+
children: "×"
|
|
3152
4186
|
})
|
|
3153
4187
|
]
|
|
3154
4188
|
});
|
|
@@ -3273,109 +4307,379 @@ window.__ModuleLoader__.load({
|
|
|
3273
4307
|
marketState.status !== "loading" ? rows.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react.Fragment, { children: row.element }, row.key)) : null
|
|
3274
4308
|
]
|
|
3275
4309
|
}),
|
|
3276
|
-
repoDiscoverState.status
|
|
3277
|
-
className: panel_module_css_default.
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
children:
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
onClick: () => {
|
|
3307
|
-
setRepoSelected(/* @__PURE__ */ new Set());
|
|
3308
|
-
},
|
|
3309
|
-
children: tt("repo.clearAll")
|
|
3310
|
-
})]
|
|
3311
|
-
}),
|
|
3312
|
-
["skills", "design-templates"].map((root) => {
|
|
3313
|
-
const rootEntries = entries.filter((entry) => entry.root === root);
|
|
3314
|
-
if (rootEntries.length === 0) return null;
|
|
3315
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3316
|
-
className: panel_module_css_default.section,
|
|
3317
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3318
|
-
className: panel_module_css_default.sectionTitle,
|
|
3319
|
-
children: root === "skills" ? tt("repo.root.skills") : tt("repo.root.designTemplates")
|
|
3320
|
-
}), rootEntries.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
3321
|
-
className: panel_module_css_default.row + (entry.existing ? " " + panel_module_css_default.rowMuted : ""),
|
|
4310
|
+
repoDiscoverState.status !== "idle" || repoResult !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
4311
|
+
className: panel_module_css_default.section,
|
|
4312
|
+
style: {
|
|
4313
|
+
borderLeft: "3px solid var(--hub-model, #2f81f7)",
|
|
4314
|
+
background: "rgba(47,129,247,0.04)"
|
|
4315
|
+
},
|
|
4316
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4317
|
+
className: panel_module_css_default.sectionTitle + " " + panel_module_css_default.sectionHeadRow,
|
|
4318
|
+
style: { margin: "10px 14px 6px" },
|
|
4319
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4320
|
+
className: panel_module_css_default.sectionTitleFill,
|
|
4321
|
+
style: {
|
|
4322
|
+
display: "flex",
|
|
4323
|
+
alignItems: "center",
|
|
4324
|
+
gap: 6
|
|
4325
|
+
},
|
|
4326
|
+
children: [
|
|
4327
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
|
|
4328
|
+
width: 7,
|
|
4329
|
+
height: 7,
|
|
4330
|
+
borderRadius: "50%",
|
|
4331
|
+
background: "var(--hub-model, #2f81f7)",
|
|
4332
|
+
display: "inline-block"
|
|
4333
|
+
} }),
|
|
4334
|
+
"扫描结果",
|
|
4335
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4336
|
+
style: {
|
|
4337
|
+
fontWeight: 400,
|
|
4338
|
+
opacity: .75
|
|
4339
|
+
},
|
|
3322
4340
|
children: [
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
}
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
className: panel_module_css_default.rowMain,
|
|
3333
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3334
|
-
className: panel_module_css_default.rowName,
|
|
3335
|
-
children: entry.name
|
|
3336
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
3337
|
-
className: panel_module_css_default.rowDesc,
|
|
3338
|
-
children: [
|
|
3339
|
-
entry.dir,
|
|
3340
|
-
" · ",
|
|
3341
|
-
tt("repo.files", {
|
|
3342
|
-
count: entry.fileCount,
|
|
3343
|
-
size: formatBytes(entry.totalBytes)
|
|
3344
|
-
})
|
|
3345
|
-
]
|
|
3346
|
-
})]
|
|
3347
|
-
}),
|
|
3348
|
-
entry.existing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3349
|
-
className: panel_module_css_default.badge + " " + panel_module_css_default.badgeReadonly,
|
|
3350
|
-
children: tt("repo.existing")
|
|
3351
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
3352
|
-
className: panel_module_css_default.badge + " " + panel_module_css_default.badgeSource,
|
|
3353
|
-
children: entry.origin
|
|
3354
|
-
})
|
|
4341
|
+
"— ",
|
|
4342
|
+
repoDiscoverState.status === "ready" ? repoDiscoverState.data.repo : repoDiscoverState.status === "error" ? scanningRepo ?? "" : scanningRepo ?? "",
|
|
4343
|
+
repoDiscoverState.status === "ready" && repoDiscoverState.data.ref !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4344
|
+
style: {
|
|
4345
|
+
opacity: .5,
|
|
4346
|
+
marginLeft: 6
|
|
4347
|
+
},
|
|
4348
|
+
children: ["ref ", repoDiscoverState.data.ref]
|
|
4349
|
+
}) : null
|
|
3355
4350
|
]
|
|
3356
|
-
}
|
|
3357
|
-
|
|
3358
|
-
}),
|
|
3359
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4351
|
+
})
|
|
4352
|
+
]
|
|
4353
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
3360
4354
|
type: "button",
|
|
3361
|
-
className: panel_module_css_default.
|
|
3362
|
-
disabled: selected.length === 0 || repoImporting,
|
|
4355
|
+
className: panel_module_css_default.opBtn,
|
|
3363
4356
|
onClick: () => {
|
|
3364
|
-
|
|
4357
|
+
hub.clearScan();
|
|
3365
4358
|
},
|
|
3366
|
-
children:
|
|
3367
|
-
})
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
4359
|
+
children: "关闭"
|
|
4360
|
+
})]
|
|
4361
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4362
|
+
style: { padding: "0 12px 12px" },
|
|
4363
|
+
children: [
|
|
4364
|
+
repoDiscoverState.status === "scanning" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4365
|
+
className: panel_module_css_default.empty,
|
|
4366
|
+
style: { padding: "12px 0" },
|
|
4367
|
+
children: tt("market.scanning")
|
|
4368
|
+
}) : null,
|
|
4369
|
+
repoDiscoverState.status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4370
|
+
className: panel_module_css_default.errorBanner,
|
|
4371
|
+
children: repoDiscoverState.message
|
|
4372
|
+
}) : null,
|
|
4373
|
+
repoDiscoverState.status === "ready" ? (() => {
|
|
4374
|
+
const entries = repoDiscoverState.data.entries;
|
|
4375
|
+
const installedNames = /* @__PURE__ */ new Set([
|
|
4376
|
+
...(hub.catalog?.skills ?? []).map((s) => s.name),
|
|
4377
|
+
...(hub.catalog?.disabled ?? []).map((d) => d.name),
|
|
4378
|
+
...(repoResult?.imported ?? []).map((r) => r.name),
|
|
4379
|
+
...(repoResult?.skipped ?? []).map((r) => r.name)
|
|
4380
|
+
]);
|
|
4381
|
+
const isExisting = (entry) => entry.existing || installedNames.has(entry.name);
|
|
4382
|
+
const selected = entries.filter((entry) => repoSelected.has(entry.path) && !isExisting(entry));
|
|
4383
|
+
const selectedBytes = selected.reduce((s, e) => s + e.totalBytes, 0);
|
|
4384
|
+
const filtered = entries.filter((entry) => {
|
|
4385
|
+
if (repoFilter !== "all" && entry.root !== repoFilter) return false;
|
|
4386
|
+
if (repoSearch.trim() !== "" && !entry.name.toLowerCase().includes(repoSearch.trim().toLowerCase())) return false;
|
|
4387
|
+
return true;
|
|
4388
|
+
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
4389
|
+
const paged = filtered.slice(0, visibleCount);
|
|
4390
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
4391
|
+
className: panel_module_css_default.hintLine + " " + panel_module_css_default.hintInline,
|
|
4392
|
+
children: [
|
|
4393
|
+
tt("repo.ready", { count: entries.length }),
|
|
4394
|
+
" · ",
|
|
4395
|
+
repoDiscoverState.data.ref !== null ? `ref ${repoDiscoverState.data.ref}` : ""
|
|
4396
|
+
]
|
|
4397
|
+
}), entries.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4398
|
+
className: panel_module_css_default.empty,
|
|
4399
|
+
children: tt("repo.empty")
|
|
4400
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
4401
|
+
(() => {
|
|
4402
|
+
const order = /* @__PURE__ */ new Map([["skills", 0], ["design-templates", 1]]);
|
|
4403
|
+
const roots = [...new Set(entries.map((e) => e.root))].sort((a, b) => {
|
|
4404
|
+
const ao = order.get(a) ?? 99;
|
|
4405
|
+
const bo = order.get(b) ?? 99;
|
|
4406
|
+
return ao !== bo ? ao - bo : a.localeCompare(b);
|
|
4407
|
+
});
|
|
4408
|
+
const counts = /* @__PURE__ */ new Map();
|
|
4409
|
+
for (const e of entries) counts.set(e.root, (counts.get(e.root) ?? 0) + 1);
|
|
4410
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4411
|
+
style: {
|
|
4412
|
+
display: "flex",
|
|
4413
|
+
gap: 8,
|
|
4414
|
+
marginBottom: 8,
|
|
4415
|
+
flexWrap: "wrap"
|
|
4416
|
+
},
|
|
4417
|
+
children: [
|
|
4418
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
4419
|
+
className: panel_module_css_default.input,
|
|
4420
|
+
style: {
|
|
4421
|
+
flex: 1,
|
|
4422
|
+
minWidth: 140
|
|
4423
|
+
},
|
|
4424
|
+
value: repoSearch,
|
|
4425
|
+
onChange: (e) => setRepoSearch(e.target.value),
|
|
4426
|
+
placeholder: "搜索技能名…"
|
|
4427
|
+
}),
|
|
4428
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4429
|
+
type: "button",
|
|
4430
|
+
className: panel_module_css_default.button + (repoFilter === "all" ? " " + panel_module_css_default.primary : ""),
|
|
4431
|
+
style: {
|
|
4432
|
+
padding: "6px 10px",
|
|
4433
|
+
fontSize: 12
|
|
4434
|
+
},
|
|
4435
|
+
onClick: () => setRepoFilter("all"),
|
|
4436
|
+
children: ["全部 ", entries.length]
|
|
4437
|
+
}),
|
|
4438
|
+
roots.map((root) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4439
|
+
type: "button",
|
|
4440
|
+
className: panel_module_css_default.button + (repoFilter === root ? " " + panel_module_css_default.primary : ""),
|
|
4441
|
+
style: {
|
|
4442
|
+
padding: "6px 10px",
|
|
4443
|
+
fontSize: 12
|
|
4444
|
+
},
|
|
4445
|
+
onClick: () => setRepoFilter(root),
|
|
4446
|
+
children: [
|
|
4447
|
+
root,
|
|
4448
|
+
" ",
|
|
4449
|
+
counts.get(root) ?? 0
|
|
4450
|
+
]
|
|
4451
|
+
}, root))
|
|
4452
|
+
]
|
|
4453
|
+
});
|
|
4454
|
+
})(),
|
|
4455
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4456
|
+
className: panel_module_css_default.hintLine,
|
|
4457
|
+
style: {
|
|
4458
|
+
display: "flex",
|
|
4459
|
+
justifyContent: "space-between",
|
|
4460
|
+
flexWrap: "wrap",
|
|
4461
|
+
gap: 8
|
|
4462
|
+
},
|
|
4463
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
4464
|
+
"已选 ",
|
|
4465
|
+
selected.length,
|
|
4466
|
+
"/",
|
|
4467
|
+
entries.length,
|
|
4468
|
+
" · ",
|
|
4469
|
+
formatBytes(selectedBytes),
|
|
4470
|
+
" · 显示 ",
|
|
4471
|
+
paged.length,
|
|
4472
|
+
"/",
|
|
4473
|
+
filtered.length,
|
|
4474
|
+
"(过滤后)"
|
|
4475
|
+
] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4476
|
+
style: {
|
|
4477
|
+
display: "flex",
|
|
4478
|
+
gap: 6
|
|
4479
|
+
},
|
|
4480
|
+
children: [
|
|
4481
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4482
|
+
type: "button",
|
|
4483
|
+
className: panel_module_css_default.button,
|
|
4484
|
+
style: {
|
|
4485
|
+
padding: "4px 8px",
|
|
4486
|
+
fontSize: 11
|
|
4487
|
+
},
|
|
4488
|
+
onClick: () => {
|
|
4489
|
+
setRepoSelected(new Set(filtered.filter((e) => !isExisting(e)).slice(0, visibleCount).map((e) => e.path)));
|
|
4490
|
+
},
|
|
4491
|
+
children: "全选当前显示"
|
|
4492
|
+
}),
|
|
4493
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4494
|
+
type: "button",
|
|
4495
|
+
className: panel_module_css_default.button,
|
|
4496
|
+
style: {
|
|
4497
|
+
padding: "4px 8px",
|
|
4498
|
+
fontSize: 11
|
|
4499
|
+
},
|
|
4500
|
+
onClick: () => {
|
|
4501
|
+
setRepoSelected(new Set(entries.filter((e) => !isExisting(e)).map((e) => e.path)));
|
|
4502
|
+
},
|
|
4503
|
+
children: ["全选全部 ", entries.filter((e) => !isExisting(e)).length]
|
|
4504
|
+
}),
|
|
4505
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4506
|
+
type: "button",
|
|
4507
|
+
className: panel_module_css_default.button,
|
|
4508
|
+
style: {
|
|
4509
|
+
padding: "4px 8px",
|
|
4510
|
+
fontSize: 11
|
|
4511
|
+
},
|
|
4512
|
+
onClick: () => {
|
|
4513
|
+
setRepoSelected(/* @__PURE__ */ new Set());
|
|
4514
|
+
},
|
|
4515
|
+
children: tt("repo.clearAll")
|
|
4516
|
+
})
|
|
4517
|
+
]
|
|
4518
|
+
})]
|
|
4519
|
+
}),
|
|
4520
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4521
|
+
className: panel_module_css_default.scanList,
|
|
4522
|
+
children: paged.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4523
|
+
className: panel_module_css_default.empty,
|
|
4524
|
+
style: { padding: 20 },
|
|
4525
|
+
children: "无匹配技能"
|
|
4526
|
+
}) : paged.map((entry) => {
|
|
4527
|
+
const existing = isExisting(entry);
|
|
4528
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
4529
|
+
className: panel_module_css_default.row + (existing ? " " + panel_module_css_default.rowMuted : ""),
|
|
4530
|
+
children: [
|
|
4531
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
4532
|
+
type: "checkbox",
|
|
4533
|
+
checked: repoSelected.has(entry.path),
|
|
4534
|
+
disabled: existing,
|
|
4535
|
+
onChange: (event) => {
|
|
4536
|
+
toggleRepoSelected(entry.path, event.target.checked);
|
|
4537
|
+
}
|
|
4538
|
+
}),
|
|
4539
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4540
|
+
className: panel_module_css_default.rowMain,
|
|
4541
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4542
|
+
className: panel_module_css_default.rowName,
|
|
4543
|
+
children: entry.name
|
|
4544
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4545
|
+
className: panel_module_css_default.rowDesc,
|
|
4546
|
+
children: [
|
|
4547
|
+
entry.dir,
|
|
4548
|
+
" · ",
|
|
4549
|
+
tt("repo.files", {
|
|
4550
|
+
count: entry.fileCount,
|
|
4551
|
+
size: formatBytes(entry.totalBytes)
|
|
4552
|
+
})
|
|
4553
|
+
]
|
|
4554
|
+
})]
|
|
4555
|
+
}),
|
|
4556
|
+
existing ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4557
|
+
className: panel_module_css_default.badge + " " + panel_module_css_default.badgeReadonly,
|
|
4558
|
+
children: tt("repo.existing")
|
|
4559
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4560
|
+
className: panel_module_css_default.badge + " " + panel_module_css_default.badgeSource,
|
|
4561
|
+
children: entry.origin
|
|
4562
|
+
})
|
|
4563
|
+
]
|
|
4564
|
+
}, entry.path);
|
|
4565
|
+
})
|
|
4566
|
+
}),
|
|
4567
|
+
visibleCount < filtered.length ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4568
|
+
style: {
|
|
4569
|
+
textAlign: "center",
|
|
4570
|
+
marginTop: 8
|
|
4571
|
+
},
|
|
4572
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
4573
|
+
type: "button",
|
|
4574
|
+
className: panel_module_css_default.button,
|
|
4575
|
+
onClick: () => setVisibleCount((n) => n + 50),
|
|
4576
|
+
children: [
|
|
4577
|
+
"加载更多 50 (剩余 ",
|
|
4578
|
+
filtered.length - visibleCount,
|
|
4579
|
+
")"
|
|
4580
|
+
]
|
|
4581
|
+
})
|
|
4582
|
+
}) : null,
|
|
4583
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4584
|
+
className: panel_module_css_default.buttons + " " + panel_module_css_default.actionsTop,
|
|
4585
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4586
|
+
type: "button",
|
|
4587
|
+
className: panel_module_css_default.button + " " + panel_module_css_default.primary,
|
|
4588
|
+
disabled: selected.length === 0 || repoImporting,
|
|
4589
|
+
onClick: () => {
|
|
4590
|
+
importRepo();
|
|
4591
|
+
},
|
|
4592
|
+
children: repoImporting ? tt("repo.importing") : `${tt("repo.import", { count: selected.length })} · ${formatBytes(selectedBytes)}`
|
|
4593
|
+
}), repoImporting ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
4594
|
+
type: "button",
|
|
4595
|
+
className: panel_module_css_default.button,
|
|
4596
|
+
onClick: () => {
|
|
4597
|
+
cancelImport();
|
|
4598
|
+
},
|
|
4599
|
+
children: tt("repo.cancel")
|
|
4600
|
+
}) : null]
|
|
4601
|
+
}),
|
|
4602
|
+
repoResult !== null && repoResult.status === "running" ? (() => {
|
|
4603
|
+
const pct = repoResult.totalBytes > 0 ? Math.min(100, Math.round(repoResult.downloadedBytes / repoResult.totalBytes * 100)) : repoResult.total > 0 ? Math.round(repoResult.done / repoResult.total * 100) : 0;
|
|
4604
|
+
const speed = formatSpeed(repoResult.bytesPerSecond);
|
|
4605
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4606
|
+
className: panel_module_css_default.formSuccess + " " + panel_module_css_default.actionsTop,
|
|
4607
|
+
children: [
|
|
4608
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4609
|
+
style: {
|
|
4610
|
+
display: "flex",
|
|
4611
|
+
alignItems: "center",
|
|
4612
|
+
gap: 8,
|
|
4613
|
+
marginBottom: 6
|
|
4614
|
+
},
|
|
4615
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4616
|
+
className: panel_module_css_default.scanProgressTrack,
|
|
4617
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4618
|
+
className: panel_module_css_default.scanProgressFill,
|
|
4619
|
+
style: { width: `${pct}%` }
|
|
4620
|
+
})
|
|
4621
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4622
|
+
style: {
|
|
4623
|
+
fontSize: 11,
|
|
4624
|
+
opacity: .75,
|
|
4625
|
+
minWidth: 32,
|
|
4626
|
+
textAlign: "right"
|
|
4627
|
+
},
|
|
4628
|
+
children: [pct, "%"]
|
|
4629
|
+
})]
|
|
4630
|
+
}),
|
|
4631
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4632
|
+
className: panel_module_css_default.hintLine,
|
|
4633
|
+
children: [
|
|
4634
|
+
formatBytes(repoResult.downloadedBytes),
|
|
4635
|
+
" / ",
|
|
4636
|
+
formatBytes(repoResult.totalBytes),
|
|
4637
|
+
speed !== "" ? ` · ${speed}` : "",
|
|
4638
|
+
" · ",
|
|
4639
|
+
repoResult.done,
|
|
4640
|
+
"/",
|
|
4641
|
+
repoResult.total,
|
|
4642
|
+
" 个技能"
|
|
4643
|
+
]
|
|
4644
|
+
}),
|
|
4645
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4646
|
+
className: panel_module_css_default.hintLine,
|
|
4647
|
+
style: { marginTop: 4 },
|
|
4648
|
+
children: [repoResult.current !== void 0 ? tt("repo.importingCurrent", {
|
|
4649
|
+
name: repoResult.current,
|
|
4650
|
+
done: repoResult.done + 1,
|
|
4651
|
+
total: repoResult.total
|
|
4652
|
+
}) : tt("repo.importing"), repoResult.currentFile !== void 0 ? ` · ${repoResult.currentFile}` : ""]
|
|
4653
|
+
}),
|
|
4654
|
+
repoResult.failed.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4655
|
+
className: panel_module_css_default.errorBanner,
|
|
4656
|
+
style: { marginTop: 6 },
|
|
4657
|
+
children: repoResult.failed.map((f) => `${f.name}: ${f.error}`).join("; ")
|
|
4658
|
+
}) : null
|
|
4659
|
+
]
|
|
4660
|
+
});
|
|
4661
|
+
})() : null
|
|
4662
|
+
] })] });
|
|
4663
|
+
})() : null,
|
|
4664
|
+
repoResult !== null && repoResult.status !== "running" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4665
|
+
className: repoResult.status === "cancelled" ? panel_module_css_default.errorBanner + " " + panel_module_css_default.actionsTop : panel_module_css_default.formSuccess + " " + panel_module_css_default.actionsTop,
|
|
4666
|
+
children: [
|
|
4667
|
+
repoResult.status === "cancelled" ? tt("repo.cancelled", {
|
|
4668
|
+
imported: repoResult.imported.length,
|
|
4669
|
+
total: repoResult.total
|
|
4670
|
+
}) : `${tt("repo.imported", { count: repoResult.imported.length })} · ${tt("repo.skippedExisting", { count: repoResult.skipped.length })} · ${tt("repo.failed", { count: repoResult.failed.length })}`,
|
|
4671
|
+
repoResult.failed.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4672
|
+
style: { marginTop: 6 },
|
|
4673
|
+
children: repoResult.failed.map((f) => `${f.name}: ${f.error}`).join("; ")
|
|
4674
|
+
}) : null,
|
|
4675
|
+
repoResult.status === "error" && repoResult.error ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4676
|
+
style: { marginTop: 6 },
|
|
4677
|
+
children: repoResult.error
|
|
4678
|
+
}) : null
|
|
4679
|
+
]
|
|
4680
|
+
}) : null
|
|
4681
|
+
]
|
|
4682
|
+
})]
|
|
3379
4683
|
}) : null,
|
|
3380
4684
|
updateAllDialog ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfirmDialog, {
|
|
3381
4685
|
title: tt("market.updateAllConfirmTitle"),
|
|
@@ -3442,6 +4746,8 @@ window.__ModuleLoader__.load({
|
|
|
3442
4746
|
const [repoSelected, setRepoSelected] = (0, react.useState)(/* @__PURE__ */ new Set());
|
|
3443
4747
|
const [repoImporting, setRepoImporting] = (0, react.useState)(false);
|
|
3444
4748
|
const [repoResult, setRepoResult] = (0, react.useState)(null);
|
|
4749
|
+
const [importJobId, setImportJobId] = (0, react.useState)(null);
|
|
4750
|
+
const pollAbortRef = (0, react.useRef)(null);
|
|
3445
4751
|
const [search, setSearch] = (0, react.useState)("");
|
|
3446
4752
|
/** 工作区(项目)路径;空 = 只看用户级技能。 */
|
|
3447
4753
|
const [workspace, setWorkspace] = (0, react.useState)("");
|
|
@@ -3480,6 +4786,7 @@ window.__ModuleLoader__.load({
|
|
|
3480
4786
|
const [conflictDialog, setConflictDialog] = (0, react.useState)(null);
|
|
3481
4787
|
const [confirmDialog, setConfirmDialog] = (0, react.useState)(null);
|
|
3482
4788
|
const [deleteSkillDialog, setDeleteSkillDialog] = (0, react.useState)(null);
|
|
4789
|
+
const [deleteGroupDialog, setDeleteGroupDialog] = (0, react.useState)(null);
|
|
3483
4790
|
const [confirmClearTrash, setConfirmClearTrash] = (0, react.useState)(false);
|
|
3484
4791
|
/** 「全部更新」确认对话框(市场 tab)。 */
|
|
3485
4792
|
const [updateAllDialog, setUpdateAllDialog] = (0, react.useState)(false);
|
|
@@ -3494,6 +4801,7 @@ window.__ModuleLoader__.load({
|
|
|
3494
4801
|
/** 项目级三级树里已细分(按 .dsh/.agents)的项目键。 */
|
|
3495
4802
|
const [subdividedProjects, setSubdividedProjects] = (0, react.useState)(/* @__PURE__ */ new Set());
|
|
3496
4803
|
const [showLegend, setShowLegend] = (0, react.useState)(false);
|
|
4804
|
+
const [editMode, setEditMode] = (0, react.useState)(false);
|
|
3497
4805
|
const toggleGroupCollapse = (0, react.useCallback)((key) => {
|
|
3498
4806
|
setCollapsedGroups((previous) => {
|
|
3499
4807
|
const next = new Set(previous);
|
|
@@ -3870,6 +5178,43 @@ window.__ModuleLoader__.load({
|
|
|
3870
5178
|
loadGroups,
|
|
3871
5179
|
loadSources
|
|
3872
5180
|
]);
|
|
5181
|
+
/** 打开整组删除确认(来源分组一键删除)。 */
|
|
5182
|
+
const requestDeleteGroup = (0, react.useCallback)((name, skillNames) => {
|
|
5183
|
+
setDeleteGroupDialog({
|
|
5184
|
+
name,
|
|
5185
|
+
skillNames
|
|
5186
|
+
});
|
|
5187
|
+
}, []);
|
|
5188
|
+
/** 执行整组删除(逐个移入回收站,跳过只读)。 */
|
|
5189
|
+
const runDeleteGroup = (0, react.useCallback)(async () => {
|
|
5190
|
+
const dialog = deleteGroupDialog;
|
|
5191
|
+
if (dialog === null) return;
|
|
5192
|
+
setDeleteGroupDialog(null);
|
|
5193
|
+
setTagBusy(true);
|
|
5194
|
+
setLoadError(null);
|
|
5195
|
+
const failures = [];
|
|
5196
|
+
let done = 0;
|
|
5197
|
+
for (const name of dialog.skillNames) try {
|
|
5198
|
+
await api.deleteSkill(name);
|
|
5199
|
+
done += 1;
|
|
5200
|
+
} catch (error) {
|
|
5201
|
+
failures.push(name + ": " + errorMessage(error));
|
|
5202
|
+
}
|
|
5203
|
+
await Promise.all([
|
|
5204
|
+
load(),
|
|
5205
|
+
loadGroups(),
|
|
5206
|
+
loadSources()
|
|
5207
|
+
]);
|
|
5208
|
+
setTagBusy(false);
|
|
5209
|
+
if (failures.length > 0) setLoadError(`删除整组 "${dialog.name}":成功 ${done} 个,失败 ${failures.length} 个:` + failures.join("; "));
|
|
5210
|
+
else if (done > 0) setSuccessBanner(`已删除整组 "${dialog.name}":${done} 个技能已移入回收站`);
|
|
5211
|
+
}, [
|
|
5212
|
+
api,
|
|
5213
|
+
deleteGroupDialog,
|
|
5214
|
+
load,
|
|
5215
|
+
loadGroups,
|
|
5216
|
+
loadSources
|
|
5217
|
+
]);
|
|
3873
5218
|
/** 新建一个空 tag 分组。 */
|
|
3874
5219
|
const createTag = (0, react.useCallback)(async (event) => {
|
|
3875
5220
|
event.preventDefault();
|
|
@@ -3924,6 +5269,89 @@ window.__ModuleLoader__.load({
|
|
|
3924
5269
|
setTagBusy(false);
|
|
3925
5270
|
}
|
|
3926
5271
|
}, [api, applyTags]);
|
|
5272
|
+
/** 拖拽重排场景分组 */
|
|
5273
|
+
const reorderTags = (0, react.useCallback)(async (orderedIds) => {
|
|
5274
|
+
setTagBusy(true);
|
|
5275
|
+
setLoadError(null);
|
|
5276
|
+
try {
|
|
5277
|
+
const tags = await api.reorderTags(orderedIds);
|
|
5278
|
+
applyTags(tags);
|
|
5279
|
+
} catch (error) {
|
|
5280
|
+
const msg = errorMessage(error);
|
|
5281
|
+
if (msg.includes("404") || msg.toLowerCase().includes("not found")) {
|
|
5282
|
+
const byId = new Map(groupsState?.tags.map((t) => [t.id, t]) ?? []);
|
|
5283
|
+
const reordered = orderedIds.map((id) => byId.get(id)).filter((t) => t !== void 0);
|
|
5284
|
+
if (reordered.length === orderedIds.length) {
|
|
5285
|
+
applyTags(reordered);
|
|
5286
|
+
setSuccessBanner("已临时调整顺序(本地生效,重启宿主后持久化)");
|
|
5287
|
+
} else setLoadError(msg);
|
|
5288
|
+
} else setLoadError(msg);
|
|
5289
|
+
} finally {
|
|
5290
|
+
setTagBusy(false);
|
|
5291
|
+
}
|
|
5292
|
+
}, [
|
|
5293
|
+
api,
|
|
5294
|
+
applyTags,
|
|
5295
|
+
groupsState
|
|
5296
|
+
]);
|
|
5297
|
+
/** 拖拽重排来源集合 */
|
|
5298
|
+
const reorderCollections = (0, react.useCallback)(async (orderedNames) => {
|
|
5299
|
+
setTagBusy(true);
|
|
5300
|
+
setLoadError(null);
|
|
5301
|
+
try {
|
|
5302
|
+
const collections = await api.reorderCollections(orderedNames);
|
|
5303
|
+
setGroupsState((prev) => prev === null ? prev : {
|
|
5304
|
+
...prev,
|
|
5305
|
+
collections
|
|
5306
|
+
});
|
|
5307
|
+
loadGroups();
|
|
5308
|
+
} catch (error) {
|
|
5309
|
+
const msg = errorMessage(error);
|
|
5310
|
+
if (msg.includes("404") || msg.toLowerCase().includes("not found")) {
|
|
5311
|
+
setGroupsState((prev) => {
|
|
5312
|
+
if (prev === null) return prev;
|
|
5313
|
+
const map = new Map(prev.collections.map((c) => [c.name, c]));
|
|
5314
|
+
const reordered = orderedNames.map((n) => map.get(n)).filter((c) => c !== void 0);
|
|
5315
|
+
for (const c of prev.collections) if (!reordered.some((r) => r.name === c.name)) reordered.push(c);
|
|
5316
|
+
return {
|
|
5317
|
+
...prev,
|
|
5318
|
+
collections: reordered
|
|
5319
|
+
};
|
|
5320
|
+
});
|
|
5321
|
+
setSuccessBanner("已临时调整顺序(本地生效,重启宿主后持久化)");
|
|
5322
|
+
} else setLoadError(msg);
|
|
5323
|
+
} finally {
|
|
5324
|
+
setTagBusy(false);
|
|
5325
|
+
}
|
|
5326
|
+
}, [api, loadGroups]);
|
|
5327
|
+
/** 拖拽重排来源顶层分组(project / col:xxx / personal 全量可拖) */
|
|
5328
|
+
const reorderSourceGroups = (0, react.useCallback)(async (orderedKeys) => {
|
|
5329
|
+
setTagBusy(true);
|
|
5330
|
+
setLoadError(null);
|
|
5331
|
+
try {
|
|
5332
|
+
await api.reorderSourceGroups(orderedKeys);
|
|
5333
|
+
await loadGroups();
|
|
5334
|
+
} catch (error) {
|
|
5335
|
+
const msg = errorMessage(error);
|
|
5336
|
+
if (msg.includes("404") || msg.toLowerCase().includes("not found")) {
|
|
5337
|
+
setGroupsState((prev) => {
|
|
5338
|
+
if (prev === null) return prev;
|
|
5339
|
+
const colOrder = orderedKeys.filter((k) => k.startsWith("col:")).map((k) => k.slice(4));
|
|
5340
|
+
const map = new Map(prev.collections.map((c) => [c.name, c]));
|
|
5341
|
+
const reordered = colOrder.map((n) => map.get(n)).filter((c) => c !== void 0);
|
|
5342
|
+
for (const c of prev.collections) if (!reordered.some((r) => r.name === c.name)) reordered.push(c);
|
|
5343
|
+
return {
|
|
5344
|
+
...prev,
|
|
5345
|
+
collections: reordered,
|
|
5346
|
+
sourceGroupOrder: orderedKeys
|
|
5347
|
+
};
|
|
5348
|
+
});
|
|
5349
|
+
setSuccessBanner("已临时调整顺序(本地生效,重启宿主后持久化)");
|
|
5350
|
+
} else setLoadError(msg);
|
|
5351
|
+
} finally {
|
|
5352
|
+
setTagBusy(false);
|
|
5353
|
+
}
|
|
5354
|
+
}, [api, loadGroups]);
|
|
3927
5355
|
/** 添加一个市场源(内置市场目录与手动输入共用),并立即扫描它。 */
|
|
3928
5356
|
const addSource = (0, react.useCallback)(async (input) => {
|
|
3929
5357
|
const value = input.trim();
|
|
@@ -4040,15 +5468,50 @@ window.__ModuleLoader__.load({
|
|
|
4040
5468
|
return next;
|
|
4041
5469
|
});
|
|
4042
5470
|
}, []);
|
|
4043
|
-
/** Import every checked, non-existing repo skill
|
|
5471
|
+
/** Import every checked, non-existing repo skill (B方案:job+轮询+进度). */
|
|
4044
5472
|
const importRepo = (0, react.useCallback)(async () => {
|
|
4045
5473
|
if (repoDiscoverState.status !== "ready") return;
|
|
4046
5474
|
setRepoImporting(true);
|
|
4047
5475
|
setRepoResult(null);
|
|
5476
|
+
setImportJobId(null);
|
|
4048
5477
|
setLoadError(null);
|
|
5478
|
+
let finalProgress = null;
|
|
4049
5479
|
try {
|
|
4050
|
-
const
|
|
4051
|
-
|
|
5480
|
+
const created = await api.repoImport(repoDiscoverState.data.repo, [...repoSelected], repoDiscoverState.data.ref ?? void 0);
|
|
5481
|
+
setImportJobId(created.jobId);
|
|
5482
|
+
setRepoResult({
|
|
5483
|
+
ok: true,
|
|
5484
|
+
jobId: created.jobId,
|
|
5485
|
+
status: "running",
|
|
5486
|
+
total: created.total,
|
|
5487
|
+
done: 0,
|
|
5488
|
+
totalBytes: created.totalBytes,
|
|
5489
|
+
downloadedBytes: 0,
|
|
5490
|
+
imported: [],
|
|
5491
|
+
skipped: [],
|
|
5492
|
+
failed: []
|
|
5493
|
+
});
|
|
5494
|
+
pollAbortRef.current?.abort();
|
|
5495
|
+
pollAbortRef.current = new AbortController();
|
|
5496
|
+
const signal = pollAbortRef.current.signal;
|
|
5497
|
+
let attempt = 0;
|
|
5498
|
+
for (;;) {
|
|
5499
|
+
if (signal.aborted) break;
|
|
5500
|
+
const delay = Math.min(2e3, 800 + attempt * 200);
|
|
5501
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
5502
|
+
if (signal.aborted) break;
|
|
5503
|
+
try {
|
|
5504
|
+
const progress = await api.repoImportProgress(created.jobId);
|
|
5505
|
+
setRepoResult(progress);
|
|
5506
|
+
finalProgress = progress;
|
|
5507
|
+
if (progress.status !== "running") break;
|
|
5508
|
+
attempt = 0;
|
|
5509
|
+
} catch (pollError) {
|
|
5510
|
+
if (errorMessage(pollError).includes("not found")) break;
|
|
5511
|
+
attempt += 1;
|
|
5512
|
+
if (attempt > 8) break;
|
|
5513
|
+
}
|
|
5514
|
+
}
|
|
4052
5515
|
await Promise.all([
|
|
4053
5516
|
load(),
|
|
4054
5517
|
loadMarket(),
|
|
@@ -4058,6 +5521,28 @@ window.__ModuleLoader__.load({
|
|
|
4058
5521
|
} catch (error) {
|
|
4059
5522
|
setLoadError(errorMessage(error));
|
|
4060
5523
|
} finally {
|
|
5524
|
+
if (finalProgress !== null && (finalProgress.imported.length > 0 || finalProgress.skipped.length > 0)) {
|
|
5525
|
+
const doneNames = /* @__PURE__ */ new Set([...finalProgress.imported.map((r) => r.name), ...finalProgress.skipped.map((r) => r.name)]);
|
|
5526
|
+
const donePaths = new Set(repoDiscoverState.data.entries.filter((e) => doneNames.has(e.name)).map((e) => e.path));
|
|
5527
|
+
setRepoSelected((prev) => {
|
|
5528
|
+
const next = new Set(prev);
|
|
5529
|
+
for (const p of donePaths) next.delete(p);
|
|
5530
|
+
return next;
|
|
5531
|
+
});
|
|
5532
|
+
setRepoDiscoverState((prev) => {
|
|
5533
|
+
if (prev.status !== "ready") return prev;
|
|
5534
|
+
return {
|
|
5535
|
+
...prev,
|
|
5536
|
+
data: {
|
|
5537
|
+
...prev.data,
|
|
5538
|
+
entries: prev.data.entries.map((e) => doneNames.has(e.name) ? {
|
|
5539
|
+
...e,
|
|
5540
|
+
existing: true
|
|
5541
|
+
} : e)
|
|
5542
|
+
}
|
|
5543
|
+
};
|
|
5544
|
+
});
|
|
5545
|
+
}
|
|
4061
5546
|
setRepoImporting(false);
|
|
4062
5547
|
}
|
|
4063
5548
|
}, [
|
|
@@ -4069,6 +5554,41 @@ window.__ModuleLoader__.load({
|
|
|
4069
5554
|
loadGroups,
|
|
4070
5555
|
loadSources
|
|
4071
5556
|
]);
|
|
5557
|
+
(0, react.useEffect)(() => {
|
|
5558
|
+
return () => {
|
|
5559
|
+
pollAbortRef.current?.abort();
|
|
5560
|
+
};
|
|
5561
|
+
}, []);
|
|
5562
|
+
/** 取消正在进行的导入(选项2:唯有取消才停) */
|
|
5563
|
+
const cancelImport = (0, react.useCallback)(async () => {
|
|
5564
|
+
if (importJobId === null) return;
|
|
5565
|
+
pollAbortRef.current?.abort();
|
|
5566
|
+
try {
|
|
5567
|
+
const res = await api.repoImportCancel(importJobId);
|
|
5568
|
+
try {
|
|
5569
|
+
const progress = await api.repoImportProgress(importJobId);
|
|
5570
|
+
setRepoResult(progress);
|
|
5571
|
+
} catch {
|
|
5572
|
+
setRepoResult((prev) => prev !== null ? {
|
|
5573
|
+
...prev,
|
|
5574
|
+
status: res.status
|
|
5575
|
+
} : prev);
|
|
5576
|
+
}
|
|
5577
|
+
} catch (error) {
|
|
5578
|
+
setLoadError(errorMessage(error));
|
|
5579
|
+
} finally {
|
|
5580
|
+
setRepoImporting(false);
|
|
5581
|
+
}
|
|
5582
|
+
}, [api, importJobId]);
|
|
5583
|
+
/** 清空扫描结果(关闭归属卡片) */
|
|
5584
|
+
const clearScan = (0, react.useCallback)(() => {
|
|
5585
|
+
setRepoDiscoverState({ status: "idle" });
|
|
5586
|
+
setScanningRepo(null);
|
|
5587
|
+
setRepoResult(null);
|
|
5588
|
+
setRepoSelected(/* @__PURE__ */ new Set());
|
|
5589
|
+
setImportJobId(null);
|
|
5590
|
+
pollAbortRef.current?.abort();
|
|
5591
|
+
}, []);
|
|
4072
5592
|
/** 检查所有市场源的上游更新(服务端节流)。 */
|
|
4073
5593
|
const checkMarket = (0, react.useCallback)(async () => {
|
|
4074
5594
|
try {
|
|
@@ -4233,6 +5753,7 @@ window.__ModuleLoader__.load({
|
|
|
4233
5753
|
repoSelected,
|
|
4234
5754
|
repoImporting,
|
|
4235
5755
|
repoResult,
|
|
5756
|
+
importJobId,
|
|
4236
5757
|
search,
|
|
4237
5758
|
workspace,
|
|
4238
5759
|
detail,
|
|
@@ -4267,6 +5788,7 @@ window.__ModuleLoader__.load({
|
|
|
4267
5788
|
conflictDialog,
|
|
4268
5789
|
confirmDialog,
|
|
4269
5790
|
deleteSkillDialog,
|
|
5791
|
+
deleteGroupDialog,
|
|
4270
5792
|
confirmClearTrash,
|
|
4271
5793
|
updateAllDialog,
|
|
4272
5794
|
editingTag,
|
|
@@ -4278,6 +5800,7 @@ window.__ModuleLoader__.load({
|
|
|
4278
5800
|
collapsedGroups,
|
|
4279
5801
|
subdividedProjects,
|
|
4280
5802
|
showLegend,
|
|
5803
|
+
editMode,
|
|
4281
5804
|
actionNames,
|
|
4282
5805
|
viewNames,
|
|
4283
5806
|
normalized,
|
|
@@ -4310,6 +5833,7 @@ window.__ModuleLoader__.load({
|
|
|
4310
5833
|
setConflictDialog,
|
|
4311
5834
|
setConfirmDialog,
|
|
4312
5835
|
setDeleteSkillDialog,
|
|
5836
|
+
setDeleteGroupDialog,
|
|
4313
5837
|
setConfirmClearTrash,
|
|
4314
5838
|
setUpdateAllDialog,
|
|
4315
5839
|
setEditingTag,
|
|
@@ -4318,6 +5842,7 @@ window.__ModuleLoader__.load({
|
|
|
4318
5842
|
setNewTagName,
|
|
4319
5843
|
setEditSearch,
|
|
4320
5844
|
setShowLegend,
|
|
5845
|
+
setEditMode,
|
|
4321
5846
|
toggleGroupCollapse,
|
|
4322
5847
|
toggleSubdivide,
|
|
4323
5848
|
checkUpdate,
|
|
@@ -4336,9 +5861,14 @@ window.__ModuleLoader__.load({
|
|
|
4336
5861
|
clearTrash,
|
|
4337
5862
|
requestDeleteSkill,
|
|
4338
5863
|
runDeleteSkill,
|
|
5864
|
+
requestDeleteGroup,
|
|
5865
|
+
runDeleteGroup,
|
|
4339
5866
|
createTag,
|
|
4340
5867
|
deleteTag,
|
|
4341
5868
|
saveTag,
|
|
5869
|
+
reorderTags,
|
|
5870
|
+
reorderCollections,
|
|
5871
|
+
reorderSourceGroups,
|
|
4342
5872
|
addSource,
|
|
4343
5873
|
addMarketSource,
|
|
4344
5874
|
removeMarketSource,
|
|
@@ -4346,6 +5876,8 @@ window.__ModuleLoader__.load({
|
|
|
4346
5876
|
confirmBranchChoice,
|
|
4347
5877
|
toggleRepoSelected,
|
|
4348
5878
|
importRepo,
|
|
5879
|
+
cancelImport,
|
|
5880
|
+
clearScan,
|
|
4349
5881
|
checkMarket,
|
|
4350
5882
|
syncMarketSource,
|
|
4351
5883
|
confirmMarketSync,
|
|
@@ -4372,7 +5904,7 @@ window.__ModuleLoader__.load({
|
|
|
4372
5904
|
const hub = useSkillHub(props.api);
|
|
4373
5905
|
/** 工作区输入草稿:回车才应用,避免每次按键都触发目录重拉。 */
|
|
4374
5906
|
const [workspaceDraft, setWorkspaceDraft] = (0, react.useState)("");
|
|
4375
|
-
const { catalog, loading, loadError, successBanner, updateState, detail, detailLoading, showForm, formName, formDesc, formRoot, formBusy, formMessage, hubConfig, tab, skillView, sourceFilter, sortKey, search, workspace, setWorkspace, sourcesState, tagBusy, busyNames, normalized, origins, sourceOptions, filtered, conflictDialog, confirmDialog, deleteSkillDialog, confirmClearTrash, branchChoice, branchBusy, marketSyncDialog, syncBusy, editingTag, editName, membersDraft, editSearch, uses, groupsState, sourceCheck, checkingSource, syncingSource, showLegend, setLoadError, setSuccessBanner, setDetail, setShowForm, setFormName, setFormDesc, setFormRoot, setFormMessage, setTab, setSkillView, setSourceFilter, setSortKey, setSearch, setConflictDialog, setConfirmDialog, setDeleteSkillDialog, setConfirmClearTrash, setBranchChoice, setMarketSyncDialog, setEditingTag, setEditName, setMembersDraft, setEditSearch, setShowLegend, checkUpdate, loadMarket, checkSources, requestSync, requestDelete, restoreTrash, clearTrash, runDeleteSkill, runConfirmed, resolveConflict, confirmBranchChoice, confirmMarketSync, create, saveTag, deleteTag, enableDisabled } = hub;
|
|
5907
|
+
const { catalog, loading, loadError, successBanner, updateState, detail, detailLoading, showForm, formName, formDesc, formRoot, formBusy, formMessage, hubConfig, tab, skillView, sourceFilter, sortKey, search, workspace, setWorkspace, sourcesState, tagBusy, busyNames, normalized, origins, sourceOptions, filtered, conflictDialog, confirmDialog, deleteSkillDialog, deleteGroupDialog, confirmClearTrash, branchChoice, branchBusy, marketSyncDialog, syncBusy, editingTag, editName, membersDraft, editSearch, uses, groupsState, sourceCheck, checkingSource, syncingSource, showLegend, editMode, setLoadError, setSuccessBanner, setDetail, setShowForm, setFormName, setFormDesc, setFormRoot, setFormMessage, setTab, setSkillView, setSourceFilter, setSortKey, setSearch, setConflictDialog, setConfirmDialog, setDeleteSkillDialog, setDeleteGroupDialog, setConfirmClearTrash, setBranchChoice, setMarketSyncDialog, setEditingTag, setEditName, setMembersDraft, setEditSearch, setShowLegend, setEditMode, checkUpdate, loadMarket, checkSources, requestSync, requestDelete, restoreTrash, clearTrash, runDeleteSkill, runDeleteGroup, runConfirmed, resolveConflict, confirmBranchChoice, confirmMarketSync, create, saveTag, deleteTag, enableDisabled } = hub;
|
|
4376
5908
|
if (detail !== null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SkillDetailView, {
|
|
4377
5909
|
detail,
|
|
4378
5910
|
hubConfig,
|
|
@@ -4763,6 +6295,13 @@ window.__ModuleLoader__.load({
|
|
|
4763
6295
|
setSearch(event.target.value);
|
|
4764
6296
|
},
|
|
4765
6297
|
placeholder: tt("panel.search")
|
|
6298
|
+
}),
|
|
6299
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
6300
|
+
type: "button",
|
|
6301
|
+
className: panel_module_css_default.button + (editMode ? " " + panel_module_css_default.primary : ""),
|
|
6302
|
+
style: { marginLeft: "auto" },
|
|
6303
|
+
onClick: () => setEditMode((v) => !v),
|
|
6304
|
+
children: editMode ? "完成" : "编辑"
|
|
4766
6305
|
})
|
|
4767
6306
|
]
|
|
4768
6307
|
}),
|
|
@@ -4813,19 +6352,6 @@ window.__ModuleLoader__.load({
|
|
|
4813
6352
|
})]
|
|
4814
6353
|
}, entry.name))]
|
|
4815
6354
|
}) : null,
|
|
4816
|
-
catalog.disabled.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
4817
|
-
className: panel_module_css_default.section,
|
|
4818
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4819
|
-
className: panel_module_css_default.sectionTitle,
|
|
4820
|
-
children: tt("panel.disabled")
|
|
4821
|
-
}), catalog.disabled.filter((record) => (normalized.length === 0 || record.name.toLocaleLowerCase().includes(normalized) || record.description.toLocaleLowerCase().includes(normalized)) && (sourceFilter === "all" || (origins[record.name] ?? "private") === sourceFilter)).map((record) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DisabledRow, {
|
|
4822
|
-
record,
|
|
4823
|
-
busy: busyNames.has(record.name),
|
|
4824
|
-
onEnable: () => {
|
|
4825
|
-
enableDisabled(record);
|
|
4826
|
-
}
|
|
4827
|
-
}, record.name))]
|
|
4828
|
-
}) : null,
|
|
4829
6355
|
catalog.diagnostics.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
4830
6356
|
className: panel_module_css_default.section,
|
|
4831
6357
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -4917,6 +6443,22 @@ window.__ModuleLoader__.load({
|
|
|
4917
6443
|
runDeleteSkill();
|
|
4918
6444
|
}
|
|
4919
6445
|
}) : null,
|
|
6446
|
+
deleteGroupDialog !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfirmDialog, {
|
|
6447
|
+
title: tt("source.deleteGroupTitle"),
|
|
6448
|
+
text: tt("source.deleteGroupText", {
|
|
6449
|
+
name: deleteGroupDialog.name,
|
|
6450
|
+
count: deleteGroupDialog.skillNames.length
|
|
6451
|
+
}),
|
|
6452
|
+
items: deleteGroupDialog.skillNames,
|
|
6453
|
+
confirmLabel: tt("source.deleteGroup"),
|
|
6454
|
+
danger: true,
|
|
6455
|
+
onCancel: () => {
|
|
6456
|
+
setDeleteGroupDialog(null);
|
|
6457
|
+
},
|
|
6458
|
+
onConfirm: () => {
|
|
6459
|
+
runDeleteGroup();
|
|
6460
|
+
}
|
|
6461
|
+
}) : null,
|
|
4920
6462
|
confirmClearTrash ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ConfirmDialog, {
|
|
4921
6463
|
title: tt("source.clearTrashConfirmTitle"),
|
|
4922
6464
|
text: tt("source.clearTrashConfirmText"),
|