dsh-plugin-shop 0.4.13 → 0.5.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/LICENSE +201 -21
- package/README.md +4 -4
- package/lib/client.js +211 -118
- package/lib/index.js +523 -49
- package/lib/typert.host.js +31 -20
- package/lib/typert.remote-client.js +22 -11
- package/lib/types/client/locales.d.ts +10 -0
- package/lib/types/client/present.d.ts +11 -2
- package/lib/types/host/catalog.d.ts +5 -2
- package/lib/types/host/executor.d.ts +13 -2
- package/lib/types/host/hot.d.ts +105 -0
- package/lib/types/host/index.d.ts +80 -2
- package/lib/types/host/install.d.ts +2 -2
- package/lib/types/host/supervisor.d.ts +15 -0
- package/lib/types/host/types.d.ts +18 -1
- package/package.json +7 -2
package/lib/client.js
CHANGED
|
@@ -4281,6 +4281,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4281
4281
|
"review": union([_undefined(), object({
|
|
4282
4282
|
"reviewedVersion": union([_undefined(), string()]).optional(),
|
|
4283
4283
|
"reviewedCommit": union([_undefined(), string()]).optional(),
|
|
4284
|
+
"reviewedSha256": union([_undefined(), string()]).optional(),
|
|
4284
4285
|
"reviewer": string(),
|
|
4285
4286
|
"reviewCommit": string(),
|
|
4286
4287
|
"notes": string()
|
|
@@ -4292,6 +4293,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4292
4293
|
literal("ui"),
|
|
4293
4294
|
literal("workflow"),
|
|
4294
4295
|
literal("integration"),
|
|
4296
|
+
literal("theme"),
|
|
4295
4297
|
literal("other")
|
|
4296
4298
|
]),
|
|
4297
4299
|
"summary": object({
|
|
@@ -4301,11 +4303,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4301
4303
|
"capabilities": array(string())
|
|
4302
4304
|
})]).optional(),
|
|
4303
4305
|
"source": union([literal("npm"), literal("github")]),
|
|
4304
|
-
"repo": union([_undefined(), string()]).optional()
|
|
4306
|
+
"repo": union([_undefined(), string()]).optional(),
|
|
4307
|
+
"subdir": union([_undefined(), string()]).optional(),
|
|
4308
|
+
"tarball": union([_undefined(), object({
|
|
4309
|
+
"url": string(),
|
|
4310
|
+
"sha256": string()
|
|
4311
|
+
})]).optional(),
|
|
4312
|
+
"added": string()
|
|
4305
4313
|
})),
|
|
4306
4314
|
"denied": array(object({
|
|
4307
4315
|
"name": string(),
|
|
4308
|
-
"detail": string()
|
|
4316
|
+
"detail": string(),
|
|
4317
|
+
"replacement": union([_undefined(), string()]).optional()
|
|
4309
4318
|
})),
|
|
4310
4319
|
"stars": record(string(), number())
|
|
4311
4320
|
});
|
|
@@ -4313,7 +4322,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4313
4322
|
"name": string(),
|
|
4314
4323
|
"installed": string(),
|
|
4315
4324
|
"latest": string(),
|
|
4316
|
-
"outdated": boolean()
|
|
4325
|
+
"outdated": boolean(),
|
|
4326
|
+
"enabled": boolean()
|
|
4317
4327
|
}));
|
|
4318
4328
|
const dsh_plugin_shop_shop_installStart_parameter_0$schema = object({
|
|
4319
4329
|
"name": string(),
|
|
@@ -4334,7 +4344,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4334
4344
|
literal("denied"),
|
|
4335
4345
|
literal("version-mismatch"),
|
|
4336
4346
|
literal("needs-acknowledgement"),
|
|
4337
|
-
literal("git-missing")
|
|
4347
|
+
literal("git-missing"),
|
|
4348
|
+
literal("tarball-integrity")
|
|
4338
4349
|
]),
|
|
4339
4350
|
"detail": string()
|
|
4340
4351
|
})]);
|
|
@@ -4352,6 +4363,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4352
4363
|
literal(false),
|
|
4353
4364
|
literal(true)
|
|
4354
4365
|
]).optional(),
|
|
4366
|
+
"restartReason": union([_undefined(), string()]).optional(),
|
|
4355
4367
|
"detail": union([_undefined(), string()]).optional()
|
|
4356
4368
|
});
|
|
4357
4369
|
const dsh_plugin_shop_shop_restart_result$schema = union([object({ "ok": literal(true) }), object({
|
|
@@ -4385,7 +4397,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4385
4397
|
const dsh_plugin_shop_shop_version_result$schema = object({
|
|
4386
4398
|
"installed": string(),
|
|
4387
4399
|
"latest": union([literal(null), string()]),
|
|
4388
|
-
"outdated": boolean()
|
|
4400
|
+
"outdated": boolean(),
|
|
4401
|
+
"restartSupported": boolean()
|
|
4389
4402
|
});
|
|
4390
4403
|
const TYPERT_REMOTE = {
|
|
4391
4404
|
package: "dsh-plugin-shop",
|
|
@@ -4414,7 +4427,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4414
4427
|
},
|
|
4415
4428
|
sourceLocation: {
|
|
4416
4429
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4417
|
-
"line":
|
|
4430
|
+
"line": 447,
|
|
4418
4431
|
"column": 9
|
|
4419
4432
|
}
|
|
4420
4433
|
},
|
|
@@ -4432,7 +4445,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4432
4445
|
},
|
|
4433
4446
|
sourceLocation: {
|
|
4434
4447
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4435
|
-
"line":
|
|
4448
|
+
"line": 597,
|
|
4436
4449
|
"column": 9
|
|
4437
4450
|
}
|
|
4438
4451
|
},
|
|
@@ -4460,7 +4473,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4460
4473
|
},
|
|
4461
4474
|
sourceLocation: {
|
|
4462
4475
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4463
|
-
"line":
|
|
4476
|
+
"line": 473,
|
|
4464
4477
|
"column": 9
|
|
4465
4478
|
}
|
|
4466
4479
|
},
|
|
@@ -4487,7 +4500,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4487
4500
|
},
|
|
4488
4501
|
sourceLocation: {
|
|
4489
4502
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4490
|
-
"line":
|
|
4503
|
+
"line": 586,
|
|
4491
4504
|
"column": 3
|
|
4492
4505
|
}
|
|
4493
4506
|
},
|
|
@@ -4505,7 +4518,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4505
4518
|
},
|
|
4506
4519
|
sourceLocation: {
|
|
4507
4520
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4508
|
-
"line":
|
|
4521
|
+
"line": 721,
|
|
4509
4522
|
"column": 9
|
|
4510
4523
|
}
|
|
4511
4524
|
},
|
|
@@ -4532,7 +4545,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4532
4545
|
},
|
|
4533
4546
|
sourceLocation: {
|
|
4534
4547
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4535
|
-
"line":
|
|
4548
|
+
"line": 400,
|
|
4536
4549
|
"column": 3
|
|
4537
4550
|
}
|
|
4538
4551
|
},
|
|
@@ -4560,7 +4573,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4560
4573
|
},
|
|
4561
4574
|
sourceLocation: {
|
|
4562
4575
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4563
|
-
"line":
|
|
4576
|
+
"line": 668,
|
|
4564
4577
|
"column": 9
|
|
4565
4578
|
}
|
|
4566
4579
|
},
|
|
@@ -4587,7 +4600,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4587
4600
|
},
|
|
4588
4601
|
sourceLocation: {
|
|
4589
4602
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4590
|
-
"line":
|
|
4603
|
+
"line": 781,
|
|
4591
4604
|
"column": 9
|
|
4592
4605
|
}
|
|
4593
4606
|
},
|
|
@@ -4605,7 +4618,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4605
4618
|
},
|
|
4606
4619
|
sourceLocation: {
|
|
4607
4620
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4608
|
-
"line":
|
|
4621
|
+
"line": 765,
|
|
4609
4622
|
"column": 9
|
|
4610
4623
|
}
|
|
4611
4624
|
}
|
|
@@ -4635,12 +4648,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4635
4648
|
case "version-mismatch": return "versionMismatchCode";
|
|
4636
4649
|
case "needs-acknowledgement": return "needsAcknowledgementCode";
|
|
4637
4650
|
case "git-missing": return "gitMissingCode";
|
|
4651
|
+
case "tarball-integrity": return "tarballIntegrityCode";
|
|
4638
4652
|
}
|
|
4639
4653
|
}
|
|
4640
4654
|
/** The version a card displays: full for npm, the short commit for github. */
|
|
4641
4655
|
function displayVersion(entry) {
|
|
4642
4656
|
return entry.source === "github" ? entry.version.slice(0, 7) : entry.version;
|
|
4643
4657
|
}
|
|
4658
|
+
/** The hash pin a stale line names for a repo or release-rescued entry — the
|
|
4659
|
+
* reviewed commit, or the reviewed release tarball sha256 (market borrowings
|
|
4660
|
+
* §3.1). Shortened to 7 chars like the card's own version; '' only for a
|
|
4661
|
+
* hand-built fixture carrying no pin. The npm form is a version and keeps the
|
|
4662
|
+
* version line's `v` prefix instead. */
|
|
4663
|
+
function reviewHashPin(review) {
|
|
4664
|
+
return (review.reviewedCommit ?? review.reviewedSha256 ?? "").slice(0, 7);
|
|
4665
|
+
}
|
|
4644
4666
|
/**
|
|
4645
4667
|
* Whether the license value is the npm idiom `SEE LICENSE IN <file>` — a
|
|
4646
4668
|
* valid SPDX form meaning "custom license, text ships in that file". The
|
|
@@ -4690,7 +4712,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4690
4712
|
};
|
|
4691
4713
|
if (status.state === "done") return {
|
|
4692
4714
|
kind: "done",
|
|
4693
|
-
needsRestart: !!status.needsRestart
|
|
4715
|
+
needsRestart: !!status.needsRestart,
|
|
4716
|
+
...status.restartReason !== void 0 ? { restartReason: status.restartReason } : {}
|
|
4694
4717
|
};
|
|
4695
4718
|
return {
|
|
4696
4719
|
kind: "failed",
|
|
@@ -4758,6 +4781,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4758
4781
|
ui: "categoryUi",
|
|
4759
4782
|
workflow: "categoryWorkflow",
|
|
4760
4783
|
integration: "categoryIntegration",
|
|
4784
|
+
theme: "categoryTheme",
|
|
4761
4785
|
other: "categoryOther"
|
|
4762
4786
|
};
|
|
4763
4787
|
/** The locale key for one entry's category — derived entries read as `other`
|
|
@@ -4765,7 +4789,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4765
4789
|
function categoryKey(entry) {
|
|
4766
4790
|
return CATEGORY_KEYS[entry.catalog?.category ?? "other"];
|
|
4767
4791
|
}
|
|
4768
|
-
/** The
|
|
4792
|
+
/** The seven categories in display order (map insertion order). */
|
|
4769
4793
|
const CATEGORY_ORDER = Object.keys(CATEGORY_KEYS);
|
|
4770
4794
|
/** The locale key for one bare category value (the filter buttons). */
|
|
4771
4795
|
function categoryLocaleKey(category) {
|
|
@@ -4806,6 +4830,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4806
4830
|
categoryUi: "界面",
|
|
4807
4831
|
categoryWorkflow: "工作流",
|
|
4808
4832
|
categoryIntegration: "集成",
|
|
4833
|
+
categoryTheme: "主题与外观",
|
|
4809
4834
|
categoryOther: "其他",
|
|
4810
4835
|
empty: "目录中暂无插件。",
|
|
4811
4836
|
emptySearch: "没有匹配的插件。",
|
|
@@ -4830,11 +4855,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4830
4855
|
license: "许可",
|
|
4831
4856
|
customLicense: "自定义许可",
|
|
4832
4857
|
reviewedVersionLine: "已审核 v{reviewed} / 当前 v{current} 未审核",
|
|
4858
|
+
reviewedPinLine: "已审核 {pin} / 当前 {current} 未审核",
|
|
4833
4859
|
deniedCode: "已拒绝",
|
|
4834
4860
|
notInCatalogCode: "不在目录中",
|
|
4835
4861
|
versionMismatchCode: "版本不匹配",
|
|
4836
4862
|
needsAcknowledgementCode: "需要确认",
|
|
4837
4863
|
gitMissingCode: "缺少 git",
|
|
4864
|
+
tarballIntegrityCode: "压缩包完整性校验失败",
|
|
4838
4865
|
githubSource: "来自 GitHub 仓库安装",
|
|
4839
4866
|
installedSection: "已安装",
|
|
4840
4867
|
installedVersion: "已安装 v{version}",
|
|
@@ -4845,6 +4872,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4845
4872
|
uninstalling: "正在卸载…",
|
|
4846
4873
|
uninstallFailed: "卸载失败",
|
|
4847
4874
|
uninstallTransportFailed: "卸载请求未能送达。请稍后重试。",
|
|
4875
|
+
uninstalledLiveNotice: "已卸载并立即停止。profile 的启动组合将在下次重启时应用该移除。",
|
|
4848
4876
|
uninstalledRestartNotice: "已卸载;重启 dsh 后生效",
|
|
4849
4877
|
restart: "重启 dsh",
|
|
4850
4878
|
restartTitle: "确认重启 dsh",
|
|
@@ -4853,6 +4881,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4853
4881
|
restarting: "正在重启,服务器就绪后页面会自动刷新…",
|
|
4854
4882
|
restartFailedNotice: "重启后服务器没有回来。请手动启动:dsh web(新进程的日志在商店缓存目录的 restart.log)",
|
|
4855
4883
|
restartTransportFailed: "重启请求未能送达。请稍后重试。",
|
|
4884
|
+
restartDisabledNotice: "当前 dsh 由 systemd 服务托管,商店无法安全地重启它。请手动重启服务,或在商店行配置中设置 allowRestart: true。",
|
|
4856
4885
|
updateFailed: "更新失败",
|
|
4857
4886
|
updateTransportFailed: "更新请求未能送达。请稍后重试。",
|
|
4858
4887
|
checkUpdate: "检查更新",
|
|
@@ -4878,6 +4907,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4878
4907
|
categoryUi: "UI",
|
|
4879
4908
|
categoryWorkflow: "Workflow",
|
|
4880
4909
|
categoryIntegration: "Integration",
|
|
4910
|
+
categoryTheme: "Themes & appearance",
|
|
4881
4911
|
categoryOther: "Other",
|
|
4882
4912
|
empty: "No plugins in the catalog.",
|
|
4883
4913
|
emptySearch: "No matching plugins.",
|
|
@@ -4902,11 +4932,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4902
4932
|
license: "License",
|
|
4903
4933
|
customLicense: "Custom license",
|
|
4904
4934
|
reviewedVersionLine: "reviewed v{reviewed} / current v{current} unreviewed",
|
|
4935
|
+
reviewedPinLine: "reviewed {pin} / current {current} unreviewed",
|
|
4905
4936
|
deniedCode: "Denied",
|
|
4906
4937
|
notInCatalogCode: "Not in the catalog",
|
|
4907
4938
|
versionMismatchCode: "Version mismatch",
|
|
4908
4939
|
needsAcknowledgementCode: "Acknowledgement required",
|
|
4909
4940
|
gitMissingCode: "git missing",
|
|
4941
|
+
tarballIntegrityCode: "tarball integrity check failed",
|
|
4910
4942
|
githubSource: "installed from a GitHub repository",
|
|
4911
4943
|
installedSection: "Installed",
|
|
4912
4944
|
installedVersion: "installed v{version}",
|
|
@@ -4917,6 +4949,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4917
4949
|
uninstalling: "Uninstalling…",
|
|
4918
4950
|
uninstallFailed: "Uninstall failed",
|
|
4919
4951
|
uninstallTransportFailed: "The uninstall request could not be delivered. Please retry.",
|
|
4952
|
+
uninstalledLiveNotice: "Removed and stopped immediately. The profile's boot composition picks up the removal at the next restart.",
|
|
4920
4953
|
uninstalledRestartNotice: "uninstalled; restart dsh to activate",
|
|
4921
4954
|
restart: "Restart dsh",
|
|
4922
4955
|
restartTitle: "Restart dsh?",
|
|
@@ -4925,6 +4958,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4925
4958
|
restarting: "Restarting; the page will refresh once the server is back…",
|
|
4926
4959
|
restartFailedNotice: "The server did not come back after the restart. Start it manually: dsh web (see restart.log in the shop cache directory)",
|
|
4927
4960
|
restartTransportFailed: "The restart request could not be delivered. Please retry.",
|
|
4961
|
+
restartDisabledNotice: "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.",
|
|
4928
4962
|
updateFailed: "Update failed",
|
|
4929
4963
|
updateTransportFailed: "The update request could not be delivered. Please retry.",
|
|
4930
4964
|
checkUpdate: "Check for updates",
|
|
@@ -5084,7 +5118,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5084
5118
|
}
|
|
5085
5119
|
//#endregion
|
|
5086
5120
|
//#region \0dsh-plugin-shop-css:e3675a89
|
|
5087
|
-
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 /* The shelf never scrolls sideways: a card too wide for its track wraps\n * its badges rather than widening the panel. */\n overflow-x: hidden;\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.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/* 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(--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/* 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(--spine-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(--spine-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.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.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";
|
|
5121
|
+
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 overflow-x: hidden;\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.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/* 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(--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='theme'] { --spine-hue: #F472B6; }\n.s65f383_card[data-category='other'] { --spine-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(--spine-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(--spine-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.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.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\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 gap: 8px;\n}\n";
|
|
5088
5122
|
if (typeof document !== "undefined" && !document.querySelector("style[data-plugin-css=\"e3675a89\"]")) {
|
|
5089
5123
|
const tag = document.createElement("style");
|
|
5090
5124
|
tag.dataset.pluginCss = "e3675a89";
|
|
@@ -5179,7 +5213,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5179
5213
|
"outdatedActions": "sa1f556_outdatedActions",
|
|
5180
5214
|
"switch": "s11c019_switch",
|
|
5181
5215
|
"switchOn": "s7580e8_switchOn",
|
|
5182
|
-
"switchKnob": "saab2b3_switchKnob"
|
|
5216
|
+
"switchKnob": "saab2b3_switchKnob",
|
|
5217
|
+
"switchWrap": "sd9f9e4_switchWrap"
|
|
5183
5218
|
};
|
|
5184
5219
|
//#endregion
|
|
5185
5220
|
//#region src/client/ShopTab.tsx
|
|
@@ -5209,7 +5244,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5209
5244
|
* install controls. An installed plugin's card carries its installed row:
|
|
5210
5245
|
* current → the non-interactive installed label, behind → the update button;
|
|
5211
5246
|
* uninstalled → the install button. */
|
|
5212
|
-
const EntryCard = (0, react.memo)(function EntryCard({ entry, stars, installed, t, install, installStatus, uninstall, restart }) {
|
|
5247
|
+
const EntryCard = (0, react.memo)(function EntryCard({ entry, stars, installed, t, install, installStatus, uninstall, restart, restartSupported, setEnabled }) {
|
|
5213
5248
|
const [open, setOpen] = (0, react.useState)(false);
|
|
5214
5249
|
const detailId = (0, react.useId)();
|
|
5215
5250
|
const summary = entry.catalog?.summary;
|
|
@@ -5312,9 +5347,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5312
5347
|
})]
|
|
5313
5348
|
}), entry.tier === "verified-stale" && entry.review !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5314
5349
|
className: _dsh_plugin_shop_css_e3675a89_default.reviewedLine,
|
|
5315
|
-
children: t("reviewedVersionLine", {
|
|
5350
|
+
children: entry.review.reviewedVersion !== void 0 ? t("reviewedVersionLine", {
|
|
5316
5351
|
reviewed: entry.review.reviewedVersion,
|
|
5317
5352
|
current: entry.version
|
|
5353
|
+
}) : t("reviewedPinLine", {
|
|
5354
|
+
pin: reviewHashPin(entry.review),
|
|
5355
|
+
current: displayVersion(entry)
|
|
5318
5356
|
})
|
|
5319
5357
|
})]
|
|
5320
5358
|
})
|
|
@@ -5329,27 +5367,38 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5329
5367
|
t,
|
|
5330
5368
|
install,
|
|
5331
5369
|
installStatus,
|
|
5332
|
-
restart
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5370
|
+
restart,
|
|
5371
|
+
restartSupported
|
|
5372
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
5373
|
+
installed.outdated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstallPanel, {
|
|
5374
|
+
name: entry.name,
|
|
5375
|
+
version: entry.version,
|
|
5376
|
+
tier: entry.tier,
|
|
5377
|
+
variant: "update",
|
|
5378
|
+
t,
|
|
5379
|
+
install,
|
|
5380
|
+
installStatus,
|
|
5381
|
+
restart,
|
|
5382
|
+
restartSupported
|
|
5383
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5384
|
+
className: _dsh_plugin_shop_css_e3675a89_default.installedLabel,
|
|
5385
|
+
"data-shop-installed": true,
|
|
5386
|
+
children: t("installed")
|
|
5387
|
+
}),
|
|
5388
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(EnabledSwitch, {
|
|
5389
|
+
row: installed,
|
|
5390
|
+
t,
|
|
5391
|
+
setEnabled
|
|
5392
|
+
}),
|
|
5393
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(UninstallPanel, {
|
|
5394
|
+
name: entry.name,
|
|
5395
|
+
t,
|
|
5396
|
+
uninstall,
|
|
5397
|
+
installStatus,
|
|
5398
|
+
restart,
|
|
5399
|
+
restartSupported
|
|
5400
|
+
})
|
|
5401
|
+
] })
|
|
5353
5402
|
})
|
|
5354
5403
|
]
|
|
5355
5404
|
});
|
|
@@ -5359,7 +5408,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5359
5408
|
* failure detail, rejection detail — driven by `useInstall`. Shared by the
|
|
5360
5409
|
* catalog cards (`variant: 'install'`) and the outdated rows' update button
|
|
5361
5410
|
* (`variant: 'update'`, which drives the same install flow for `name@latest`). */
|
|
5362
|
-
function InstallPanel({ name, version, tier, variant = "install", t, install, installStatus, restart }) {
|
|
5411
|
+
function InstallPanel({ name, version, tier, variant = "install", t, install, installStatus, restart, restartSupported }) {
|
|
5363
5412
|
const [gateOpen, setGateOpen] = (0, react.useState)(false);
|
|
5364
5413
|
const { view, start } = useInstall(install, installStatus);
|
|
5365
5414
|
if (view.kind === "running") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -5377,14 +5426,22 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5377
5426
|
});
|
|
5378
5427
|
if (view.kind === "done") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5379
5428
|
className: _dsh_plugin_shop_css_e3675a89_default.installedActions,
|
|
5380
|
-
children: [
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5387
|
-
|
|
5429
|
+
children: [
|
|
5430
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5431
|
+
className: _dsh_plugin_shop_css_e3675a89_default.notice,
|
|
5432
|
+
"data-shop-restart-notice": true,
|
|
5433
|
+
children: view.needsRestart ? view.restartReason ?? t("installedRestartNotice") : t("installedNoRestartNotice")
|
|
5434
|
+
}),
|
|
5435
|
+
view.needsRestart && restartSupported && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RestartPanel, {
|
|
5436
|
+
t,
|
|
5437
|
+
restart
|
|
5438
|
+
}),
|
|
5439
|
+
view.needsRestart && !restartSupported && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5440
|
+
className: _dsh_plugin_shop_css_e3675a89_default.notice,
|
|
5441
|
+
"data-shop-restart-disabled": true,
|
|
5442
|
+
children: t("restartDisabledNotice")
|
|
5443
|
+
})
|
|
5444
|
+
]
|
|
5388
5445
|
});
|
|
5389
5446
|
if (view.kind === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5390
5447
|
className: _dsh_plugin_shop_css_e3675a89_default.installPanel,
|
|
@@ -5473,7 +5530,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5473
5530
|
* §9.3 is about granting. A business failure (not in the catalog / not
|
|
5474
5531
|
* installed) lands in the failed view with the host's published detail; a
|
|
5475
5532
|
* transport failure carries the empty detail and the localized fallback. */
|
|
5476
|
-
function UninstallPanel({ name, t, uninstall, installStatus, restart }) {
|
|
5533
|
+
function UninstallPanel({ name, t, uninstall, installStatus, restart, restartSupported }) {
|
|
5477
5534
|
const { view, start } = useUninstall(uninstall, installStatus);
|
|
5478
5535
|
if (view.kind === "running") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5479
5536
|
className: _dsh_plugin_shop_css_e3675a89_default.installPanel,
|
|
@@ -5490,14 +5547,22 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5490
5547
|
});
|
|
5491
5548
|
if (view.kind === "done") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5492
5549
|
className: _dsh_plugin_shop_css_e3675a89_default.installedActions,
|
|
5493
|
-
children: [
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5550
|
+
children: [
|
|
5551
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5552
|
+
className: _dsh_plugin_shop_css_e3675a89_default.notice,
|
|
5553
|
+
"data-shop-uninstall-done": true,
|
|
5554
|
+
children: view.needsRestart ? t("uninstalledRestartNotice") : t("uninstalledLiveNotice")
|
|
5555
|
+
}),
|
|
5556
|
+
view.needsRestart && restartSupported && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RestartPanel, {
|
|
5557
|
+
t,
|
|
5558
|
+
restart
|
|
5559
|
+
}),
|
|
5560
|
+
view.needsRestart && !restartSupported && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5561
|
+
className: _dsh_plugin_shop_css_e3675a89_default.notice,
|
|
5562
|
+
"data-shop-restart-disabled": true,
|
|
5563
|
+
children: t("restartDisabledNotice")
|
|
5564
|
+
})
|
|
5565
|
+
]
|
|
5501
5566
|
});
|
|
5502
5567
|
if (view.kind === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5503
5568
|
className: _dsh_plugin_shop_css_e3675a89_default.installPanel,
|
|
@@ -5624,14 +5689,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5624
5689
|
children: t("restart")
|
|
5625
5690
|
});
|
|
5626
5691
|
}
|
|
5627
|
-
/**
|
|
5628
|
-
*
|
|
5629
|
-
*
|
|
5630
|
-
*
|
|
5631
|
-
*
|
|
5632
|
-
*
|
|
5633
|
-
|
|
5634
|
-
|
|
5692
|
+
/**
|
|
5693
|
+
* The hot enable/disable switch (§8). The initial state is the Host's
|
|
5694
|
+
* inventory verdict carried on the installed row; the click is optimistic
|
|
5695
|
+
* (the value flips on success) and a success renders the §8 hot note. A
|
|
5696
|
+
* transport throw renders the localized failure line — its private detail
|
|
5697
|
+
* (which can name hosts and ports) never reaches the UI.
|
|
5698
|
+
*/
|
|
5699
|
+
function EnabledSwitch({ row, t, setEnabled }) {
|
|
5700
|
+
const [enabled, setEnabledState] = (0, react.useState)(row.enabled);
|
|
5635
5701
|
const [toggle, setToggle] = (0, react.useState)({ kind: "idle" });
|
|
5636
5702
|
const onToggle = async () => {
|
|
5637
5703
|
if (toggle.kind === "saving") return;
|
|
@@ -5657,47 +5723,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5657
5723
|
}
|
|
5658
5724
|
};
|
|
5659
5725
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5660
|
-
className: _dsh_plugin_shop_css_e3675a89_default.
|
|
5661
|
-
"data-shop-
|
|
5726
|
+
className: _dsh_plugin_shop_css_e3675a89_default.switchWrap,
|
|
5727
|
+
"data-shop-enabled-switch": row.name,
|
|
5662
5728
|
children: [
|
|
5663
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5674
|
-
className: _dsh_plugin_shop_css_e3675a89_default.outdatedVersion,
|
|
5675
|
-
children: t("latestVersion", { version: row.latest })
|
|
5676
|
-
})]
|
|
5677
|
-
})]
|
|
5678
|
-
}),
|
|
5679
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5680
|
-
className: _dsh_plugin_shop_css_e3675a89_default.outdatedActions,
|
|
5681
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5682
|
-
type: "button",
|
|
5683
|
-
role: "switch",
|
|
5684
|
-
"aria-checked": enabled,
|
|
5685
|
-
"aria-label": t("enabledSwitch"),
|
|
5686
|
-
"data-shop-toggle": true,
|
|
5687
|
-
className: `${_dsh_plugin_shop_css_e3675a89_default.switch} ${enabled ? _dsh_plugin_shop_css_e3675a89_default.switchOn : ""}`,
|
|
5688
|
-
onClick: () => void onToggle(),
|
|
5689
|
-
disabled: toggle.kind === "saving",
|
|
5690
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_e3675a89_default.switchKnob })
|
|
5691
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstallPanel, {
|
|
5692
|
-
name: row.name,
|
|
5693
|
-
version: row.latest,
|
|
5694
|
-
tier,
|
|
5695
|
-
variant: "update",
|
|
5696
|
-
t,
|
|
5697
|
-
install,
|
|
5698
|
-
installStatus,
|
|
5699
|
-
restart
|
|
5700
|
-
})]
|
|
5729
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
5730
|
+
type: "button",
|
|
5731
|
+
role: "switch",
|
|
5732
|
+
"aria-checked": enabled,
|
|
5733
|
+
"aria-label": t("enabledSwitch"),
|
|
5734
|
+
"data-shop-toggle": true,
|
|
5735
|
+
className: `${_dsh_plugin_shop_css_e3675a89_default.switch} ${enabled ? _dsh_plugin_shop_css_e3675a89_default.switchOn : ""}`,
|
|
5736
|
+
onClick: () => void onToggle(),
|
|
5737
|
+
disabled: toggle.kind === "saving",
|
|
5738
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_e3675a89_default.switchKnob })
|
|
5701
5739
|
}),
|
|
5702
5740
|
toggle.kind === "saved" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5703
5741
|
className: _dsh_plugin_shop_css_e3675a89_default.notice,
|
|
@@ -5712,6 +5750,48 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5712
5750
|
]
|
|
5713
5751
|
});
|
|
5714
5752
|
}
|
|
5753
|
+
/** One outdated install row (§7.3): the name, the installed and latest
|
|
5754
|
+
* versions, the hot enable/disable switch, and the update button (the
|
|
5755
|
+
* install flow for `name@latest`, reusing `InstallPanel`). */
|
|
5756
|
+
function OutdatedRow({ row, tier, source, t, setEnabled, install, installStatus, restart, restartSupported }) {
|
|
5757
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5758
|
+
className: _dsh_plugin_shop_css_e3675a89_default.outdatedRow,
|
|
5759
|
+
"data-shop-outdated-entry": row.name,
|
|
5760
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5761
|
+
className: _dsh_plugin_shop_css_e3675a89_default.outdatedInfo,
|
|
5762
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5763
|
+
className: _dsh_plugin_shop_css_e3675a89_default.name,
|
|
5764
|
+
children: row.name
|
|
5765
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
5766
|
+
className: _dsh_plugin_shop_css_e3675a89_default.outdatedVersions,
|
|
5767
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5768
|
+
className: _dsh_plugin_shop_css_e3675a89_default.outdatedVersion,
|
|
5769
|
+
children: t("installedVersion", { version: source === "github" ? row.installed.slice(0, 7) : row.installed })
|
|
5770
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5771
|
+
className: _dsh_plugin_shop_css_e3675a89_default.outdatedVersion,
|
|
5772
|
+
children: t("latestVersion", { version: row.latest })
|
|
5773
|
+
})]
|
|
5774
|
+
})]
|
|
5775
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5776
|
+
className: _dsh_plugin_shop_css_e3675a89_default.outdatedActions,
|
|
5777
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(EnabledSwitch, {
|
|
5778
|
+
row,
|
|
5779
|
+
t,
|
|
5780
|
+
setEnabled
|
|
5781
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstallPanel, {
|
|
5782
|
+
name: row.name,
|
|
5783
|
+
version: row.latest,
|
|
5784
|
+
tier,
|
|
5785
|
+
variant: "update",
|
|
5786
|
+
t,
|
|
5787
|
+
install,
|
|
5788
|
+
installStatus,
|
|
5789
|
+
restart,
|
|
5790
|
+
restartSupported
|
|
5791
|
+
})]
|
|
5792
|
+
})]
|
|
5793
|
+
});
|
|
5794
|
+
}
|
|
5715
5795
|
/** The §7.3 installed list, rendered as the "installed" section: each row
|
|
5716
5796
|
* shows both versions, a switch, and an update button. The rows are the
|
|
5717
5797
|
* installed entries filtered to `outdated` — a current install is already
|
|
@@ -5719,7 +5799,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5719
5799
|
* tier for the update gate is looked up from the catalog by name (community →
|
|
5720
5800
|
* acknowledgement); an entry absent from the catalog defaults to the
|
|
5721
5801
|
* community gate (the safer read). */
|
|
5722
|
-
function OutdatedSection({ state, tiers, sources, t, setEnabled, install, installStatus, restart }) {
|
|
5802
|
+
function OutdatedSection({ state, tiers, sources, t, setEnabled, install, installStatus, restart, restartSupported }) {
|
|
5723
5803
|
if (state.kind === "loading") return null;
|
|
5724
5804
|
if (state.kind === "error") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
5725
5805
|
className: _dsh_plugin_shop_css_e3675a89_default.stateLine,
|
|
@@ -5744,7 +5824,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5744
5824
|
setEnabled,
|
|
5745
5825
|
install,
|
|
5746
5826
|
installStatus,
|
|
5747
|
-
restart
|
|
5827
|
+
restart,
|
|
5828
|
+
restartSupported
|
|
5748
5829
|
}) }, row.name))
|
|
5749
5830
|
})]
|
|
5750
5831
|
});
|
|
@@ -5756,6 +5837,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5756
5837
|
const [catalogState, setCatalogState] = (0, react.useState)({ kind: "loading" });
|
|
5757
5838
|
const [installedState, setInstalledState] = (0, react.useState)({ kind: "loading" });
|
|
5758
5839
|
const [selfVersion, setSelfVersion] = (0, react.useState)(null);
|
|
5840
|
+
const restartSupported = selfVersion?.restartSupported ?? true;
|
|
5759
5841
|
const selfUpdate = useUpdateSelf(updateStart, installStatus);
|
|
5760
5842
|
const [request, setRequest] = (0, react.useState)({ kind: "initial" });
|
|
5761
5843
|
const [query, setQuery] = (0, react.useState)("");
|
|
@@ -6031,13 +6113,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6031
6113
|
"data-shop-self-update-done": true,
|
|
6032
6114
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
6033
6115
|
className: _dsh_plugin_shop_css_e3675a89_default.installedActions,
|
|
6034
|
-
children: [
|
|
6035
|
-
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
|
|
6039
|
-
|
|
6040
|
-
|
|
6116
|
+
children: [
|
|
6117
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6118
|
+
className: _dsh_plugin_shop_css_e3675a89_default.notice,
|
|
6119
|
+
children: t("installedRestartNotice")
|
|
6120
|
+
}),
|
|
6121
|
+
restartSupported && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RestartPanel, {
|
|
6122
|
+
t,
|
|
6123
|
+
restart
|
|
6124
|
+
}),
|
|
6125
|
+
!restartSupported && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
6126
|
+
className: _dsh_plugin_shop_css_e3675a89_default.notice,
|
|
6127
|
+
"data-shop-restart-disabled": true,
|
|
6128
|
+
children: t("restartDisabledNotice")
|
|
6129
|
+
})
|
|
6130
|
+
]
|
|
6041
6131
|
})
|
|
6042
6132
|
}),
|
|
6043
6133
|
selfUpdate.view.kind === "failed" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -6133,7 +6223,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6133
6223
|
install,
|
|
6134
6224
|
installStatus,
|
|
6135
6225
|
uninstall,
|
|
6136
|
-
restart
|
|
6226
|
+
restart,
|
|
6227
|
+
restartSupported,
|
|
6228
|
+
setEnabled
|
|
6137
6229
|
}) }, entry.name)), incremental && visibleCount < filtered.length && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
|
|
6138
6230
|
ref: sentinelRef,
|
|
6139
6231
|
className: _dsh_plugin_shop_css_e3675a89_default.cardsSentry,
|
|
@@ -6156,7 +6248,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6156
6248
|
setEnabled,
|
|
6157
6249
|
install,
|
|
6158
6250
|
installStatus,
|
|
6159
|
-
restart
|
|
6251
|
+
restart,
|
|
6252
|
+
restartSupported
|
|
6160
6253
|
})
|
|
6161
6254
|
]
|
|
6162
6255
|
});
|