dsh-plugin-shop 0.8.5 → 0.8.6-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +1788 -358
- package/lib/index.js +87 -33
- package/lib/typert.host.js +20 -15
- package/lib/typert.remote-client.js +16 -11
- package/lib/types/client/ShopTab.d.ts +6 -0
- package/lib/types/client/index.d.ts +11 -0
- package/lib/types/client/locales.d.ts +32 -0
- package/lib/types/client/present.d.ts +21 -1
- package/lib/types/client/restart-monitor.d.ts +38 -7
- package/lib/types/client/useFlows.d.ts +6 -0
- package/lib/types/client/useUpdateSelf.d.ts +5 -0
- package/lib/types/host/index.d.ts +21 -2
- package/lib/types/host/restart.d.ts +9 -1
- package/lib/types/host/types.d.ts +4 -0
- package/lib/types/own-version.d.ts +13 -1
- package/lib/types/shared/zh-intents.d.ts +89 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -4322,7 +4322,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4322
4322
|
})]).optional(),
|
|
4323
4323
|
"dshPeers": union([_undefined(), record(string(), string())]).optional(),
|
|
4324
4324
|
"unpackedSize": union([_undefined(), number()]).optional(),
|
|
4325
|
-
"installSize": union([_undefined(), number()]).optional()
|
|
4325
|
+
"installSize": union([_undefined(), number()]).optional(),
|
|
4326
|
+
"archived": union([
|
|
4327
|
+
_undefined(),
|
|
4328
|
+
literal(false),
|
|
4329
|
+
literal(true)
|
|
4330
|
+
]).optional()
|
|
4326
4331
|
})),
|
|
4327
4332
|
"denied": array(object({
|
|
4328
4333
|
"name": string(),
|
|
@@ -4354,9 +4359,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4354
4359
|
"repo": union([_undefined(), string()]).optional(),
|
|
4355
4360
|
"subdir": union([_undefined(), string()]).optional(),
|
|
4356
4361
|
"installed": string(),
|
|
4357
|
-
"latest": string(),
|
|
4362
|
+
"latest": union([literal(null), string()]),
|
|
4358
4363
|
"outdated": boolean(),
|
|
4359
|
-
"enabled": boolean()
|
|
4364
|
+
"enabled": boolean(),
|
|
4365
|
+
"shadowed": union([_undefined(), literal(true)]).optional()
|
|
4360
4366
|
}));
|
|
4361
4367
|
const dsh_plugin_shop_shop_installedSpecs_result$schema = union([literal(null), record(string(), string())]);
|
|
4362
4368
|
const dsh_plugin_shop_shop_installStart_parameter_0$schema = object({
|
|
@@ -4427,7 +4433,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4427
4433
|
});
|
|
4428
4434
|
const dsh_plugin_shop_shop_restart_result$schema = union([object({
|
|
4429
4435
|
"ok": literal(true),
|
|
4430
|
-
"logFile": union([_undefined(), string()]).optional()
|
|
4436
|
+
"logFile": union([_undefined(), string()]).optional(),
|
|
4437
|
+
"bootId": union([_undefined(), string()]).optional(),
|
|
4438
|
+
"pid": union([_undefined(), number()]).optional()
|
|
4431
4439
|
}), object({
|
|
4432
4440
|
"ok": literal(false),
|
|
4433
4441
|
"detail": string()
|
|
@@ -4484,7 +4492,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4484
4492
|
_undefined(),
|
|
4485
4493
|
literal(null),
|
|
4486
4494
|
string()
|
|
4487
|
-
]).optional()
|
|
4495
|
+
]).optional(),
|
|
4496
|
+
"bootId": union([_undefined(), string()]).optional()
|
|
4488
4497
|
});
|
|
4489
4498
|
const TYPERT_REMOTE = {
|
|
4490
4499
|
package: "dsh-plugin-shop",
|
|
@@ -4515,7 +4524,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4515
4524
|
},
|
|
4516
4525
|
sourceLocation: {
|
|
4517
4526
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4518
|
-
"line":
|
|
4527
|
+
"line": 1272,
|
|
4519
4528
|
"column": 9
|
|
4520
4529
|
}
|
|
4521
4530
|
},
|
|
@@ -4534,7 +4543,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4534
4543
|
},
|
|
4535
4544
|
sourceLocation: {
|
|
4536
4545
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4537
|
-
"line":
|
|
4546
|
+
"line": 1739,
|
|
4538
4547
|
"column": 9
|
|
4539
4548
|
}
|
|
4540
4549
|
},
|
|
@@ -4553,7 +4562,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4553
4562
|
},
|
|
4554
4563
|
sourceLocation: {
|
|
4555
4564
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4556
|
-
"line":
|
|
4565
|
+
"line": 1730,
|
|
4557
4566
|
"column": 9
|
|
4558
4567
|
}
|
|
4559
4568
|
},
|
|
@@ -4583,7 +4592,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4583
4592
|
},
|
|
4584
4593
|
sourceLocation: {
|
|
4585
4594
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4586
|
-
"line":
|
|
4595
|
+
"line": 1398,
|
|
4587
4596
|
"column": 9
|
|
4588
4597
|
}
|
|
4589
4598
|
},
|
|
@@ -4612,7 +4621,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4612
4621
|
},
|
|
4613
4622
|
sourceLocation: {
|
|
4614
4623
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4615
|
-
"line":
|
|
4624
|
+
"line": 1706,
|
|
4616
4625
|
"column": 3
|
|
4617
4626
|
}
|
|
4618
4627
|
},
|
|
@@ -4631,7 +4640,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4631
4640
|
},
|
|
4632
4641
|
sourceLocation: {
|
|
4633
4642
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4634
|
-
"line":
|
|
4643
|
+
"line": 1990,
|
|
4635
4644
|
"column": 9
|
|
4636
4645
|
}
|
|
4637
4646
|
},
|
|
@@ -4660,7 +4669,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4660
4669
|
},
|
|
4661
4670
|
sourceLocation: {
|
|
4662
4671
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4663
|
-
"line":
|
|
4672
|
+
"line": 943,
|
|
4664
4673
|
"column": 9
|
|
4665
4674
|
}
|
|
4666
4675
|
},
|
|
@@ -4690,7 +4699,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4690
4699
|
},
|
|
4691
4700
|
sourceLocation: {
|
|
4692
4701
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4693
|
-
"line":
|
|
4702
|
+
"line": 1884,
|
|
4694
4703
|
"column": 9
|
|
4695
4704
|
}
|
|
4696
4705
|
},
|
|
@@ -4719,7 +4728,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4719
4728
|
},
|
|
4720
4729
|
sourceLocation: {
|
|
4721
4730
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4722
|
-
"line":
|
|
4731
|
+
"line": 2097,
|
|
4723
4732
|
"column": 9
|
|
4724
4733
|
}
|
|
4725
4734
|
},
|
|
@@ -4738,7 +4747,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4738
4747
|
},
|
|
4739
4748
|
sourceLocation: {
|
|
4740
4749
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4741
|
-
"line":
|
|
4750
|
+
"line": 2054,
|
|
4742
4751
|
"column": 9
|
|
4743
4752
|
}
|
|
4744
4753
|
}
|
|
@@ -6588,6 +6597,31 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6588
6597
|
if (input.stableSinceMs === null && input.elapsedMs > 3e4) return "failed";
|
|
6589
6598
|
return "wait";
|
|
6590
6599
|
}
|
|
6600
|
+
/**
|
|
6601
|
+
* Whether two `version()` answers came from different dsh processes — dsh
|
|
6602
|
+
* restarted under the page between them (design §8, 2026-10-06 amendment).
|
|
6603
|
+
*
|
|
6604
|
+
* A process names itself by `bootId` for as long as it lives, so where either
|
|
6605
|
+
* answer carries one, a difference decides: a host older than the field never
|
|
6606
|
+
* starts naming itself, and a newer one never stops. Where neither does, only
|
|
6607
|
+
* the code they run can differ — one process runs one version, read once at
|
|
6608
|
+
* its boot — which still catches the first restart after a self-update from
|
|
6609
|
+
* a host older than the field, the one that matters most.
|
|
6610
|
+
*/
|
|
6611
|
+
function answeredByAnotherProcess(previous, next) {
|
|
6612
|
+
if (previous.bootId !== void 0 || next.bootId !== void 0) return previous.bootId !== next.bootId;
|
|
6613
|
+
return previous.installed !== next.installed;
|
|
6614
|
+
}
|
|
6615
|
+
/**
|
|
6616
|
+
* Whether dsh restarting under the page settles this receipt: a change that
|
|
6617
|
+
* landed and was waiting for exactly that. The tab clears these once the
|
|
6618
|
+
* answers come from another process, and nothing else — a reload is still
|
|
6619
|
+
* owed by a page nobody reloaded, a failure still happened, and a flow still
|
|
6620
|
+
* running settles by its own poll.
|
|
6621
|
+
*/
|
|
6622
|
+
function settledByRestart(view) {
|
|
6623
|
+
return view.kind === "done" && view.activation === "restart";
|
|
6624
|
+
}
|
|
6591
6625
|
/** How long the check-update button reports "up to date" after a re-check
|
|
6592
6626
|
* finds no newer release, before reverting to its idle label. */
|
|
6593
6627
|
const CHECK_UP_TO_DATE_MS = 3e3;
|
|
@@ -6851,6 +6885,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6851
6885
|
* 2026-09-26-market-borrowings §4). */
|
|
6852
6886
|
tabCrashed: "插件商店在显示这一页时出错了。",
|
|
6853
6887
|
search: "搜索插件",
|
|
6888
|
+
/** One subdued line under the search box when a Chinese query fired a
|
|
6889
|
+
* dictionary intent (design 2026-10-08-search-query-expansion §4).
|
|
6890
|
+
* `{terms}` is the comma-joined recall terms, without the `#` marker
|
|
6891
|
+
* (boundary-only surface detail, not a reader's fact). */
|
|
6892
|
+
zhExpansionNote: "同时匹配:{terms}",
|
|
6854
6893
|
all: "全部",
|
|
6855
6894
|
catalog: "插件目录",
|
|
6856
6895
|
catalogStats: "{count} 个插件 · 构建于 {date}",
|
|
@@ -6867,6 +6906,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6867
6906
|
tierVerified: "已审核",
|
|
6868
6907
|
tierVerifiedStale: "审核版本过期",
|
|
6869
6908
|
tierCommunity: "社区",
|
|
6909
|
+
/** The github repository GitHub reports as archived (design 2026-10-08 §1.3).
|
|
6910
|
+
* Subdued register, like `tierCommunity`: nothing is wrong with the install
|
|
6911
|
+
* itself; the owner has simply frozen the project. */
|
|
6912
|
+
archivedBadge: "已归档",
|
|
6870
6913
|
capabilitiesNote: "作者自述,无强制约束",
|
|
6871
6914
|
install: "安装",
|
|
6872
6915
|
confirm: "确认",
|
|
@@ -6948,6 +6991,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6948
6991
|
nameTakenDetail: "已安装同名的另一个插件({holder})。\n两个同名插件声明相同的加载项 id,无法共存;安装它会替换掉已装的那个。",
|
|
6949
6992
|
npmSource: "npm 包",
|
|
6950
6993
|
githubSource: "源码在 GitHub",
|
|
6994
|
+
/** The badge and the explanation for an installed github entry no catalog
|
|
6995
|
+
* row claims (C10 MVP). The mechanism synthesizes a row for ANY github spec
|
|
6996
|
+
* with no catalog row — shadowed by a same-named npm package, an unlisted
|
|
6997
|
+
* fork, a removed listing, or a same-named conflict with a different repo —
|
|
6998
|
+
* so the copy names the catalog fact and lists the causes, rather than
|
|
6999
|
+
* claiming the npm-twin one. */
|
|
7000
|
+
shadowedBadge: "目录未列出此包",
|
|
7001
|
+
shadowedDetail: "此包已安装,但当前的插件目录没有它(可能是被同名 npm 包遮蔽、作者已下架、或目录尚未收录);它照常运行。",
|
|
6951
7002
|
updatableSection: "可更新",
|
|
6952
7003
|
installedVersion: "已安装 v{version}",
|
|
6953
7004
|
latestVersion: "最新 v{version}",
|
|
@@ -6973,6 +7024,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6973
7024
|
* that answered and then died. */
|
|
6974
7025
|
restartFailedLogNotice: "重启后服务器没有回来,或回来后没能保持运行。新进程的日志在 {log};也可以手动启动:dsh web",
|
|
6975
7026
|
restartTransportFailed: "重启请求未能送达。请稍后重试。",
|
|
7027
|
+
notRestartedNotice: "dsh 没有重启:页面刷新后,回答的仍是被要求重启的那个进程,所以更改还没有生效。请手动重启:退出正在运行的 dsh,再运行 dsh web。",
|
|
7028
|
+
notRestartedPidNotice: "dsh 没有重启:页面刷新后,回答的仍是被要求重启的那个进程(pid {pid}),所以更改还没有生效。请手动重启:运行 kill {pid}(进程仍不退出时用 kill -9 {pid}),再运行 dsh web。",
|
|
7029
|
+
notRestartedLogNotice: "这次重启的经过记录在 {log}。",
|
|
6976
7030
|
restartBlockedDesktopNotice: "此 profile 由 DeepSeek Harness 桌面版管理,商店无法重启它。请重启桌面版,以应用更改。",
|
|
6977
7031
|
restartBlockedWindowsNotice: "商店暂不支持在 Windows 上重启 dsh。请退出 dsh 后重新启动,以应用更改。",
|
|
6978
7032
|
restartBlockedSystemdNotice: "当前 dsh 由 systemd 服务托管,商店无法安全地重启它。请手动重启服务,或在商店行配置中设置 allowRestart: true。",
|
|
@@ -6997,6 +7051,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6997
7051
|
retry: "Retry",
|
|
6998
7052
|
tabCrashed: "The plugin shop hit an error while showing this page.",
|
|
6999
7053
|
search: "Search plugins",
|
|
7054
|
+
/** One subdued line under the search box when a Chinese query fired a
|
|
7055
|
+
* dictionary intent (design 2026-10-08-search-query-expansion §4). */
|
|
7056
|
+
zhExpansionNote: "Also matching: {terms}",
|
|
7000
7057
|
all: "All",
|
|
7001
7058
|
catalog: "Plugin catalog",
|
|
7002
7059
|
catalogStats: "{count} packages · built {date}",
|
|
@@ -7011,6 +7068,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7011
7068
|
empty: "No plugins in the catalog.",
|
|
7012
7069
|
emptySearch: "No matching plugins.",
|
|
7013
7070
|
tierVerified: "Verified",
|
|
7071
|
+
/** The github repository GitHub reports as archived (design 2026-10-08 §1.3).
|
|
7072
|
+
* Subdued register: nothing is wrong with the install; the owner has simply
|
|
7073
|
+
* frozen the project. */
|
|
7074
|
+
archivedBadge: "Archived",
|
|
7014
7075
|
tierVerifiedStale: "Reviewed version stale",
|
|
7015
7076
|
tierCommunity: "Community",
|
|
7016
7077
|
capabilitiesNote: "self-declared by the author, not enforced",
|
|
@@ -7070,6 +7131,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7070
7131
|
nameTakenDetail: "A different plugin of this name is installed ({holder}).\nTwo plugins of one name declare the same loader entry id and cannot both load; installing this one would replace it.",
|
|
7071
7132
|
npmSource: "npm package",
|
|
7072
7133
|
githubSource: "source on GitHub",
|
|
7134
|
+
shadowedBadge: "Not in the current catalog",
|
|
7135
|
+
shadowedDetail: "This package is installed but the current catalog does not list it (it may be shadowed by a same-named npm package, removed by the author, or never listed); it runs as before.",
|
|
7073
7136
|
updatableSection: "Updatable",
|
|
7074
7137
|
installedVersion: "installed v{version}",
|
|
7075
7138
|
latestVersion: "latest v{version}",
|
|
@@ -7090,6 +7153,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7090
7153
|
restartFailedNotice: "The server did not come back after the restart. Start it manually: dsh web (see restart.log in the shop cache directory)",
|
|
7091
7154
|
restartFailedLogNotice: "The server did not come back after the restart, or did not stay up. The new process logs to {log}; you can also start it manually: dsh web",
|
|
7092
7155
|
restartTransportFailed: "The restart request could not be delivered. Please retry.",
|
|
7156
|
+
notRestartedNotice: "dsh did not restart: after the reload, the process that was asked to restart is still the one answering, so the change has not taken effect. Restart it by hand: quit the running dsh, then run dsh web.",
|
|
7157
|
+
notRestartedPidNotice: "dsh did not restart: after the reload, the process that was asked to restart (pid {pid}) is still the one answering, so the change has not taken effect. Restart it by hand: run kill {pid} (kill -9 {pid} if it does not exit), then run dsh web.",
|
|
7158
|
+
notRestartedLogNotice: "What this restart did is logged in {log}.",
|
|
7093
7159
|
restartBlockedDesktopNotice: "This profile is managed by the DeepSeek Harness desktop app, so the shop cannot restart it. Restart the app to apply the change.",
|
|
7094
7160
|
restartBlockedWindowsNotice: "The shop cannot restart dsh on Windows yet. Quit dsh and start it again to apply the change.",
|
|
7095
7161
|
restartBlockedSystemdNotice: "This dsh process runs as a systemd service, so the shop cannot restart it safely. Restart the service manually, or set allowRestart: true in the shop row config.",
|
|
@@ -7316,6 +7382,48 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7316
7382
|
store.state = state;
|
|
7317
7383
|
for (const listener of [...store.listeners]) listener();
|
|
7318
7384
|
}
|
|
7385
|
+
/** sessionStorage key of the restart this tab last saw committed. Not the
|
|
7386
|
+
* page store: the verdict belongs to the page the reload produces, and a
|
|
7387
|
+
* reload clears `globalThis`. Not localStorage: it is this tab's restart, and
|
|
7388
|
+
* another tab never asked for it. */
|
|
7389
|
+
const RECORD_KEY = "dsh-plugin-shop.restart";
|
|
7390
|
+
function writeRecord(record) {
|
|
7391
|
+
try {
|
|
7392
|
+
globalThis.sessionStorage.setItem(RECORD_KEY, JSON.stringify(record));
|
|
7393
|
+
} catch {}
|
|
7394
|
+
}
|
|
7395
|
+
/** The record, or null when there is none this module could have written.
|
|
7396
|
+
* Every plugin on the origin shares the tab's sessionStorage, and the pid is
|
|
7397
|
+
* rendered into a command the reader is told to run, so each field is
|
|
7398
|
+
* checked again on the way back in. */
|
|
7399
|
+
function readRecord() {
|
|
7400
|
+
let raw;
|
|
7401
|
+
try {
|
|
7402
|
+
raw = globalThis.sessionStorage.getItem(RECORD_KEY);
|
|
7403
|
+
} catch {
|
|
7404
|
+
return null;
|
|
7405
|
+
}
|
|
7406
|
+
if (raw === null) return null;
|
|
7407
|
+
let parsed;
|
|
7408
|
+
try {
|
|
7409
|
+
parsed = JSON.parse(raw);
|
|
7410
|
+
} catch {
|
|
7411
|
+
return null;
|
|
7412
|
+
}
|
|
7413
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
7414
|
+
const { bootId, pid, logFile } = parsed;
|
|
7415
|
+
if (typeof bootId !== "string") return null;
|
|
7416
|
+
return {
|
|
7417
|
+
bootId,
|
|
7418
|
+
...typeof pid === "number" && Number.isSafeInteger(pid) && pid > 0 ? { pid } : {},
|
|
7419
|
+
...typeof logFile === "string" ? { logFile } : {}
|
|
7420
|
+
};
|
|
7421
|
+
}
|
|
7422
|
+
function forgetRecord() {
|
|
7423
|
+
try {
|
|
7424
|
+
globalThis.sessionStorage.removeItem(RECORD_KEY);
|
|
7425
|
+
} catch {}
|
|
7426
|
+
}
|
|
7319
7427
|
/** The page's restart, or null when it has committed none. The same object
|
|
7320
7428
|
* until the state changes, as `useSyncExternalStore` requires. */
|
|
7321
7429
|
function readRestartMonitor() {
|
|
@@ -7348,7 +7456,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7348
7456
|
const store = monitorStore();
|
|
7349
7457
|
if (store.state?.kind === "restarting") return;
|
|
7350
7458
|
store.stop?.();
|
|
7351
|
-
const { reload, logFile } = options;
|
|
7459
|
+
const { reload, logFile, bootId, pid } = options;
|
|
7460
|
+
if (bootId !== void 0) writeRecord({
|
|
7461
|
+
bootId,
|
|
7462
|
+
...pid === void 0 ? {} : { pid },
|
|
7463
|
+
...logFile === void 0 ? {} : { logFile }
|
|
7464
|
+
});
|
|
7352
7465
|
const started = Date.now();
|
|
7353
7466
|
let stableSince = null;
|
|
7354
7467
|
let stopped = false;
|
|
@@ -7474,7 +7587,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7474
7587
|
finish(store, attempt, { kind: "started" });
|
|
7475
7588
|
startRestartMonitor({
|
|
7476
7589
|
reload: attempt.reload,
|
|
7477
|
-
...typeof result.logFile === "string" ? { logFile: result.logFile } : {}
|
|
7590
|
+
...typeof result.logFile === "string" ? { logFile: result.logFile } : {},
|
|
7591
|
+
...typeof result.bootId === "string" ? { bootId: result.bootId } : {},
|
|
7592
|
+
...typeof result.pid === "number" ? { pid: result.pid } : {}
|
|
7478
7593
|
});
|
|
7479
7594
|
return;
|
|
7480
7595
|
}
|
|
@@ -7504,6 +7619,34 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7504
7619
|
store.attempt = null;
|
|
7505
7620
|
for (const waiter of attempt.waiters) waiter(outcome);
|
|
7506
7621
|
}
|
|
7622
|
+
/**
|
|
7623
|
+
* Judge this tab's last committed restart by the process answering now. The
|
|
7624
|
+
* client half hands every `version()` answer here (index.ts), so the verdict
|
|
7625
|
+
* is made on the first answer a reloaded page receives, before any tab opens.
|
|
7626
|
+
*
|
|
7627
|
+
* The same boot as the one that committed is a restart that never happened:
|
|
7628
|
+
* the page says so, naming that process, for as long as it keeps answering
|
|
7629
|
+
* — across reloads too, since the record stays. Any other answer, an older
|
|
7630
|
+
* host's included, is a process that replaced it, and the record is spent.
|
|
7631
|
+
* Nothing is judged while this page's own restart is under way: the process
|
|
7632
|
+
* that committed answers until it exits, two seconds later.
|
|
7633
|
+
*/
|
|
7634
|
+
function noteBoot(bootId) {
|
|
7635
|
+
const store = monitorStore();
|
|
7636
|
+
if (store.state?.kind === "requesting" || store.state?.kind === "restarting") return;
|
|
7637
|
+
const record = readRecord();
|
|
7638
|
+
if (record === null) return;
|
|
7639
|
+
if (record.bootId === bootId) {
|
|
7640
|
+
if (store.state?.kind !== "not-restarted") settle(store, {
|
|
7641
|
+
kind: "not-restarted",
|
|
7642
|
+
...record.pid === void 0 ? {} : { pid: record.pid },
|
|
7643
|
+
...record.logFile === void 0 ? {} : { logFile: record.logFile }
|
|
7644
|
+
});
|
|
7645
|
+
return;
|
|
7646
|
+
}
|
|
7647
|
+
forgetRecord();
|
|
7648
|
+
if (store.state?.kind === "not-restarted") settle(store, null);
|
|
7649
|
+
}
|
|
7507
7650
|
//#endregion
|
|
7508
7651
|
//#region src/client/useFlows.ts
|
|
7509
7652
|
/**
|
|
@@ -7577,6 +7720,17 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7577
7720
|
return next;
|
|
7578
7721
|
});
|
|
7579
7722
|
}, []);
|
|
7723
|
+
const resetWhere = (0, react.useCallback)((predicate) => {
|
|
7724
|
+
setViews((current) => {
|
|
7725
|
+
let next = null;
|
|
7726
|
+
for (const [key, view] of current) {
|
|
7727
|
+
if (!predicate(view)) continue;
|
|
7728
|
+
next ??= new Map(current);
|
|
7729
|
+
next.delete(key);
|
|
7730
|
+
}
|
|
7731
|
+
return next ?? current;
|
|
7732
|
+
});
|
|
7733
|
+
}, []);
|
|
7580
7734
|
(0, react.useEffect)(() => {
|
|
7581
7735
|
const running = [];
|
|
7582
7736
|
for (const [key, view] of views) if (view.kind === "running") running.push([key, view.installId]);
|
|
@@ -7635,6 +7789,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7635
7789
|
reset
|
|
7636
7790
|
]),
|
|
7637
7791
|
resetFlow: reset,
|
|
7792
|
+
resetWhere,
|
|
7638
7793
|
pending
|
|
7639
7794
|
};
|
|
7640
7795
|
}
|
|
@@ -7777,128 +7932,1235 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7777
7932
|
});
|
|
7778
7933
|
}
|
|
7779
7934
|
}, [updateStart]);
|
|
7935
|
+
const resetWhere = (0, react.useCallback)((predicate) => {
|
|
7936
|
+
setView((current) => predicate(current) ? { kind: "idle" } : current);
|
|
7937
|
+
}, []);
|
|
7780
7938
|
usePollStatus(view, setView, installStatus);
|
|
7781
7939
|
return {
|
|
7782
7940
|
view,
|
|
7783
|
-
start
|
|
7941
|
+
start,
|
|
7942
|
+
resetWhere
|
|
7943
|
+
};
|
|
7944
|
+
}
|
|
7945
|
+
//#endregion
|
|
7946
|
+
//#region src/shared/zh-intents.ts
|
|
7947
|
+
/**
|
|
7948
|
+
* Word-boundary (token) match: `term` appears in `text` as a standalone
|
|
7949
|
+
* word. A non-ASCII-alphanumeric character is a boundary, so `ai-chat` and
|
|
7950
|
+
* `AI聊天` hit `ai` while `email`, `main` and `pipeline` do not. The text
|
|
7951
|
+
* must already be lowercased; the regex is cached per term because the
|
|
7952
|
+
* matcher runs this for every entry on every keystroke.
|
|
7953
|
+
*/
|
|
7954
|
+
const tokenRegexCache = /* @__PURE__ */ new Map();
|
|
7955
|
+
function tokenInText(term, text) {
|
|
7956
|
+
let re = tokenRegexCache.get(term);
|
|
7957
|
+
if (re === void 0) {
|
|
7958
|
+
const escaped = term.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
7959
|
+
re = new RegExp(`(?:^|[^a-z0-9])${escaped}(?![a-z0-9])`, "i");
|
|
7960
|
+
tokenRegexCache.set(term, re);
|
|
7961
|
+
}
|
|
7962
|
+
return re.test(text);
|
|
7963
|
+
}
|
|
7964
|
+
/**
|
|
7965
|
+
* The curated dictionary. Ordered by design §3's measured marginal recall
|
|
7966
|
+
* (largest first). Rows trimmed from 2BingLing's eighty-four to eighty by
|
|
7967
|
+
* dropping the four whose marginal recall on the 2026-10-08 catalog was
|
|
7968
|
+
* single-digit: `农历节日`, `Twitter`, `微博`, `Notion`.
|
|
7969
|
+
*
|
|
7970
|
+
* To add a row: write the intent's key in Chinese, its triggers (≥ 2 Han
|
|
7971
|
+
* characters each, as a defensive rule, so an isolated Han inside someone
|
|
7972
|
+
* else's name never fires the intent), its recall terms with `#` where a
|
|
7973
|
+
* bare short Latin term would otherwise act like a substring, and one
|
|
7974
|
+
* comment line pointing at this document's measured table.
|
|
7975
|
+
*/
|
|
7976
|
+
const ZH_INTENTS = [
|
|
7977
|
+
{
|
|
7978
|
+
key: "记忆",
|
|
7979
|
+
words: [
|
|
7980
|
+
"记忆",
|
|
7981
|
+
"记住",
|
|
7982
|
+
"想起"
|
|
7983
|
+
],
|
|
7984
|
+
terms: [
|
|
7985
|
+
"#memory",
|
|
7986
|
+
"#memories",
|
|
7987
|
+
"#remember"
|
|
7988
|
+
]
|
|
7989
|
+
},
|
|
7990
|
+
{
|
|
7991
|
+
key: "主题",
|
|
7992
|
+
words: [
|
|
7993
|
+
"主题",
|
|
7994
|
+
"皮肤",
|
|
7995
|
+
"外观"
|
|
7996
|
+
],
|
|
7997
|
+
terms: [
|
|
7998
|
+
"#theme",
|
|
7999
|
+
"#skin",
|
|
8000
|
+
"#appearance",
|
|
8001
|
+
"主题",
|
|
8002
|
+
"皮肤",
|
|
8003
|
+
"外观"
|
|
8004
|
+
]
|
|
8005
|
+
},
|
|
8006
|
+
{
|
|
8007
|
+
key: "搜索",
|
|
8008
|
+
words: [
|
|
8009
|
+
"搜索",
|
|
8010
|
+
"查找",
|
|
8011
|
+
"查找插件"
|
|
8012
|
+
],
|
|
8013
|
+
terms: [
|
|
8014
|
+
"#search",
|
|
8015
|
+
"search-engine",
|
|
8016
|
+
"搜索",
|
|
8017
|
+
"查找"
|
|
8018
|
+
]
|
|
8019
|
+
},
|
|
8020
|
+
{
|
|
8021
|
+
key: "浏览器",
|
|
8022
|
+
words: [
|
|
8023
|
+
"浏览器",
|
|
8024
|
+
"网页",
|
|
8025
|
+
"上网"
|
|
8026
|
+
],
|
|
8027
|
+
terms: [
|
|
8028
|
+
"#browser",
|
|
8029
|
+
"#web",
|
|
8030
|
+
"浏览器",
|
|
8031
|
+
"网页"
|
|
8032
|
+
]
|
|
8033
|
+
},
|
|
8034
|
+
{
|
|
8035
|
+
key: "AI大模型",
|
|
8036
|
+
words: [
|
|
8037
|
+
"大模型",
|
|
8038
|
+
"llm",
|
|
8039
|
+
"gpt",
|
|
8040
|
+
"智能体",
|
|
8041
|
+
"人工智能"
|
|
8042
|
+
],
|
|
8043
|
+
terms: [
|
|
8044
|
+
"#ai",
|
|
8045
|
+
"#llm",
|
|
8046
|
+
"#gpt",
|
|
8047
|
+
"#agent",
|
|
8048
|
+
"大模型",
|
|
8049
|
+
"人工智能"
|
|
8050
|
+
]
|
|
8051
|
+
},
|
|
8052
|
+
{
|
|
8053
|
+
key: "代码开发",
|
|
8054
|
+
words: [
|
|
8055
|
+
"代码",
|
|
8056
|
+
"编程",
|
|
8057
|
+
"开发"
|
|
8058
|
+
],
|
|
8059
|
+
terms: [
|
|
8060
|
+
"#code",
|
|
8061
|
+
"#coding",
|
|
8062
|
+
"#dev",
|
|
8063
|
+
"代码",
|
|
8064
|
+
"编程"
|
|
8065
|
+
]
|
|
8066
|
+
},
|
|
8067
|
+
{
|
|
8068
|
+
key: "文件管理",
|
|
8069
|
+
words: [
|
|
8070
|
+
"文件管理",
|
|
8071
|
+
"资源管理",
|
|
8072
|
+
"文件整理",
|
|
8073
|
+
"整理文件",
|
|
8074
|
+
"整理",
|
|
8075
|
+
"重命名"
|
|
8076
|
+
],
|
|
8077
|
+
terms: [
|
|
8078
|
+
"#file",
|
|
8079
|
+
"#files",
|
|
8080
|
+
"#explorer",
|
|
8081
|
+
"文件",
|
|
8082
|
+
"#rename",
|
|
8083
|
+
"#renamer",
|
|
8084
|
+
"重命名"
|
|
8085
|
+
]
|
|
8086
|
+
},
|
|
8087
|
+
{
|
|
8088
|
+
key: "聊天",
|
|
8089
|
+
words: [
|
|
8090
|
+
"聊天",
|
|
8091
|
+
"对话",
|
|
8092
|
+
"陪聊"
|
|
8093
|
+
],
|
|
8094
|
+
terms: [
|
|
8095
|
+
"#chat",
|
|
8096
|
+
"#chatbot",
|
|
8097
|
+
"聊天",
|
|
8098
|
+
"对话"
|
|
8099
|
+
]
|
|
8100
|
+
},
|
|
8101
|
+
{
|
|
8102
|
+
key: "搜索引擎",
|
|
8103
|
+
words: ["搜索引擎"],
|
|
8104
|
+
terms: [
|
|
8105
|
+
"#search",
|
|
8106
|
+
"search-engine",
|
|
8107
|
+
"搜索"
|
|
8108
|
+
]
|
|
8109
|
+
},
|
|
8110
|
+
{
|
|
8111
|
+
key: "图片",
|
|
8112
|
+
words: [
|
|
8113
|
+
"图片",
|
|
8114
|
+
"相册",
|
|
8115
|
+
"壁纸",
|
|
8116
|
+
"水印"
|
|
8117
|
+
],
|
|
8118
|
+
terms: [
|
|
8119
|
+
"#image",
|
|
8120
|
+
"#images",
|
|
8121
|
+
"#photo",
|
|
8122
|
+
"#wallpaper",
|
|
8123
|
+
"图片",
|
|
8124
|
+
"壁纸",
|
|
8125
|
+
"相册",
|
|
8126
|
+
"视觉理解",
|
|
8127
|
+
"图像识别",
|
|
8128
|
+
"图片问答",
|
|
8129
|
+
"图像处理",
|
|
8130
|
+
"水印"
|
|
8131
|
+
]
|
|
8132
|
+
},
|
|
8133
|
+
{
|
|
8134
|
+
key: "待办",
|
|
8135
|
+
words: [
|
|
8136
|
+
"待办",
|
|
8137
|
+
"任务管理",
|
|
8138
|
+
"清单",
|
|
8139
|
+
"todo"
|
|
8140
|
+
],
|
|
8141
|
+
terms: [
|
|
8142
|
+
"#todo",
|
|
8143
|
+
"#task",
|
|
8144
|
+
"#checklist",
|
|
8145
|
+
"待办",
|
|
8146
|
+
"任务",
|
|
8147
|
+
"清单",
|
|
8148
|
+
"任务看板",
|
|
8149
|
+
"#kanban"
|
|
8150
|
+
]
|
|
8151
|
+
},
|
|
8152
|
+
{
|
|
8153
|
+
key: "终端",
|
|
8154
|
+
words: [
|
|
8155
|
+
"终端",
|
|
8156
|
+
"命令行",
|
|
8157
|
+
"shell"
|
|
8158
|
+
],
|
|
8159
|
+
terms: [
|
|
8160
|
+
"#terminal",
|
|
8161
|
+
"#shell",
|
|
8162
|
+
"#cli",
|
|
8163
|
+
"终端",
|
|
8164
|
+
"命令行"
|
|
8165
|
+
]
|
|
8166
|
+
},
|
|
8167
|
+
{
|
|
8168
|
+
key: "Git",
|
|
8169
|
+
words: [
|
|
8170
|
+
"git",
|
|
8171
|
+
"github",
|
|
8172
|
+
"gitee",
|
|
8173
|
+
"提交"
|
|
8174
|
+
],
|
|
8175
|
+
terms: [
|
|
8176
|
+
"#git",
|
|
8177
|
+
"#github",
|
|
8178
|
+
"#gitee",
|
|
8179
|
+
"#commit",
|
|
8180
|
+
"版本控制"
|
|
8181
|
+
]
|
|
8182
|
+
},
|
|
8183
|
+
{
|
|
8184
|
+
key: "系统进程",
|
|
8185
|
+
words: [
|
|
8186
|
+
"系统",
|
|
8187
|
+
"进程",
|
|
8188
|
+
"任务管理"
|
|
8189
|
+
],
|
|
8190
|
+
terms: [
|
|
8191
|
+
"#system",
|
|
8192
|
+
"#process",
|
|
8193
|
+
"系统",
|
|
8194
|
+
"进程"
|
|
8195
|
+
]
|
|
8196
|
+
},
|
|
8197
|
+
{
|
|
8198
|
+
key: "阅读",
|
|
8199
|
+
words: [
|
|
8200
|
+
"阅读",
|
|
8201
|
+
"电子书",
|
|
8202
|
+
"小说"
|
|
8203
|
+
],
|
|
8204
|
+
terms: [
|
|
8205
|
+
"#read",
|
|
8206
|
+
"#reader",
|
|
8207
|
+
"#ebook",
|
|
8208
|
+
"#novel",
|
|
8209
|
+
"#epub",
|
|
8210
|
+
"阅读",
|
|
8211
|
+
"小说"
|
|
8212
|
+
]
|
|
8213
|
+
},
|
|
8214
|
+
{
|
|
8215
|
+
key: "记事本",
|
|
8216
|
+
words: [
|
|
8217
|
+
"记事本",
|
|
8218
|
+
"便签",
|
|
8219
|
+
"笔记本",
|
|
8220
|
+
"记事",
|
|
8221
|
+
"笔记"
|
|
8222
|
+
],
|
|
8223
|
+
terms: [
|
|
8224
|
+
"#notes",
|
|
8225
|
+
"#note",
|
|
8226
|
+
"#notepad",
|
|
8227
|
+
"便签",
|
|
8228
|
+
"笔记",
|
|
8229
|
+
"markdown"
|
|
8230
|
+
]
|
|
8231
|
+
},
|
|
8232
|
+
{
|
|
8233
|
+
key: "MCP",
|
|
8234
|
+
words: ["mcp", "mcp服务器"],
|
|
8235
|
+
terms: [
|
|
8236
|
+
"#mcp",
|
|
8237
|
+
"mcp-server",
|
|
8238
|
+
"#mcp-manager",
|
|
8239
|
+
"#mcp-debug"
|
|
8240
|
+
]
|
|
8241
|
+
},
|
|
8242
|
+
{
|
|
8243
|
+
key: "代理网络",
|
|
8244
|
+
words: [
|
|
8245
|
+
"代理",
|
|
8246
|
+
"梯子",
|
|
8247
|
+
"组网"
|
|
8248
|
+
],
|
|
8249
|
+
terms: [
|
|
8250
|
+
"#proxy",
|
|
8251
|
+
"#network",
|
|
8252
|
+
"代理",
|
|
8253
|
+
"网络",
|
|
8254
|
+
"网络代理",
|
|
8255
|
+
"内网穿透"
|
|
8256
|
+
]
|
|
8257
|
+
},
|
|
8258
|
+
{
|
|
8259
|
+
key: "日志",
|
|
8260
|
+
words: ["日志"],
|
|
8261
|
+
terms: [
|
|
8262
|
+
"#log",
|
|
8263
|
+
"#logs",
|
|
8264
|
+
"日志"
|
|
8265
|
+
]
|
|
8266
|
+
},
|
|
8267
|
+
{
|
|
8268
|
+
key: "提醒",
|
|
8269
|
+
words: ["提醒", "通知"],
|
|
8270
|
+
terms: [
|
|
8271
|
+
"#remind",
|
|
8272
|
+
"#reminder",
|
|
8273
|
+
"#notification",
|
|
8274
|
+
"#notify",
|
|
8275
|
+
"提醒",
|
|
8276
|
+
"消息通知"
|
|
8277
|
+
]
|
|
8278
|
+
},
|
|
8279
|
+
{
|
|
8280
|
+
key: "写作文案",
|
|
8281
|
+
words: [
|
|
8282
|
+
"写作",
|
|
8283
|
+
"文案",
|
|
8284
|
+
"改写"
|
|
8285
|
+
],
|
|
8286
|
+
terms: [
|
|
8287
|
+
"#write",
|
|
8288
|
+
"#writing",
|
|
8289
|
+
"写作",
|
|
8290
|
+
"文案",
|
|
8291
|
+
"内容创作"
|
|
8292
|
+
]
|
|
8293
|
+
},
|
|
8294
|
+
{
|
|
8295
|
+
key: "截图",
|
|
8296
|
+
words: [
|
|
8297
|
+
"截图",
|
|
8298
|
+
"截屏",
|
|
8299
|
+
"录屏",
|
|
8300
|
+
"屏幕录制",
|
|
8301
|
+
"长图"
|
|
8302
|
+
],
|
|
8303
|
+
terms: [
|
|
8304
|
+
"#screenshot",
|
|
8305
|
+
"#capture",
|
|
8306
|
+
"#screen",
|
|
8307
|
+
"截图",
|
|
8308
|
+
"录屏",
|
|
8309
|
+
"截图工具",
|
|
8310
|
+
"长图"
|
|
8311
|
+
]
|
|
8312
|
+
},
|
|
8313
|
+
{
|
|
8314
|
+
key: "提示词",
|
|
8315
|
+
words: ["提示词", "prompt"],
|
|
8316
|
+
terms: [
|
|
8317
|
+
"#prompt",
|
|
8318
|
+
"提示词管理",
|
|
8319
|
+
"提示词优化"
|
|
8320
|
+
]
|
|
8321
|
+
},
|
|
8322
|
+
{
|
|
8323
|
+
key: "备份恢复",
|
|
8324
|
+
words: ["备份", "恢复"],
|
|
8325
|
+
terms: [
|
|
8326
|
+
"#backup",
|
|
8327
|
+
"#restore",
|
|
8328
|
+
"备份",
|
|
8329
|
+
"恢复"
|
|
8330
|
+
]
|
|
8331
|
+
},
|
|
8332
|
+
{
|
|
8333
|
+
key: "压缩",
|
|
8334
|
+
words: ["压缩", "解压"],
|
|
8335
|
+
terms: [
|
|
8336
|
+
"#zip",
|
|
8337
|
+
"#compress",
|
|
8338
|
+
"#archive",
|
|
8339
|
+
"压缩",
|
|
8340
|
+
"解压"
|
|
8341
|
+
]
|
|
8342
|
+
},
|
|
8343
|
+
{
|
|
8344
|
+
key: "自动化",
|
|
8345
|
+
words: ["自动化", "rpa"],
|
|
8346
|
+
terms: [
|
|
8347
|
+
"#automation",
|
|
8348
|
+
"#automate",
|
|
8349
|
+
"#workflow",
|
|
8350
|
+
"任务编排",
|
|
8351
|
+
"桌面自动化"
|
|
8352
|
+
]
|
|
8353
|
+
},
|
|
8354
|
+
{
|
|
8355
|
+
key: "代码审查",
|
|
8356
|
+
words: [
|
|
8357
|
+
"审查",
|
|
8358
|
+
"评审",
|
|
8359
|
+
"review"
|
|
8360
|
+
],
|
|
8361
|
+
terms: [
|
|
8362
|
+
"#review",
|
|
8363
|
+
"code-review",
|
|
8364
|
+
"审查"
|
|
8365
|
+
]
|
|
8366
|
+
},
|
|
8367
|
+
{
|
|
8368
|
+
key: "语音合成",
|
|
8369
|
+
words: [
|
|
8370
|
+
"语音",
|
|
8371
|
+
"朗读",
|
|
8372
|
+
"tts",
|
|
8373
|
+
"配音",
|
|
8374
|
+
"声音克隆",
|
|
8375
|
+
"克隆声音"
|
|
8376
|
+
],
|
|
8377
|
+
terms: [
|
|
8378
|
+
"#tts",
|
|
8379
|
+
"#speech",
|
|
8380
|
+
"#voice",
|
|
8381
|
+
"语音",
|
|
8382
|
+
"朗读",
|
|
8383
|
+
"声音克隆"
|
|
8384
|
+
]
|
|
8385
|
+
},
|
|
8386
|
+
{
|
|
8387
|
+
key: "视频",
|
|
8388
|
+
words: ["视频"],
|
|
8389
|
+
terms: [
|
|
8390
|
+
"#video",
|
|
8391
|
+
"视频",
|
|
8392
|
+
"剪辑"
|
|
8393
|
+
]
|
|
8394
|
+
},
|
|
8395
|
+
{
|
|
8396
|
+
key: "监控",
|
|
8397
|
+
words: ["监控"],
|
|
8398
|
+
terms: ["#monitor", "监控"]
|
|
8399
|
+
},
|
|
8400
|
+
{
|
|
8401
|
+
key: "办公文档",
|
|
8402
|
+
words: [
|
|
8403
|
+
"word",
|
|
8404
|
+
"excel",
|
|
8405
|
+
"ppt",
|
|
8406
|
+
"表格",
|
|
8407
|
+
"幻灯片",
|
|
8408
|
+
"演示"
|
|
8409
|
+
],
|
|
8410
|
+
terms: [
|
|
8411
|
+
"#excel",
|
|
8412
|
+
"#word",
|
|
8413
|
+
"#office",
|
|
8414
|
+
"#xlsx",
|
|
8415
|
+
"表格",
|
|
8416
|
+
"幻灯片",
|
|
8417
|
+
"#ppt"
|
|
8418
|
+
]
|
|
8419
|
+
},
|
|
8420
|
+
{
|
|
8421
|
+
key: "知识库",
|
|
8422
|
+
words: [
|
|
8423
|
+
"知识库",
|
|
8424
|
+
"维基",
|
|
8425
|
+
"wiki"
|
|
8426
|
+
],
|
|
8427
|
+
terms: [
|
|
8428
|
+
"#wiki",
|
|
8429
|
+
"#knowledge",
|
|
8430
|
+
"知识库",
|
|
8431
|
+
"维基"
|
|
8432
|
+
]
|
|
8433
|
+
},
|
|
8434
|
+
{
|
|
8435
|
+
key: "网盘",
|
|
8436
|
+
words: ["网盘", "云盘"],
|
|
8437
|
+
terms: [
|
|
8438
|
+
"#cloud",
|
|
8439
|
+
"#drive",
|
|
8440
|
+
"网盘",
|
|
8441
|
+
"云盘",
|
|
8442
|
+
"#onedrive"
|
|
8443
|
+
]
|
|
8444
|
+
},
|
|
8445
|
+
{
|
|
8446
|
+
key: "网络诊断",
|
|
8447
|
+
words: [
|
|
8448
|
+
"ip",
|
|
8449
|
+
"端口",
|
|
8450
|
+
"ping"
|
|
8451
|
+
],
|
|
8452
|
+
terms: [
|
|
8453
|
+
"#ip",
|
|
8454
|
+
"#port",
|
|
8455
|
+
"#tcp",
|
|
8456
|
+
"#udp",
|
|
8457
|
+
"#ping",
|
|
8458
|
+
"端口"
|
|
8459
|
+
]
|
|
8460
|
+
},
|
|
8461
|
+
{
|
|
8462
|
+
key: "清理",
|
|
8463
|
+
words: [
|
|
8464
|
+
"清理",
|
|
8465
|
+
"垃圾",
|
|
8466
|
+
"瘦身"
|
|
8467
|
+
],
|
|
8468
|
+
terms: [
|
|
8469
|
+
"#clean",
|
|
8470
|
+
"#cleanup",
|
|
8471
|
+
"清理",
|
|
8472
|
+
"垃圾"
|
|
8473
|
+
]
|
|
8474
|
+
},
|
|
8475
|
+
{
|
|
8476
|
+
key: "PDF",
|
|
8477
|
+
words: ["pdf"],
|
|
8478
|
+
terms: ["#pdf"]
|
|
8479
|
+
},
|
|
8480
|
+
{
|
|
8481
|
+
key: "飞书钉钉",
|
|
8482
|
+
words: [
|
|
8483
|
+
"飞书",
|
|
8484
|
+
"钉钉",
|
|
8485
|
+
"lark"
|
|
8486
|
+
],
|
|
8487
|
+
terms: [
|
|
8488
|
+
"#feishu",
|
|
8489
|
+
"#lark",
|
|
8490
|
+
"#dingtalk",
|
|
8491
|
+
"飞书",
|
|
8492
|
+
"钉钉"
|
|
8493
|
+
]
|
|
8494
|
+
},
|
|
8495
|
+
{
|
|
8496
|
+
key: "数据库",
|
|
8497
|
+
words: ["数据库", "sql"],
|
|
8498
|
+
terms: [
|
|
8499
|
+
"#database",
|
|
8500
|
+
"#sql",
|
|
8501
|
+
"sqlite",
|
|
8502
|
+
"mysql",
|
|
8503
|
+
"数据库",
|
|
8504
|
+
"数据库连接"
|
|
8505
|
+
]
|
|
8506
|
+
},
|
|
8507
|
+
{
|
|
8508
|
+
key: "日历日程",
|
|
8509
|
+
words: [
|
|
8510
|
+
"日历",
|
|
8511
|
+
"日程",
|
|
8512
|
+
"闹钟",
|
|
8513
|
+
"时钟"
|
|
8514
|
+
],
|
|
8515
|
+
terms: [
|
|
8516
|
+
"#calendar",
|
|
8517
|
+
"#schedule",
|
|
8518
|
+
"#clock",
|
|
8519
|
+
"#alarm",
|
|
8520
|
+
"日历",
|
|
8521
|
+
"日程",
|
|
8522
|
+
"时钟"
|
|
8523
|
+
]
|
|
8524
|
+
},
|
|
8525
|
+
{
|
|
8526
|
+
key: "音乐",
|
|
8527
|
+
words: [
|
|
8528
|
+
"音乐",
|
|
8529
|
+
"歌曲",
|
|
8530
|
+
"听歌"
|
|
8531
|
+
],
|
|
8532
|
+
terms: [
|
|
8533
|
+
"#music",
|
|
8534
|
+
"#audio",
|
|
8535
|
+
"音乐"
|
|
8536
|
+
]
|
|
8537
|
+
},
|
|
8538
|
+
{
|
|
8539
|
+
key: "机器人",
|
|
8540
|
+
words: ["机器人"],
|
|
8541
|
+
terms: [
|
|
8542
|
+
"#bot",
|
|
8543
|
+
"#robot",
|
|
8544
|
+
"机器人"
|
|
8545
|
+
]
|
|
8546
|
+
},
|
|
8547
|
+
{
|
|
8548
|
+
key: "论文文献",
|
|
8549
|
+
words: [
|
|
8550
|
+
"论文",
|
|
8551
|
+
"文献",
|
|
8552
|
+
"学术"
|
|
8553
|
+
],
|
|
8554
|
+
terms: [
|
|
8555
|
+
"#paper",
|
|
8556
|
+
"#arxiv",
|
|
8557
|
+
"#scholar",
|
|
8558
|
+
"论文",
|
|
8559
|
+
"文献"
|
|
8560
|
+
]
|
|
8561
|
+
},
|
|
8562
|
+
{
|
|
8563
|
+
key: "下载",
|
|
8564
|
+
words: ["下载"],
|
|
8565
|
+
terms: [
|
|
8566
|
+
"#download",
|
|
8567
|
+
"#downloader",
|
|
8568
|
+
"下载"
|
|
8569
|
+
]
|
|
8570
|
+
},
|
|
8571
|
+
{
|
|
8572
|
+
key: "健康健身",
|
|
8573
|
+
words: [
|
|
8574
|
+
"健康",
|
|
8575
|
+
"健身",
|
|
8576
|
+
"体重"
|
|
8577
|
+
],
|
|
8578
|
+
terms: [
|
|
8579
|
+
"#health",
|
|
8580
|
+
"#fitness",
|
|
8581
|
+
"健康",
|
|
8582
|
+
"健身"
|
|
8583
|
+
]
|
|
8584
|
+
},
|
|
8585
|
+
{
|
|
8586
|
+
key: "学习背词",
|
|
8587
|
+
words: [
|
|
8588
|
+
"学习",
|
|
8589
|
+
"背单词",
|
|
8590
|
+
"单词",
|
|
8591
|
+
"英语"
|
|
8592
|
+
],
|
|
8593
|
+
terms: [
|
|
8594
|
+
"#learn",
|
|
8595
|
+
"#study",
|
|
8596
|
+
"#vocabulary",
|
|
8597
|
+
"#english",
|
|
8598
|
+
"学习",
|
|
8599
|
+
"单词",
|
|
8600
|
+
"英语"
|
|
8601
|
+
]
|
|
8602
|
+
},
|
|
8603
|
+
{
|
|
8604
|
+
key: "流程图",
|
|
8605
|
+
words: [
|
|
8606
|
+
"流程图",
|
|
8607
|
+
"图表",
|
|
8608
|
+
"绘图"
|
|
8609
|
+
],
|
|
8610
|
+
terms: [
|
|
8611
|
+
"#diagram",
|
|
8612
|
+
"#flowchart",
|
|
8613
|
+
"#mermaid",
|
|
8614
|
+
"流程图",
|
|
8615
|
+
"图表"
|
|
8616
|
+
]
|
|
8617
|
+
},
|
|
8618
|
+
{
|
|
8619
|
+
key: "报告生成",
|
|
8620
|
+
words: [
|
|
8621
|
+
"报告生成",
|
|
8622
|
+
"周报",
|
|
8623
|
+
"日报"
|
|
8624
|
+
],
|
|
8625
|
+
terms: [
|
|
8626
|
+
"#report",
|
|
8627
|
+
"报告生成",
|
|
8628
|
+
"周报",
|
|
8629
|
+
"日报"
|
|
8630
|
+
]
|
|
8631
|
+
},
|
|
8632
|
+
{
|
|
8633
|
+
key: "桌宠",
|
|
8634
|
+
words: ["桌宠", "桌面宠物"],
|
|
8635
|
+
terms: ["#desktop-pet", "桌宠"]
|
|
8636
|
+
},
|
|
8637
|
+
{
|
|
8638
|
+
key: "记账",
|
|
8639
|
+
words: [
|
|
8640
|
+
"记账",
|
|
8641
|
+
"账单",
|
|
8642
|
+
"花销"
|
|
8643
|
+
],
|
|
8644
|
+
terms: [
|
|
8645
|
+
"#expense",
|
|
8646
|
+
"#billing",
|
|
8647
|
+
"记账",
|
|
8648
|
+
"账单"
|
|
8649
|
+
]
|
|
8650
|
+
},
|
|
8651
|
+
{
|
|
8652
|
+
key: "微信",
|
|
8653
|
+
words: [
|
|
8654
|
+
"微信",
|
|
8655
|
+
"公众号",
|
|
8656
|
+
"wechat"
|
|
8657
|
+
],
|
|
8658
|
+
terms: [
|
|
8659
|
+
"wechat",
|
|
8660
|
+
"weixin",
|
|
8661
|
+
"微信",
|
|
8662
|
+
"公众号"
|
|
8663
|
+
]
|
|
8664
|
+
},
|
|
8665
|
+
{
|
|
8666
|
+
key: "股票基金",
|
|
8667
|
+
words: [
|
|
8668
|
+
"股票",
|
|
8669
|
+
"基金",
|
|
8670
|
+
"a股",
|
|
8671
|
+
"行情",
|
|
8672
|
+
"炒股"
|
|
8673
|
+
],
|
|
8674
|
+
terms: [
|
|
8675
|
+
"#stock",
|
|
8676
|
+
"#finance",
|
|
8677
|
+
"股票",
|
|
8678
|
+
"基金",
|
|
8679
|
+
"行情"
|
|
8680
|
+
]
|
|
8681
|
+
},
|
|
8682
|
+
{
|
|
8683
|
+
key: "语音识别",
|
|
8684
|
+
words: [
|
|
8685
|
+
"语音识别",
|
|
8686
|
+
"听写",
|
|
8687
|
+
"whisper"
|
|
8688
|
+
],
|
|
8689
|
+
terms: [
|
|
8690
|
+
"#whisper",
|
|
8691
|
+
"#asr",
|
|
8692
|
+
"#stt",
|
|
8693
|
+
"语音识别",
|
|
8694
|
+
"听写",
|
|
8695
|
+
"语音输入"
|
|
8696
|
+
]
|
|
8697
|
+
},
|
|
8698
|
+
{
|
|
8699
|
+
key: "白板",
|
|
8700
|
+
words: ["白板", "画布"],
|
|
8701
|
+
terms: [
|
|
8702
|
+
"#whiteboard",
|
|
8703
|
+
"#canvas",
|
|
8704
|
+
"白板",
|
|
8705
|
+
"画布"
|
|
8706
|
+
]
|
|
8707
|
+
},
|
|
8708
|
+
{
|
|
8709
|
+
key: "计算换算",
|
|
8710
|
+
words: [
|
|
8711
|
+
"计算器",
|
|
8712
|
+
"换算",
|
|
8713
|
+
"单位",
|
|
8714
|
+
"格式转换"
|
|
8715
|
+
],
|
|
8716
|
+
terms: [
|
|
8717
|
+
"#calculator",
|
|
8718
|
+
"#convert",
|
|
8719
|
+
"计算器",
|
|
8720
|
+
"换算",
|
|
8721
|
+
"数学计算"
|
|
8722
|
+
]
|
|
8723
|
+
},
|
|
8724
|
+
{
|
|
8725
|
+
key: "番茄钟",
|
|
8726
|
+
words: [
|
|
8727
|
+
"番茄钟",
|
|
8728
|
+
"专注",
|
|
8729
|
+
"计时"
|
|
8730
|
+
],
|
|
8731
|
+
terms: [
|
|
8732
|
+
"#pomodoro",
|
|
8733
|
+
"#focus",
|
|
8734
|
+
"#timer",
|
|
8735
|
+
"番茄钟",
|
|
8736
|
+
"专注",
|
|
8737
|
+
"计时"
|
|
8738
|
+
]
|
|
8739
|
+
},
|
|
8740
|
+
{
|
|
8741
|
+
key: "游戏",
|
|
8742
|
+
words: ["游戏", "摸鱼"],
|
|
8743
|
+
terms: [
|
|
8744
|
+
"#game",
|
|
8745
|
+
"#games",
|
|
8746
|
+
"游戏"
|
|
8747
|
+
]
|
|
8748
|
+
},
|
|
8749
|
+
{
|
|
8750
|
+
key: "绘画",
|
|
8751
|
+
words: [
|
|
8752
|
+
"画图",
|
|
8753
|
+
"绘画",
|
|
8754
|
+
"生图",
|
|
8755
|
+
"文生图",
|
|
8756
|
+
"画"
|
|
8757
|
+
],
|
|
8758
|
+
terms: [
|
|
8759
|
+
"#draw",
|
|
8760
|
+
"#drawing",
|
|
8761
|
+
"stable-diffusion",
|
|
8762
|
+
"#image-generation",
|
|
8763
|
+
"绘画",
|
|
8764
|
+
"生图"
|
|
8765
|
+
]
|
|
8766
|
+
},
|
|
8767
|
+
{
|
|
8768
|
+
key: "RSS订阅",
|
|
8769
|
+
words: ["rss", "订阅"],
|
|
8770
|
+
terms: [
|
|
8771
|
+
"#rss",
|
|
8772
|
+
"#feed",
|
|
8773
|
+
"订阅"
|
|
8774
|
+
]
|
|
8775
|
+
},
|
|
8776
|
+
{
|
|
8777
|
+
key: "多模态",
|
|
8778
|
+
words: ["多模态"],
|
|
8779
|
+
terms: [
|
|
8780
|
+
"#multimodal",
|
|
8781
|
+
"#multi-modal",
|
|
8782
|
+
"多模态"
|
|
8783
|
+
]
|
|
8784
|
+
},
|
|
8785
|
+
{
|
|
8786
|
+
key: "OCR",
|
|
8787
|
+
words: ["ocr", "文字识别"],
|
|
8788
|
+
terms: ["#ocr", "文字识别"]
|
|
8789
|
+
},
|
|
8790
|
+
{
|
|
8791
|
+
key: "密码",
|
|
8792
|
+
words: ["密码"],
|
|
8793
|
+
terms: ["#password", "密码"]
|
|
8794
|
+
},
|
|
8795
|
+
{
|
|
8796
|
+
key: "翻译",
|
|
8797
|
+
words: ["翻译", "translate"],
|
|
8798
|
+
terms: [
|
|
8799
|
+
"#translate",
|
|
8800
|
+
"#translation",
|
|
8801
|
+
"翻译"
|
|
8802
|
+
]
|
|
8803
|
+
},
|
|
8804
|
+
{
|
|
8805
|
+
key: "新闻资讯",
|
|
8806
|
+
words: [
|
|
8807
|
+
"新闻",
|
|
8808
|
+
"资讯",
|
|
8809
|
+
"头条",
|
|
8810
|
+
"热搜"
|
|
8811
|
+
],
|
|
8812
|
+
terms: [
|
|
8813
|
+
"#news",
|
|
8814
|
+
"#feed",
|
|
8815
|
+
"#trending",
|
|
8816
|
+
"新闻",
|
|
8817
|
+
"资讯",
|
|
8818
|
+
"热搜",
|
|
8819
|
+
"新闻聚合"
|
|
8820
|
+
]
|
|
8821
|
+
},
|
|
8822
|
+
{
|
|
8823
|
+
key: "简历",
|
|
8824
|
+
words: ["简历"],
|
|
8825
|
+
terms: [
|
|
8826
|
+
"#resume",
|
|
8827
|
+
"#cv",
|
|
8828
|
+
"简历"
|
|
8829
|
+
]
|
|
8830
|
+
},
|
|
8831
|
+
{
|
|
8832
|
+
key: "邮件",
|
|
8833
|
+
words: ["邮件", "邮箱"],
|
|
8834
|
+
terms: [
|
|
8835
|
+
"#mail",
|
|
8836
|
+
"#email",
|
|
8837
|
+
"#smtp",
|
|
8838
|
+
"邮件"
|
|
8839
|
+
]
|
|
8840
|
+
},
|
|
8841
|
+
{
|
|
8842
|
+
key: "随机抽签",
|
|
8843
|
+
words: [
|
|
8844
|
+
"随机",
|
|
8845
|
+
"抽签",
|
|
8846
|
+
"骰子"
|
|
8847
|
+
],
|
|
8848
|
+
terms: [
|
|
8849
|
+
"#random",
|
|
8850
|
+
"#dice",
|
|
8851
|
+
"随机"
|
|
8852
|
+
]
|
|
8853
|
+
},
|
|
8854
|
+
{
|
|
8855
|
+
key: "播放器",
|
|
8856
|
+
words: ["播放器"],
|
|
8857
|
+
terms: ["#player", "播放器"]
|
|
8858
|
+
},
|
|
8859
|
+
{
|
|
8860
|
+
key: "爬虫",
|
|
8861
|
+
words: ["爬虫", "抓取"],
|
|
8862
|
+
terms: [
|
|
8863
|
+
"#crawler",
|
|
8864
|
+
"#scraper",
|
|
8865
|
+
"#spider",
|
|
8866
|
+
"爬虫",
|
|
8867
|
+
"抓取"
|
|
8868
|
+
]
|
|
8869
|
+
},
|
|
8870
|
+
{
|
|
8871
|
+
key: "加密",
|
|
8872
|
+
words: ["加密", "解密"],
|
|
8873
|
+
terms: [
|
|
8874
|
+
"#encrypt",
|
|
8875
|
+
"#crypto",
|
|
8876
|
+
"加密"
|
|
8877
|
+
]
|
|
8878
|
+
},
|
|
8879
|
+
{
|
|
8880
|
+
key: "视频下载",
|
|
8881
|
+
words: [
|
|
8882
|
+
"视频下载",
|
|
8883
|
+
"youtube",
|
|
8884
|
+
"油管",
|
|
8885
|
+
"b站",
|
|
8886
|
+
"bilibili",
|
|
8887
|
+
"哔哩"
|
|
8888
|
+
],
|
|
8889
|
+
terms: [
|
|
8890
|
+
"youtube",
|
|
8891
|
+
"bilibili",
|
|
8892
|
+
"yt-dlp",
|
|
8893
|
+
"哔站"
|
|
8894
|
+
]
|
|
8895
|
+
},
|
|
8896
|
+
{
|
|
8897
|
+
key: "Telegram",
|
|
8898
|
+
words: ["telegram"],
|
|
8899
|
+
terms: ["telegram"]
|
|
8900
|
+
},
|
|
8901
|
+
{
|
|
8902
|
+
key: "数据分析",
|
|
8903
|
+
words: ["数据分析", "数据统计"],
|
|
8904
|
+
terms: [
|
|
8905
|
+
"#data-analysis",
|
|
8906
|
+
"#analytics",
|
|
8907
|
+
"数据分析",
|
|
8908
|
+
"数据统计"
|
|
8909
|
+
]
|
|
8910
|
+
},
|
|
8911
|
+
{
|
|
8912
|
+
key: "问卷投票",
|
|
8913
|
+
words: [
|
|
8914
|
+
"问卷",
|
|
8915
|
+
"投票",
|
|
8916
|
+
"抽奖"
|
|
8917
|
+
],
|
|
8918
|
+
terms: [
|
|
8919
|
+
"#survey",
|
|
8920
|
+
"#poll",
|
|
8921
|
+
"#quiz",
|
|
8922
|
+
"问卷",
|
|
8923
|
+
"投票",
|
|
8924
|
+
"抽奖"
|
|
8925
|
+
]
|
|
8926
|
+
},
|
|
8927
|
+
{
|
|
8928
|
+
key: "Obsidian",
|
|
8929
|
+
words: ["obsidian"],
|
|
8930
|
+
terms: ["obsidian"]
|
|
8931
|
+
},
|
|
8932
|
+
{
|
|
8933
|
+
key: "剪贴板",
|
|
8934
|
+
words: ["剪贴板"],
|
|
8935
|
+
terms: ["#clipboard", "剪贴板"]
|
|
8936
|
+
},
|
|
8937
|
+
{
|
|
8938
|
+
key: "加密货币",
|
|
8939
|
+
words: [
|
|
8940
|
+
"比特币",
|
|
8941
|
+
"加密货币",
|
|
8942
|
+
"币圈"
|
|
8943
|
+
],
|
|
8944
|
+
terms: [
|
|
8945
|
+
"#bitcoin",
|
|
8946
|
+
"#crypto",
|
|
8947
|
+
"#blockchain",
|
|
8948
|
+
"加密货币"
|
|
8949
|
+
]
|
|
8950
|
+
},
|
|
8951
|
+
{
|
|
8952
|
+
key: "天气",
|
|
8953
|
+
words: ["天气"],
|
|
8954
|
+
terms: ["#weather", "天气"]
|
|
8955
|
+
},
|
|
8956
|
+
{
|
|
8957
|
+
key: "汇率",
|
|
8958
|
+
words: ["汇率", "货币"],
|
|
8959
|
+
terms: [
|
|
8960
|
+
"#exchange",
|
|
8961
|
+
"#currency",
|
|
8962
|
+
"汇率"
|
|
8963
|
+
]
|
|
8964
|
+
},
|
|
8965
|
+
{
|
|
8966
|
+
key: "思维导图",
|
|
8967
|
+
words: ["思维导图"],
|
|
8968
|
+
terms: [
|
|
8969
|
+
"#mindmap",
|
|
8970
|
+
"#mind",
|
|
8971
|
+
"思维导图"
|
|
8972
|
+
]
|
|
8973
|
+
},
|
|
8974
|
+
{
|
|
8975
|
+
key: "字幕",
|
|
8976
|
+
words: ["字幕"],
|
|
8977
|
+
terms: ["#subtitle", "字幕"]
|
|
8978
|
+
},
|
|
8979
|
+
{
|
|
8980
|
+
key: "娱乐",
|
|
8981
|
+
words: [
|
|
8982
|
+
"娱乐",
|
|
8983
|
+
"段子",
|
|
8984
|
+
"笑话"
|
|
8985
|
+
],
|
|
8986
|
+
terms: [
|
|
8987
|
+
"#fun",
|
|
8988
|
+
"#joke",
|
|
8989
|
+
"#meme",
|
|
8990
|
+
"娱乐",
|
|
8991
|
+
"段子"
|
|
8992
|
+
]
|
|
8993
|
+
},
|
|
8994
|
+
{
|
|
8995
|
+
key: "面试",
|
|
8996
|
+
words: ["面试"],
|
|
8997
|
+
terms: ["#interview", "面试"]
|
|
8998
|
+
},
|
|
8999
|
+
{
|
|
9000
|
+
key: "Discord",
|
|
9001
|
+
words: ["discord"],
|
|
9002
|
+
terms: ["discord"]
|
|
9003
|
+
}
|
|
9004
|
+
];
|
|
9005
|
+
/**
|
|
9006
|
+
* Fire every intent whose trigger appears in the query (lowercase, substring),
|
|
9007
|
+
* deduplicate the recall vocabulary, and return the query itself first.
|
|
9008
|
+
*
|
|
9009
|
+
* The call site never has to know whether expansion happened: `terms` is
|
|
9010
|
+
* always at least `[query]`. The caller joins these with OR against each
|
|
9011
|
+
* entry's searchable fields, in the same `includes`/`tokenInText` grammar
|
|
9012
|
+
* as the raw query.
|
|
9013
|
+
*/
|
|
9014
|
+
function expandZhQuery(rawQuery) {
|
|
9015
|
+
const query = (rawQuery ?? "").trim().toLowerCase();
|
|
9016
|
+
if (query === "") return {
|
|
9017
|
+
query: "",
|
|
9018
|
+
intents: [],
|
|
9019
|
+
expansions: [],
|
|
9020
|
+
terms: [""]
|
|
9021
|
+
};
|
|
9022
|
+
const intents = [];
|
|
9023
|
+
const expansions = [];
|
|
9024
|
+
const terms = /* @__PURE__ */ new Set();
|
|
9025
|
+
for (const it of ZH_INTENTS) {
|
|
9026
|
+
if (!it.words.some((w) => query.includes(w))) continue;
|
|
9027
|
+
intents.push(it.key);
|
|
9028
|
+
const added = [...new Set(it.terms)].filter((t) => {
|
|
9029
|
+
const lower = t.toLowerCase();
|
|
9030
|
+
return lower !== "" && lower !== query && !terms.has(lower) && !terms.has(t);
|
|
9031
|
+
});
|
|
9032
|
+
for (const t of added) terms.add(t);
|
|
9033
|
+
expansions.push({
|
|
9034
|
+
intent: it.key,
|
|
9035
|
+
terms: added
|
|
9036
|
+
});
|
|
9037
|
+
}
|
|
9038
|
+
return {
|
|
9039
|
+
query,
|
|
9040
|
+
intents,
|
|
9041
|
+
expansions,
|
|
9042
|
+
terms: [query, ...terms]
|
|
7784
9043
|
};
|
|
7785
9044
|
}
|
|
7786
9045
|
//#endregion
|
|
7787
|
-
//#region \0dsh-plugin-shop-css:
|
|
7788
|
-
const css = "/* Shop tab styles — a market shelf inside the settings surface. The visual\n * language is the dsh theme's alias tokens ONLY (the four tokens the previous\n * styles referenced that do not exist — bg-layer-3, interactive-bg-hover,\n * state-error, state-success — are corrected to bg-layer-2 and the\n * state-*-primary set). The signature is the category spine: one HUE per\n * category (seven fixed hues, not the brand token — the brand resolves to\n * near-black in the light theme, so seven brand opacities read as seven\n * shades of gray), with the category name as the cover label in the same hue. Class names are mapped to content-derived names at bundle time\n * (tsdown.sdb5735_client.sa77885_config.s462e50_ts) and stubbed in tests; only the keys are relied\n * on.\n *\n * One token rule the theme forces: the background LAYERS do not carry\n * contrast. In the light theme bg-base, bg-layer-1, bg-layer-2 and\n * bg-layer-3 all resolve to the same white; only the dark theme spreads them\n * (950/875/850/800). So a layer-2 fill on a layer-1 ground is invisible for\n * half of all users, and anything whose fill is its ONLY affordance — the\n * loading skeleton, the borderless capability chips — derives its fill from\n * label-primary instead, which inverts with the theme by construction.\n * Elements that also carry a border or text may keep a layer fill.\n * `css-tokens.sdb5735_client.sd5d636_spec.s462e50_ts` pins this. */\n\n.s61c8a3_panel {\n display: flex;\n flex-direction: column;\n gap: 14px;\n /* The shelf never scrolls sideways: a card too wide for its track wraps\n * its badges rather than widening the panel.\n *\n * Clipped on BOTH axes, with a margin, because `.sdd33f6_searchInput` sits at\n * exactly (0, 0) of this box — it is the first child of `.s3684ff_toolbar`, itself\n * the first child here, and this rule carries no padding — so its focus\n * ring paints ENTIRELY outside the panel.\n *\n * Both axes is the load-bearing part: `overflow-clip-margin` is inert\n * unless the element clips on both. Clipping x alone left the margin doing\n * nothing — measured against the live app on 0.7.1, the ring reached 0 of\n * its 3px on the left at every device pixel ratio (1, 1.25, 1.5, 2), while\n * keeping the full 3px on the right, where the 280px input stops short of\n * the panel and never meets the clip edge. Raising the margin to 6px\n * changed nothing; clipping both axes with the SAME 3px restored the whole\n * ring. A test that reads declarations cannot see that difference, which is\n * why the first attempt at this shipped broken.\n *\n * `clip` is not a scroll container, so this does not bring back the\n * accidental scrolling `hidden` caused by coercing the other axis to\n * `auto`. Nothing is lost vertically: the panel's scrollHeight equals its\n * height and no descendant paints outside the clip rect (measured).\n * `overflow-x: hidden` stays as the fallback for engines without `clip`,\n * where the ring is clipped as it was before, never worse. */\n overflow-x: hidden;\n overflow: clip;\n overflow-clip-margin: 3px;\n}\n\n.sc09ffe_stateLine {\n margin: 0;\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n/* Visually hidden but screen-reader readable (the loading copy while the\n * skeleton stands in visually). */\n.s5293ee_srOnly {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n border: 0;\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n overflow: hidden;\n white-space: nowrap;\n}\n\n/* The loading shelf: ghost cards with the same geometry as the real grid, so\n * the swap to content is a same-shape handoff instead of a jump. */\n.sbd5adb_skeletonGrid {\n display: grid;\n grid-template-columns: 1fr;\n gap: 8px;\n}\n\n.s35b119_skeletonCard {\n position: relative;\n display: flex;\n flex-direction: column;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-1);\n overflow: hidden;\n}\n\n/* The shimmer sweep: one gradient band travels across each ghost card while\n * the bars breathe, so the loading state reads as \"the shelf is scanning\". */\n.s35b119_skeletonCard::after {\n content: '';\n position: absolute;\n inset: 0;\n background: linear-gradient(\n 100deg,\n transparent 25%,\n color-mix(in srgb, var(--dsw-alias-label-primary) 14%, transparent) 50%,\n transparent 75%\n );\n transform: translateX(-100%);\n animation: dshShopSkeletonSweep 1.4s ease-in-out infinite;\n pointer-events: none;\n}\n\n@keyframes dshShopSkeletonSweep {\n to { transform: translateX(100%); }\n}\n\n.s4877cb_skeletonName {\n width: 55%;\n height: 14px;\n margin: 10px 12px 0;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.s74236b_skeletonSummary {\n width: 92%;\n height: 12px;\n margin: 10px 12px 0;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.s75d872_skeletonSummaryShort {\n width: 68%;\n height: 12px;\n margin: 6px 12px 10px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n/* The ghost of the action line, right where the real buttons land. */\n.sf95048_skeletonActions {\n display: block;\n width: 84px;\n height: 24px;\n margin: 0 12px 10px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.s35b119_skeletonCard span {\n animation: dshShopSkeletonPulse 1.6s ease-in-out infinite;\n}\n\n@keyframes dshShopSkeletonPulse {\n 0%, 100% { opacity: 0.5; }\n 50% { opacity: 1; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .s35b119_skeletonCard span { animation: none; opacity: 0.75; }\n .s35b119_skeletonCard::after { content: none; }\n}\n\n.s2c1a05_actionButton {\n align-self: flex-start;\n padding: 5px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.s2c1a05_actionButton:hover {\n border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l2));\n color: var(--dsw-alias-brand-primary);\n}\n\n.s2c1a05_actionButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.s3684ff_toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.sc71852_toolbarRight {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n/* The shop's own version, right of the search box: `v0.4.4`, plus the\n * update button when the version check found a newer release. */\n.s5ce118_versionBlock {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.s9345af_versionText {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sf60373_updateSelfButton {\n padding: 2px 10px;\n border: 1px solid var(--dsw-alias-brand-primary);\n border-radius: 999px;\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n color: var(--dsw-alias-brand-primary);\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n}\n\n.sf60373_updateSelfButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 20%, transparent);\n}\n\n.sf60373_updateSelfButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The on-demand re-check next to the version number: a quiet pill; the\n * border and text carry it in the light theme where bg layers are white. */\n.s85d95b_checkUpdateButton {\n padding: 2px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n cursor: pointer;\n}\n\n.s85d95b_checkUpdateButton:hover {\n border-color: var(--dsw-alias-label-secondary);\n color: var(--dsw-alias-label-primary);\n}\n\n.s85d95b_checkUpdateButton:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n.s85d95b_checkUpdateButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The project's GitHub mark, right of the version row: a quiet icon link.\n * The octocat carries itself — color shift on hover, no text, no border. */\n.s5d0778_githubLink {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 3px;\n border-radius: 6px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s5d0778_githubLink:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.s5d0778_githubLink:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The self-update's expanded states (running log / done + restart /\n * failure detail) sit under the toolbar, above the category bar. */\n.s82fd39_selfUpdatePanel {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 8px;\n}\n\n.sdd33f6_searchInput {\n width: min(280px, 100%);\n padding: 6px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n.sdd33f6_searchInput:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.s327f87_staleBadge {\n padding: 2px 8px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary) 40%, transparent);\n color: var(--dsw-alias-state-warn-primary);\n font-size: 12px;\n}\n\n.s5a84d4_categoryBar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n margin-top: 6px;\n}\n\n.sb8b87d_categoryButton {\n /* The hue this button carries when selected. `All` and `Installed` select by\n * something other than a category and keep the brand token; the six category\n * buttons override it from the shared hue table below, so a pressed Tool tab\n * turns the same blue as a tool card's spine. */\n --category-hue: var(--dsw-alias-brand-primary);\n padding: 3px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n cursor: pointer;\n}\n\n/* Blend text with the theme foreground: darker on light backgrounds, lighter\n * on dark ones. The unblended category hue belongs on the border and fill. */\n.sb8b87d_categoryButton:hover {\n border-color: color-mix(in srgb, var(--category-hue) 45%, var(--dsw-alias-border-l2));\n color: color-mix(in srgb, var(--category-hue) 45%, var(--dsw-alias-label-primary) 55%);\n}\n\n/* Selected. Written `.sb8b87d_categoryButton.s18618e_categoryButtonOn` — TWO classes — so it\n * matches `.sb8b87d_categoryButton:hover`'s specificity and, coming after it, wins.\n * As a bare `.s18618e_categoryButtonOn` it lost both `border-color` and `color` to the\n * hover rule for as long as the pointer stayed on the tab that had just been\n * clicked, which is precisely when a reader looks for confirmation.\n *\n * EVERY declaration here is a colour or a shadow: a pressed tab must occupy\n * exactly the same box as an unpressed one, so the bar does not reflow — and\n * with ~9 pills on a wrapping row, one that grows can push the rest onto\n * another line and move the tab out from under the pointer that just clicked\n * it. That is what `font-weight: 600` did (bold metrics are wider than\n * regular), and it is why nothing may be added here that resolves to a length:\n * no padding, no border-width, no font-size, no letter-spacing.\n * `css-tokens.sdb5735_client.sd5d636_spec.s462e50_ts` fails the build on one.\n *\n * `box-shadow` is the exception that earns its place: it paints outside the\n * layout box, so an inset ring doubles the border's weight at zero cost to the\n * metrics. It is what `font-weight: 600` used to be — a second, thickness-\n * based signal that hover does not also produce — and the `other` tab needs it\n * most: its hue IS the neutral gray this file uses for \"no category\", so on\n * the light theme hue alone barely separates pressed from unpressed. */\n.sb8b87d_categoryButton.s18618e_categoryButtonOn {\n border-color: var(--category-hue);\n background: color-mix(in srgb, var(--category-hue) 14%, transparent);\n color: color-mix(in srgb, var(--category-hue) 45%, var(--dsw-alias-label-primary) 55%);\n box-shadow: inset 0 0 0 1px var(--category-hue);\n}\n\n/* Forced colours replace every colour above with the system palette, which\n * would leave the three colour declarations saying nothing and the pressed tab\n * indistinguishable from its neighbours. `box-shadow` is dropped in this mode\n * too, so the ring has to come back as an outline. */\n@media (forced-colors: active) {\n .sb8b87d_categoryButton.s18618e_categoryButtonOn {\n outline: 2px solid ButtonText;\n outline-offset: -4px;\n }\n}\n\n.sb8b87d_categoryButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The incompatible filter, at the right edge of the category bar: `margin-left:\n * auto` pushes it past the pills, and the bar wraps, so on a narrow panel it\n * drops to its own line rather than squeezing them. It is a MODIFIER, not a\n * ninth category — the categories select what to show and this one subtracts\n * from whatever they selected — so it takes the error hue it filters on rather\n * than joining the hue table.\n *\n * And it is a SWITCH, which is the difference the shape now carries. It used\n * to wear `.sb8b87d_categoryButton`: same pill, same row, same size as the eight tabs,\n * differing only in being red. That reads as a ninth tab, and it is not one —\n * the tabs are choose-one and this is on-or-off. So it drops the pill chrome\n * entirely (no border, no fill, nothing to compete with the row it sits\n * beside) and puts the state in a track, the same one every installed card\n * carries. Nothing here restates the switch geometry; `--switch-hue` is the\n * whole of what this control says about it, and it reaches the label too, so\n * one declaration keeps the word and the track the same colour. */\n.sd6cb42_incompatibleFilter {\n --switch-hue: var(--dsw-alias-state-error-primary);\n margin-left: auto;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n /* Matches the pills' vertical padding so the bar keeps one line box whether\n * this control wraps onto a row of its own or shares theirs. */\n padding: 3px 0;\n border: 0;\n background: none;\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n cursor: pointer;\n}\n\n/* Hover lifts the word to full contrast and warms the track's border. Scoped\n * to the OFF track: `.sd6cb42_incompatibleFilter:hover .saaea46_switch` outranks `.sd9eed4_switchOn`\n * by two classes, so unscoped it would strip the hue from the border at\n * exactly the moment a reader has the pointer on it to check the state. */\n.sd6cb42_incompatibleFilter:hover {\n color: var(--dsw-alias-label-primary);\n}\n\n.sd6cb42_incompatibleFilter:hover .saaea46_switch:not(.sd9eed4_switchOn) {\n border-color: color-mix(in srgb, var(--switch-hue) 45%, var(--dsw-alias-border-l2));\n}\n\n/* On. Written with both classes for the reason `.sb8b87d_categoryButton.s18618e_categoryButtonOn`\n * is: it has to match `:hover`'s specificity and come after it, or the colour\n * reverts under a pointer that has not moved since the click. */\n.sd6cb42_incompatibleFilter.sad5334_incompatibleFilterOn {\n color: color-mix(in srgb, var(--switch-hue) 45%, var(--dsw-alias-label-primary) 55%);\n}\n\n/* Off here means \"nothing is being taken away\" — the resting state — so the\n * knob steps back to the secondary label. The enable switch keeps the full\n * strength dot, where off means \"this plugin is disabled\" and is worth\n * noticing. In this bar it would be the heaviest mark in a row of hairline\n * pills, and on the light theme it is the WHOLE control: bg-layer-2 is the\n * same white as the panel there, so an off track is a hairline outline with a\n * near-black dot sitting in it. */\n.sd6cb42_incompatibleFilter .saaea46_switch:not(.sd9eed4_switchOn) .seaa2dc_switchKnob {\n background: var(--dsw-alias-label-secondary);\n}\n\n/* The ring has no border to trace, so it takes an offset and the pill radius\n * of the tabs it sits beside rather than hugging the text. */\n.sd6cb42_incompatibleFilter:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 2px;\n border-radius: 999px;\n}\n\n.sde2f0d_updatableHeading {\n margin: 2px 0 0;\n font-size: 16px;\n font-weight: 600;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-primary);\n}\n\n.sa33481_catalogStatsRow {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 6px 0 0;\n}\n\n.s4a0bd4_catalogStats {\n margin: 0;\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* Sits beside the 12px stats caption, so it is quieter than `.s2c1a05_actionButton`:\n * this is an affordance, not a call to action — the catalog reloads on its\n * own at mount and the shelf is already usable without it. Named\n * `catalogRefresh*` (not `reload*`) so it cannot collide with the §4 page\n * reload offer below, which reloads the whole tab rather than refetching\n * the catalog data. */\n.sc86217_catalogRefreshButton {\n padding: 1px 9px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n line-height: 17px;\n cursor: pointer;\n}\n\n.sc86217_catalogRefreshButton:hover:not(:disabled) {\n border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l2));\n color: var(--dsw-alias-brand-primary);\n}\n\n.sc86217_catalogRefreshButton:disabled {\n cursor: default;\n opacity: 0.6;\n}\n\n.sc86217_catalogRefreshButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.s7a8776_catalogRefreshFailed {\n font-size: 11px;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n.s1120fc_emptyLine {\n margin: 0;\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n/* The shelf: a responsive grid of cards. */\n/* Single-line rows: one full-width column, one plugin per line. */\n.sef40c8_cards {\n display: grid;\n grid-template-columns: 1fr;\n max-width: 100%;\n gap: 8px;\n list-style: none;\n margin: 0;\n padding: 0;\n animation: dshShopCardsIn 220ms ease;\n}\n\n@keyframes dshShopCardsIn {\n from { opacity: 0; transform: translateY(3px); }\n to { opacity: 1; transform: none; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .sef40c8_cards { animation: none; }\n}\n\n.sda151f_card {\n position: relative;\n display: flex;\n flex-direction: column;\n /* A grid item's min-width is auto by default: its content would widen the\n * 1fr track past the panel and overflow horizontally. */\n min-width: 0;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-1);\n overflow: hidden;\n transition: border-color 120ms ease, transform 120ms ease, box-shadow 120ms ease;\n /* Off-screen cards skip layout and paint; the placeholder keeps scrollbar\n * estimation sane before the browser has measured the real height. */\n content-visibility: auto;\n contain-intrinsic-size: auto 140px;\n}\n\n/* The sentinel that triggers the next shelf batch: zero visual footprint. */\n.s22a6a9_cardsSentry {\n height: 1px;\n margin: 0;\n padding: 0;\n}\n\n.sec1f7d_showingLine {\n margin: 0;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sda151f_card:hover {\n border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l1));\n transform: translateY(-1px);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .sda151f_card { transition: none; }\n .sda151f_card:hover { transform: none; }\n}\n\n/* The signature: a spine and cover in the category's own hue. The hues are\n * fixed mid-bright colors that read on both themes; `other` deliberately\n * stays a neutral gray so the fallback category does not compete. */\n.saa352e_cardSpine {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 3px;\n background: var(--category-hue, #8B8E96);\n}\n\n/* The hue table, keyed by `data-category` and read by BOTH surfaces that\n * speak in category colour: a card's spine and badge, and the category bar's\n * own tab. One table, so a tab can never disagree with the cards it filters to\n * — which is the whole point of colouring the tab. `.sb8b87d_categoryButton` sets the\n * brand fallback for `All`/`Installed`; these attribute selectors are one\n * specificity step above it and win wherever the attribute is present. */\n.sda151f_card[data-category='tool'], .sb8b87d_categoryButton[data-category='tool'] { --category-hue: #4C8DFF; }\n.sda151f_card[data-category='provider'], .sb8b87d_categoryButton[data-category='provider'] { --category-hue: #A78BFA; }\n.sda151f_card[data-category='ui'], .sb8b87d_categoryButton[data-category='ui'] { --category-hue: #2DD4BF; }\n.sda151f_card[data-category='workflow'], .sb8b87d_categoryButton[data-category='workflow'] { --category-hue: #F59E0B; }\n.sda151f_card[data-category='integration'], .sb8b87d_categoryButton[data-category='integration'] { --category-hue: #34D399; }\n.sda151f_card[data-category='theme'], .sb8b87d_categoryButton[data-category='theme'] { --category-hue: #F472B6; }\n.sda151f_card[data-category='other'], .sb8b87d_categoryButton[data-category='other'] { --category-hue: #8B8E96; }\n\n/* The category sits in the badge row as a quiet chip in the category's own\n * hue — the border carries it where the light theme's white layers cannot\n * (the same lesson as the check button). */\n.sde8d65_categoryBadge {\n padding: 1px 7px;\n border: 1px solid color-mix(in srgb, var(--category-hue, #8B8E96) 45%, transparent);\n border-radius: 999px;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--category-hue, #8B8E96);\n white-space: nowrap;\n}\n\n.s9a4741_entryHeader {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 12px;\n min-width: 0;\n padding: 8px 12px 2px;\n border: none;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n\n.s9a4741_entryHeader:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n/* The name fills the row and truncates rather than wraps; the badges stay\n * intact beside it. */\n.s86793e_name {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 13px;\n font-weight: 600;\n flex: 1 1 auto;\n min-width: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--dsw-alias-label-primary);\n}\n\n.s9fd7a8_badges {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n/* The action line: buttons side by side; an active install/uninstall flow\n * (gate, log, notices) takes the full width below them. */\n.scb6ea7_cardActions {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n padding: 8px 12px 10px;\n}\n\n.scb6ea7_cardActions > .s1d9b49_gate,\n.scb6ea7_cardActions > .s55951e_installPanel,\n.scb6ea7_cardActions > .s70ba39_installedActions {\n flex-basis: 100%;\n}\n\n/* The entry's version, always visible on the card (the expanded detail\n * repeats it in the version row) — the same quiet mono as the toolbar. */\n.s99fcd8_cardVersion {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* The octocat marking a github-sourced entry, beside its short commit. */\n.se3f041_sourceBadge {\n display: inline-flex;\n align-items: center;\n color: var(--dsw-alias-label-secondary);\n}\n\n.scbdeae_tierBadge {\n padding: 1px 7px;\n border-radius: 999px;\n font-size: 11px;\n line-height: 1.6;\n}\n\n.scbdeae_tierBadge[data-tier='verified'] {\n color: var(--dsw-alias-state-success-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-success-primary) 40%, transparent);\n}\n\n.scbdeae_tierBadge[data-tier='verified-stale'] {\n color: var(--dsw-alias-state-warn-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary) 40%, transparent);\n}\n\n.scbdeae_tierBadge[data-tier='community'] {\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n border: 1px solid var(--dsw-alias-border-l2);\n}\n\n.s888f2a_incompatibleBadge {\n flex-shrink: 0;\n padding: 3px 9px;\n border-radius: 999px;\n font-size: 12px;\n white-space: nowrap;\n color: var(--dsw-alias-state-error-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 40%, transparent);\n}\n\n.s738616_starsBadge {\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s4866d4_chevron, .sfa79fc_chevronOpen {\n color: var(--dsw-alias-label-secondary);\n transition: transform 120ms ease;\n}\n\n.sfa79fc_chevronOpen { transform: rotate(180deg); }\n\n@media (prefers-reduced-motion: reduce) {\n .s4866d4_chevron, .sfa79fc_chevronOpen { transition: none; }\n}\n\n.sbc4ab9_body {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 0 12px;\n}\n\n.s86ccc3_summary {\n margin: 0;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.sdbbc60_summaryZh {\n margin: -4px 0 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n display: -webkit-box;\n -webkit-line-clamp: 1;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n/* Expanded cards show the full summary: the clamp is lifted, never raised —\n * a higher number would still truncate the author's text, just later. */\n.sfd6f7e_summaryExpanded {\n -webkit-line-clamp: unset;\n display: block;\n}\n\n.sd8f966_summaryZhExpanded {\n -webkit-line-clamp: unset;\n display: block;\n}\n\n/* `white-space: pre-line` so the newline in the copy is the copy's own: the\n dictionary decides where the line breaks, and a translator changes it\n without touching the component. Collapses runs of spaces like `normal`, so\n only the explicit \\n survives. */\n.sc61f12_incompatibleDetail {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n white-space: pre-line;\n color: var(--dsw-alias-state-error-primary);\n}\n\n/* The way out of dsh's own refusal: the lead-in, then the exemption command\n * on its own line. */\n.s7925b1_remedy {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.sc8866b_remedyLead {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n}\n\n/* The exemption command, bound for a terminal unchanged: one click selects\n * all of it, and it wraps anywhere rather than widening the card. A border,\n * not a fill, marks the box: the bg layers are one white in the light theme. */\n.s9bd892_allowCommand {\n display: block;\n padding: 6px 8px;\n border-radius: 6px;\n background: var(--dsw-alias-bg-base);\n border: 1px solid var(--dsw-alias-border-l1);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px;\n line-height: 1.5;\n white-space: pre-wrap;\n word-break: break-all;\n user-select: all;\n color: var(--dsw-alias-label-primary);\n}\n\n/* A refused install on a surface that does not state its blockers — the\n * outdated row — writes them out on their own full-width line under the\n * disabled button, the command that clears dsh's refusal included. */\n.s9612af_refusal {\n display: flex;\n flex-direction: column;\n gap: 6px;\n flex-basis: 100%;\n}\n\n.s9138b6_capabilitiesBlock {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.s8a9920_capabilitiesNote {\n margin: 0;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sb90bfe_capabilities {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.sb90bfe_capabilities li {\n padding: 1px 6px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 6%, transparent);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sf0358c_detail {\n border-top: 1px solid var(--dsw-alias-border-l1);\n padding-top: 8px;\n}\n\n.secfa90_detailRows {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin: 0;\n}\n\n.s64fa44_detailRow {\n display: flex;\n justify-content: space-between;\n gap: 12px;\n font-size: 12px;\n}\n\n.s64fa44_detailRow dt {\n color: var(--dsw-alias-label-secondary);\n}\n\n.s64fa44_detailRow dd {\n margin: 0;\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n overflow-wrap: anywhere;\n text-align: right;\n color: var(--dsw-alias-label-primary);\n}\n\n.s64fa44_detailRow a {\n color: var(--dsw-alias-brand-primary);\n text-decoration: none;\n}\n\n.s64fa44_detailRow a:hover {\n text-decoration: underline;\n}\n\n.s64fa44_detailRow a:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* What the entry IS rather than what you can do with it — how big it is and\n * who published it — at the right edge of the action row. `margin-left: auto`\n * is what pushes it there, and it lives on this wrapper rather than on either\n * label so the group stays flush right when only one of the two has a value —\n * a packument that named no account has no author, and an entry the registry\n * could not measure honestly has no size. Rarer than it was (every live entry\n * now carries a size, github included), but the wrapper is not what makes it\n * rare: without one, an EMPTY meta group still costs 8px, because\n * `.scb6ea7_cardActions` is a flex row with `gap: 8px` and a zero-width item is still\n * an item. The row is a wrapping flex, so an active install flow (which claims\n * the full width) drops the whole group to the next line rather than squeezing\n * it. */\n.s3cb9f0_cardMeta {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n margin-left: auto;\n min-width: 0;\n}\n\n/* The on-disk size, left of the author. Mono and tabular so the digits of one\n * figure line up with the digits of another WHEREVER the two land: `.s3cb9f0_cardMeta`\n * is flush right and the author sits outside the size, so a size's x position\n * moves with the author's width and is different again on the many entries\n * that carry no author at all. There is no column to align to, which is why\n * the reason given here is per-figure legibility rather than the column the\n * first version of this comment claimed. Secondary colour, because it\n * qualifies the entry rather than announcing it.\n *\n * `font-variant-numeric` is safe HERE and would not be on a pill: this element\n * has no state rules, so nothing it does can reflow a wrapping row. */\n.sd5e1c9_size {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-variant-numeric: tabular-nums;\n font-size: 12px;\n letter-spacing: 0.02em;\n white-space: nowrap;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* The publishing account, at the outer end of `.s3cb9f0_cardMeta` on the action row —\n * NOT beside the npm link in the expanded detail, which is where it used to\n * live and what this comment described until the accurate half was deleted\n * with the `margin-left: auto` that moved to `.s3cb9f0_cardMeta`. There is no middot:\n * `.s3cb9f0_cardMeta`'s 8px gap separates it from the size.\n *\n * Secondary label colour carries the distinction — never a background fill,\n * which is invisible in the light theme where every bg-layer token is the\n * same white. */\n.s1cd598_author {\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n}\n\n.se81873_reviewedLine {\n margin: 8px 0 0;\n padding: 6px 8px;\n border-radius: 6px;\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 12%, transparent);\n font-size: 12px;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n/* Install flow. */\n.s55951e_installPanel {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 2px;\n}\n\n.s30c7c1_installButton, .sdf53e9_confirmButton, .sfe6376_cancelButton {\n align-self: flex-start;\n padding: 5px 14px;\n border-radius: 6px;\n font-size: 13px;\n cursor: pointer;\n}\n\n.s30c7c1_installButton {\n border: 1px solid var(--dsw-alias-brand-primary);\n background: var(--dsw-alias-brand-primary);\n color: var(--dsw-alias-bg-base);\n}\n\n.s30c7c1_installButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 88%, var(--dsw-alias-label-primary));\n}\n\n.sdf53e9_confirmButton {\n border: 1px solid var(--dsw-alias-brand-primary);\n background: var(--dsw-alias-brand-primary);\n color: var(--dsw-alias-bg-base);\n}\n\n.sdf53e9_confirmButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 88%, var(--dsw-alias-label-primary));\n}\n\n.sfe6376_cancelButton {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n}\n\n.sfe6376_cancelButton:hover {\n border-color: var(--dsw-alias-label-secondary);\n}\n\n.s30c7c1_installButton:focus-visible, .sdf53e9_confirmButton:focus-visible, .sfe6376_cancelButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The shelf card of a plugin that is installed and current carries this\n * non-interactive label instead of an install button. Border + text do the\n * work in the light theme, where every bg-layer token resolves to white. */\n.sc51ddc_installedLabel {\n align-self: flex-start;\n margin: 0;\n padding: 5px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n font-size: 13px;\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n}\n\n/* An installed card's controls: the installed label or the update button,\n * plus the uninstall button. The install panel's running/failed states are\n * column layouts and sit as one item of this row. */\n.s70ba39_installedActions {\n display: flex;\n align-items: flex-start;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n/* Uninstall revokes privilege; the quiet outline keeps it from competing\n * with the primary action, and the error tint says \"removes\". */\n.s7a7545_uninstallButton {\n align-self: flex-start;\n padding: 5px 14px;\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 55%, transparent);\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-state-error-primary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.s7a7545_uninstallButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);\n}\n\n.s7a7545_uninstallButton:focus-visible {\n outline: 2px solid var(--dsw-alias-state-error-primary);\n outline-offset: 1px;\n}\n\n/* The restart dsh button on a done install: quiet outline, same rank as\n * the other secondary actions. */\n.s888e38_restartSelfButton {\n /* The version row's third state. Geometry matches .s85d95b_checkUpdateButton and\n * .sf60373_updateSelfButton exactly — the row is one evolving control and a\n * different pill would read as a different kind of thing. It takes the\n * emphasised fill rather than the neutral one because at that moment the\n * restart is the only thing left to do, the same reason Update has it. */\n padding: 2px 10px;\n border: 1px solid var(--dsw-alias-brand-primary);\n border-radius: 999px;\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n color: var(--dsw-alias-brand-primary);\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n}\n\n.s888e38_restartSelfButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The two activation offers share one recipe: a border in an alias token,\n * never a `bg-layer` fill alone — all four bg-layer tokens collapse to the\n * same white under `body` in the light theme, and a fill-only button would\n * vanish there. Grouped rather than copied, because a copy is a rule that\n * can be corrected on one button and left wrong on the other, silently. */\n.sb72afe_restartButton,\n.sada3ca_reloadButton {\n align-self: flex-start;\n padding: 5px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.sb72afe_restartButton:hover,\n.sada3ca_reloadButton:hover {\n border-color: var(--dsw-alias-label-secondary);\n}\n\n.sb72afe_restartButton:focus-visible,\n.sada3ca_reloadButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The §4 reload offer: a notice plus its button, one item of\n * `.s70ba39_installedActions` the way the restart gate is — a column of its own\n * rather than inline with the row. */\n.s5e909c_reloadPanel {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 6px;\n}\n\n\n.s1d9b49_gate {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 10px;\n border: 1px solid var(--dsw-alias-state-warn-primary);\n border-radius: 8px;\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 8%, transparent);\n}\n\n.sf297e2_gateTitle {\n margin: 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n.s355722_gateBody {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-primary);\n}\n\n.sad7d6e_gateWarning {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n white-space: pre-line;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.s4c3412_gateActions {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n.s8107d5_installing {\n margin: 0;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s8459f6_log {\n max-height: 160px;\n overflow: auto;\n margin: 0;\n padding: 8px;\n border-radius: 6px;\n background: var(--dsw-alias-bg-base);\n border: 1px solid var(--dsw-alias-border-l1);\n list-style: none;\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px;\n line-height: 1.5;\n}\n\n.sd2b885_logLine {\n white-space: pre-wrap;\n word-break: break-all;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s838f51_notice {\n margin: 0;\n font-size: 12px;\n color: var(--dsw-alias-state-success-primary);\n}\n\n.sf7a8b1_failedHeading {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.s13de59_failedDetail {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-primary);\n}\n\n.s2b86d5_rejectedCode {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.s62d32b_rejectedDetail {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-primary);\n}\n\n/* The Updatable section. The class keeps the Host's field name; only the\n heading the reader sees was renamed. */\n.sa62a7c_outdatedSection {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 6px;\n}\n\n.sc2080f_outdatedList {\n display: flex;\n flex-direction: column;\n gap: 8px;\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.s7ec61a_outdatedRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n flex-wrap: wrap;\n padding: 10px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-1);\n}\n\n.sf5f2cb_outdatedInfo {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.sf5f2cb_outdatedInfo strong {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 13px;\n word-break: break-all;\n color: var(--dsw-alias-label-primary);\n}\n\n.sb5d2f9_outdatedVersions {\n /* Two inline labels with nothing between them in the markup ran together as\n * `installed v1.0.0latest v2.0.0`: JSX drops the whitespace-only line, and\n * the per-label class the markup asked for was never defined (singular\n * against this rule's plural). The container owns the gap, the same idiom\n * as .s9fd7a8_badges — the labels need no class of their own. */\n display: inline-flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.se09e8b_outdatedActions {\n display: flex;\n align-items: center;\n /* The reload offer (§4) mounts inside the switch beside the Update button\n * and is a two-line block, not a control: without wrapping, a row reading\n * [switch][notice + Reload][Update] overflows the settings dialog. */\n flex-wrap: wrap;\n gap: 10px;\n}\n\n/* The shop's one switch. It is worn by two controls that mean different\n * things — the enable/disable toggle on an installed card, and the shelf's\n * incompatible filter — so the geometry lives here once and each caller states\n * only its hue.\n *\n * `--switch-hue` is that knob, and it is read from the ANCESTOR rather than\n * declared per state: `.sd6cb42_incompatibleFilter` sets it once and it reaches the\n * track and the label together, which is what keeps a red switch from sitting\n * under a neutral word. A declaration on `.saaea46_switch` itself would shadow the\n * inherited value (an element's own rule beats what it inherits), so the\n * default rides in the `var()` fallback and is resolved once into\n * `--track-hue` instead of being spelled out at each use.\n *\n * `box-sizing` and `padding` are stated because the two callers are different\n * elements — a `<button>`, which the UA gives padding, and a `<span>`, which\n * it does not. Without them the same class renders two sizes, and the knob\n * sits 2px above centre in the taller one. */\n.saaea46_switch {\n --track-hue: var(--switch-hue, var(--dsw-alias-state-success-primary));\n box-sizing: border-box;\n display: inline-block;\n position: relative;\n width: 32px;\n height: 18px;\n padding: 0;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-2);\n cursor: pointer;\n transition: background 120ms ease, border-color 120ms ease;\n}\n\n/* The knob's POSITION is the state, which is the reason this control needs no\n * `forced-colors` fallback the way the category pills do: a system palette\n * replaces every colour below and moves nothing. */\n.sd9eed4_switchOn {\n border-color: var(--track-hue);\n background: color-mix(in srgb, var(--track-hue) 55%, transparent);\n}\n\n.seaa2dc_switchKnob {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 12px;\n height: 12px;\n border-radius: 999px;\n background: var(--dsw-alias-label-primary);\n transition: transform 120ms ease, background 120ms ease;\n}\n\n.sd9eed4_switchOn .seaa2dc_switchKnob {\n transform: translateX(14px);\n background: var(--dsw-alias-bg-base);\n}\n\n.saaea46_switch:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .saaea46_switch, .seaa2dc_switchKnob { transition: none; }\n}\n\n/* The hot enable/disable switch on installed rows: the knob button plus the\n * §8 hot-apply / failure notices rendered beside it. */\n.s514551_switchWrap {\n display: inline-flex;\n align-items: center;\n /* Same reason as `.se09e8b_outdatedActions`: `.s5e909c_reloadPanel`'s own comment calls\n * itself \"a column of its own rather than inline with the row\", which held\n * for `.s70ba39_installedActions` and not here until this line. */\n flex-wrap: wrap;\n gap: 8px;\n}\n";
|
|
7789
|
-
if (typeof document !== "undefined" && !document.querySelector("style[data-plugin-css=\"
|
|
9046
|
+
//#region \0dsh-plugin-shop-css:e3675a89
|
|
9047
|
+
const css = "/* Shop tab styles — a market shelf inside the settings surface. The visual\n * language is the dsh theme's alias tokens ONLY (the four tokens the previous\n * styles referenced that do not exist — bg-layer-3, interactive-bg-hover,\n * state-error, state-success — are corrected to bg-layer-2 and the\n * state-*-primary set). The signature is the category spine: one HUE per\n * category (seven fixed hues, not the brand token — the brand resolves to\n * near-black in the light theme, so seven brand opacities read as seven\n * shades of gray), with the category name as the cover label in the same hue. Class names are mapped to content-derived names at bundle time\n * (tsdown.s8583e3_client.s942a79_config.s5f67aa_ts) and stubbed in tests; only the keys are relied\n * on.\n *\n * One token rule the theme forces: the background LAYERS do not carry\n * contrast. In the light theme bg-base, bg-layer-1, bg-layer-2 and\n * bg-layer-3 all resolve to the same white; only the dark theme spreads them\n * (950/875/850/800). So a layer-2 fill on a layer-1 ground is invisible for\n * half of all users, and anything whose fill is its ONLY affordance — the\n * loading skeleton, the borderless capability chips — derives its fill from\n * label-primary instead, which inverts with the theme by construction.\n * Elements that also carry a border or text may keep a layer fill.\n * `css-tokens.s8583e3_client.sfd9d8a_spec.s5f67aa_ts` pins this. */\n\n.s255e5a_panel {\n display: flex;\n flex-direction: column;\n gap: 14px;\n /* The shelf never scrolls sideways: a card too wide for its track wraps\n * its badges rather than widening the panel.\n *\n * Clipped on BOTH axes, with a margin, because `.s232ec4_searchInput` sits at\n * exactly (0, 0) of this box — it is the first child of `.s646509_toolbar`, itself\n * the first child here, and this rule carries no padding — so its focus\n * ring paints ENTIRELY outside the panel.\n *\n * Both axes is the load-bearing part: `overflow-clip-margin` is inert\n * unless the element clips on both. Clipping x alone left the margin doing\n * nothing — measured against the live app on 0.7.1, the ring reached 0 of\n * its 3px on the left at every device pixel ratio (1, 1.25, 1.5, 2), while\n * keeping the full 3px on the right, where the 280px input stops short of\n * the panel and never meets the clip edge. Raising the margin to 6px\n * changed nothing; clipping both axes with the SAME 3px restored the whole\n * ring. A test that reads declarations cannot see that difference, which is\n * why the first attempt at this shipped broken.\n *\n * `clip` is not a scroll container, so this does not bring back the\n * accidental scrolling `hidden` caused by coercing the other axis to\n * `auto`. Nothing is lost vertically: the panel's scrollHeight equals its\n * height and no descendant paints outside the clip rect (measured).\n * `overflow-x: hidden` stays as the fallback for engines without `clip`,\n * where the ring is clipped as it was before, never worse. */\n overflow-x: hidden;\n overflow: clip;\n overflow-clip-margin: 3px;\n}\n\n.s44a3ec_stateLine {\n margin: 0;\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n/* Visually hidden but screen-reader readable (the loading copy while the\n * skeleton stands in visually). */\n.s3a0ece_srOnly {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n border: 0;\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n overflow: hidden;\n white-space: nowrap;\n}\n\n/* The loading shelf: ghost cards with the same geometry as the real grid, so\n * the swap to content is a same-shape handoff instead of a jump. */\n.se0c3b4_skeletonGrid {\n display: grid;\n grid-template-columns: 1fr;\n gap: 8px;\n}\n\n.s520416_skeletonCard {\n position: relative;\n display: flex;\n flex-direction: column;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-1);\n overflow: hidden;\n}\n\n/* The shimmer sweep: one gradient band travels across each ghost card while\n * the bars breathe, so the loading state reads as \"the shelf is scanning\". */\n.s520416_skeletonCard::after {\n content: '';\n position: absolute;\n inset: 0;\n background: linear-gradient(\n 100deg,\n transparent 25%,\n color-mix(in srgb, var(--dsw-alias-label-primary) 14%, transparent) 50%,\n transparent 75%\n );\n transform: translateX(-100%);\n animation: dshShopSkeletonSweep 1.4s ease-in-out infinite;\n pointer-events: none;\n}\n\n@keyframes dshShopSkeletonSweep {\n to { transform: translateX(100%); }\n}\n\n.s8c7d6f_skeletonName {\n width: 55%;\n height: 14px;\n margin: 10px 12px 0;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.sf5179e_skeletonSummary {\n width: 92%;\n height: 12px;\n margin: 10px 12px 0;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.sbb0d25_skeletonSummaryShort {\n width: 68%;\n height: 12px;\n margin: 6px 12px 10px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n/* The ghost of the action line, right where the real buttons land. */\n.s96e910_skeletonActions {\n display: block;\n width: 84px;\n height: 24px;\n margin: 0 12px 10px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.s520416_skeletonCard span {\n animation: dshShopSkeletonPulse 1.6s ease-in-out infinite;\n}\n\n@keyframes dshShopSkeletonPulse {\n 0%, 100% { opacity: 0.5; }\n 50% { opacity: 1; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .s520416_skeletonCard span { animation: none; opacity: 0.75; }\n .s520416_skeletonCard::after { content: none; }\n}\n\n.s73242a_actionButton {\n align-self: flex-start;\n padding: 5px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.s73242a_actionButton:hover {\n border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l2));\n color: var(--dsw-alias-brand-primary);\n}\n\n.s73242a_actionButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.s646509_toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.s745e3c_toolbarRight {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n/* The shop's own version, right of the search box: `v0.4.4`, plus the\n * update button when the version check found a newer release. */\n.s4fe61f_versionBlock {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.sb9b924_versionText {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sa04455_updateSelfButton {\n padding: 2px 10px;\n border: 1px solid var(--dsw-alias-brand-primary);\n border-radius: 999px;\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n color: var(--dsw-alias-brand-primary);\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n}\n\n.sa04455_updateSelfButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 20%, transparent);\n}\n\n.sa04455_updateSelfButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The on-demand re-check next to the version number: a quiet pill; the\n * border and text carry it in the light theme where bg layers are white. */\n.s614a38_checkUpdateButton {\n padding: 2px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n cursor: pointer;\n}\n\n.s614a38_checkUpdateButton:hover {\n border-color: var(--dsw-alias-label-secondary);\n color: var(--dsw-alias-label-primary);\n}\n\n.s614a38_checkUpdateButton:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n.s614a38_checkUpdateButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The project's GitHub mark, right of the version row: a quiet icon link.\n * The octocat carries itself — color shift on hover, no text, no border. */\n.sa987f7_githubLink {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 3px;\n border-radius: 6px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sa987f7_githubLink:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sa987f7_githubLink:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The self-update's expanded states (running log / done + restart /\n * failure detail) sit under the toolbar, above the category bar. */\n.sb81ba5_selfUpdatePanel {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 8px;\n}\n\n.s232ec4_searchInput {\n width: min(280px, 100%);\n padding: 6px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n.s232ec4_searchInput:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* Subdued line under the search box when a dictionary intent fired (design\n * 2026-10-08-search-query-expansion §4). One fact, no badge. */\n.s76fb13_expansionNote {\n margin: 4px 0 0;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s3946f1_staleBadge {\n padding: 2px 8px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary) 40%, transparent);\n color: var(--dsw-alias-state-warn-primary);\n font-size: 12px;\n}\n\n.sb2c859_categoryBar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n margin-top: 6px;\n}\n\n.s26d58e_categoryButton {\n /* The hue this button carries when selected. `All` and `Installed` select by\n * something other than a category and keep the brand token; the six category\n * buttons override it from the shared hue table below, so a pressed Tool tab\n * turns the same blue as a tool card's spine. */\n --category-hue: var(--dsw-alias-brand-primary);\n padding: 3px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n cursor: pointer;\n}\n\n/* Blend text with the theme foreground: darker on light backgrounds, lighter\n * on dark ones. The unblended category hue belongs on the border and fill. */\n.s26d58e_categoryButton:hover {\n border-color: color-mix(in srgb, var(--category-hue) 45%, var(--dsw-alias-border-l2));\n color: color-mix(in srgb, var(--category-hue) 45%, var(--dsw-alias-label-primary) 55%);\n}\n\n/* Selected. Written `.s26d58e_categoryButton.s5f68c4_categoryButtonOn` — TWO classes — so it\n * matches `.s26d58e_categoryButton:hover`'s specificity and, coming after it, wins.\n * As a bare `.s5f68c4_categoryButtonOn` it lost both `border-color` and `color` to the\n * hover rule for as long as the pointer stayed on the tab that had just been\n * clicked, which is precisely when a reader looks for confirmation.\n *\n * EVERY declaration here is a colour or a shadow: a pressed tab must occupy\n * exactly the same box as an unpressed one, so the bar does not reflow — and\n * with ~9 pills on a wrapping row, one that grows can push the rest onto\n * another line and move the tab out from under the pointer that just clicked\n * it. That is what `font-weight: 600` did (bold metrics are wider than\n * regular), and it is why nothing may be added here that resolves to a length:\n * no padding, no border-width, no font-size, no letter-spacing.\n * `css-tokens.s8583e3_client.sfd9d8a_spec.s5f67aa_ts` fails the build on one.\n *\n * `box-shadow` is the exception that earns its place: it paints outside the\n * layout box, so an inset ring doubles the border's weight at zero cost to the\n * metrics. It is what `font-weight: 600` used to be — a second, thickness-\n * based signal that hover does not also produce — and the `other` tab needs it\n * most: its hue IS the neutral gray this file uses for \"no category\", so on\n * the light theme hue alone barely separates pressed from unpressed. */\n.s26d58e_categoryButton.s5f68c4_categoryButtonOn {\n border-color: var(--category-hue);\n background: color-mix(in srgb, var(--category-hue) 14%, transparent);\n color: color-mix(in srgb, var(--category-hue) 45%, var(--dsw-alias-label-primary) 55%);\n box-shadow: inset 0 0 0 1px var(--category-hue);\n}\n\n/* Forced colours replace every colour above with the system palette, which\n * would leave the three colour declarations saying nothing and the pressed tab\n * indistinguishable from its neighbours. `box-shadow` is dropped in this mode\n * too, so the ring has to come back as an outline. */\n@media (forced-colors: active) {\n .s26d58e_categoryButton.s5f68c4_categoryButtonOn {\n outline: 2px solid ButtonText;\n outline-offset: -4px;\n }\n}\n\n.s26d58e_categoryButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The incompatible filter, at the right edge of the category bar: `margin-left:\n * auto` pushes it past the pills, and the bar wraps, so on a narrow panel it\n * drops to its own line rather than squeezing them. It is a MODIFIER, not a\n * ninth category — the categories select what to show and this one subtracts\n * from whatever they selected — so it takes the error hue it filters on rather\n * than joining the hue table.\n *\n * And it is a SWITCH, which is the difference the shape now carries. It used\n * to wear `.s26d58e_categoryButton`: same pill, same row, same size as the eight tabs,\n * differing only in being red. That reads as a ninth tab, and it is not one —\n * the tabs are choose-one and this is on-or-off. So it drops the pill chrome\n * entirely (no border, no fill, nothing to compete with the row it sits\n * beside) and puts the state in a track, the same one every installed card\n * carries. Nothing here restates the switch geometry; `--switch-hue` is the\n * whole of what this control says about it, and it reaches the label too, so\n * one declaration keeps the word and the track the same colour. */\n.sfd887b_incompatibleFilter {\n --switch-hue: var(--dsw-alias-state-error-primary);\n margin-left: auto;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n /* Matches the pills' vertical padding so the bar keeps one line box whether\n * this control wraps onto a row of its own or shares theirs. */\n padding: 3px 0;\n border: 0;\n background: none;\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n cursor: pointer;\n}\n\n/* Hover lifts the word to full contrast and warms the track's border. Scoped\n * to the OFF track: `.sfd887b_incompatibleFilter:hover .s11c019_switch` outranks `.s7580e8_switchOn`\n * by two classes, so unscoped it would strip the hue from the border at\n * exactly the moment a reader has the pointer on it to check the state. */\n.sfd887b_incompatibleFilter:hover {\n color: var(--dsw-alias-label-primary);\n}\n\n.sfd887b_incompatibleFilter:hover .s11c019_switch:not(.s7580e8_switchOn) {\n border-color: color-mix(in srgb, var(--switch-hue) 45%, var(--dsw-alias-border-l2));\n}\n\n/* On. Written with both classes for the reason `.s26d58e_categoryButton.s5f68c4_categoryButtonOn`\n * is: it has to match `:hover`'s specificity and come after it, or the colour\n * reverts under a pointer that has not moved since the click. */\n.sfd887b_incompatibleFilter.s6e4cba_incompatibleFilterOn {\n color: color-mix(in srgb, var(--switch-hue) 45%, var(--dsw-alias-label-primary) 55%);\n}\n\n/* Off here means \"nothing is being taken away\" — the resting state — so the\n * knob steps back to the secondary label. The enable switch keeps the full\n * strength dot, where off means \"this plugin is disabled\" and is worth\n * noticing. In this bar it would be the heaviest mark in a row of hairline\n * pills, and on the light theme it is the WHOLE control: bg-layer-2 is the\n * same white as the panel there, so an off track is a hairline outline with a\n * near-black dot sitting in it. */\n.sfd887b_incompatibleFilter .s11c019_switch:not(.s7580e8_switchOn) .saab2b3_switchKnob {\n background: var(--dsw-alias-label-secondary);\n}\n\n/* The ring has no border to trace, so it takes an offset and the pill radius\n * of the tabs it sits beside rather than hugging the text. */\n.sfd887b_incompatibleFilter:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 2px;\n border-radius: 999px;\n}\n\n.sebf017_updatableHeading {\n margin: 2px 0 0;\n font-size: 16px;\n font-weight: 600;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-primary);\n}\n\n.seb0a1e_catalogStatsRow {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 6px 0 0;\n}\n\n.sccc4d1_catalogStats {\n margin: 0;\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* Sits beside the 12px stats caption, so it is quieter than `.s73242a_actionButton`:\n * this is an affordance, not a call to action — the catalog reloads on its\n * own at mount and the shelf is already usable without it. Named\n * `catalogRefresh*` (not `reload*`) so it cannot collide with the §4 page\n * reload offer below, which reloads the whole tab rather than refetching\n * the catalog data. */\n.sff243d_catalogRefreshButton {\n padding: 1px 9px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n line-height: 17px;\n cursor: pointer;\n}\n\n.sff243d_catalogRefreshButton:hover:not(:disabled) {\n border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l2));\n color: var(--dsw-alias-brand-primary);\n}\n\n.sff243d_catalogRefreshButton:disabled {\n cursor: default;\n opacity: 0.6;\n}\n\n.sff243d_catalogRefreshButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.s194168_catalogRefreshFailed {\n font-size: 11px;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n.s6de1ab_emptyLine {\n margin: 0;\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n/* The shelf: a responsive grid of cards. */\n/* Single-line rows: one full-width column, one plugin per line. */\n.sf657f5_cards {\n display: grid;\n grid-template-columns: 1fr;\n max-width: 100%;\n gap: 8px;\n list-style: none;\n margin: 0;\n padding: 0;\n animation: dshShopCardsIn 220ms ease;\n}\n\n@keyframes dshShopCardsIn {\n from { opacity: 0; transform: translateY(3px); }\n to { opacity: 1; transform: none; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .sf657f5_cards { animation: none; }\n}\n\n.s65f383_card {\n position: relative;\n display: flex;\n flex-direction: column;\n /* A grid item's min-width is auto by default: its content would widen the\n * 1fr track past the panel and overflow horizontally. */\n min-width: 0;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-1);\n overflow: hidden;\n transition: border-color 120ms ease, transform 120ms ease, box-shadow 120ms ease;\n /* Off-screen cards skip layout and paint; the placeholder keeps scrollbar\n * estimation sane before the browser has measured the real height. */\n content-visibility: auto;\n contain-intrinsic-size: auto 140px;\n}\n\n/* The sentinel that triggers the next shelf batch: zero visual footprint. */\n.sd7ee01_cardsSentry {\n height: 1px;\n margin: 0;\n padding: 0;\n}\n\n.s705cc6_showingLine {\n margin: 0;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s65f383_card:hover {\n border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l1));\n transform: translateY(-1px);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .s65f383_card { transition: none; }\n .s65f383_card:hover { transform: none; }\n}\n\n/* The signature: a spine and cover in the category's own hue. The hues are\n * fixed mid-bright colors that read on both themes; `other` deliberately\n * stays a neutral gray so the fallback category does not compete. */\n.s4ce25d_cardSpine {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 3px;\n background: var(--category-hue, #8B8E96);\n}\n\n/* The hue table, keyed by `data-category` and read by BOTH surfaces that\n * speak in category colour: a card's spine and badge, and the category bar's\n * own tab. One table, so a tab can never disagree with the cards it filters to\n * — which is the whole point of colouring the tab. `.s26d58e_categoryButton` sets the\n * brand fallback for `All`/`Installed`; these attribute selectors are one\n * specificity step above it and win wherever the attribute is present. */\n.s65f383_card[data-category='tool'], .s26d58e_categoryButton[data-category='tool'] { --category-hue: #4C8DFF; }\n.s65f383_card[data-category='provider'], .s26d58e_categoryButton[data-category='provider'] { --category-hue: #A78BFA; }\n.s65f383_card[data-category='ui'], .s26d58e_categoryButton[data-category='ui'] { --category-hue: #2DD4BF; }\n.s65f383_card[data-category='workflow'], .s26d58e_categoryButton[data-category='workflow'] { --category-hue: #F59E0B; }\n.s65f383_card[data-category='integration'], .s26d58e_categoryButton[data-category='integration'] { --category-hue: #34D399; }\n.s65f383_card[data-category='theme'], .s26d58e_categoryButton[data-category='theme'] { --category-hue: #F472B6; }\n.s65f383_card[data-category='other'], .s26d58e_categoryButton[data-category='other'] { --category-hue: #8B8E96; }\n\n/* The category sits in the badge row as a quiet chip in the category's own\n * hue — the border carries it where the light theme's white layers cannot\n * (the same lesson as the check button). */\n.s9c8eaa_categoryBadge {\n padding: 1px 7px;\n border: 1px solid color-mix(in srgb, var(--category-hue, #8B8E96) 45%, transparent);\n border-radius: 999px;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--category-hue, #8B8E96);\n white-space: nowrap;\n}\n\n.sa97234_entryHeader {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 12px;\n min-width: 0;\n padding: 8px 12px 2px;\n border: none;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n\n.sa97234_entryHeader:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n/* The name fills the row and truncates rather than wraps; the badges stay\n * intact beside it. */\n.sc3f149_name {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 13px;\n font-weight: 600;\n flex: 1 1 auto;\n min-width: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--dsw-alias-label-primary);\n}\n\n.s513026_badges {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n/* The action line: buttons side by side; an active install/uninstall flow\n * (gate, log, notices) takes the full width below them. */\n.s9b5048_cardActions {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n padding: 8px 12px 10px;\n}\n\n.s9b5048_cardActions > .sc4f2ac_gate,\n.s9b5048_cardActions > .s4d374c_installPanel,\n.s9b5048_cardActions > .sf53ca0_installedActions {\n flex-basis: 100%;\n}\n\n/* The entry's version, always visible on the card (the expanded detail\n * repeats it in the version row) — the same quiet mono as the toolbar. */\n.sc6ec6e_cardVersion {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* The octocat marking a github-sourced entry, beside its short commit. */\n.s71f10f_sourceBadge {\n display: inline-flex;\n align-items: center;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s5e82cb_tierBadge {\n padding: 1px 7px;\n border-radius: 999px;\n font-size: 11px;\n line-height: 1.6;\n}\n\n.s5e82cb_tierBadge[data-tier='verified'] {\n color: var(--dsw-alias-state-success-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-success-primary) 40%, transparent);\n}\n\n.s5e82cb_tierBadge[data-tier='verified-stale'] {\n color: var(--dsw-alias-state-warn-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary) 40%, transparent);\n}\n\n.s5e82cb_tierBadge[data-tier='community'] {\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n border: 1px solid var(--dsw-alias-border-l2);\n}\n\n/* The §C10 shadowed badge: an installed github entry the catalog refuses\n * shadowed-by-npm. Subdued/secondary rather than a warn tint — nothing is\n * wrong with the install itself; the shelf just cannot show it. */\n.s5e82cb_tierBadge[data-tier='shadowed'] {\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n border: 1px solid var(--dsw-alias-border-l2);\n}\n\n/* The §C5 archived badge: a github repository the owner has frozen.\n * Subdued register, same as shadowed — nothing is wrong with the install;\n * the pinned commit still serves, so the mark is informational only. */\n.s5e82cb_tierBadge[data-tier='archived'] {\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n border: 1px solid var(--dsw-alias-border-l2);\n}\n\n.s4bbe02_incompatibleBadge {\n flex-shrink: 0;\n padding: 3px 9px;\n border-radius: 999px;\n font-size: 12px;\n white-space: nowrap;\n color: var(--dsw-alias-state-error-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 40%, transparent);\n}\n\n.sff43b4_starsBadge {\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s73f7fb_chevron, .s61393c_chevronOpen {\n color: var(--dsw-alias-label-secondary);\n transition: transform 120ms ease;\n}\n\n.s61393c_chevronOpen { transform: rotate(180deg); }\n\n@media (prefers-reduced-motion: reduce) {\n .s73f7fb_chevron, .s61393c_chevronOpen { transition: none; }\n}\n\n.s820ebf_body {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 0 12px;\n}\n\n.sb8fbc8_summary {\n margin: 0;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.se507db_summaryZh {\n margin: -4px 0 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n display: -webkit-box;\n -webkit-line-clamp: 1;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n/* Expanded cards show the full summary: the clamp is lifted, never raised —\n * a higher number would still truncate the author's text, just later. */\n.s10baf0_summaryExpanded {\n -webkit-line-clamp: unset;\n display: block;\n}\n\n.sb9dba9_summaryZhExpanded {\n -webkit-line-clamp: unset;\n display: block;\n}\n\n/* `white-space: pre-line` so the newline in the copy is the copy's own: the\n dictionary decides where the line breaks, and a translator changes it\n without touching the component. Collapses runs of spaces like `normal`, so\n only the explicit \\n survives. */\n.s6eca19_incompatibleDetail {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n white-space: pre-line;\n color: var(--dsw-alias-state-error-primary);\n}\n\n/* The way out of dsh's own refusal: the lead-in, then the exemption command\n * on its own line. */\n.s3d0044_remedy {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.sdf203f_remedyLead {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n}\n\n/* The exemption command, bound for a terminal unchanged: one click selects\n * all of it, and it wraps anywhere rather than widening the card. A border,\n * not a fill, marks the box: the bg layers are one white in the light theme. */\n.sc32335_allowCommand {\n display: block;\n padding: 6px 8px;\n border-radius: 6px;\n background: var(--dsw-alias-bg-base);\n border: 1px solid var(--dsw-alias-border-l1);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px;\n line-height: 1.5;\n white-space: pre-wrap;\n word-break: break-all;\n user-select: all;\n color: var(--dsw-alias-label-primary);\n}\n\n/* A refused install on a surface that does not state its blockers — the\n * outdated row — writes them out on their own full-width line under the\n * disabled button, the command that clears dsh's refusal included. */\n.sc74941_refusal {\n display: flex;\n flex-direction: column;\n gap: 6px;\n flex-basis: 100%;\n}\n\n.s9d2029_capabilitiesBlock {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.s3bdbd5_capabilitiesNote {\n margin: 0;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s441702_capabilities {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.s441702_capabilities li {\n padding: 1px 6px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 6%, transparent);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sde3f0c_detail {\n border-top: 1px solid var(--dsw-alias-border-l1);\n padding-top: 8px;\n}\n\n.se98179_detailRows {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin: 0;\n}\n\n.sfb4d2f_detailRow {\n display: flex;\n justify-content: space-between;\n gap: 12px;\n font-size: 12px;\n}\n\n.sfb4d2f_detailRow dt {\n color: var(--dsw-alias-label-secondary);\n}\n\n.sfb4d2f_detailRow dd {\n margin: 0;\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n overflow-wrap: anywhere;\n text-align: right;\n color: var(--dsw-alias-label-primary);\n}\n\n.sfb4d2f_detailRow a {\n color: var(--dsw-alias-brand-primary);\n text-decoration: none;\n}\n\n.sfb4d2f_detailRow a:hover {\n text-decoration: underline;\n}\n\n.sfb4d2f_detailRow a:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* What the entry IS rather than what you can do with it — how big it is and\n * who published it — at the right edge of the action row. `margin-left: auto`\n * is what pushes it there, and it lives on this wrapper rather than on either\n * label so the group stays flush right when only one of the two has a value —\n * a packument that named no account has no author, and an entry the registry\n * could not measure honestly has no size. Rarer than it was (every live entry\n * now carries a size, github included), but the wrapper is not what makes it\n * rare: without one, an EMPTY meta group still costs 8px, because\n * `.s9b5048_cardActions` is a flex row with `gap: 8px` and a zero-width item is still\n * an item. The row is a wrapping flex, so an active install flow (which claims\n * the full width) drops the whole group to the next line rather than squeezing\n * it. */\n.s5fbaf9_cardMeta {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n margin-left: auto;\n min-width: 0;\n}\n\n/* The on-disk size, left of the author. Mono and tabular so the digits of one\n * figure line up with the digits of another WHEREVER the two land: `.s5fbaf9_cardMeta`\n * is flush right and the author sits outside the size, so a size's x position\n * moves with the author's width and is different again on the many entries\n * that carry no author at all. There is no column to align to, which is why\n * the reason given here is per-figure legibility rather than the column the\n * first version of this comment claimed. Secondary colour, because it\n * qualifies the entry rather than announcing it.\n *\n * `font-variant-numeric` is safe HERE and would not be on a pill: this element\n * has no state rules, so nothing it does can reflow a wrapping row. */\n.s2d3c05_size {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-variant-numeric: tabular-nums;\n font-size: 12px;\n letter-spacing: 0.02em;\n white-space: nowrap;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* The publishing account, at the outer end of `.s5fbaf9_cardMeta` on the action row —\n * NOT beside the npm link in the expanded detail, which is where it used to\n * live and what this comment described until the accurate half was deleted\n * with the `margin-left: auto` that moved to `.s5fbaf9_cardMeta`. There is no middot:\n * `.s5fbaf9_cardMeta`'s 8px gap separates it from the size.\n *\n * Secondary label colour carries the distinction — never a background fill,\n * which is invisible in the light theme where every bg-layer token is the\n * same white. */\n.s1ff66f_author {\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n}\n\n.sd58c6d_reviewedLine {\n margin: 8px 0 0;\n padding: 6px 8px;\n border-radius: 6px;\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 12%, transparent);\n font-size: 12px;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n/* Install flow. */\n.s4d374c_installPanel {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 2px;\n}\n\n.se7aaed_installButton, .sb43a49_confirmButton, .s6e45d1_cancelButton {\n align-self: flex-start;\n padding: 5px 14px;\n border-radius: 6px;\n font-size: 13px;\n cursor: pointer;\n}\n\n.se7aaed_installButton {\n border: 1px solid var(--dsw-alias-brand-primary);\n background: var(--dsw-alias-brand-primary);\n color: var(--dsw-alias-bg-base);\n}\n\n.se7aaed_installButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 88%, var(--dsw-alias-label-primary));\n}\n\n.sb43a49_confirmButton {\n border: 1px solid var(--dsw-alias-brand-primary);\n background: var(--dsw-alias-brand-primary);\n color: var(--dsw-alias-bg-base);\n}\n\n.sb43a49_confirmButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 88%, var(--dsw-alias-label-primary));\n}\n\n.s6e45d1_cancelButton {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n}\n\n.s6e45d1_cancelButton:hover {\n border-color: var(--dsw-alias-label-secondary);\n}\n\n.se7aaed_installButton:focus-visible, .sb43a49_confirmButton:focus-visible, .s6e45d1_cancelButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The shelf card of a plugin that is installed and current carries this\n * non-interactive label instead of an install button. Border + text do the\n * work in the light theme, where every bg-layer token resolves to white. */\n.sb6fc74_installedLabel {\n align-self: flex-start;\n margin: 0;\n padding: 5px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n font-size: 13px;\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n}\n\n/* An installed card's controls: the installed label or the update button,\n * plus the uninstall button. The install panel's running/failed states are\n * column layouts and sit as one item of this row. */\n.sf53ca0_installedActions {\n display: flex;\n align-items: flex-start;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n/* Uninstall revokes privilege; the quiet outline keeps it from competing\n * with the primary action, and the error tint says \"removes\". */\n.s96967f_uninstallButton {\n align-self: flex-start;\n padding: 5px 14px;\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 55%, transparent);\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-state-error-primary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.s96967f_uninstallButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);\n}\n\n.s96967f_uninstallButton:focus-visible {\n outline: 2px solid var(--dsw-alias-state-error-primary);\n outline-offset: 1px;\n}\n\n/* The restart dsh button on a done install: quiet outline, same rank as\n * the other secondary actions. */\n.sb00c36_restartSelfButton {\n /* The version row's third state. Geometry matches .s614a38_checkUpdateButton and\n * .sa04455_updateSelfButton exactly — the row is one evolving control and a\n * different pill would read as a different kind of thing. It takes the\n * emphasised fill rather than the neutral one because at that moment the\n * restart is the only thing left to do, the same reason Update has it. */\n padding: 2px 10px;\n border: 1px solid var(--dsw-alias-brand-primary);\n border-radius: 999px;\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n color: var(--dsw-alias-brand-primary);\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n}\n\n.sb00c36_restartSelfButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The two activation offers share one recipe: a border in an alias token,\n * never a `bg-layer` fill alone — all four bg-layer tokens collapse to the\n * same white under `body` in the light theme, and a fill-only button would\n * vanish there. Grouped rather than copied, because a copy is a rule that\n * can be corrected on one button and left wrong on the other, silently. */\n.s6ac237_restartButton,\n.s64a5ff_reloadButton {\n align-self: flex-start;\n padding: 5px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.s6ac237_restartButton:hover,\n.s64a5ff_reloadButton:hover {\n border-color: var(--dsw-alias-label-secondary);\n}\n\n.s6ac237_restartButton:focus-visible,\n.s64a5ff_reloadButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The §4 reload offer: a notice plus its button, one item of\n * `.sf53ca0_installedActions` the way the restart gate is — a column of its own\n * rather than inline with the row. */\n.sf79aef_reloadPanel {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 6px;\n}\n\n\n.sc4f2ac_gate {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 10px;\n border: 1px solid var(--dsw-alias-state-warn-primary);\n border-radius: 8px;\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 8%, transparent);\n}\n\n.s559c38_gateTitle {\n margin: 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n.s3c0ace_gateBody {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-primary);\n}\n\n.sc323e5_gateWarning {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n white-space: pre-line;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.s4b2373_gateActions {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n.s52b5a5_installing {\n margin: 0;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s64ab71_log {\n max-height: 160px;\n overflow: auto;\n margin: 0;\n padding: 8px;\n border-radius: 6px;\n background: var(--dsw-alias-bg-base);\n border: 1px solid var(--dsw-alias-border-l1);\n list-style: none;\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px;\n line-height: 1.5;\n}\n\n.s1531a8_logLine {\n white-space: pre-wrap;\n word-break: break-all;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sf20dda_notice {\n margin: 0;\n font-size: 12px;\n color: var(--dsw-alias-state-success-primary);\n}\n\n.seb9b08_failedHeading {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.sfd3021_failedDetail {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-primary);\n}\n\n.s50ba35_rejectedCode {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.se75489_rejectedDetail {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-primary);\n}\n\n/* The Updatable section. The class keeps the Host's field name; only the\n heading the reader sees was renamed. */\n.sa276cf_outdatedSection {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 6px;\n}\n\n.sceae6e_outdatedList {\n display: flex;\n flex-direction: column;\n gap: 8px;\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.s8d2072_outdatedRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n flex-wrap: wrap;\n padding: 10px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-1);\n}\n\n.s5c2545_outdatedInfo {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.s5c2545_outdatedInfo strong {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 13px;\n word-break: break-all;\n color: var(--dsw-alias-label-primary);\n}\n\n.s877c68_outdatedVersions {\n /* Two inline labels with nothing between them in the markup ran together as\n * `installed v1.0.0latest v2.0.0`: JSX drops the whitespace-only line, and\n * the per-label class the markup asked for was never defined (singular\n * against this rule's plural). The container owns the gap, the same idiom\n * as .s513026_badges — the labels need no class of their own. */\n display: inline-flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* The §C10 shadowed section. Reuses the outdated list's visual language; only\n * the section spacing and the per-row explanation paragraph are its own. */\n.s6c4649_shadowedSection {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 6px;\n}\n\n.scc2a21_shadowedDetail {\n margin: 4px 0 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sa1f556_outdatedActions {\n display: flex;\n align-items: center;\n /* The reload offer (§4) mounts inside the switch beside the Update button\n * and is a two-line block, not a control: without wrapping, a row reading\n * [switch][notice + Reload][Update] overflows the settings dialog. */\n flex-wrap: wrap;\n gap: 10px;\n}\n\n/* The shop's one switch. It is worn by two controls that mean different\n * things — the enable/disable toggle on an installed card, and the shelf's\n * incompatible filter — so the geometry lives here once and each caller states\n * only its hue.\n *\n * `--switch-hue` is that knob, and it is read from the ANCESTOR rather than\n * declared per state: `.sfd887b_incompatibleFilter` sets it once and it reaches the\n * track and the label together, which is what keeps a red switch from sitting\n * under a neutral word. A declaration on `.s11c019_switch` itself would shadow the\n * inherited value (an element's own rule beats what it inherits), so the\n * default rides in the `var()` fallback and is resolved once into\n * `--track-hue` instead of being spelled out at each use.\n *\n * `box-sizing` and `padding` are stated because the two callers are different\n * elements — a `<button>`, which the UA gives padding, and a `<span>`, which\n * it does not. Without them the same class renders two sizes, and the knob\n * sits 2px above centre in the taller one. */\n.s11c019_switch {\n --track-hue: var(--switch-hue, var(--dsw-alias-state-success-primary));\n box-sizing: border-box;\n display: inline-block;\n position: relative;\n width: 32px;\n height: 18px;\n padding: 0;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-2);\n cursor: pointer;\n transition: background 120ms ease, border-color 120ms ease;\n}\n\n/* The knob's POSITION is the state, which is the reason this control needs no\n * `forced-colors` fallback the way the category pills do: a system palette\n * replaces every colour below and moves nothing. */\n.s7580e8_switchOn {\n border-color: var(--track-hue);\n background: color-mix(in srgb, var(--track-hue) 55%, transparent);\n}\n\n.saab2b3_switchKnob {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 12px;\n height: 12px;\n border-radius: 999px;\n background: var(--dsw-alias-label-primary);\n transition: transform 120ms ease, background 120ms ease;\n}\n\n.s7580e8_switchOn .saab2b3_switchKnob {\n transform: translateX(14px);\n background: var(--dsw-alias-bg-base);\n}\n\n.s11c019_switch:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .s11c019_switch, .saab2b3_switchKnob { transition: none; }\n}\n\n/* The hot enable/disable switch on installed rows: the knob button plus the\n * §8 hot-apply / failure notices rendered beside it. */\n.sd9f9e4_switchWrap {\n display: inline-flex;\n align-items: center;\n /* Same reason as `.sa1f556_outdatedActions`: `.sf79aef_reloadPanel`'s own comment calls\n * itself \"a column of its own rather than inline with the row\", which held\n * for `.sf53ca0_installedActions` and not here until this line. */\n flex-wrap: wrap;\n gap: 8px;\n}\n";
|
|
9048
|
+
if (typeof document !== "undefined" && !document.querySelector("style[data-plugin-css=\"e3675a89\"]")) {
|
|
7790
9049
|
const tag = document.createElement("style");
|
|
7791
|
-
tag.dataset.pluginCss = "
|
|
9050
|
+
tag.dataset.pluginCss = "e3675a89";
|
|
7792
9051
|
tag.textContent = css;
|
|
7793
9052
|
document.head.appendChild(tag);
|
|
7794
9053
|
}
|
|
7795
|
-
var
|
|
7796
|
-
"client": "
|
|
7797
|
-
"config": "
|
|
7798
|
-
"ts": "
|
|
7799
|
-
"spec": "
|
|
7800
|
-
"panel": "
|
|
7801
|
-
"searchInput": "
|
|
7802
|
-
"toolbar": "
|
|
7803
|
-
"stateLine": "
|
|
7804
|
-
"srOnly": "
|
|
7805
|
-
"skeletonGrid": "
|
|
7806
|
-
"skeletonCard": "
|
|
7807
|
-
"skeletonName": "
|
|
7808
|
-
"skeletonSummary": "
|
|
7809
|
-
"skeletonSummaryShort": "
|
|
7810
|
-
"skeletonActions": "
|
|
7811
|
-
"actionButton": "
|
|
7812
|
-
"toolbarRight": "
|
|
7813
|
-
"versionBlock": "
|
|
7814
|
-
"versionText": "
|
|
7815
|
-
"updateSelfButton": "
|
|
7816
|
-
"checkUpdateButton": "
|
|
7817
|
-
"githubLink": "
|
|
7818
|
-
"selfUpdatePanel": "
|
|
7819
|
-
"
|
|
7820
|
-
"
|
|
7821
|
-
"
|
|
7822
|
-
"
|
|
7823
|
-
"
|
|
7824
|
-
"
|
|
7825
|
-
"
|
|
7826
|
-
"
|
|
7827
|
-
"
|
|
7828
|
-
"
|
|
7829
|
-
"
|
|
7830
|
-
"
|
|
7831
|
-
"
|
|
7832
|
-
"
|
|
7833
|
-
"
|
|
7834
|
-
"
|
|
7835
|
-
"
|
|
7836
|
-
"
|
|
7837
|
-
"
|
|
7838
|
-
"
|
|
7839
|
-
"
|
|
7840
|
-
"
|
|
7841
|
-
"
|
|
7842
|
-
"
|
|
7843
|
-
"
|
|
7844
|
-
"
|
|
7845
|
-
"
|
|
7846
|
-
"
|
|
7847
|
-
"
|
|
7848
|
-
"
|
|
7849
|
-
"
|
|
7850
|
-
"
|
|
7851
|
-
"
|
|
7852
|
-
"
|
|
7853
|
-
"
|
|
7854
|
-
"
|
|
7855
|
-
"
|
|
7856
|
-
"
|
|
7857
|
-
"
|
|
7858
|
-
"
|
|
7859
|
-
"
|
|
7860
|
-
"
|
|
7861
|
-
"
|
|
7862
|
-
"
|
|
7863
|
-
"
|
|
7864
|
-
"
|
|
7865
|
-
"
|
|
7866
|
-
"
|
|
7867
|
-
"
|
|
7868
|
-
"
|
|
7869
|
-
"
|
|
7870
|
-
"
|
|
7871
|
-
"
|
|
7872
|
-
"
|
|
7873
|
-
"
|
|
7874
|
-
"
|
|
7875
|
-
"
|
|
7876
|
-
"
|
|
7877
|
-
"
|
|
7878
|
-
"
|
|
7879
|
-
"
|
|
7880
|
-
"
|
|
7881
|
-
"
|
|
7882
|
-
"
|
|
7883
|
-
"
|
|
7884
|
-
"
|
|
7885
|
-
"
|
|
7886
|
-
"
|
|
7887
|
-
"
|
|
7888
|
-
"
|
|
7889
|
-
"
|
|
7890
|
-
"
|
|
7891
|
-
"
|
|
7892
|
-
"
|
|
7893
|
-
"
|
|
7894
|
-
"
|
|
7895
|
-
"
|
|
7896
|
-
"
|
|
7897
|
-
"
|
|
7898
|
-
"
|
|
7899
|
-
"
|
|
7900
|
-
"
|
|
7901
|
-
"
|
|
9054
|
+
var _dsh_plugin_shop_css_e3675a89_default = {
|
|
9055
|
+
"client": "s8583e3_client",
|
|
9056
|
+
"config": "s942a79_config",
|
|
9057
|
+
"ts": "s5f67aa_ts",
|
|
9058
|
+
"spec": "sfd9d8a_spec",
|
|
9059
|
+
"panel": "s255e5a_panel",
|
|
9060
|
+
"searchInput": "s232ec4_searchInput",
|
|
9061
|
+
"toolbar": "s646509_toolbar",
|
|
9062
|
+
"stateLine": "s44a3ec_stateLine",
|
|
9063
|
+
"srOnly": "s3a0ece_srOnly",
|
|
9064
|
+
"skeletonGrid": "se0c3b4_skeletonGrid",
|
|
9065
|
+
"skeletonCard": "s520416_skeletonCard",
|
|
9066
|
+
"skeletonName": "s8c7d6f_skeletonName",
|
|
9067
|
+
"skeletonSummary": "sf5179e_skeletonSummary",
|
|
9068
|
+
"skeletonSummaryShort": "sbb0d25_skeletonSummaryShort",
|
|
9069
|
+
"skeletonActions": "s96e910_skeletonActions",
|
|
9070
|
+
"actionButton": "s73242a_actionButton",
|
|
9071
|
+
"toolbarRight": "s745e3c_toolbarRight",
|
|
9072
|
+
"versionBlock": "s4fe61f_versionBlock",
|
|
9073
|
+
"versionText": "sb9b924_versionText",
|
|
9074
|
+
"updateSelfButton": "sa04455_updateSelfButton",
|
|
9075
|
+
"checkUpdateButton": "s614a38_checkUpdateButton",
|
|
9076
|
+
"githubLink": "sa987f7_githubLink",
|
|
9077
|
+
"selfUpdatePanel": "sb81ba5_selfUpdatePanel",
|
|
9078
|
+
"expansionNote": "s76fb13_expansionNote",
|
|
9079
|
+
"staleBadge": "s3946f1_staleBadge",
|
|
9080
|
+
"categoryBar": "sb2c859_categoryBar",
|
|
9081
|
+
"categoryButton": "s26d58e_categoryButton",
|
|
9082
|
+
"categoryButtonOn": "s5f68c4_categoryButtonOn",
|
|
9083
|
+
"incompatibleFilter": "sfd887b_incompatibleFilter",
|
|
9084
|
+
"switch": "s11c019_switch",
|
|
9085
|
+
"switchOn": "s7580e8_switchOn",
|
|
9086
|
+
"incompatibleFilterOn": "s6e4cba_incompatibleFilterOn",
|
|
9087
|
+
"switchKnob": "saab2b3_switchKnob",
|
|
9088
|
+
"updatableHeading": "sebf017_updatableHeading",
|
|
9089
|
+
"catalogStatsRow": "seb0a1e_catalogStatsRow",
|
|
9090
|
+
"catalogStats": "sccc4d1_catalogStats",
|
|
9091
|
+
"catalogRefreshButton": "sff243d_catalogRefreshButton",
|
|
9092
|
+
"catalogRefreshFailed": "s194168_catalogRefreshFailed",
|
|
9093
|
+
"emptyLine": "s6de1ab_emptyLine",
|
|
9094
|
+
"cards": "sf657f5_cards",
|
|
9095
|
+
"card": "s65f383_card",
|
|
9096
|
+
"cardsSentry": "sd7ee01_cardsSentry",
|
|
9097
|
+
"showingLine": "s705cc6_showingLine",
|
|
9098
|
+
"cardSpine": "s4ce25d_cardSpine",
|
|
9099
|
+
"categoryBadge": "s9c8eaa_categoryBadge",
|
|
9100
|
+
"entryHeader": "sa97234_entryHeader",
|
|
9101
|
+
"name": "sc3f149_name",
|
|
9102
|
+
"badges": "s513026_badges",
|
|
9103
|
+
"cardActions": "s9b5048_cardActions",
|
|
9104
|
+
"gate": "sc4f2ac_gate",
|
|
9105
|
+
"installPanel": "s4d374c_installPanel",
|
|
9106
|
+
"installedActions": "sf53ca0_installedActions",
|
|
9107
|
+
"cardVersion": "sc6ec6e_cardVersion",
|
|
9108
|
+
"sourceBadge": "s71f10f_sourceBadge",
|
|
9109
|
+
"tierBadge": "s5e82cb_tierBadge",
|
|
9110
|
+
"incompatibleBadge": "s4bbe02_incompatibleBadge",
|
|
9111
|
+
"starsBadge": "sff43b4_starsBadge",
|
|
9112
|
+
"chevron": "s73f7fb_chevron",
|
|
9113
|
+
"chevronOpen": "s61393c_chevronOpen",
|
|
9114
|
+
"body": "s820ebf_body",
|
|
9115
|
+
"summary": "sb8fbc8_summary",
|
|
9116
|
+
"summaryZh": "se507db_summaryZh",
|
|
9117
|
+
"summaryExpanded": "s10baf0_summaryExpanded",
|
|
9118
|
+
"summaryZhExpanded": "sb9dba9_summaryZhExpanded",
|
|
9119
|
+
"incompatibleDetail": "s6eca19_incompatibleDetail",
|
|
9120
|
+
"remedy": "s3d0044_remedy",
|
|
9121
|
+
"remedyLead": "sdf203f_remedyLead",
|
|
9122
|
+
"allowCommand": "sc32335_allowCommand",
|
|
9123
|
+
"refusal": "sc74941_refusal",
|
|
9124
|
+
"capabilitiesBlock": "s9d2029_capabilitiesBlock",
|
|
9125
|
+
"capabilitiesNote": "s3bdbd5_capabilitiesNote",
|
|
9126
|
+
"capabilities": "s441702_capabilities",
|
|
9127
|
+
"detail": "sde3f0c_detail",
|
|
9128
|
+
"detailRows": "se98179_detailRows",
|
|
9129
|
+
"detailRow": "sfb4d2f_detailRow",
|
|
9130
|
+
"cardMeta": "s5fbaf9_cardMeta",
|
|
9131
|
+
"size": "s2d3c05_size",
|
|
9132
|
+
"author": "s1ff66f_author",
|
|
9133
|
+
"reviewedLine": "sd58c6d_reviewedLine",
|
|
9134
|
+
"installButton": "se7aaed_installButton",
|
|
9135
|
+
"confirmButton": "sb43a49_confirmButton",
|
|
9136
|
+
"cancelButton": "s6e45d1_cancelButton",
|
|
9137
|
+
"installedLabel": "sb6fc74_installedLabel",
|
|
9138
|
+
"uninstallButton": "s96967f_uninstallButton",
|
|
9139
|
+
"restartSelfButton": "sb00c36_restartSelfButton",
|
|
9140
|
+
"restartButton": "s6ac237_restartButton",
|
|
9141
|
+
"reloadButton": "s64a5ff_reloadButton",
|
|
9142
|
+
"reloadPanel": "sf79aef_reloadPanel",
|
|
9143
|
+
"gateTitle": "s559c38_gateTitle",
|
|
9144
|
+
"gateBody": "s3c0ace_gateBody",
|
|
9145
|
+
"gateWarning": "sc323e5_gateWarning",
|
|
9146
|
+
"gateActions": "s4b2373_gateActions",
|
|
9147
|
+
"installing": "s52b5a5_installing",
|
|
9148
|
+
"log": "s64ab71_log",
|
|
9149
|
+
"logLine": "s1531a8_logLine",
|
|
9150
|
+
"notice": "sf20dda_notice",
|
|
9151
|
+
"failedHeading": "seb9b08_failedHeading",
|
|
9152
|
+
"failedDetail": "sfd3021_failedDetail",
|
|
9153
|
+
"rejectedCode": "s50ba35_rejectedCode",
|
|
9154
|
+
"rejectedDetail": "se75489_rejectedDetail",
|
|
9155
|
+
"outdatedSection": "sa276cf_outdatedSection",
|
|
9156
|
+
"outdatedList": "sceae6e_outdatedList",
|
|
9157
|
+
"outdatedRow": "s8d2072_outdatedRow",
|
|
9158
|
+
"outdatedInfo": "s5c2545_outdatedInfo",
|
|
9159
|
+
"outdatedVersions": "s877c68_outdatedVersions",
|
|
9160
|
+
"shadowedSection": "s6c4649_shadowedSection",
|
|
9161
|
+
"shadowedDetail": "scc2a21_shadowedDetail",
|
|
9162
|
+
"outdatedActions": "sa1f556_outdatedActions",
|
|
9163
|
+
"switchWrap": "sd9f9e4_switchWrap"
|
|
7902
9164
|
};
|
|
7903
9165
|
//#endregion
|
|
7904
9166
|
//#region src/client/ShopTab.tsx
|
|
@@ -7915,14 +9177,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7915
9177
|
* those nodes, and React's next commit throws on the ones it no longer finds
|
|
7916
9178
|
* (design 2026-09-26-market-borrowings §4). The tab renders no portals, so its
|
|
7917
9179
|
* root is its whole surface. */
|
|
7918
|
-
const PANEL_ROOT = `${
|
|
9180
|
+
const PANEL_ROOT = `${_dsh_plugin_shop_css_e3675a89_default.panel} notranslate`;
|
|
7919
9181
|
function ChevronIcon({ open }) {
|
|
7920
9182
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
7921
9183
|
viewBox: "0 0 16 16",
|
|
7922
9184
|
width: "14",
|
|
7923
9185
|
height: "14",
|
|
7924
9186
|
"aria-hidden": "true",
|
|
7925
|
-
className: open ?
|
|
9187
|
+
className: open ? _dsh_plugin_shop_css_e3675a89_default.chevronOpen : _dsh_plugin_shop_css_e3675a89_default.chevron,
|
|
7926
9188
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
7927
9189
|
fill: "currentColor",
|
|
7928
9190
|
d: "M3.5 6 8 10.5 12.5 6l1 1L8 12.5 2.5 7l1-1Z"
|
|
@@ -7935,7 +9197,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7935
9197
|
* install controls. An installed plugin's card carries its installed row:
|
|
7936
9198
|
* current → the non-interactive installed label, behind → the update button;
|
|
7937
9199
|
* uninstalled → the install button. */
|
|
7938
|
-
const EntryCard = (0, react.memo)(function EntryCard({ entry, stars, installed, missing, harness, nameTakenBy, t, flow, uninstallFlow, restart, restartBlocked, reload, setEnabled, inInstalledView }) {
|
|
9200
|
+
const EntryCard = (0, react.memo)(function EntryCard({ entry, stars, installed, missing, harness, nameTakenBy, t, flow, uninstallFlow, restart, restartBlocked, reload, setEnabled, inInstalledView, restarts }) {
|
|
7939
9201
|
const [open, setOpen] = (0, react.useState)(false);
|
|
7940
9202
|
const blockers = stateBlockers(blockersOf(missing, harness, nameTakenBy), t);
|
|
7941
9203
|
const detailId = (0, react.useId)();
|
|
@@ -7952,32 +9214,32 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7952
9214
|
subdir: entry.subdir
|
|
7953
9215
|
};
|
|
7954
9216
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
7955
|
-
className:
|
|
9217
|
+
className: _dsh_plugin_shop_css_e3675a89_default.card,
|
|
7956
9218
|
"data-shop-entry": entry.name,
|
|
7957
9219
|
"data-category": category,
|
|
7958
9220
|
children: [
|
|
7959
9221
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7960
|
-
className:
|
|
9222
|
+
className: _dsh_plugin_shop_css_e3675a89_default.cardSpine,
|
|
7961
9223
|
"aria-hidden": "true"
|
|
7962
9224
|
}),
|
|
7963
9225
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
7964
9226
|
type: "button",
|
|
7965
|
-
className:
|
|
9227
|
+
className: _dsh_plugin_shop_css_e3675a89_default.entryHeader,
|
|
7966
9228
|
"aria-expanded": open,
|
|
7967
9229
|
"aria-controls": detailId,
|
|
7968
9230
|
onClick: () => setOpen((current) => !current),
|
|
7969
9231
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7970
|
-
className:
|
|
9232
|
+
className: _dsh_plugin_shop_css_e3675a89_default.name,
|
|
7971
9233
|
children: entry.name
|
|
7972
9234
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
7973
|
-
className:
|
|
9235
|
+
className: _dsh_plugin_shop_css_e3675a89_default.badges,
|
|
7974
9236
|
children: [
|
|
7975
9237
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7976
|
-
className:
|
|
9238
|
+
className: _dsh_plugin_shop_css_e3675a89_default.categoryBadge,
|
|
7977
9239
|
children: t(categoryKey(entry))
|
|
7978
9240
|
}),
|
|
7979
9241
|
entry.source === "npm" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7980
|
-
className:
|
|
9242
|
+
className: _dsh_plugin_shop_css_e3675a89_default.sourceBadge,
|
|
7981
9243
|
"data-shop-source-npm": true,
|
|
7982
9244
|
role: "img",
|
|
7983
9245
|
"aria-label": t("npmSource"),
|
|
@@ -7994,7 +9256,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7994
9256
|
})
|
|
7995
9257
|
}),
|
|
7996
9258
|
hasGithubHome(entry) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
7997
|
-
className:
|
|
9259
|
+
className: _dsh_plugin_shop_css_e3675a89_default.sourceBadge,
|
|
7998
9260
|
"data-shop-source-github": true,
|
|
7999
9261
|
role: "img",
|
|
8000
9262
|
"aria-label": t("githubSource"),
|
|
@@ -8008,17 +9270,22 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8008
9270
|
})
|
|
8009
9271
|
}),
|
|
8010
9272
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
8011
|
-
className:
|
|
9273
|
+
className: _dsh_plugin_shop_css_e3675a89_default.cardVersion,
|
|
8012
9274
|
"data-card-version": true,
|
|
8013
9275
|
children: ["v", displayVersion(entry)]
|
|
8014
9276
|
}),
|
|
8015
9277
|
entry.tier !== "community" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
8016
|
-
className:
|
|
9278
|
+
className: _dsh_plugin_shop_css_e3675a89_default.tierBadge,
|
|
8017
9279
|
"data-tier": entry.tier,
|
|
8018
9280
|
children: t(tierKey(entry.tier))
|
|
8019
9281
|
}),
|
|
9282
|
+
entry.archived === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9283
|
+
className: _dsh_plugin_shop_css_e3675a89_default.tierBadge,
|
|
9284
|
+
"data-tier": "archived",
|
|
9285
|
+
children: t("archivedBadge")
|
|
9286
|
+
}),
|
|
8020
9287
|
stars !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
8021
|
-
className:
|
|
9288
|
+
className: _dsh_plugin_shop_css_e3675a89_default.starsBadge,
|
|
8022
9289
|
role: "img",
|
|
8023
9290
|
"aria-label": t("stars", { count: stars }),
|
|
8024
9291
|
children: ["★ ", formatStars(stars)]
|
|
@@ -8028,39 +9295,39 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8028
9295
|
})]
|
|
8029
9296
|
}),
|
|
8030
9297
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8031
|
-
className:
|
|
9298
|
+
className: _dsh_plugin_shop_css_e3675a89_default.body,
|
|
8032
9299
|
children: [
|
|
8033
9300
|
summary !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8034
|
-
className: open ? `${
|
|
9301
|
+
className: open ? `${_dsh_plugin_shop_css_e3675a89_default.summary} ${_dsh_plugin_shop_css_e3675a89_default.summaryExpanded}` : _dsh_plugin_shop_css_e3675a89_default.summary,
|
|
8035
9302
|
children: summary.en
|
|
8036
9303
|
}),
|
|
8037
9304
|
summary?.zh !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8038
|
-
className: open ? `${
|
|
9305
|
+
className: open ? `${_dsh_plugin_shop_css_e3675a89_default.summaryZh} ${_dsh_plugin_shop_css_e3675a89_default.summaryZhExpanded}` : _dsh_plugin_shop_css_e3675a89_default.summaryZh,
|
|
8039
9306
|
children: summary.zh
|
|
8040
9307
|
}),
|
|
8041
9308
|
blockers.map((blocker) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockerLine, {
|
|
8042
9309
|
blocker,
|
|
8043
|
-
className:
|
|
9310
|
+
className: _dsh_plugin_shop_css_e3675a89_default.incompatibleDetail,
|
|
8044
9311
|
surface: "detail"
|
|
8045
9312
|
}, blocker.kind)),
|
|
8046
9313
|
open && entry.catalog !== void 0 && entry.catalog.capabilities.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8047
|
-
className:
|
|
9314
|
+
className: _dsh_plugin_shop_css_e3675a89_default.capabilitiesBlock,
|
|
8048
9315
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8049
|
-
className:
|
|
9316
|
+
className: _dsh_plugin_shop_css_e3675a89_default.capabilitiesNote,
|
|
8050
9317
|
children: t("capabilitiesNote")
|
|
8051
9318
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
8052
|
-
className:
|
|
9319
|
+
className: _dsh_plugin_shop_css_e3675a89_default.capabilities,
|
|
8053
9320
|
children: entry.catalog.capabilities.map((capability) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: capability }, capability))
|
|
8054
9321
|
})]
|
|
8055
9322
|
}),
|
|
8056
9323
|
open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
8057
9324
|
id: detailId,
|
|
8058
|
-
className:
|
|
9325
|
+
className: _dsh_plugin_shop_css_e3675a89_default.detail,
|
|
8059
9326
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dl", {
|
|
8060
|
-
className:
|
|
9327
|
+
className: _dsh_plugin_shop_css_e3675a89_default.detailRows,
|
|
8061
9328
|
children: [
|
|
8062
9329
|
npmUrl !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8063
|
-
className:
|
|
9330
|
+
className: _dsh_plugin_shop_css_e3675a89_default.detailRow,
|
|
8064
9331
|
"data-shop-npm": true,
|
|
8065
9332
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("npmPage") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
8066
9333
|
href: npmUrl,
|
|
@@ -8070,7 +9337,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8070
9337
|
}) })]
|
|
8071
9338
|
}),
|
|
8072
9339
|
entry.repository !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8073
|
-
className:
|
|
9340
|
+
className: _dsh_plugin_shop_css_e3675a89_default.detailRow,
|
|
8074
9341
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("repository") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: /^https?:\/\//.test(entry.repository) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
8075
9342
|
href: entry.repository,
|
|
8076
9343
|
target: "_blank",
|
|
@@ -8079,12 +9346,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8079
9346
|
}) : entry.repository })]
|
|
8080
9347
|
}),
|
|
8081
9348
|
entry.license !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8082
|
-
className:
|
|
9349
|
+
className: _dsh_plugin_shop_css_e3675a89_default.detailRow,
|
|
8083
9350
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("license") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: isCustomLicense(entry.license) ? t("customLicense") : entry.license })]
|
|
8084
9351
|
})
|
|
8085
9352
|
]
|
|
8086
9353
|
}), entry.tier === "verified-stale" && entry.review !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8087
|
-
className:
|
|
9354
|
+
className: _dsh_plugin_shop_css_e3675a89_default.reviewedLine,
|
|
8088
9355
|
children: entry.review.reviewedVersion !== void 0 ? t("reviewedVersionLine", {
|
|
8089
9356
|
reviewed: entry.review.reviewedVersion,
|
|
8090
9357
|
current: entry.version
|
|
@@ -8097,7 +9364,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8097
9364
|
]
|
|
8098
9365
|
}),
|
|
8099
9366
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8100
|
-
className:
|
|
9367
|
+
className: _dsh_plugin_shop_css_e3675a89_default.cardActions,
|
|
8101
9368
|
"data-shop-actions": true,
|
|
8102
9369
|
children: [installed === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [!inInstalledView && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstallPanel, {
|
|
8103
9370
|
target: installTarget,
|
|
@@ -8129,7 +9396,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8129
9396
|
restartBlocked,
|
|
8130
9397
|
reload
|
|
8131
9398
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8132
|
-
className:
|
|
9399
|
+
className: _dsh_plugin_shop_css_e3675a89_default.installedLabel,
|
|
8133
9400
|
"data-shop-installed": true,
|
|
8134
9401
|
children: t("installed")
|
|
8135
9402
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockerBadge, {
|
|
@@ -8142,7 +9409,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8142
9409
|
setEnabled,
|
|
8143
9410
|
restart,
|
|
8144
9411
|
restartBlocked,
|
|
8145
|
-
reload
|
|
9412
|
+
reload,
|
|
9413
|
+
restarts
|
|
8146
9414
|
}),
|
|
8147
9415
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UninstallPanel, {
|
|
8148
9416
|
name: entry.name,
|
|
@@ -8153,16 +9421,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8153
9421
|
flow: uninstallFlow
|
|
8154
9422
|
})
|
|
8155
9423
|
] }), (size !== void 0 || author !== null) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
8156
|
-
className:
|
|
9424
|
+
className: _dsh_plugin_shop_css_e3675a89_default.cardMeta,
|
|
8157
9425
|
children: [size !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
8158
|
-
className:
|
|
9426
|
+
className: _dsh_plugin_shop_css_e3675a89_default.size,
|
|
8159
9427
|
"data-shop-size": true,
|
|
8160
9428
|
role: "img",
|
|
8161
9429
|
"aria-label": t("sizeLabel", { size }),
|
|
8162
9430
|
title: t("sizeLabel", { size }),
|
|
8163
9431
|
children: size
|
|
8164
9432
|
}), author !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
8165
|
-
className:
|
|
9433
|
+
className: _dsh_plugin_shop_css_e3675a89_default.author,
|
|
8166
9434
|
"data-shop-author": true,
|
|
8167
9435
|
children: t("authorLine", { author })
|
|
8168
9436
|
})]
|
|
@@ -8211,13 +9479,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8211
9479
|
...attribute,
|
|
8212
9480
|
children: blocker.text
|
|
8213
9481
|
}), blocker.remedy !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8214
|
-
className:
|
|
9482
|
+
className: _dsh_plugin_shop_css_e3675a89_default.remedy,
|
|
8215
9483
|
"data-shop-remedy": blocker.kind,
|
|
8216
9484
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8217
|
-
className:
|
|
9485
|
+
className: _dsh_plugin_shop_css_e3675a89_default.remedyLead,
|
|
8218
9486
|
children: blocker.remedy.lead
|
|
8219
9487
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
8220
|
-
className:
|
|
9488
|
+
className: _dsh_plugin_shop_css_e3675a89_default.allowCommand,
|
|
8221
9489
|
"data-shop-allow-command": true,
|
|
8222
9490
|
children: blocker.remedy.command
|
|
8223
9491
|
})]
|
|
@@ -8255,7 +9523,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8255
9523
|
if (lead === void 0 || word === null) return null;
|
|
8256
9524
|
const detail = blockers.map((blocker) => blocker.text).join("\n");
|
|
8257
9525
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
8258
|
-
className:
|
|
9526
|
+
className: _dsh_plugin_shop_css_e3675a89_default.incompatibleBadge,
|
|
8259
9527
|
"data-shop-blocker": lead.kind,
|
|
8260
9528
|
role: "img",
|
|
8261
9529
|
"aria-label": detail,
|
|
@@ -8272,37 +9540,37 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8272
9540
|
const [gateOpen, setGateOpen] = (0, react.useState)(false);
|
|
8273
9541
|
const { view, start } = flow;
|
|
8274
9542
|
if (view.kind === "running") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8275
|
-
className:
|
|
9543
|
+
className: _dsh_plugin_shop_css_e3675a89_default.installPanel,
|
|
8276
9544
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8277
|
-
className:
|
|
9545
|
+
className: _dsh_plugin_shop_css_e3675a89_default.installing,
|
|
8278
9546
|
children: t(installPhaseKey(view.phase))
|
|
8279
9547
|
}), view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8280
|
-
className:
|
|
9548
|
+
className: _dsh_plugin_shop_css_e3675a89_default.log,
|
|
8281
9549
|
children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8282
|
-
className:
|
|
9550
|
+
className: _dsh_plugin_shop_css_e3675a89_default.logLine,
|
|
8283
9551
|
"data-shop-log-line": true,
|
|
8284
9552
|
children: line
|
|
8285
9553
|
}, index))
|
|
8286
9554
|
})]
|
|
8287
9555
|
});
|
|
8288
9556
|
if (view.kind === "done") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8289
|
-
className:
|
|
9557
|
+
className: _dsh_plugin_shop_css_e3675a89_default.installedActions,
|
|
8290
9558
|
children: [
|
|
8291
9559
|
view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8292
|
-
className:
|
|
9560
|
+
className: _dsh_plugin_shop_css_e3675a89_default.log,
|
|
8293
9561
|
children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8294
|
-
className:
|
|
9562
|
+
className: _dsh_plugin_shop_css_e3675a89_default.logLine,
|
|
8295
9563
|
"data-shop-log-line": true,
|
|
8296
9564
|
children: line
|
|
8297
9565
|
}, index))
|
|
8298
9566
|
}),
|
|
8299
9567
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8300
|
-
className:
|
|
9568
|
+
className: _dsh_plugin_shop_css_e3675a89_default.notice,
|
|
8301
9569
|
"data-shop-restart-notice": true,
|
|
8302
9570
|
children: t(activationNoticeKey(view.activation, view.restartReason))
|
|
8303
9571
|
}),
|
|
8304
9572
|
view.detail !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8305
|
-
className:
|
|
9573
|
+
className: _dsh_plugin_shop_css_e3675a89_default.notice,
|
|
8306
9574
|
"data-shop-done-note": true,
|
|
8307
9575
|
children: view.detail
|
|
8308
9576
|
}),
|
|
@@ -8317,57 +9585,57 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8317
9585
|
]
|
|
8318
9586
|
});
|
|
8319
9587
|
if (view.kind === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8320
|
-
className:
|
|
9588
|
+
className: _dsh_plugin_shop_css_e3675a89_default.installPanel,
|
|
8321
9589
|
children: [
|
|
8322
9590
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8323
|
-
className:
|
|
9591
|
+
className: _dsh_plugin_shop_css_e3675a89_default.failedHeading,
|
|
8324
9592
|
children: t("installFailed")
|
|
8325
9593
|
}),
|
|
8326
9594
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8327
|
-
className:
|
|
9595
|
+
className: _dsh_plugin_shop_css_e3675a89_default.log,
|
|
8328
9596
|
children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8329
|
-
className:
|
|
9597
|
+
className: _dsh_plugin_shop_css_e3675a89_default.logLine,
|
|
8330
9598
|
"data-shop-log-line": true,
|
|
8331
9599
|
children: line
|
|
8332
9600
|
}, index))
|
|
8333
9601
|
}),
|
|
8334
9602
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8335
|
-
className:
|
|
9603
|
+
className: _dsh_plugin_shop_css_e3675a89_default.failedDetail,
|
|
8336
9604
|
children: view.detail === "" ? t("installTransportFailed") : view.detail
|
|
8337
9605
|
})
|
|
8338
9606
|
]
|
|
8339
9607
|
});
|
|
8340
9608
|
if (view.kind === "rejected") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8341
|
-
className:
|
|
9609
|
+
className: _dsh_plugin_shop_css_e3675a89_default.installPanel,
|
|
8342
9610
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8343
|
-
className:
|
|
9611
|
+
className: _dsh_plugin_shop_css_e3675a89_default.rejectedCode,
|
|
8344
9612
|
children: t(rejectionCodeKey(view.code))
|
|
8345
9613
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8346
|
-
className:
|
|
9614
|
+
className: _dsh_plugin_shop_css_e3675a89_default.rejectedDetail,
|
|
8347
9615
|
children: view.detail
|
|
8348
9616
|
})]
|
|
8349
9617
|
});
|
|
8350
9618
|
if (gateOpen) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8351
|
-
className:
|
|
9619
|
+
className: _dsh_plugin_shop_css_e3675a89_default.gate,
|
|
8352
9620
|
children: [
|
|
8353
9621
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8354
|
-
className:
|
|
9622
|
+
className: _dsh_plugin_shop_css_e3675a89_default.gateTitle,
|
|
8355
9623
|
children: t("acknowledgementTitle")
|
|
8356
9624
|
}),
|
|
8357
9625
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8358
|
-
className:
|
|
9626
|
+
className: _dsh_plugin_shop_css_e3675a89_default.gateBody,
|
|
8359
9627
|
children: t("acknowledgementBody")
|
|
8360
9628
|
}),
|
|
8361
9629
|
!blockersStated && blockers.map((blocker) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockerLine, {
|
|
8362
9630
|
blocker,
|
|
8363
|
-
className:
|
|
9631
|
+
className: _dsh_plugin_shop_css_e3675a89_default.gateWarning,
|
|
8364
9632
|
surface: "warning"
|
|
8365
9633
|
}, blocker.kind)),
|
|
8366
9634
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8367
|
-
className:
|
|
9635
|
+
className: _dsh_plugin_shop_css_e3675a89_default.gateActions,
|
|
8368
9636
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
8369
9637
|
type: "button",
|
|
8370
|
-
className:
|
|
9638
|
+
className: _dsh_plugin_shop_css_e3675a89_default.confirmButton,
|
|
8371
9639
|
"data-shop-confirm": true,
|
|
8372
9640
|
onClick: () => {
|
|
8373
9641
|
setGateOpen(false);
|
|
@@ -8379,7 +9647,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8379
9647
|
children: t("confirm")
|
|
8380
9648
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
8381
9649
|
type: "button",
|
|
8382
|
-
className:
|
|
9650
|
+
className: _dsh_plugin_shop_css_e3675a89_default.cancelButton,
|
|
8383
9651
|
onClick: () => setGateOpen(false),
|
|
8384
9652
|
children: t("cancel")
|
|
8385
9653
|
})]
|
|
@@ -8391,7 +9659,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8391
9659
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
8392
9660
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
8393
9661
|
type: "button",
|
|
8394
|
-
className:
|
|
9662
|
+
className: _dsh_plugin_shop_css_e3675a89_default.installButton,
|
|
8395
9663
|
disabled: refused,
|
|
8396
9664
|
...refused ? { "data-shop-refused": true } : {},
|
|
8397
9665
|
...update ? { "data-shop-update": true } : { "data-shop-install": true },
|
|
@@ -8409,11 +9677,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8409
9677
|
t
|
|
8410
9678
|
}),
|
|
8411
9679
|
refused && !blockersStated && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8412
|
-
className:
|
|
9680
|
+
className: _dsh_plugin_shop_css_e3675a89_default.refusal,
|
|
8413
9681
|
"data-shop-refusal": true,
|
|
8414
9682
|
children: blockers.map((blocker) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockerLine, {
|
|
8415
9683
|
blocker,
|
|
8416
|
-
className:
|
|
9684
|
+
className: _dsh_plugin_shop_css_e3675a89_default.gateWarning,
|
|
8417
9685
|
surface: "warning"
|
|
8418
9686
|
}, blocker.kind))
|
|
8419
9687
|
})
|
|
@@ -8428,32 +9696,32 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8428
9696
|
function UninstallPanel({ name, t, restart, restartBlocked, reload, flow }) {
|
|
8429
9697
|
const { view, start, reset } = flow;
|
|
8430
9698
|
if (view.kind === "running") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8431
|
-
className:
|
|
9699
|
+
className: _dsh_plugin_shop_css_e3675a89_default.installPanel,
|
|
8432
9700
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8433
|
-
className:
|
|
9701
|
+
className: _dsh_plugin_shop_css_e3675a89_default.installing,
|
|
8434
9702
|
children: t("uninstalling")
|
|
8435
9703
|
}), view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8436
|
-
className:
|
|
9704
|
+
className: _dsh_plugin_shop_css_e3675a89_default.log,
|
|
8437
9705
|
children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8438
|
-
className:
|
|
9706
|
+
className: _dsh_plugin_shop_css_e3675a89_default.logLine,
|
|
8439
9707
|
"data-shop-log-line": true,
|
|
8440
9708
|
children: line
|
|
8441
9709
|
}, index))
|
|
8442
9710
|
})]
|
|
8443
9711
|
});
|
|
8444
9712
|
if (view.kind === "done") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8445
|
-
className:
|
|
9713
|
+
className: _dsh_plugin_shop_css_e3675a89_default.installedActions,
|
|
8446
9714
|
children: [
|
|
8447
9715
|
view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8448
|
-
className:
|
|
9716
|
+
className: _dsh_plugin_shop_css_e3675a89_default.log,
|
|
8449
9717
|
children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8450
|
-
className:
|
|
9718
|
+
className: _dsh_plugin_shop_css_e3675a89_default.logLine,
|
|
8451
9719
|
"data-shop-log-line": true,
|
|
8452
9720
|
children: line
|
|
8453
9721
|
}, index))
|
|
8454
9722
|
}),
|
|
8455
9723
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8456
|
-
className:
|
|
9724
|
+
className: _dsh_plugin_shop_css_e3675a89_default.notice,
|
|
8457
9725
|
"data-shop-uninstall-done": true,
|
|
8458
9726
|
children: t(uninstallActivationNoticeKey(view.activation))
|
|
8459
9727
|
}),
|
|
@@ -8467,7 +9735,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8467
9735
|
}),
|
|
8468
9736
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
8469
9737
|
type: "button",
|
|
8470
|
-
className:
|
|
9738
|
+
className: _dsh_plugin_shop_css_e3675a89_default.cancelButton,
|
|
8471
9739
|
"data-shop-uninstall-dismiss": true,
|
|
8472
9740
|
onClick: reset,
|
|
8473
9741
|
children: t("dismiss")
|
|
@@ -8475,27 +9743,27 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8475
9743
|
]
|
|
8476
9744
|
});
|
|
8477
9745
|
if (view.kind === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8478
|
-
className:
|
|
9746
|
+
className: _dsh_plugin_shop_css_e3675a89_default.installPanel,
|
|
8479
9747
|
children: [
|
|
8480
9748
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8481
|
-
className:
|
|
9749
|
+
className: _dsh_plugin_shop_css_e3675a89_default.failedHeading,
|
|
8482
9750
|
children: t("uninstallFailed")
|
|
8483
9751
|
}),
|
|
8484
9752
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8485
|
-
className:
|
|
9753
|
+
className: _dsh_plugin_shop_css_e3675a89_default.log,
|
|
8486
9754
|
children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
8487
|
-
className:
|
|
9755
|
+
className: _dsh_plugin_shop_css_e3675a89_default.logLine,
|
|
8488
9756
|
"data-shop-log-line": true,
|
|
8489
9757
|
children: line
|
|
8490
9758
|
}, index))
|
|
8491
9759
|
}),
|
|
8492
9760
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8493
|
-
className:
|
|
9761
|
+
className: _dsh_plugin_shop_css_e3675a89_default.failedDetail,
|
|
8494
9762
|
children: view.detail === "" ? t("uninstallTransportFailed") : view.detail
|
|
8495
9763
|
}),
|
|
8496
9764
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
8497
9765
|
type: "button",
|
|
8498
|
-
className:
|
|
9766
|
+
className: _dsh_plugin_shop_css_e3675a89_default.uninstallButton,
|
|
8499
9767
|
"data-shop-uninstall-retry": true,
|
|
8500
9768
|
onClick: () => void start({ name }),
|
|
8501
9769
|
children: t("retry")
|
|
@@ -8505,7 +9773,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8505
9773
|
if (view.kind !== "idle") return null;
|
|
8506
9774
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
8507
9775
|
type: "button",
|
|
8508
|
-
className:
|
|
9776
|
+
className: _dsh_plugin_shop_css_e3675a89_default.uninstallButton,
|
|
8509
9777
|
"data-shop-uninstall": true,
|
|
8510
9778
|
onClick: () => void start({ name }),
|
|
8511
9779
|
children: t("uninstall")
|
|
@@ -8546,37 +9814,38 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8546
9814
|
else if (outcome.kind === "unreachable") setRefusal(t("restartTransportFailed"));
|
|
8547
9815
|
};
|
|
8548
9816
|
if (monitor?.kind === "requesting" || monitor?.kind === "restarting") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8549
|
-
className:
|
|
9817
|
+
className: _dsh_plugin_shop_css_e3675a89_default.notice,
|
|
8550
9818
|
"data-shop-restarting": true,
|
|
8551
9819
|
children: t("restarting")
|
|
8552
9820
|
});
|
|
8553
9821
|
if (monitor?.kind === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8554
|
-
className:
|
|
9822
|
+
className: _dsh_plugin_shop_css_e3675a89_default.failedDetail,
|
|
8555
9823
|
"data-shop-restart-error": true,
|
|
8556
9824
|
children: monitor.logFile === void 0 ? t("restartFailedNotice") : t("restartFailedLogNotice", { log: monitor.logFile })
|
|
8557
9825
|
});
|
|
9826
|
+
if (monitor?.kind === "not-restarted") return null;
|
|
8558
9827
|
if (gateOpen) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8559
|
-
className:
|
|
9828
|
+
className: _dsh_plugin_shop_css_e3675a89_default.gate,
|
|
8560
9829
|
children: [
|
|
8561
9830
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8562
|
-
className:
|
|
9831
|
+
className: _dsh_plugin_shop_css_e3675a89_default.gateTitle,
|
|
8563
9832
|
children: t("restartTitle")
|
|
8564
9833
|
}),
|
|
8565
9834
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8566
|
-
className:
|
|
9835
|
+
className: _dsh_plugin_shop_css_e3675a89_default.gateBody,
|
|
8567
9836
|
children: t("restartBody")
|
|
8568
9837
|
}),
|
|
8569
9838
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8570
|
-
className:
|
|
9839
|
+
className: _dsh_plugin_shop_css_e3675a89_default.gateActions,
|
|
8571
9840
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
8572
9841
|
type: "button",
|
|
8573
|
-
className:
|
|
9842
|
+
className: _dsh_plugin_shop_css_e3675a89_default.confirmButton,
|
|
8574
9843
|
"data-shop-restart-confirm": true,
|
|
8575
9844
|
onClick: () => void onConfirm(),
|
|
8576
9845
|
children: t("restartConfirm")
|
|
8577
9846
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
8578
9847
|
type: "button",
|
|
8579
|
-
className:
|
|
9848
|
+
className: _dsh_plugin_shop_css_e3675a89_default.cancelButton,
|
|
8580
9849
|
onClick: () => setGateOpen(false),
|
|
8581
9850
|
children: t("cancel")
|
|
8582
9851
|
})]
|
|
@@ -8584,20 +9853,43 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8584
9853
|
]
|
|
8585
9854
|
});
|
|
8586
9855
|
const refused = refusal === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8587
|
-
className:
|
|
9856
|
+
className: _dsh_plugin_shop_css_e3675a89_default.failedDetail,
|
|
8588
9857
|
"data-shop-restart-error": true,
|
|
8589
9858
|
children: refusal
|
|
8590
9859
|
});
|
|
8591
9860
|
if (gate !== void 0) return refused;
|
|
8592
9861
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [refused, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
8593
9862
|
type: "button",
|
|
8594
|
-
className:
|
|
9863
|
+
className: _dsh_plugin_shop_css_e3675a89_default.restartButton,
|
|
8595
9864
|
"data-shop-restart": true,
|
|
8596
9865
|
onClick: () => setGateOpen(true),
|
|
8597
9866
|
children: t("restart")
|
|
8598
9867
|
})] });
|
|
8599
9868
|
}
|
|
8600
9869
|
/**
|
|
9870
|
+
* The page reloaded into the very process it asked to restart
|
|
9871
|
+
* (restart-monitor.ts `noteBoot`): said once, above everything, because
|
|
9872
|
+
* whatever asked for the restart — a self-update, a card, a switch — has
|
|
9873
|
+
* nothing left on screen after the reload to say it under. It names the
|
|
9874
|
+
* process to stop, when the host named it, and the command that starts dsh
|
|
9875
|
+
* again; the log holds what the handoff did, which tells an old process that
|
|
9876
|
+
* never exited from a new one that died in boot (design §8, 2026-10-06
|
|
9877
|
+
* amendment).
|
|
9878
|
+
*/
|
|
9879
|
+
function NotRestartedNotice({ t, pid, logFile }) {
|
|
9880
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9881
|
+
className: _dsh_plugin_shop_css_e3675a89_default.selfUpdatePanel,
|
|
9882
|
+
"data-shop-not-restarted": true,
|
|
9883
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9884
|
+
className: _dsh_plugin_shop_css_e3675a89_default.failedDetail,
|
|
9885
|
+
children: pid === void 0 ? t("notRestartedNotice") : t("notRestartedPidNotice", { pid: String(pid) })
|
|
9886
|
+
}), logFile !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9887
|
+
className: _dsh_plugin_shop_css_e3675a89_default.failedDetail,
|
|
9888
|
+
children: t("notRestartedLogNotice", { log: logFile })
|
|
9889
|
+
})]
|
|
9890
|
+
});
|
|
9891
|
+
}
|
|
9892
|
+
/**
|
|
8601
9893
|
* The §4/§8 offer under a settled install, update or uninstall: a reload
|
|
8602
9894
|
* when the change is already live on the server and only this page is
|
|
8603
9895
|
* stale, a restart when the host actually needs one — and can restart at
|
|
@@ -8622,7 +9914,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8622
9914
|
restart,
|
|
8623
9915
|
reload
|
|
8624
9916
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8625
|
-
className:
|
|
9917
|
+
className: _dsh_plugin_shop_css_e3675a89_default.notice,
|
|
8626
9918
|
"data-shop-restart-disabled": true,
|
|
8627
9919
|
children: t(restartBlockedNoticeKey(restartBlocked))
|
|
8628
9920
|
});
|
|
@@ -8633,14 +9925,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8633
9925
|
* would help without knowing that now is a good time. */
|
|
8634
9926
|
function ReloadPanel({ t, reload, where }) {
|
|
8635
9927
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8636
|
-
className:
|
|
9928
|
+
className: _dsh_plugin_shop_css_e3675a89_default.reloadPanel,
|
|
8637
9929
|
"data-shop-reload": where,
|
|
8638
9930
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8639
|
-
className:
|
|
9931
|
+
className: _dsh_plugin_shop_css_e3675a89_default.notice,
|
|
8640
9932
|
children: t("reloadNote")
|
|
8641
9933
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
8642
9934
|
type: "button",
|
|
8643
|
-
className:
|
|
9935
|
+
className: _dsh_plugin_shop_css_e3675a89_default.reloadButton,
|
|
8644
9936
|
onClick: reload,
|
|
8645
9937
|
children: t("reload")
|
|
8646
9938
|
})]
|
|
@@ -8653,10 +9945,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8653
9945
|
* transport throw renders the localized failure line — its private detail
|
|
8654
9946
|
* (which can name hosts and ports) never reaches the UI.
|
|
8655
9947
|
*/
|
|
8656
|
-
function EnabledSwitch({ row, t, setEnabled, restart, restartBlocked, reload }) {
|
|
9948
|
+
function EnabledSwitch({ row, t, setEnabled, restart, restartBlocked, reload, restarts }) {
|
|
8657
9949
|
const [enabled, setEnabledState] = (0, react.useState)(row.enabled);
|
|
8658
9950
|
const [toggle, setToggle] = (0, react.useState)({ kind: "idle" });
|
|
8659
9951
|
const [owed, setOwed] = (0, react.useState)("nothing");
|
|
9952
|
+
const restartsSeen = (0, react.useRef)(restarts);
|
|
9953
|
+
(0, react.useEffect)(() => {
|
|
9954
|
+
if (restartsSeen.current === restarts) return;
|
|
9955
|
+
restartsSeen.current = restarts;
|
|
9956
|
+
if (owed !== "restart") return;
|
|
9957
|
+
setOwed("nothing");
|
|
9958
|
+
setToggle((current) => current.kind === "saved" ? { kind: "idle" } : current);
|
|
9959
|
+
}, [restarts, owed]);
|
|
8660
9960
|
const onToggle = async () => {
|
|
8661
9961
|
if (toggle.kind === "saving") return;
|
|
8662
9962
|
const next = !enabled;
|
|
@@ -8683,7 +9983,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8683
9983
|
}
|
|
8684
9984
|
};
|
|
8685
9985
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8686
|
-
className:
|
|
9986
|
+
className: _dsh_plugin_shop_css_e3675a89_default.switchWrap,
|
|
8687
9987
|
"data-shop-enabled-switch": row.name,
|
|
8688
9988
|
children: [
|
|
8689
9989
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -8692,18 +9992,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8692
9992
|
"aria-checked": enabled,
|
|
8693
9993
|
"aria-label": t("enabledSwitch"),
|
|
8694
9994
|
"data-shop-toggle": true,
|
|
8695
|
-
className: `${
|
|
9995
|
+
className: `${_dsh_plugin_shop_css_e3675a89_default.switch} ${enabled ? _dsh_plugin_shop_css_e3675a89_default.switchOn : ""}`,
|
|
8696
9996
|
onClick: () => void onToggle(),
|
|
8697
9997
|
disabled: toggle.kind === "saving",
|
|
8698
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className:
|
|
9998
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_e3675a89_default.switchKnob })
|
|
8699
9999
|
}),
|
|
8700
10000
|
toggle.kind === "saved" && owed === "nothing" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8701
|
-
className:
|
|
10001
|
+
className: _dsh_plugin_shop_css_e3675a89_default.notice,
|
|
8702
10002
|
"data-shop-hot-apply": true,
|
|
8703
10003
|
children: t("hotApplyNote")
|
|
8704
10004
|
}),
|
|
8705
10005
|
owed === "restart" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8706
|
-
className:
|
|
10006
|
+
className: _dsh_plugin_shop_css_e3675a89_default.notice,
|
|
8707
10007
|
"data-shop-toggle-restart": true,
|
|
8708
10008
|
children: t("toggleRestartNote")
|
|
8709
10009
|
}),
|
|
@@ -8716,7 +10016,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8716
10016
|
where: "toggle"
|
|
8717
10017
|
}),
|
|
8718
10018
|
toggle.kind === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8719
|
-
className:
|
|
10019
|
+
className: _dsh_plugin_shop_css_e3675a89_default.failedDetail,
|
|
8720
10020
|
"data-shop-toggle-error": true,
|
|
8721
10021
|
children: toggle.detail
|
|
8722
10022
|
})
|
|
@@ -8725,29 +10025,33 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8725
10025
|
}
|
|
8726
10026
|
/** One outdated install row (§7.3): the name, the installed and latest
|
|
8727
10027
|
* versions, the hot enable/disable switch, and the update button (the
|
|
8728
|
-
* install flow for `name@latest`, reusing `InstallPanel`).
|
|
8729
|
-
|
|
10028
|
+
* install flow for `name@latest`, reusing `InstallPanel`). The row MUST
|
|
10029
|
+
* carry a `latest` string — a catalog truth to compare against; §C10's
|
|
10030
|
+
* shadowed rows hold `latest: null` and never reach this control, so the
|
|
10031
|
+
* field is narrowed at the parameter. */
|
|
10032
|
+
function OutdatedRow({ row, tier, missing, harness, t, setEnabled, flowFor, restart, restartBlocked, reload, restarts }) {
|
|
8730
10033
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8731
|
-
className:
|
|
10034
|
+
className: _dsh_plugin_shop_css_e3675a89_default.outdatedRow,
|
|
8732
10035
|
"data-shop-outdated-entry": row.name,
|
|
8733
10036
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8734
|
-
className:
|
|
10037
|
+
className: _dsh_plugin_shop_css_e3675a89_default.outdatedInfo,
|
|
8735
10038
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
8736
|
-
className:
|
|
10039
|
+
className: _dsh_plugin_shop_css_e3675a89_default.name,
|
|
8737
10040
|
children: row.name
|
|
8738
10041
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
8739
|
-
className:
|
|
10042
|
+
className: _dsh_plugin_shop_css_e3675a89_default.outdatedVersions,
|
|
8740
10043
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("installedVersion", { version: row.source === "github" ? row.installed.slice(0, 7) : row.installed }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("latestVersion", { version: row.latest }) })]
|
|
8741
10044
|
})]
|
|
8742
10045
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
8743
|
-
className:
|
|
10046
|
+
className: _dsh_plugin_shop_css_e3675a89_default.outdatedActions,
|
|
8744
10047
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(EnabledSwitch, {
|
|
8745
10048
|
row,
|
|
8746
10049
|
t,
|
|
8747
10050
|
setEnabled,
|
|
8748
10051
|
restart,
|
|
8749
10052
|
restartBlocked,
|
|
8750
|
-
reload
|
|
10053
|
+
reload,
|
|
10054
|
+
restarts
|
|
8751
10055
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstallPanel, {
|
|
8752
10056
|
target: {
|
|
8753
10057
|
name: row.name,
|
|
@@ -8785,25 +10089,25 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8785
10089
|
* tier for the update gate is looked up from the catalog by install IDENTITY,
|
|
8786
10090
|
* never by name (community → acknowledgement); an entry absent from the
|
|
8787
10091
|
* catalog defaults to the community gate (the safer read). */
|
|
8788
|
-
function OutdatedSection({ state, entriesByKey, missingByKey, harnessVerdicts, t, setEnabled, flowFor, restart, restartBlocked, reload }) {
|
|
10092
|
+
function OutdatedSection({ state, entriesByKey, missingByKey, harnessVerdicts, t, setEnabled, flowFor, restart, restartBlocked, reload, restarts }) {
|
|
8789
10093
|
if (state.kind === "loading") return null;
|
|
8790
10094
|
if (state.kind === "error") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8791
|
-
className:
|
|
10095
|
+
className: _dsh_plugin_shop_css_e3675a89_default.stateLine,
|
|
8792
10096
|
"data-shop-outdated-error": true,
|
|
8793
10097
|
children: t("installedError")
|
|
8794
10098
|
});
|
|
8795
|
-
const outdated = state.entries.filter((entry) => entry.outdated);
|
|
10099
|
+
const outdated = state.entries.filter((entry) => entry.outdated && entry.latest !== null);
|
|
8796
10100
|
if (outdated.length === 0) return null;
|
|
8797
10101
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
8798
|
-
className:
|
|
10102
|
+
className: _dsh_plugin_shop_css_e3675a89_default.outdatedSection,
|
|
8799
10103
|
"data-shop-outdated": true,
|
|
8800
10104
|
"aria-labelledby": UPDATABLE_HEADING_ID,
|
|
8801
10105
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
8802
|
-
className:
|
|
10106
|
+
className: _dsh_plugin_shop_css_e3675a89_default.updatableHeading,
|
|
8803
10107
|
id: UPDATABLE_HEADING_ID,
|
|
8804
10108
|
children: t("updatableSection")
|
|
8805
10109
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
8806
|
-
className:
|
|
10110
|
+
className: _dsh_plugin_shop_css_e3675a89_default.outdatedList,
|
|
8807
10111
|
children: outdated.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OutdatedRow, {
|
|
8808
10112
|
row,
|
|
8809
10113
|
tier: entriesByKey.get(identityKey(row))?.tier ?? "community",
|
|
@@ -8814,11 +10118,68 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8814
10118
|
flowFor,
|
|
8815
10119
|
restart,
|
|
8816
10120
|
restartBlocked,
|
|
8817
|
-
reload
|
|
10121
|
+
reload,
|
|
10122
|
+
restarts
|
|
8818
10123
|
}) }, identityKey(row)))
|
|
8819
10124
|
})]
|
|
8820
10125
|
});
|
|
8821
10126
|
}
|
|
10127
|
+
/** The §C10 shadowed rows: installed github dependencies whose name the
|
|
10128
|
+
* catalog now holds as an npm package — the pipeline refused the github
|
|
10129
|
+
* entry `shadowed-by-npm`, so it has no shelf card. Rendered as a single
|
|
10130
|
+
* strip inside the Installed view, with the badge and the explanation
|
|
10131
|
+
* naming the harness fact, and an Uninstall button as the only offered
|
|
10132
|
+
* action: there is no catalog truth to compare against, so no Install /
|
|
10133
|
+
* Update / Enable / Restart control makes sense. */
|
|
10134
|
+
function ShadowedSection({ entries, t, uninstallFlows, restart, restartBlocked, reload }) {
|
|
10135
|
+
const shadowed = entries.filter((entry) => entry.shadowed === true);
|
|
10136
|
+
if (shadowed.length === 0) return null;
|
|
10137
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
|
|
10138
|
+
className: _dsh_plugin_shop_css_e3675a89_default.shadowedSection,
|
|
10139
|
+
"data-shop-shadowed": true,
|
|
10140
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
10141
|
+
className: _dsh_plugin_shop_css_e3675a89_default.outdatedList,
|
|
10142
|
+
children: shadowed.map((row) => {
|
|
10143
|
+
const key = identityKey(row);
|
|
10144
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10145
|
+
className: _dsh_plugin_shop_css_e3675a89_default.outdatedRow,
|
|
10146
|
+
"data-shop-shadowed-entry": row.name,
|
|
10147
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10148
|
+
className: _dsh_plugin_shop_css_e3675a89_default.outdatedInfo,
|
|
10149
|
+
children: [
|
|
10150
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10151
|
+
className: _dsh_plugin_shop_css_e3675a89_default.name,
|
|
10152
|
+
children: row.name
|
|
10153
|
+
}),
|
|
10154
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10155
|
+
className: _dsh_plugin_shop_css_e3675a89_default.badges,
|
|
10156
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10157
|
+
className: _dsh_plugin_shop_css_e3675a89_default.tierBadge,
|
|
10158
|
+
"data-tier": "shadowed",
|
|
10159
|
+
children: t("shadowedBadge")
|
|
10160
|
+
})
|
|
10161
|
+
}),
|
|
10162
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10163
|
+
className: _dsh_plugin_shop_css_e3675a89_default.shadowedDetail,
|
|
10164
|
+
children: t("shadowedDetail")
|
|
10165
|
+
})
|
|
10166
|
+
]
|
|
10167
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10168
|
+
className: _dsh_plugin_shop_css_e3675a89_default.outdatedActions,
|
|
10169
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UninstallPanel, {
|
|
10170
|
+
name: row.name,
|
|
10171
|
+
t,
|
|
10172
|
+
restart,
|
|
10173
|
+
restartBlocked,
|
|
10174
|
+
reload,
|
|
10175
|
+
flow: uninstallFlows.flowFor(key)
|
|
10176
|
+
})
|
|
10177
|
+
})]
|
|
10178
|
+
}) }, key);
|
|
10179
|
+
})
|
|
10180
|
+
})
|
|
10181
|
+
});
|
|
10182
|
+
}
|
|
8822
10183
|
/**
|
|
8823
10184
|
* The shop's own error boundary (design 2026-09-26-market-borrowings §4).
|
|
8824
10185
|
*
|
|
@@ -8854,16 +10215,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8854
10215
|
role: "alert",
|
|
8855
10216
|
children: [
|
|
8856
10217
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8857
|
-
className:
|
|
10218
|
+
className: _dsh_plugin_shop_css_e3675a89_default.stateLine,
|
|
8858
10219
|
children: say("tabCrashed", en.tabCrashed)
|
|
8859
10220
|
}),
|
|
8860
10221
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
8861
|
-
className:
|
|
10222
|
+
className: _dsh_plugin_shop_css_e3675a89_default.failedDetail,
|
|
8862
10223
|
children: error.message
|
|
8863
10224
|
}),
|
|
8864
10225
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
8865
10226
|
type: "button",
|
|
8866
|
-
className:
|
|
10227
|
+
className: _dsh_plugin_shop_css_e3675a89_default.actionButton,
|
|
8867
10228
|
onClick: () => this.setState({ error: null }),
|
|
8868
10229
|
children: say("retry", en.retry)
|
|
8869
10230
|
})
|
|
@@ -8883,7 +10244,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8883
10244
|
/** The shop tab body: browse, search, refresh, and render one card per
|
|
8884
10245
|
* entry. Data attributes on the e2e-relevant nodes follow the Task 3 list. */
|
|
8885
10246
|
function ShopTabBody(props) {
|
|
8886
|
-
const { t, catalog, install, installStatus, setEnabled, installed, installedSpecs, uninstall, noteUninstalled, restart, version, updateStart, reload: injectedReload } = props;
|
|
10247
|
+
const { t, catalog, install, installStatus, setEnabled, installed, installedSpecs, uninstall, noteUninstalled, restart, version, onRestarted, updateStart, reload: injectedReload } = props;
|
|
8887
10248
|
const [catalogState, setCatalogState] = (0, react.useState)({ kind: "loading" });
|
|
8888
10249
|
const [installedState, setInstalledState] = (0, react.useState)({ kind: "loading" });
|
|
8889
10250
|
/** The install gate's own input, or undefined while it is unknown — loading,
|
|
@@ -8927,6 +10288,25 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
8927
10288
|
noteUninstalled
|
|
8928
10289
|
]));
|
|
8929
10290
|
uninstallResetRef.current = uninstallFlows.resetFlow;
|
|
10291
|
+
const [restarts, setRestarts] = (0, react.useState)(0);
|
|
10292
|
+
const resetSelfUpdate = selfUpdate.resetWhere;
|
|
10293
|
+
const resetInstalls = flows.resetWhere;
|
|
10294
|
+
const resetUninstalls = uninstallFlows.resetWhere;
|
|
10295
|
+
(0, react.useEffect)(() => onRestarted?.((fresh) => {
|
|
10296
|
+
setSelfVersion(fresh);
|
|
10297
|
+
resetSelfUpdate(settledByRestart);
|
|
10298
|
+
resetInstalls(settledByRestart);
|
|
10299
|
+
resetUninstalls(settledByRestart);
|
|
10300
|
+
setRestarts((current) => current + 1);
|
|
10301
|
+
noteMutation();
|
|
10302
|
+
setRequest({ kind: "reverdict" });
|
|
10303
|
+
}), [
|
|
10304
|
+
onRestarted,
|
|
10305
|
+
resetSelfUpdate,
|
|
10306
|
+
resetInstalls,
|
|
10307
|
+
resetUninstalls,
|
|
10308
|
+
noteMutation
|
|
10309
|
+
]);
|
|
8930
10310
|
const [reloading, setReloading] = (0, react.useState)(false);
|
|
8931
10311
|
const [reloadFailed, setReloadFailed] = (0, react.useState)(false);
|
|
8932
10312
|
const [query, setQuery] = (0, react.useState)("");
|
|
@@ -9056,8 +10436,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9056
10436
|
harnessVerdicts,
|
|
9057
10437
|
nameTakenByKey
|
|
9058
10438
|
]);
|
|
10439
|
+
const expansionMemo = (0, react.useMemo)(() => expandZhQuery(query), [query]);
|
|
9059
10440
|
const matched = (0, react.useMemo)(() => {
|
|
9060
|
-
const q = query
|
|
10441
|
+
const q = expansionMemo.query;
|
|
9061
10442
|
return (category === "installed" ? sortedCatalog : browsable).filter((entry) => {
|
|
9062
10443
|
if (category === "installed") {
|
|
9063
10444
|
const key = entryKey(entry);
|
|
@@ -9066,12 +10447,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9066
10447
|
if (q === "") return true;
|
|
9067
10448
|
const summaryEn = entry.catalog?.summary.en ?? "";
|
|
9068
10449
|
const summaryZh = entry.catalog?.summary.zh ?? "";
|
|
9069
|
-
|
|
10450
|
+
const expansion = expansionMemo;
|
|
10451
|
+
const name = entry.name.toLowerCase();
|
|
10452
|
+
const en = summaryEn.toLowerCase();
|
|
10453
|
+
const zh = summaryZh.toLowerCase();
|
|
10454
|
+
return expansion.terms.some((term) => term.startsWith("#") ? tokenInText(term.slice(1), name + " " + en + " " + zh) : name.includes(term) || en.includes(term) || zh.includes(term));
|
|
9070
10455
|
});
|
|
9071
10456
|
}, [
|
|
9072
10457
|
sortedCatalog,
|
|
9073
10458
|
browsable,
|
|
9074
|
-
|
|
10459
|
+
expansionMemo,
|
|
9075
10460
|
category,
|
|
9076
10461
|
installedByKey,
|
|
9077
10462
|
uninstallFlows.pending
|
|
@@ -9120,19 +10505,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9120
10505
|
"data-shop-tab": true,
|
|
9121
10506
|
"aria-busy": "true",
|
|
9122
10507
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9123
|
-
className:
|
|
10508
|
+
className: _dsh_plugin_shop_css_e3675a89_default.srOnly,
|
|
9124
10509
|
children: t("loading")
|
|
9125
10510
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9126
|
-
className:
|
|
10511
|
+
className: _dsh_plugin_shop_css_e3675a89_default.skeletonGrid,
|
|
9127
10512
|
"data-shop-skeleton": true,
|
|
9128
10513
|
"aria-hidden": "true",
|
|
9129
10514
|
children: Array.from({ length: 8 }, (_, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9130
|
-
className:
|
|
10515
|
+
className: _dsh_plugin_shop_css_e3675a89_default.skeletonCard,
|
|
9131
10516
|
children: [
|
|
9132
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className:
|
|
9133
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className:
|
|
9134
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className:
|
|
9135
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className:
|
|
10517
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_e3675a89_default.skeletonName }),
|
|
10518
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_e3675a89_default.skeletonSummary }),
|
|
10519
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_e3675a89_default.skeletonSummaryShort }),
|
|
10520
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_e3675a89_default.skeletonActions })
|
|
9136
10521
|
]
|
|
9137
10522
|
}, i))
|
|
9138
10523
|
})]
|
|
@@ -9142,11 +10527,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9142
10527
|
translate: "no",
|
|
9143
10528
|
"data-shop-tab": true,
|
|
9144
10529
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9145
|
-
className:
|
|
10530
|
+
className: _dsh_plugin_shop_css_e3675a89_default.stateLine,
|
|
9146
10531
|
children: t("error")
|
|
9147
10532
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
9148
10533
|
type: "button",
|
|
9149
|
-
className:
|
|
10534
|
+
className: _dsh_plugin_shop_css_e3675a89_default.actionButton,
|
|
9150
10535
|
onClick: () => {
|
|
9151
10536
|
setRequest({ kind: "retry" });
|
|
9152
10537
|
setVersionReload((current) => current + 1);
|
|
@@ -9161,96 +10546,109 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9161
10546
|
"data-shop-tab": true,
|
|
9162
10547
|
children: [
|
|
9163
10548
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9164
|
-
className:
|
|
9165
|
-
children: [
|
|
9166
|
-
|
|
9167
|
-
|
|
9168
|
-
|
|
9169
|
-
|
|
9170
|
-
|
|
9171
|
-
|
|
9172
|
-
|
|
9173
|
-
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
|
|
9181
|
-
|
|
9182
|
-
|
|
9183
|
-
|
|
9184
|
-
|
|
9185
|
-
|
|
9186
|
-
|
|
9187
|
-
children:
|
|
9188
|
-
}),
|
|
9189
|
-
|
|
9190
|
-
className:
|
|
9191
|
-
"
|
|
9192
|
-
|
|
9193
|
-
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
9201
|
-
|
|
9202
|
-
|
|
9203
|
-
|
|
9204
|
-
|
|
9205
|
-
|
|
9206
|
-
|
|
9207
|
-
|
|
9208
|
-
|
|
9209
|
-
|
|
9210
|
-
|
|
9211
|
-
|
|
9212
|
-
|
|
9213
|
-
|
|
9214
|
-
|
|
9215
|
-
|
|
9216
|
-
|
|
9217
|
-
|
|
9218
|
-
|
|
9219
|
-
|
|
9220
|
-
|
|
9221
|
-
|
|
9222
|
-
|
|
9223
|
-
|
|
9224
|
-
|
|
9225
|
-
|
|
10549
|
+
className: _dsh_plugin_shop_css_e3675a89_default.toolbar,
|
|
10550
|
+
children: [
|
|
10551
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
10552
|
+
type: "search",
|
|
10553
|
+
className: _dsh_plugin_shop_css_e3675a89_default.searchInput,
|
|
10554
|
+
"aria-label": t("search"),
|
|
10555
|
+
placeholder: t("search"),
|
|
10556
|
+
value: query,
|
|
10557
|
+
onChange: (event) => {
|
|
10558
|
+
setQuery(event.target.value);
|
|
10559
|
+
setVisibleCount(48);
|
|
10560
|
+
}
|
|
10561
|
+
}),
|
|
10562
|
+
expansionMemo.intents.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10563
|
+
"data-shop-expansion-note": true,
|
|
10564
|
+
className: _dsh_plugin_shop_css_e3675a89_default.expansionNote,
|
|
10565
|
+
children: t("zhExpansionNote", { terms: expansionMemo.terms.slice(1).map((term) => term.startsWith("#") ? term.slice(1) : term).join(", ") })
|
|
10566
|
+
}),
|
|
10567
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10568
|
+
className: _dsh_plugin_shop_css_e3675a89_default.toolbarRight,
|
|
10569
|
+
children: [
|
|
10570
|
+
result.stale && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10571
|
+
className: _dsh_plugin_shop_css_e3675a89_default.staleBadge,
|
|
10572
|
+
children: t("staleLabel", { date: result.builtAt.slice(0, 10) })
|
|
10573
|
+
}),
|
|
10574
|
+
selfVersion !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10575
|
+
className: _dsh_plugin_shop_css_e3675a89_default.versionBlock,
|
|
10576
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
10577
|
+
className: _dsh_plugin_shop_css_e3675a89_default.versionText,
|
|
10578
|
+
"data-shop-version": true,
|
|
10579
|
+
children: ["v", selfVersion.installed]
|
|
10580
|
+
}), restartMonitor !== null ? null : selfPending && restartBlocked === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
10581
|
+
type: "button",
|
|
10582
|
+
className: _dsh_plugin_shop_css_e3675a89_default.restartSelfButton,
|
|
10583
|
+
"data-shop-restart": true,
|
|
10584
|
+
onClick: () => setSelfRestartGate(true),
|
|
10585
|
+
children: t("restart")
|
|
10586
|
+
}) : !selfPending && selfVersion.outdated && selfVersion.latest !== null && selfUpdate.view.kind === "idle" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
10587
|
+
type: "button",
|
|
10588
|
+
className: _dsh_plugin_shop_css_e3675a89_default.updateSelfButton,
|
|
10589
|
+
"data-shop-update-self": true,
|
|
10590
|
+
onClick: () => {
|
|
10591
|
+
if (selfVersion.latest !== null) selfUpdate.start({ version: selfVersion.latest });
|
|
10592
|
+
},
|
|
10593
|
+
children: t("update")
|
|
10594
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
10595
|
+
type: "button",
|
|
10596
|
+
className: _dsh_plugin_shop_css_e3675a89_default.checkUpdateButton,
|
|
10597
|
+
"data-shop-check-update": true,
|
|
10598
|
+
title: t("checkUpdateTitle"),
|
|
10599
|
+
disabled: checkState !== "idle",
|
|
10600
|
+
onClick: () => void checkVersion(),
|
|
10601
|
+
children: checkState === "up-to-date" ? t("upToDate") : t("checkUpdate")
|
|
10602
|
+
})]
|
|
10603
|
+
}),
|
|
10604
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
10605
|
+
className: _dsh_plugin_shop_css_e3675a89_default.githubLink,
|
|
10606
|
+
href: SHOP_REPO_URL,
|
|
10607
|
+
target: "_blank",
|
|
10608
|
+
rel: "noopener noreferrer",
|
|
10609
|
+
"aria-label": t("github"),
|
|
10610
|
+
"data-shop-github": true,
|
|
10611
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
10612
|
+
viewBox: "0 0 16 16",
|
|
10613
|
+
width: "16",
|
|
10614
|
+
height: "16",
|
|
10615
|
+
"aria-hidden": "true",
|
|
10616
|
+
fill: "currentColor",
|
|
10617
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z" })
|
|
10618
|
+
})
|
|
9226
10619
|
})
|
|
9227
|
-
|
|
9228
|
-
|
|
9229
|
-
|
|
10620
|
+
]
|
|
10621
|
+
})
|
|
10622
|
+
]
|
|
10623
|
+
}),
|
|
10624
|
+
restartMonitor?.kind === "not-restarted" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NotRestartedNotice, {
|
|
10625
|
+
t,
|
|
10626
|
+
pid: restartMonitor.pid,
|
|
10627
|
+
logFile: restartMonitor.logFile
|
|
9230
10628
|
}),
|
|
9231
10629
|
selfUpdate.view.kind === "running" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9232
|
-
className:
|
|
10630
|
+
className: _dsh_plugin_shop_css_e3675a89_default.selfUpdatePanel,
|
|
9233
10631
|
"data-shop-self-updating": true,
|
|
9234
10632
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9235
|
-
className:
|
|
10633
|
+
className: _dsh_plugin_shop_css_e3675a89_default.installing,
|
|
9236
10634
|
children: t(installPhaseKey(selfUpdate.view.phase))
|
|
9237
10635
|
}), selfUpdate.view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9238
|
-
className:
|
|
10636
|
+
className: _dsh_plugin_shop_css_e3675a89_default.log,
|
|
9239
10637
|
children: selfUpdate.view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9240
|
-
className:
|
|
10638
|
+
className: _dsh_plugin_shop_css_e3675a89_default.logLine,
|
|
9241
10639
|
"data-shop-log-line": true,
|
|
9242
10640
|
children: line
|
|
9243
10641
|
}, index))
|
|
9244
10642
|
})]
|
|
9245
10643
|
}),
|
|
9246
10644
|
selfPending && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9247
|
-
className:
|
|
10645
|
+
className: _dsh_plugin_shop_css_e3675a89_default.selfUpdatePanel,
|
|
9248
10646
|
"data-shop-self-update-done": true,
|
|
9249
10647
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9250
|
-
className:
|
|
10648
|
+
className: _dsh_plugin_shop_css_e3675a89_default.installedActions,
|
|
9251
10649
|
children: [
|
|
9252
10650
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9253
|
-
className:
|
|
10651
|
+
className: _dsh_plugin_shop_css_e3675a89_default.notice,
|
|
9254
10652
|
children: t("installedRestartNotice")
|
|
9255
10653
|
}),
|
|
9256
10654
|
restartBlocked === null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RestartPanel, {
|
|
@@ -9263,7 +10661,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9263
10661
|
}
|
|
9264
10662
|
}),
|
|
9265
10663
|
restartBlocked !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9266
|
-
className:
|
|
10664
|
+
className: _dsh_plugin_shop_css_e3675a89_default.notice,
|
|
9267
10665
|
"data-shop-restart-disabled": true,
|
|
9268
10666
|
children: t(restartBlockedNoticeKey(restartBlocked))
|
|
9269
10667
|
})
|
|
@@ -9271,35 +10669,35 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9271
10669
|
})
|
|
9272
10670
|
}),
|
|
9273
10671
|
selfUpdate.view.kind === "failed" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9274
|
-
className:
|
|
10672
|
+
className: _dsh_plugin_shop_css_e3675a89_default.selfUpdatePanel,
|
|
9275
10673
|
"data-shop-self-update-failed": true,
|
|
9276
10674
|
children: [
|
|
9277
10675
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9278
|
-
className:
|
|
10676
|
+
className: _dsh_plugin_shop_css_e3675a89_default.failedHeading,
|
|
9279
10677
|
children: t("updateFailed")
|
|
9280
10678
|
}),
|
|
9281
10679
|
selfUpdate.view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9282
|
-
className:
|
|
10680
|
+
className: _dsh_plugin_shop_css_e3675a89_default.log,
|
|
9283
10681
|
children: selfUpdate.view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9284
|
-
className:
|
|
10682
|
+
className: _dsh_plugin_shop_css_e3675a89_default.logLine,
|
|
9285
10683
|
"data-shop-log-line": true,
|
|
9286
10684
|
children: line
|
|
9287
10685
|
}, index))
|
|
9288
10686
|
}),
|
|
9289
10687
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9290
|
-
className:
|
|
10688
|
+
className: _dsh_plugin_shop_css_e3675a89_default.failedDetail,
|
|
9291
10689
|
children: selfUpdate.view.detail === "" ? t("updateTransportFailed") : selfUpdate.view.detail
|
|
9292
10690
|
})
|
|
9293
10691
|
]
|
|
9294
10692
|
}),
|
|
9295
10693
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9296
|
-
className:
|
|
10694
|
+
className: _dsh_plugin_shop_css_e3675a89_default.categoryBar,
|
|
9297
10695
|
role: "group",
|
|
9298
10696
|
"aria-label": t("catalog"),
|
|
9299
10697
|
children: [
|
|
9300
10698
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
9301
10699
|
type: "button",
|
|
9302
|
-
className: category === null ? `${
|
|
10700
|
+
className: category === null ? `${_dsh_plugin_shop_css_e3675a89_default.categoryButton} ${_dsh_plugin_shop_css_e3675a89_default.categoryButtonOn}` : _dsh_plugin_shop_css_e3675a89_default.categoryButton,
|
|
9303
10701
|
"aria-pressed": category === null,
|
|
9304
10702
|
onClick: () => {
|
|
9305
10703
|
setCategory(null);
|
|
@@ -9314,7 +10712,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9314
10712
|
}),
|
|
9315
10713
|
CATEGORY_ORDER.map((key) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
9316
10714
|
type: "button",
|
|
9317
|
-
className: category === key ? `${
|
|
10715
|
+
className: category === key ? `${_dsh_plugin_shop_css_e3675a89_default.categoryButton} ${_dsh_plugin_shop_css_e3675a89_default.categoryButtonOn}` : _dsh_plugin_shop_css_e3675a89_default.categoryButton,
|
|
9318
10716
|
"aria-pressed": category === key,
|
|
9319
10717
|
"data-category": key,
|
|
9320
10718
|
"data-shop-category-tab": key,
|
|
@@ -9330,7 +10728,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9330
10728
|
}, key)),
|
|
9331
10729
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
9332
10730
|
type: "button",
|
|
9333
|
-
className: category === "installed" ? `${
|
|
10731
|
+
className: category === "installed" ? `${_dsh_plugin_shop_css_e3675a89_default.categoryButton} ${_dsh_plugin_shop_css_e3675a89_default.categoryButtonOn}` : _dsh_plugin_shop_css_e3675a89_default.categoryButton,
|
|
9334
10732
|
"aria-pressed": category === "installed",
|
|
9335
10733
|
"data-shop-category-installed": true,
|
|
9336
10734
|
onClick: () => {
|
|
@@ -9347,7 +10745,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9347
10745
|
type: "button",
|
|
9348
10746
|
role: "switch",
|
|
9349
10747
|
"aria-checked": hideIncompatible,
|
|
9350
|
-
className: hideIncompatible ? `${
|
|
10748
|
+
className: hideIncompatible ? `${_dsh_plugin_shop_css_e3675a89_default.incompatibleFilter} ${_dsh_plugin_shop_css_e3675a89_default.incompatibleFilterOn}` : _dsh_plugin_shop_css_e3675a89_default.incompatibleFilter,
|
|
9351
10749
|
title: t(hideIncompatible ? "showIncompatibleTitle" : "incompatibleFilterTitle"),
|
|
9352
10750
|
"data-shop-hide-incompatible": true,
|
|
9353
10751
|
onClick: () => {
|
|
@@ -9355,18 +10753,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9355
10753
|
setVisibleCount(48);
|
|
9356
10754
|
},
|
|
9357
10755
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9358
|
-
className: hideIncompatible ? `${
|
|
10756
|
+
className: hideIncompatible ? `${_dsh_plugin_shop_css_e3675a89_default.switch} ${_dsh_plugin_shop_css_e3675a89_default.switchOn}` : _dsh_plugin_shop_css_e3675a89_default.switch,
|
|
9359
10757
|
"aria-hidden": "true",
|
|
9360
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className:
|
|
10758
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_e3675a89_default.switchKnob })
|
|
9361
10759
|
}), t("hideIncompatible", { count: incompatibleCount })]
|
|
9362
10760
|
})
|
|
9363
10761
|
]
|
|
9364
10762
|
}),
|
|
9365
10763
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9366
|
-
className:
|
|
10764
|
+
className: _dsh_plugin_shop_css_e3675a89_default.catalogStatsRow,
|
|
9367
10765
|
children: [
|
|
9368
10766
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9369
|
-
className:
|
|
10767
|
+
className: _dsh_plugin_shop_css_e3675a89_default.catalogStats,
|
|
9370
10768
|
"data-shop-catalog-stats": true,
|
|
9371
10769
|
children: t("catalogStats", {
|
|
9372
10770
|
count: String(browsable.length),
|
|
@@ -9375,7 +10773,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9375
10773
|
}),
|
|
9376
10774
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
9377
10775
|
type: "button",
|
|
9378
|
-
className:
|
|
10776
|
+
className: _dsh_plugin_shop_css_e3675a89_default.catalogRefreshButton,
|
|
9379
10777
|
"data-shop-catalog-refresh": true,
|
|
9380
10778
|
disabled: reloading,
|
|
9381
10779
|
onClick: () => {
|
|
@@ -9387,21 +10785,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9387
10785
|
children: reloading ? t("refreshing") : t("refresh")
|
|
9388
10786
|
}),
|
|
9389
10787
|
reloadFailed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9390
|
-
className:
|
|
10788
|
+
className: _dsh_plugin_shop_css_e3675a89_default.catalogRefreshFailed,
|
|
9391
10789
|
"data-shop-catalog-refresh-failed": true,
|
|
9392
10790
|
children: t("refreshFailed")
|
|
9393
10791
|
})
|
|
9394
10792
|
]
|
|
9395
10793
|
}),
|
|
9396
10794
|
result.plugins.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9397
|
-
className:
|
|
10795
|
+
className: _dsh_plugin_shop_css_e3675a89_default.emptyLine,
|
|
9398
10796
|
children: t("empty")
|
|
9399
10797
|
}) : filtered.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9400
|
-
className:
|
|
10798
|
+
className: _dsh_plugin_shop_css_e3675a89_default.emptyLine,
|
|
9401
10799
|
"data-shop-empty": true,
|
|
9402
10800
|
children: matched.length > 0 ? t("emptyIncompatibleFiltered") : t("emptySearch")
|
|
9403
10801
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ul", {
|
|
9404
|
-
className:
|
|
10802
|
+
className: _dsh_plugin_shop_css_e3675a89_default.cards,
|
|
9405
10803
|
children: [visible.map((entry) => {
|
|
9406
10804
|
const key = entryKey(entry);
|
|
9407
10805
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EntryCard, {
|
|
@@ -9418,16 +10816,17 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9418
10816
|
restartBlocked,
|
|
9419
10817
|
reload,
|
|
9420
10818
|
setEnabled,
|
|
9421
|
-
inInstalledView: category === "installed"
|
|
10819
|
+
inInstalledView: category === "installed",
|
|
10820
|
+
restarts
|
|
9422
10821
|
}) }, key);
|
|
9423
10822
|
}), incremental && visibleCount < filtered.length && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
|
|
9424
10823
|
ref: sentinelRef,
|
|
9425
|
-
className:
|
|
10824
|
+
className: _dsh_plugin_shop_css_e3675a89_default.cardsSentry,
|
|
9426
10825
|
"data-shop-sentry": true,
|
|
9427
10826
|
"aria-hidden": "true"
|
|
9428
10827
|
})]
|
|
9429
10828
|
}), incremental && visibleCount < filtered.length && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9430
|
-
className:
|
|
10829
|
+
className: _dsh_plugin_shop_css_e3675a89_default.showingLine,
|
|
9431
10830
|
"aria-live": "polite",
|
|
9432
10831
|
children: t("showing", {
|
|
9433
10832
|
shown: String(visibleCount),
|
|
@@ -9444,6 +10843,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9444
10843
|
flowFor: flows.flowFor,
|
|
9445
10844
|
restart,
|
|
9446
10845
|
restartBlocked,
|
|
10846
|
+
reload,
|
|
10847
|
+
restarts
|
|
10848
|
+
}),
|
|
10849
|
+
category === "installed" && installedState.kind === "ready" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ShadowedSection, {
|
|
10850
|
+
entries: installedState.entries,
|
|
10851
|
+
t,
|
|
10852
|
+
uninstallFlows,
|
|
10853
|
+
restart,
|
|
10854
|
+
restartBlocked,
|
|
9447
10855
|
reload
|
|
9448
10856
|
})
|
|
9449
10857
|
]
|
|
@@ -9518,7 +10926,23 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9518
10926
|
};
|
|
9519
10927
|
warmed.catch(() => {});
|
|
9520
10928
|
Promise.resolve(ns.installed()).then((result) => unwrap(result)).catch(() => {});
|
|
9521
|
-
|
|
10929
|
+
let lastVersion = null;
|
|
10930
|
+
const restartListeners = /* @__PURE__ */ new Set();
|
|
10931
|
+
const version = async () => {
|
|
10932
|
+
const result = unwrap(await ns.version());
|
|
10933
|
+
noteBoot(result.bootId);
|
|
10934
|
+
const previous = lastVersion;
|
|
10935
|
+
lastVersion = result;
|
|
10936
|
+
if (previous !== null && answeredByAnotherProcess(previous, result)) {
|
|
10937
|
+
warmCatalog = null;
|
|
10938
|
+
for (const listener of [...restartListeners]) listener(result);
|
|
10939
|
+
}
|
|
10940
|
+
return result;
|
|
10941
|
+
};
|
|
10942
|
+
version().catch(() => {});
|
|
10943
|
+
ctx.on("connection/reset", () => {
|
|
10944
|
+
version().catch(() => {});
|
|
10945
|
+
});
|
|
9522
10946
|
const hostCatalog = async (args) => {
|
|
9523
10947
|
if (args?.refresh === true) {
|
|
9524
10948
|
const refreshed = unwrap(await ns.catalog({ refresh: true }));
|
|
@@ -9572,7 +10996,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9572
10996
|
pageRemoved.add(name);
|
|
9573
10997
|
},
|
|
9574
10998
|
restart,
|
|
9575
|
-
version
|
|
10999
|
+
version,
|
|
11000
|
+
onRestarted: (listener) => {
|
|
11001
|
+
restartListeners.add(listener);
|
|
11002
|
+
return () => {
|
|
11003
|
+
restartListeners.delete(listener);
|
|
11004
|
+
};
|
|
11005
|
+
},
|
|
9576
11006
|
updateStart: async (args) => {
|
|
9577
11007
|
warmCatalog = null;
|
|
9578
11008
|
return unwrap(await ns.updateStart(args));
|