dsh-plugin-shop 0.4.1 → 0.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/lib/client.js +435 -89
- package/lib/index.js +264 -36
- package/lib/typert.host.js +105 -20
- package/lib/typert.remote-client.d.ts +7 -3
- package/lib/typert.remote-client.js +73 -14
- package/lib/types/client/ShopTab.d.ts +6 -2
- package/lib/types/client/index.d.ts +3 -3
- package/lib/types/client/locales.d.ts +24 -0
- package/lib/types/client/useInstall.d.ts +9 -0
- package/lib/types/client/useUninstall.d.ts +21 -0
- package/lib/types/host/executor.d.ts +18 -6
- package/lib/types/host/index.d.ts +65 -4
- package/lib/types/host/restart.d.ts +35 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -4306,6 +4306,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4306
4306
|
})),
|
|
4307
4307
|
"stars": record(string(), number())
|
|
4308
4308
|
});
|
|
4309
|
+
const dsh_plugin_shop_shop_installed_result$schema = array(object({
|
|
4310
|
+
"name": string(),
|
|
4311
|
+
"installed": string(),
|
|
4312
|
+
"latest": string(),
|
|
4313
|
+
"outdated": boolean()
|
|
4314
|
+
}));
|
|
4309
4315
|
const dsh_plugin_shop_shop_installStart_parameter_0$schema = object({
|
|
4310
4316
|
"name": string(),
|
|
4311
4317
|
"version": string(),
|
|
@@ -4344,11 +4350,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4344
4350
|
]).optional(),
|
|
4345
4351
|
"detail": union([_undefined(), string()]).optional()
|
|
4346
4352
|
});
|
|
4347
|
-
const
|
|
4348
|
-
"
|
|
4349
|
-
"
|
|
4350
|
-
|
|
4351
|
-
|
|
4353
|
+
const dsh_plugin_shop_shop_restart_result$schema = union([object({
|
|
4354
|
+
"ok": literal(true),
|
|
4355
|
+
"url": string()
|
|
4356
|
+
}), object({
|
|
4357
|
+
"ok": literal(false),
|
|
4358
|
+
"detail": string()
|
|
4359
|
+
})]);
|
|
4352
4360
|
const dsh_plugin_shop_shop_setEnabled_parameter_0$schema = object({
|
|
4353
4361
|
"name": string(),
|
|
4354
4362
|
"enabled": boolean()
|
|
@@ -4357,6 +4365,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4357
4365
|
"ok": boolean(),
|
|
4358
4366
|
"detail": union([_undefined(), string()]).optional()
|
|
4359
4367
|
});
|
|
4368
|
+
const dsh_plugin_shop_shop_uninstallStart_parameter_0$schema = object({ "name": string() });
|
|
4369
|
+
const dsh_plugin_shop_shop_uninstallStart_result$schema = union([object({
|
|
4370
|
+
"ok": literal(true),
|
|
4371
|
+
"installId": string()
|
|
4372
|
+
}), object({
|
|
4373
|
+
"ok": literal(false),
|
|
4374
|
+
"detail": string()
|
|
4375
|
+
})]);
|
|
4360
4376
|
const TYPERT_REMOTE = {
|
|
4361
4377
|
package: "dsh-plugin-shop",
|
|
4362
4378
|
descriptors: [
|
|
@@ -4384,7 +4400,25 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4384
4400
|
},
|
|
4385
4401
|
sourceLocation: {
|
|
4386
4402
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4387
|
-
"line":
|
|
4403
|
+
"line": 218,
|
|
4404
|
+
"column": 9
|
|
4405
|
+
}
|
|
4406
|
+
},
|
|
4407
|
+
{
|
|
4408
|
+
id: "dsh-plugin-shop#shop/installed",
|
|
4409
|
+
service: "shop",
|
|
4410
|
+
namespace: "shop",
|
|
4411
|
+
method: "installed",
|
|
4412
|
+
invocation: { kind: "direct" },
|
|
4413
|
+
parameters: [],
|
|
4414
|
+
result: {
|
|
4415
|
+
mode: "strict",
|
|
4416
|
+
typeSymbol: "dsh-plugin-shop#shop/installed:result",
|
|
4417
|
+
schema: dsh_plugin_shop_shop_installed_result$schema
|
|
4418
|
+
},
|
|
4419
|
+
sourceLocation: {
|
|
4420
|
+
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4421
|
+
"line": 293,
|
|
4388
4422
|
"column": 9
|
|
4389
4423
|
}
|
|
4390
4424
|
},
|
|
@@ -4412,7 +4446,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4412
4446
|
},
|
|
4413
4447
|
sourceLocation: {
|
|
4414
4448
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4415
|
-
"line":
|
|
4449
|
+
"line": 244,
|
|
4416
4450
|
"column": 9
|
|
4417
4451
|
}
|
|
4418
4452
|
},
|
|
@@ -4439,25 +4473,25 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4439
4473
|
},
|
|
4440
4474
|
sourceLocation: {
|
|
4441
4475
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4442
|
-
"line":
|
|
4476
|
+
"line": 282,
|
|
4443
4477
|
"column": 3
|
|
4444
4478
|
}
|
|
4445
4479
|
},
|
|
4446
4480
|
{
|
|
4447
|
-
id: "dsh-plugin-shop#shop/
|
|
4481
|
+
id: "dsh-plugin-shop#shop/restart",
|
|
4448
4482
|
service: "shop",
|
|
4449
4483
|
namespace: "shop",
|
|
4450
|
-
method: "
|
|
4484
|
+
method: "restart",
|
|
4451
4485
|
invocation: { kind: "direct" },
|
|
4452
4486
|
parameters: [],
|
|
4453
4487
|
result: {
|
|
4454
4488
|
mode: "strict",
|
|
4455
|
-
typeSymbol: "dsh-plugin-shop#
|
|
4456
|
-
schema:
|
|
4489
|
+
typeSymbol: "dsh-plugin-shop/types#ShopRestartResult",
|
|
4490
|
+
schema: dsh_plugin_shop_shop_restart_result$schema
|
|
4457
4491
|
},
|
|
4458
4492
|
sourceLocation: {
|
|
4459
4493
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4460
|
-
"line":
|
|
4494
|
+
"line": 367,
|
|
4461
4495
|
"column": 9
|
|
4462
4496
|
}
|
|
4463
4497
|
},
|
|
@@ -4484,9 +4518,37 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4484
4518
|
},
|
|
4485
4519
|
sourceLocation: {
|
|
4486
4520
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4487
|
-
"line":
|
|
4521
|
+
"line": 187,
|
|
4488
4522
|
"column": 3
|
|
4489
4523
|
}
|
|
4524
|
+
},
|
|
4525
|
+
{
|
|
4526
|
+
id: "dsh-plugin-shop#shop/uninstallStart",
|
|
4527
|
+
service: "shop",
|
|
4528
|
+
namespace: "shop",
|
|
4529
|
+
method: "uninstallStart",
|
|
4530
|
+
implementation: "uninstall",
|
|
4531
|
+
invocation: { kind: "direct" },
|
|
4532
|
+
parameters: [{
|
|
4533
|
+
name: "args",
|
|
4534
|
+
wire: "args",
|
|
4535
|
+
source: "json",
|
|
4536
|
+
codec: {
|
|
4537
|
+
mode: "strict",
|
|
4538
|
+
typeSymbol: "dsh-plugin-shop#shop/uninstallStart:args",
|
|
4539
|
+
schema: dsh_plugin_shop_shop_uninstallStart_parameter_0$schema
|
|
4540
|
+
}
|
|
4541
|
+
}],
|
|
4542
|
+
result: {
|
|
4543
|
+
mode: "strict",
|
|
4544
|
+
typeSymbol: "dsh-plugin-shop/types#ShopUninstallResult",
|
|
4545
|
+
schema: dsh_plugin_shop_shop_uninstallStart_result$schema
|
|
4546
|
+
},
|
|
4547
|
+
sourceLocation: {
|
|
4548
|
+
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4549
|
+
"line": 333,
|
|
4550
|
+
"column": 9
|
|
4551
|
+
}
|
|
4490
4552
|
}
|
|
4491
4553
|
]
|
|
4492
4554
|
};
|
|
@@ -4691,6 +4753,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4691
4753
|
installedVersion: "已安装 v{version}",
|
|
4692
4754
|
latestVersion: "最新 v{version}",
|
|
4693
4755
|
update: "更新",
|
|
4756
|
+
installed: "已安装",
|
|
4757
|
+
uninstall: "卸载",
|
|
4758
|
+
uninstalling: "正在卸载…",
|
|
4759
|
+
uninstallFailed: "卸载失败",
|
|
4760
|
+
uninstallTransportFailed: "卸载请求未能送达。请稍后重试。",
|
|
4761
|
+
uninstalledRestartNotice: "已卸载;重启 dsh 后生效",
|
|
4762
|
+
restart: "重启 dsh",
|
|
4763
|
+
restartTitle: "确认重启 dsh",
|
|
4764
|
+
restartBody: "重启会断开当前页面与服务器的连接,正在进行的对话和任务会中断;浏览器会自动跳转到重启后的地址,稍等片刻即可继续使用。",
|
|
4765
|
+
restartConfirm: "重启",
|
|
4766
|
+
restarting: "正在重启,浏览器即将跳转…",
|
|
4767
|
+
restartTransportFailed: "重启请求未能送达。请稍后重试。",
|
|
4694
4768
|
enabledSwitch: "启用",
|
|
4695
4769
|
toggleFailed: "设置失败,请重试。",
|
|
4696
4770
|
hotApplyNote: "无需重启,立即生效"
|
|
@@ -4744,6 +4818,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4744
4818
|
installedVersion: "installed v{version}",
|
|
4745
4819
|
latestVersion: "latest v{version}",
|
|
4746
4820
|
update: "Update",
|
|
4821
|
+
installed: "Installed",
|
|
4822
|
+
uninstall: "Uninstall",
|
|
4823
|
+
uninstalling: "Uninstalling…",
|
|
4824
|
+
uninstallFailed: "Uninstall failed",
|
|
4825
|
+
uninstallTransportFailed: "The uninstall request could not be delivered. Please retry.",
|
|
4826
|
+
uninstalledRestartNotice: "uninstalled; restart dsh to activate",
|
|
4827
|
+
restart: "Restart dsh",
|
|
4828
|
+
restartTitle: "Restart dsh?",
|
|
4829
|
+
restartBody: "Restarting disconnects this page and interrupts any conversation or task in progress; the browser will jump to the restarted address automatically, so give it a few seconds.",
|
|
4830
|
+
restartConfirm: "Restart",
|
|
4831
|
+
restarting: "Restarting; the browser will jump in a moment…",
|
|
4832
|
+
restartTransportFailed: "The restart request could not be delivered. Please retry.",
|
|
4747
4833
|
enabledSwitch: "Enable",
|
|
4748
4834
|
toggleFailed: "Failed to update. Please retry.",
|
|
4749
4835
|
hotApplyNote: "takes effect without a restart"
|
|
@@ -4751,6 +4837,29 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4751
4837
|
//#endregion
|
|
4752
4838
|
//#region src/client/useInstall.ts
|
|
4753
4839
|
/** Install driving hook for one entry: start, poll to terminal, reset. */
|
|
4840
|
+
/** The poll loop shared by the install and uninstall drivers: while the view
|
|
4841
|
+
* is `running`, poll once per second and fold each status through the
|
|
4842
|
+
* reducer. A poll failure is transient — the host retains the record, so the
|
|
4843
|
+
* next tick finds it. The rejection handler must be present: an unhandled
|
|
4844
|
+
* rejection here would escape the poll loop. */
|
|
4845
|
+
function usePollStatus(view, setView, installStatus) {
|
|
4846
|
+
(0, react.useEffect)(() => {
|
|
4847
|
+
if (view.kind !== "running") return;
|
|
4848
|
+
const timer = setInterval(() => {
|
|
4849
|
+
installStatus({ installId: view.installId }).then((status) => {
|
|
4850
|
+
setView((current) => reduceInstall(current, {
|
|
4851
|
+
type: "status",
|
|
4852
|
+
status
|
|
4853
|
+
}));
|
|
4854
|
+
}, () => {});
|
|
4855
|
+
}, INSTALL_POLL_MS);
|
|
4856
|
+
return () => clearInterval(timer);
|
|
4857
|
+
}, [
|
|
4858
|
+
view,
|
|
4859
|
+
setView,
|
|
4860
|
+
installStatus
|
|
4861
|
+
]);
|
|
4862
|
+
}
|
|
4754
4863
|
/** Drive one install: rejections, the polling loop at INSTALL_POLL_MS, and
|
|
4755
4864
|
* terminal states. The interval is cleared on unmount and whenever the view
|
|
4756
4865
|
* leaves `running`, so a done/failed/rejected install never polls again. */
|
|
@@ -4781,18 +4890,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4781
4890
|
});
|
|
4782
4891
|
}
|
|
4783
4892
|
}, [install]);
|
|
4784
|
-
(
|
|
4785
|
-
if (view.kind !== "running") return;
|
|
4786
|
-
const timer = setInterval(() => {
|
|
4787
|
-
installStatus({ installId: view.installId }).then((status) => {
|
|
4788
|
-
setView((current) => reduceInstall(current, {
|
|
4789
|
-
type: "status",
|
|
4790
|
-
status
|
|
4791
|
-
}));
|
|
4792
|
-
}, () => {});
|
|
4793
|
-
}, INSTALL_POLL_MS);
|
|
4794
|
-
return () => clearInterval(timer);
|
|
4795
|
-
}, [view, installStatus]);
|
|
4893
|
+
usePollStatus(view, setView, installStatus);
|
|
4796
4894
|
return {
|
|
4797
4895
|
view,
|
|
4798
4896
|
start,
|
|
@@ -4800,8 +4898,51 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4800
4898
|
};
|
|
4801
4899
|
}
|
|
4802
4900
|
//#endregion
|
|
4901
|
+
//#region src/client/useUninstall.ts
|
|
4902
|
+
/** Uninstall driving hook for one entry: start, poll to terminal. Shares the
|
|
4903
|
+
* install view machine and poll loop; the only difference is the start
|
|
4904
|
+
* mapping — an uninstall business failure (not in the catalog / not
|
|
4905
|
+
* installed) lands in the `failed` view with the host's published detail,
|
|
4906
|
+
* never in the install `rejected` codes, which belong to the install gate
|
|
4907
|
+
* (§7.2). */
|
|
4908
|
+
/** Drive one uninstall: the host's business union, the shared polling loop,
|
|
4909
|
+
* and terminal states. The `rejected` install state never occurs here. */
|
|
4910
|
+
function useUninstall(uninstall, installStatus) {
|
|
4911
|
+
const [view, setView] = (0, react.useState)({ kind: "idle" });
|
|
4912
|
+
const start = (0, react.useCallback)(async (args) => {
|
|
4913
|
+
setView({ kind: "idle" });
|
|
4914
|
+
try {
|
|
4915
|
+
const result = await uninstall(args);
|
|
4916
|
+
if (!result.ok) {
|
|
4917
|
+
setView({
|
|
4918
|
+
kind: "failed",
|
|
4919
|
+
detail: result.detail,
|
|
4920
|
+
log: []
|
|
4921
|
+
});
|
|
4922
|
+
return;
|
|
4923
|
+
}
|
|
4924
|
+
setView({
|
|
4925
|
+
kind: "running",
|
|
4926
|
+
installId: result.installId,
|
|
4927
|
+
log: []
|
|
4928
|
+
});
|
|
4929
|
+
} catch {
|
|
4930
|
+
setView({
|
|
4931
|
+
kind: "failed",
|
|
4932
|
+
detail: "",
|
|
4933
|
+
log: []
|
|
4934
|
+
});
|
|
4935
|
+
}
|
|
4936
|
+
}, [uninstall]);
|
|
4937
|
+
usePollStatus(view, setView, installStatus);
|
|
4938
|
+
return {
|
|
4939
|
+
view,
|
|
4940
|
+
start
|
|
4941
|
+
};
|
|
4942
|
+
}
|
|
4943
|
+
//#endregion
|
|
4803
4944
|
//#region \0dsh-plugin-shop-css:e3675a89
|
|
4804
|
-
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 (six fixed hues, not the brand token — the brand resolves to\n * near-black in the light theme, so six brand opacities read as six shades\n * 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}\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: repeat(auto-fill, minmax(260px, 1fr));\n gap: 12px;\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.s30e662_skeletonCover {\n display: block;\n height: 26px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 10%, transparent);\n}\n\n.s8c7d6f_skeletonName {\n width: 55%;\n height: 14px;\n margin: 12px 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 14px;\n border-radius: 4px;\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.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.s1555aa_staleBlock {\n display: flex;\n align-items: center;\n gap: 8px;\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 gap: 6px;\n margin-top: 10px;\n}\n\n.s26d58e_categoryButton {\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.s26d58e_categoryButton: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.s5f68c4_categoryButtonOn {\n border-color: var(--dsw-alias-brand-primary);\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n color: var(--dsw-alias-brand-primary);\n font-weight: 600;\n}\n\n.s26d58e_categoryButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.s668774_catalogHeading {\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.sccc4d1_catalogStats {\n margin: -8px 0 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.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.sf657f5_cards {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));\n gap: 12px;\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 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 240px;\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(--spine-hue, #8B8E96);\n}\n\n.s65f383_card[data-category='tool'] { --spine-hue: #4C8DFF; }\n.s65f383_card[data-category='provider'] { --spine-hue: #A78BFA; }\n.s65f383_card[data-category='ui'] { --spine-hue: #2DD4BF; }\n.s65f383_card[data-category='workflow'] { --spine-hue: #F59E0B; }\n.s65f383_card[data-category='integration'] { --spine-hue: #34D399; }\n.s65f383_card[data-category='other'] { --spine-hue: #8B8E96; }\n\n.s9e2bec_cardCover {\n display: block;\n padding: 5px 12px 5px 12px;\n border-bottom: 1px solid var(--dsw-alias-border-l1);\n background: color-mix(in srgb, var(--spine-hue, #8B8E96) 12%, transparent);\n}\n\n.s1a7c5b_coverLabel {\n display: block;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--spine-hue, #8B8E96);\n}\n\n.sa97234_entryHeader {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: 8px;\n padding: 10px 12px 6px;\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 owns the full header width; badges sit on their own line below. */\n.sc3f149_name {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 13px;\n font-weight: 600;\n overflow-wrap: anywhere;\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.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.s23e3e1_unclaimedBadge {\n padding: 1px 7px;\n border-radius: 999px;\n border: 1px dashed var(--dsw-alias-border-l2);\n font-size: 11px;\n line-height: 1.6;\n color: var(--dsw-alias-label-secondary);\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 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.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.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.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.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/* Installed section. */\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 font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sa1f556_outdatedActions {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n/* The enable/disable switch. */\n.s11c019_switch {\n position: relative;\n width: 32px;\n height: 18px;\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.s7580e8_switchOn {\n border-color: var(--dsw-alias-state-success-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 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";
|
|
4945
|
+
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 (six fixed hues, not the brand token — the brand resolves to\n * near-black in the light theme, so six brand opacities read as six shades\n * 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}\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: repeat(auto-fill, minmax(260px, 1fr));\n gap: 12px;\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.s30e662_skeletonCover {\n display: block;\n height: 26px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 10%, transparent);\n}\n\n.s8c7d6f_skeletonName {\n width: 55%;\n height: 14px;\n margin: 12px 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 14px;\n border-radius: 4px;\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.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.s1555aa_staleBlock {\n display: flex;\n align-items: center;\n gap: 8px;\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 gap: 6px;\n margin-top: 6px;\n}\n\n.s26d58e_categoryButton {\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.s26d58e_categoryButton: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.s5f68c4_categoryButtonOn {\n border-color: var(--dsw-alias-brand-primary);\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n color: var(--dsw-alias-brand-primary);\n font-weight: 600;\n}\n\n.s26d58e_categoryButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.s668774_catalogHeading {\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.sccc4d1_catalogStats {\n margin: 6px 0 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.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.sf657f5_cards {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));\n gap: 12px;\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 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 240px;\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(--spine-hue, #8B8E96);\n}\n\n.s65f383_card[data-category='tool'] { --spine-hue: #4C8DFF; }\n.s65f383_card[data-category='provider'] { --spine-hue: #A78BFA; }\n.s65f383_card[data-category='ui'] { --spine-hue: #2DD4BF; }\n.s65f383_card[data-category='workflow'] { --spine-hue: #F59E0B; }\n.s65f383_card[data-category='integration'] { --spine-hue: #34D399; }\n.s65f383_card[data-category='other'] { --spine-hue: #8B8E96; }\n\n.s9e2bec_cardCover {\n display: block;\n padding: 5px 12px 5px 12px;\n border-bottom: 1px solid var(--dsw-alias-border-l1);\n background: color-mix(in srgb, var(--spine-hue, #8B8E96) 12%, transparent);\n}\n\n.s1a7c5b_coverLabel {\n display: block;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--spine-hue, #8B8E96);\n}\n\n.sa97234_entryHeader {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: 8px;\n padding: 10px 12px 6px;\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 owns the full header width; badges sit on their own line below. */\n.sc3f149_name {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 13px;\n font-weight: 600;\n overflow-wrap: anywhere;\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.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.s23e3e1_unclaimedBadge {\n padding: 1px 7px;\n border-radius: 999px;\n border: 1px dashed var(--dsw-alias-border-l2);\n font-size: 11px;\n line-height: 1.6;\n color: var(--dsw-alias-label-secondary);\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 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.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.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.s6ac237_restartButton {\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 border-color: var(--dsw-alias-label-secondary);\n}\n\n.s6ac237_restartButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\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.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/* Installed section. */\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 font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sa1f556_outdatedActions {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n/* The enable/disable switch. */\n.s11c019_switch {\n position: relative;\n width: 32px;\n height: 18px;\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.s7580e8_switchOn {\n border-color: var(--dsw-alias-state-success-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 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";
|
|
4805
4946
|
if (typeof document !== "undefined" && !document.querySelector("style[data-plugin-css=\"e3675a89\"]")) {
|
|
4806
4947
|
const tag = document.createElement("style");
|
|
4807
4948
|
tag.dataset.pluginCss = "e3675a89";
|
|
@@ -4864,6 +5005,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4864
5005
|
"installButton": "se7aaed_installButton",
|
|
4865
5006
|
"confirmButton": "sb43a49_confirmButton",
|
|
4866
5007
|
"cancelButton": "s6e45d1_cancelButton",
|
|
5008
|
+
"installedLabel": "sb6fc74_installedLabel",
|
|
5009
|
+
"installedActions": "sf53ca0_installedActions",
|
|
5010
|
+
"uninstallButton": "s96967f_uninstallButton",
|
|
5011
|
+
"restartButton": "s6ac237_restartButton",
|
|
4867
5012
|
"gate": "sc4f2ac_gate",
|
|
4868
5013
|
"gateTitle": "s559c38_gateTitle",
|
|
4869
5014
|
"gateBody": "s3c0ace_gateBody",
|
|
@@ -4909,8 +5054,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4909
5054
|
/** One entry card: the category spine and cover band, the expandable header
|
|
4910
5055
|
* (name, tier, category, unclaimed marker), the plain-text summary in both
|
|
4911
5056
|
* languages, the self-declared capabilities, the detail section, and the
|
|
4912
|
-
* install controls.
|
|
4913
|
-
|
|
5057
|
+
* install controls. An installed plugin's card carries its installed row:
|
|
5058
|
+
* current → the non-interactive installed label, behind → the update button;
|
|
5059
|
+
* uninstalled → the install button. */
|
|
5060
|
+
const EntryCard = (0, react.memo)(function EntryCard({ entry, stars, installed, t, install, installStatus, uninstall, restart }) {
|
|
4914
5061
|
const [open, setOpen] = (0, react.useState)(false);
|
|
4915
5062
|
const detailId = (0, react.useId)();
|
|
4916
5063
|
const summary = entry.catalog?.summary;
|
|
@@ -5016,13 +5163,36 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5016
5163
|
})
|
|
5017
5164
|
})]
|
|
5018
5165
|
}),
|
|
5019
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstallPanel, {
|
|
5166
|
+
installed === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstallPanel, {
|
|
5020
5167
|
name: entry.name,
|
|
5021
5168
|
version: entry.version,
|
|
5022
5169
|
tier: entry.tier,
|
|
5023
5170
|
t,
|
|
5024
5171
|
install,
|
|
5025
|
-
installStatus
|
|
5172
|
+
installStatus,
|
|
5173
|
+
restart
|
|
5174
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5175
|
+
className: _dsh_plugin_shop_css_e3675a89_default.installedActions,
|
|
5176
|
+
children: [installed.outdated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstallPanel, {
|
|
5177
|
+
name: entry.name,
|
|
5178
|
+
version: entry.version,
|
|
5179
|
+
tier: entry.tier,
|
|
5180
|
+
variant: "update",
|
|
5181
|
+
t,
|
|
5182
|
+
install,
|
|
5183
|
+
installStatus,
|
|
5184
|
+
restart
|
|
5185
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5186
|
+
className: _dsh_plugin_shop_css_e3675a89_default.installedLabel,
|
|
5187
|
+
"data-shop-installed": true,
|
|
5188
|
+
children: t("installed")
|
|
5189
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UninstallPanel, {
|
|
5190
|
+
name: entry.name,
|
|
5191
|
+
t,
|
|
5192
|
+
uninstall,
|
|
5193
|
+
installStatus,
|
|
5194
|
+
restart
|
|
5195
|
+
})]
|
|
5026
5196
|
})
|
|
5027
5197
|
]
|
|
5028
5198
|
})
|
|
@@ -5034,7 +5204,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5034
5204
|
* failure detail, rejection detail — driven by `useInstall`. Shared by the
|
|
5035
5205
|
* catalog cards (`variant: 'install'`) and the outdated rows' update button
|
|
5036
5206
|
* (`variant: 'update'`, which drives the same install flow for `name@latest`). */
|
|
5037
|
-
function InstallPanel({ name, version, tier, variant = "install", t, install, installStatus }) {
|
|
5207
|
+
function InstallPanel({ name, version, tier, variant = "install", t, install, installStatus, restart }) {
|
|
5038
5208
|
const [gateOpen, setGateOpen] = (0, react.useState)(false);
|
|
5039
5209
|
const { view, start } = useInstall(install, installStatus);
|
|
5040
5210
|
if (view.kind === "running") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -5050,10 +5220,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5050
5220
|
}, index))
|
|
5051
5221
|
})]
|
|
5052
5222
|
});
|
|
5053
|
-
if (view.kind === "done") return /* @__PURE__ */ (0, react_jsx_runtime.
|
|
5054
|
-
className: _dsh_plugin_shop_css_e3675a89_default.
|
|
5055
|
-
"
|
|
5056
|
-
|
|
5223
|
+
if (view.kind === "done") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5224
|
+
className: _dsh_plugin_shop_css_e3675a89_default.installedActions,
|
|
5225
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5226
|
+
className: _dsh_plugin_shop_css_e3675a89_default.notice,
|
|
5227
|
+
"data-shop-restart-notice": true,
|
|
5228
|
+
children: view.needsRestart ? t("installedRestartNotice") : t("installedNoRestartNotice")
|
|
5229
|
+
}), view.needsRestart && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RestartPanel, {
|
|
5230
|
+
t,
|
|
5231
|
+
restart
|
|
5232
|
+
})]
|
|
5057
5233
|
});
|
|
5058
5234
|
if (view.kind === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5059
5235
|
className: _dsh_plugin_shop_css_e3675a89_default.installPanel,
|
|
@@ -5136,13 +5312,146 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5136
5312
|
children: t(update ? "update" : "install")
|
|
5137
5313
|
});
|
|
5138
5314
|
}
|
|
5315
|
+
/** One installed entry's uninstall flow: remove from the profile through the
|
|
5316
|
+
* same executor records and poll loop as installs. Uninstalling revokes
|
|
5317
|
+
* privilege rather than granting it, so there is no acknowledgement gate —
|
|
5318
|
+
* §9.3 is about granting. A business failure (not in the catalog / not
|
|
5319
|
+
* installed) lands in the failed view with the host's published detail; a
|
|
5320
|
+
* transport failure carries the empty detail and the localized fallback. */
|
|
5321
|
+
function UninstallPanel({ name, t, uninstall, installStatus, restart }) {
|
|
5322
|
+
const { view, start } = useUninstall(uninstall, installStatus);
|
|
5323
|
+
if (view.kind === "running") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5324
|
+
className: _dsh_plugin_shop_css_e3675a89_default.installPanel,
|
|
5325
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5326
|
+
className: _dsh_plugin_shop_css_e3675a89_default.installing,
|
|
5327
|
+
children: t("uninstalling")
|
|
5328
|
+
}), view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5329
|
+
className: _dsh_plugin_shop_css_e3675a89_default.log,
|
|
5330
|
+
children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5331
|
+
className: _dsh_plugin_shop_css_e3675a89_default.logLine,
|
|
5332
|
+
children: line
|
|
5333
|
+
}, index))
|
|
5334
|
+
})]
|
|
5335
|
+
});
|
|
5336
|
+
if (view.kind === "done") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5337
|
+
className: _dsh_plugin_shop_css_e3675a89_default.installedActions,
|
|
5338
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5339
|
+
className: _dsh_plugin_shop_css_e3675a89_default.notice,
|
|
5340
|
+
"data-shop-uninstall-done": true,
|
|
5341
|
+
children: t("uninstalledRestartNotice")
|
|
5342
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RestartPanel, {
|
|
5343
|
+
t,
|
|
5344
|
+
restart
|
|
5345
|
+
})]
|
|
5346
|
+
});
|
|
5347
|
+
if (view.kind === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5348
|
+
className: _dsh_plugin_shop_css_e3675a89_default.installPanel,
|
|
5349
|
+
children: [
|
|
5350
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5351
|
+
className: _dsh_plugin_shop_css_e3675a89_default.failedHeading,
|
|
5352
|
+
children: t("uninstallFailed")
|
|
5353
|
+
}),
|
|
5354
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5355
|
+
className: _dsh_plugin_shop_css_e3675a89_default.log,
|
|
5356
|
+
children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5357
|
+
className: _dsh_plugin_shop_css_e3675a89_default.logLine,
|
|
5358
|
+
children: line
|
|
5359
|
+
}, index))
|
|
5360
|
+
}),
|
|
5361
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5362
|
+
className: _dsh_plugin_shop_css_e3675a89_default.failedDetail,
|
|
5363
|
+
children: view.detail === "" ? t("uninstallTransportFailed") : view.detail
|
|
5364
|
+
})
|
|
5365
|
+
]
|
|
5366
|
+
});
|
|
5367
|
+
if (view.kind !== "idle") return null;
|
|
5368
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5369
|
+
type: "button",
|
|
5370
|
+
className: _dsh_plugin_shop_css_e3675a89_default.uninstallButton,
|
|
5371
|
+
"data-shop-uninstall": true,
|
|
5372
|
+
onClick: () => void start({ name }),
|
|
5373
|
+
children: t("uninstall")
|
|
5374
|
+
});
|
|
5375
|
+
}
|
|
5376
|
+
/** The §8 restart flow (amendment 2026-08-27): after an install, update, or
|
|
5377
|
+
* uninstall reports done, this panel offers a restart of dsh. The
|
|
5378
|
+
* confirmation gate states the cost — the page disconnects and in-flight
|
|
5379
|
+
* conversations/tasks are interrupted — and on confirm the restart RPC runs;
|
|
5380
|
+
* the returned URL is where the browser jumps. A failed restart renders the
|
|
5381
|
+
* host's published detail: the old server is still up, nothing was lost. */
|
|
5382
|
+
function RestartPanel({ t, restart }) {
|
|
5383
|
+
const [gateOpen, setGateOpen] = (0, react.useState)(false);
|
|
5384
|
+
const [state, setState] = (0, react.useState)({ kind: "idle" });
|
|
5385
|
+
const onConfirm = async () => {
|
|
5386
|
+
setGateOpen(false);
|
|
5387
|
+
setState({ kind: "restarting" });
|
|
5388
|
+
try {
|
|
5389
|
+
const result = await restart();
|
|
5390
|
+
if (result.ok) window.location.href = result.url;
|
|
5391
|
+
else setState({
|
|
5392
|
+
kind: "failed",
|
|
5393
|
+
detail: result.detail
|
|
5394
|
+
});
|
|
5395
|
+
} catch {
|
|
5396
|
+
setState({
|
|
5397
|
+
kind: "failed",
|
|
5398
|
+
detail: t("restartTransportFailed")
|
|
5399
|
+
});
|
|
5400
|
+
}
|
|
5401
|
+
};
|
|
5402
|
+
if (state.kind === "restarting") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5403
|
+
className: _dsh_plugin_shop_css_e3675a89_default.notice,
|
|
5404
|
+
"data-shop-restarting": true,
|
|
5405
|
+
children: t("restarting")
|
|
5406
|
+
});
|
|
5407
|
+
if (state.kind === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5408
|
+
className: _dsh_plugin_shop_css_e3675a89_default.failedDetail,
|
|
5409
|
+
"data-shop-restart-error": true,
|
|
5410
|
+
children: state.detail
|
|
5411
|
+
});
|
|
5412
|
+
if (gateOpen) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5413
|
+
className: _dsh_plugin_shop_css_e3675a89_default.gate,
|
|
5414
|
+
children: [
|
|
5415
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5416
|
+
className: _dsh_plugin_shop_css_e3675a89_default.gateTitle,
|
|
5417
|
+
children: t("restartTitle")
|
|
5418
|
+
}),
|
|
5419
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5420
|
+
className: _dsh_plugin_shop_css_e3675a89_default.gateBody,
|
|
5421
|
+
children: t("restartBody")
|
|
5422
|
+
}),
|
|
5423
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5424
|
+
className: _dsh_plugin_shop_css_e3675a89_default.gateActions,
|
|
5425
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5426
|
+
type: "button",
|
|
5427
|
+
className: _dsh_plugin_shop_css_e3675a89_default.confirmButton,
|
|
5428
|
+
"data-shop-restart-confirm": true,
|
|
5429
|
+
onClick: () => void onConfirm(),
|
|
5430
|
+
children: t("restartConfirm")
|
|
5431
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5432
|
+
type: "button",
|
|
5433
|
+
className: _dsh_plugin_shop_css_e3675a89_default.cancelButton,
|
|
5434
|
+
onClick: () => setGateOpen(false),
|
|
5435
|
+
children: t("cancel")
|
|
5436
|
+
})]
|
|
5437
|
+
})
|
|
5438
|
+
]
|
|
5439
|
+
});
|
|
5440
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5441
|
+
type: "button",
|
|
5442
|
+
className: _dsh_plugin_shop_css_e3675a89_default.restartButton,
|
|
5443
|
+
"data-shop-restart": true,
|
|
5444
|
+
onClick: () => setGateOpen(true),
|
|
5445
|
+
children: t("restart")
|
|
5446
|
+
});
|
|
5447
|
+
}
|
|
5139
5448
|
/** One outdated install row (§7.3): the name, the installed and latest
|
|
5140
5449
|
* versions, an optimistic enable/disable switch, and the update button (the
|
|
5141
5450
|
* install flow for `name@latest`, reusing `InstallPanel`). The switch has no
|
|
5142
5451
|
* installed-state RPC behind it — the tab assumes an installed plugin is on,
|
|
5143
5452
|
* so the first toggle sends the inverted value. After a successful `setEnabled`
|
|
5144
5453
|
* the §8 hot note renders. */
|
|
5145
|
-
function OutdatedRow({ row, tier, t, setEnabled, install, installStatus }) {
|
|
5454
|
+
function OutdatedRow({ row, tier, t, setEnabled, install, installStatus, restart }) {
|
|
5146
5455
|
const [enabled, setEnabledState] = (0, react.useState)(true);
|
|
5147
5456
|
const [toggle, setToggle] = (0, react.useState)({ kind: "idle" });
|
|
5148
5457
|
const onToggle = async () => {
|
|
@@ -5207,7 +5516,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5207
5516
|
variant: "update",
|
|
5208
5517
|
t,
|
|
5209
5518
|
install,
|
|
5210
|
-
installStatus
|
|
5519
|
+
installStatus,
|
|
5520
|
+
restart
|
|
5211
5521
|
})]
|
|
5212
5522
|
}),
|
|
5213
5523
|
toggle.kind === "saved" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
@@ -5223,18 +5533,22 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5223
5533
|
]
|
|
5224
5534
|
});
|
|
5225
5535
|
}
|
|
5226
|
-
/** The §7.3
|
|
5227
|
-
* both versions, a switch, and an update button. The
|
|
5228
|
-
*
|
|
5229
|
-
*
|
|
5230
|
-
|
|
5536
|
+
/** The §7.3 installed list, rendered as the "installed" section: each row
|
|
5537
|
+
* shows both versions, a switch, and an update button. The rows are the
|
|
5538
|
+
* installed entries filtered to `outdated` — a current install is already
|
|
5539
|
+
* spoken for by its shelf card's installed label, and has no row here. The
|
|
5540
|
+
* tier for the update gate is looked up from the catalog by name (community →
|
|
5541
|
+
* acknowledgement); an entry absent from the catalog defaults to the
|
|
5542
|
+
* community gate (the safer read). */
|
|
5543
|
+
function OutdatedSection({ state, tiers, t, setEnabled, install, installStatus, restart }) {
|
|
5231
5544
|
if (state.kind === "loading") return null;
|
|
5232
5545
|
if (state.kind === "error") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5233
5546
|
className: _dsh_plugin_shop_css_e3675a89_default.stateLine,
|
|
5234
5547
|
"data-shop-outdated-error": true,
|
|
5235
5548
|
children: t("error")
|
|
5236
5549
|
});
|
|
5237
|
-
|
|
5550
|
+
const outdated = state.entries.filter((entry) => entry.outdated);
|
|
5551
|
+
if (outdated.length === 0) return null;
|
|
5238
5552
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
5239
5553
|
className: _dsh_plugin_shop_css_e3675a89_default.outdatedSection,
|
|
5240
5554
|
"data-shop-outdated": true,
|
|
@@ -5243,13 +5557,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5243
5557
|
children: t("installedSection")
|
|
5244
5558
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
5245
5559
|
className: _dsh_plugin_shop_css_e3675a89_default.outdatedList,
|
|
5246
|
-
children:
|
|
5560
|
+
children: outdated.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OutdatedRow, {
|
|
5247
5561
|
row,
|
|
5248
5562
|
tier: tiers.get(row.name) ?? "community",
|
|
5249
5563
|
t,
|
|
5250
5564
|
setEnabled,
|
|
5251
5565
|
install,
|
|
5252
|
-
installStatus
|
|
5566
|
+
installStatus,
|
|
5567
|
+
restart
|
|
5253
5568
|
}) }, row.name))
|
|
5254
5569
|
})]
|
|
5255
5570
|
});
|
|
@@ -5257,9 +5572,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5257
5572
|
/** The shop tab root: browse, search, refresh, and render one card per
|
|
5258
5573
|
* entry. Data attributes on the e2e-relevant nodes follow the Task 3 list. */
|
|
5259
5574
|
function ShopTab(props) {
|
|
5260
|
-
const { t, catalog, install, installStatus, setEnabled,
|
|
5575
|
+
const { t, catalog, install, installStatus, setEnabled, installed, uninstall, restart } = props;
|
|
5261
5576
|
const [catalogState, setCatalogState] = (0, react.useState)({ kind: "loading" });
|
|
5262
|
-
const [
|
|
5577
|
+
const [installedState, setInstalledState] = (0, react.useState)({ kind: "loading" });
|
|
5263
5578
|
const [request, setRequest] = (0, react.useState)({ kind: "initial" });
|
|
5264
5579
|
const [query, setQuery] = (0, react.useState)("");
|
|
5265
5580
|
const [category, setCategory] = (0, react.useState)(null);
|
|
@@ -5290,26 +5605,33 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5290
5605
|
let cancelled = false;
|
|
5291
5606
|
const load = async () => {
|
|
5292
5607
|
try {
|
|
5293
|
-
const entries = await
|
|
5294
|
-
if (!cancelled)
|
|
5608
|
+
const entries = await installed();
|
|
5609
|
+
if (!cancelled) setInstalledState({
|
|
5295
5610
|
kind: "ready",
|
|
5296
5611
|
entries
|
|
5297
5612
|
});
|
|
5298
5613
|
} catch {
|
|
5299
|
-
if (!cancelled)
|
|
5614
|
+
if (!cancelled) setInstalledState({ kind: "error" });
|
|
5300
5615
|
}
|
|
5301
5616
|
};
|
|
5302
5617
|
load();
|
|
5303
5618
|
return () => {
|
|
5304
5619
|
cancelled = true;
|
|
5305
5620
|
};
|
|
5306
|
-
}, [
|
|
5621
|
+
}, [installed, request]);
|
|
5622
|
+
const installedByName = (0, react.useMemo)(() => {
|
|
5623
|
+
const map = /* @__PURE__ */ new Map();
|
|
5624
|
+
if (installedState.kind === "ready") for (const entry of installedState.entries) map.set(entry.name, entry);
|
|
5625
|
+
return map;
|
|
5626
|
+
}, [installedState]);
|
|
5307
5627
|
const filtered = (0, react.useMemo)(() => {
|
|
5308
5628
|
if (catalogState.kind !== "ready") return [];
|
|
5309
5629
|
const q = query.trim().toLowerCase();
|
|
5310
5630
|
return catalogState.result.plugins.filter((entry) => {
|
|
5311
5631
|
if (isShopLike(entry.name)) return false;
|
|
5312
|
-
if (category
|
|
5632
|
+
if (category === "installed") {
|
|
5633
|
+
if (!installedByName.has(entry.name)) return false;
|
|
5634
|
+
} else if (category !== null && categoryKey(entry) !== categoryLocaleKey(category)) return false;
|
|
5313
5635
|
if (q === "") return true;
|
|
5314
5636
|
const summaryEn = entry.catalog?.summary.en ?? "";
|
|
5315
5637
|
const summaryZh = entry.catalog?.summary.zh ?? "";
|
|
@@ -5318,7 +5640,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5318
5640
|
}, [
|
|
5319
5641
|
catalogState,
|
|
5320
5642
|
query,
|
|
5321
|
-
category
|
|
5643
|
+
category,
|
|
5644
|
+
installedByName
|
|
5322
5645
|
]);
|
|
5323
5646
|
filteredLenRef.current = filtered.length;
|
|
5324
5647
|
const stars = catalogState.kind === "ready" ? catalogState.result.stars : {};
|
|
@@ -5348,6 +5671,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5348
5671
|
}
|
|
5349
5672
|
return counts;
|
|
5350
5673
|
}, [catalogState]);
|
|
5674
|
+
const installedCount = (0, react.useMemo)(() => {
|
|
5675
|
+
if (installedState.kind !== "ready") return 0;
|
|
5676
|
+
return installedState.entries.filter((entry) => !isShopLike(entry.name)).length;
|
|
5677
|
+
}, [installedState]);
|
|
5351
5678
|
const tiers = (0, react.useMemo)(() => {
|
|
5352
5679
|
const map = /* @__PURE__ */ new Map();
|
|
5353
5680
|
if (catalogState.kind === "ready") for (const entry of catalogState.result.plugins) map.set(entry.name, entry.tier);
|
|
@@ -5422,37 +5749,50 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5422
5749
|
className: _dsh_plugin_shop_css_e3675a89_default.categoryBar,
|
|
5423
5750
|
role: "group",
|
|
5424
5751
|
"aria-label": t("catalog"),
|
|
5425
|
-
children: [
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5752
|
+
children: [
|
|
5753
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5754
|
+
type: "button",
|
|
5755
|
+
className: category === null ? `${_dsh_plugin_shop_css_e3675a89_default.categoryButton} ${_dsh_plugin_shop_css_e3675a89_default.categoryButtonOn}` : _dsh_plugin_shop_css_e3675a89_default.categoryButton,
|
|
5756
|
+
"aria-pressed": category === null,
|
|
5757
|
+
onClick: () => {
|
|
5758
|
+
setCategory(null);
|
|
5759
|
+
setVisibleCount(48);
|
|
5760
|
+
},
|
|
5761
|
+
children: [
|
|
5762
|
+
t("all"),
|
|
5763
|
+
" ",
|
|
5764
|
+
[...categoryCounts.values()].reduce((a, b) => a + b, 0)
|
|
5765
|
+
]
|
|
5766
|
+
}),
|
|
5767
|
+
CATEGORY_ORDER.map((key) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5768
|
+
type: "button",
|
|
5769
|
+
className: category === key ? `${_dsh_plugin_shop_css_e3675a89_default.categoryButton} ${_dsh_plugin_shop_css_e3675a89_default.categoryButtonOn}` : _dsh_plugin_shop_css_e3675a89_default.categoryButton,
|
|
5770
|
+
"aria-pressed": category === key,
|
|
5771
|
+
onClick: () => {
|
|
5772
|
+
setCategory(key);
|
|
5773
|
+
setVisibleCount(48);
|
|
5774
|
+
},
|
|
5775
|
+
children: [
|
|
5776
|
+
t(categoryLocaleKey(key)),
|
|
5777
|
+
" ",
|
|
5778
|
+
categoryCounts.get(key) ?? 0
|
|
5779
|
+
]
|
|
5780
|
+
}, key)),
|
|
5781
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5782
|
+
type: "button",
|
|
5783
|
+
className: category === "installed" ? `${_dsh_plugin_shop_css_e3675a89_default.categoryButton} ${_dsh_plugin_shop_css_e3675a89_default.categoryButtonOn}` : _dsh_plugin_shop_css_e3675a89_default.categoryButton,
|
|
5784
|
+
"aria-pressed": category === "installed",
|
|
5785
|
+
onClick: () => {
|
|
5786
|
+
setCategory("installed");
|
|
5787
|
+
setVisibleCount(48);
|
|
5788
|
+
},
|
|
5789
|
+
children: [
|
|
5790
|
+
t("installed"),
|
|
5791
|
+
" ",
|
|
5792
|
+
installedCount
|
|
5793
|
+
]
|
|
5794
|
+
})
|
|
5795
|
+
]
|
|
5456
5796
|
}),
|
|
5457
5797
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5458
5798
|
className: _dsh_plugin_shop_css_e3675a89_default.catalogStats,
|
|
@@ -5472,9 +5812,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5472
5812
|
children: [visible.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EntryCard, {
|
|
5473
5813
|
entry,
|
|
5474
5814
|
stars: stars[entry.name],
|
|
5815
|
+
installed: installedByName.get(entry.name),
|
|
5475
5816
|
t,
|
|
5476
5817
|
install,
|
|
5477
|
-
installStatus
|
|
5818
|
+
installStatus,
|
|
5819
|
+
uninstall,
|
|
5820
|
+
restart
|
|
5478
5821
|
}) }, entry.name)), incremental && visibleCount < filtered.length && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
|
|
5479
5822
|
ref: sentinelRef,
|
|
5480
5823
|
className: _dsh_plugin_shop_css_e3675a89_default.cardsSentry,
|
|
@@ -5490,12 +5833,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5490
5833
|
})
|
|
5491
5834
|
})] }),
|
|
5492
5835
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(OutdatedSection, {
|
|
5493
|
-
state:
|
|
5836
|
+
state: installedState,
|
|
5494
5837
|
tiers,
|
|
5495
5838
|
t,
|
|
5496
5839
|
setEnabled,
|
|
5497
5840
|
install,
|
|
5498
|
-
installStatus
|
|
5841
|
+
installStatus,
|
|
5842
|
+
restart
|
|
5499
5843
|
})
|
|
5500
5844
|
]
|
|
5501
5845
|
});
|
|
@@ -5538,7 +5882,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5538
5882
|
install: async (args) => unwrap(await ns.installStart(args)),
|
|
5539
5883
|
installStatus: async (args) => unwrap(await ns.installStatus(args)),
|
|
5540
5884
|
setEnabled: async (args) => unwrap(await ns.setEnabled(args)),
|
|
5541
|
-
|
|
5885
|
+
installed: async () => unwrap(await ns.installed()),
|
|
5886
|
+
uninstall: async (args) => unwrap(await ns.uninstallStart(args)),
|
|
5887
|
+
restart: async () => unwrap(await ns.restart())
|
|
5542
5888
|
});
|
|
5543
5889
|
ctx.slots.inject("settings.plugins.tab", () => ctx.slots.register({
|
|
5544
5890
|
name: "settings.plugins.tab",
|