dsh-plugin-shop 0.4.8 → 0.4.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -4279,7 +4279,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4279
4279
  ]),
4280
4280
  "metadata": union([literal("declared"), literal("derived")]),
4281
4281
  "review": union([_undefined(), object({
4282
- "reviewedVersion": string(),
4282
+ "reviewedVersion": union([_undefined(), string()]).optional(),
4283
+ "reviewedCommit": union([_undefined(), string()]).optional(),
4283
4284
  "reviewer": string(),
4284
4285
  "reviewCommit": string(),
4285
4286
  "notes": string()
@@ -4298,7 +4299,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4298
4299
  "zh": union([_undefined(), string()]).optional()
4299
4300
  }),
4300
4301
  "capabilities": array(string())
4301
- })]).optional()
4302
+ })]).optional(),
4303
+ "source": union([literal("npm"), literal("github")]),
4304
+ "repo": union([_undefined(), string()]).optional()
4302
4305
  })),
4303
4306
  "denied": array(object({
4304
4307
  "name": string(),
@@ -4330,7 +4333,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4330
4333
  literal("not-in-catalog"),
4331
4334
  literal("denied"),
4332
4335
  literal("version-mismatch"),
4333
- literal("needs-acknowledgement")
4336
+ literal("needs-acknowledgement"),
4337
+ literal("git-missing")
4334
4338
  ]),
4335
4339
  "detail": string()
4336
4340
  })]);
@@ -4410,7 +4414,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4410
4414
  },
4411
4415
  sourceLocation: {
4412
4416
  "file": "packages/dsh-plugin-shop/src/host/index.ts",
4413
- "line": 244,
4417
+ "line": 270,
4414
4418
  "column": 9
4415
4419
  }
4416
4420
  },
@@ -4428,7 +4432,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4428
4432
  },
4429
4433
  sourceLocation: {
4430
4434
  "file": "packages/dsh-plugin-shop/src/host/index.ts",
4431
- "line": 319,
4435
+ "line": 374,
4432
4436
  "column": 9
4433
4437
  }
4434
4438
  },
@@ -4456,7 +4460,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4456
4460
  },
4457
4461
  sourceLocation: {
4458
4462
  "file": "packages/dsh-plugin-shop/src/host/index.ts",
4459
- "line": 270,
4463
+ "line": 296,
4460
4464
  "column": 9
4461
4465
  }
4462
4466
  },
@@ -4483,7 +4487,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4483
4487
  },
4484
4488
  sourceLocation: {
4485
4489
  "file": "packages/dsh-plugin-shop/src/host/index.ts",
4486
- "line": 308,
4490
+ "line": 363,
4487
4491
  "column": 3
4488
4492
  }
4489
4493
  },
@@ -4501,7 +4505,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4501
4505
  },
4502
4506
  sourceLocation: {
4503
4507
  "file": "packages/dsh-plugin-shop/src/host/index.ts",
4504
- "line": 392,
4508
+ "line": 469,
4505
4509
  "column": 9
4506
4510
  }
4507
4511
  },
@@ -4528,7 +4532,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4528
4532
  },
4529
4533
  sourceLocation: {
4530
4534
  "file": "packages/dsh-plugin-shop/src/host/index.ts",
4531
- "line": 213,
4535
+ "line": 239,
4532
4536
  "column": 3
4533
4537
  }
4534
4538
  },
@@ -4556,7 +4560,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4556
4560
  },
4557
4561
  sourceLocation: {
4558
4562
  "file": "packages/dsh-plugin-shop/src/host/index.ts",
4559
- "line": 359,
4563
+ "line": 429,
4560
4564
  "column": 9
4561
4565
  }
4562
4566
  },
@@ -4583,7 +4587,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4583
4587
  },
4584
4588
  sourceLocation: {
4585
4589
  "file": "packages/dsh-plugin-shop/src/host/index.ts",
4586
- "line": 441,
4590
+ "line": 518,
4587
4591
  "column": 9
4588
4592
  }
4589
4593
  },
@@ -4601,7 +4605,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4601
4605
  },
4602
4606
  sourceLocation: {
4603
4607
  "file": "packages/dsh-plugin-shop/src/host/index.ts",
4604
- "line": 426,
4608
+ "line": 503,
4605
4609
  "column": 9
4606
4610
  }
4607
4611
  }
@@ -4636,8 +4640,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4636
4640
  case "not-in-catalog": return "notInCatalogCode";
4637
4641
  case "version-mismatch": return "versionMismatchCode";
4638
4642
  case "needs-acknowledgement": return "needsAcknowledgementCode";
4643
+ case "git-missing": return "gitMissingCode";
4639
4644
  }
4640
4645
  }
4646
+ /** The version a card displays: full for npm, the short commit for github. */
4647
+ function displayVersion(entry) {
4648
+ return entry.source === "github" ? entry.version.slice(0, 7) : entry.version;
4649
+ }
4641
4650
  /** §7.2 once-per-second poll cadence, as a named constant. */
4642
4651
  const INSTALL_POLL_MS = 1e3;
4643
4652
  /** How long the check-update button reports "up to date" after a re-check
@@ -4695,11 +4704,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4695
4704
  "shop",
4696
4705
  "marketplace"
4697
4706
  ];
4698
- /** Competing marketplaces whose names carry no store/market keyword and so
4699
- * escape the pattern rules below. Named explicitly rather than guessed:
4700
- * `dsh-plugin` is the npm package of github.com/dshplugin/dsh-plugin-hub
4701
- * a community plugin marketplace for DeepSeek Harness. */
4702
- const SHOP_LIKE_NAMES = ["dsh-plugin"];
4707
+ /**
4708
+ * Competing plugin marketplaces whose names carry no store/market keyword
4709
+ * and so escape the pattern rules below (spec §7.2). Each is a real npm
4710
+ * package that presents its own catalog of dsh plugins inside the Harness:
4711
+ * `dsh-plugin` is the npm package of github.com/dshplugin/dsh-plugin-hub,
4712
+ * `dsh-plugin-hub` and `@lanbaolu/dsh-plugin-hub` are app-store packages,
4713
+ * and `@mutocenew/dsh-plugin-catalog` ships a plugin directory with agent
4714
+ * query tools. Exact names only — nothing here is a pattern.
4715
+ */
4716
+ const SHOP_LIKE_NAMES = [
4717
+ "dsh-plugin",
4718
+ "dsh-plugin-hub",
4719
+ "@lanbaolu/dsh-plugin-hub",
4720
+ "@mutocenew/dsh-plugin-catalog"
4721
+ ];
4703
4722
  /**
4704
4723
  * Whether a package name reads as a plugin shop (a marketplace for dsh
4705
4724
  * plugins, e.g. `dsh-plugin-shop`, `dsh-store`, `pluginstore`). The shop
@@ -4813,6 +4832,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4813
4832
  notInCatalogCode: "不在目录中",
4814
4833
  versionMismatchCode: "版本不匹配",
4815
4834
  needsAcknowledgementCode: "需要确认",
4835
+ gitMissingCode: "缺少 git",
4836
+ githubSource: "来自 GitHub 仓库安装",
4816
4837
  installedSection: "已安装",
4817
4838
  installedVersion: "已安装 v{version}",
4818
4839
  latestVersion: "最新 v{version}",
@@ -4834,6 +4855,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4834
4855
  updateTransportFailed: "更新请求未能送达。请稍后重试。",
4835
4856
  checkUpdate: "检查更新",
4836
4857
  upToDate: "已是最新版本",
4858
+ github: "GitHub",
4837
4859
  enabledSwitch: "启用",
4838
4860
  toggleFailed: "设置失败,请重试。",
4839
4861
  hotApplyNote: "无需重启,立即生效"
@@ -4883,6 +4905,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4883
4905
  notInCatalogCode: "Not in the catalog",
4884
4906
  versionMismatchCode: "Version mismatch",
4885
4907
  needsAcknowledgementCode: "Acknowledgement required",
4908
+ gitMissingCode: "git missing",
4909
+ githubSource: "installed from a GitHub repository",
4886
4910
  installedSection: "Installed",
4887
4911
  installedVersion: "installed v{version}",
4888
4912
  latestVersion: "latest v{version}",
@@ -4904,6 +4928,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4904
4928
  updateTransportFailed: "The update request could not be delivered. Please retry.",
4905
4929
  checkUpdate: "Check for updates",
4906
4930
  upToDate: "Up to date",
4931
+ github: "GitHub",
4907
4932
  enabledSwitch: "Enable",
4908
4933
  toggleFailed: "Failed to update. Please retry.",
4909
4934
  hotApplyNote: "takes effect without a restart"
@@ -5058,7 +5083,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5058
5083
  }
5059
5084
  //#endregion
5060
5085
  //#region \0dsh-plugin-shop-css:e3675a89
5061
- 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.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 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.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";
5086
+ 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.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 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: row;\n align-items: center;\n flex-wrap: wrap;\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 52px;\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 /* Basis 0: the header takes only the space the action buttons leave,\n * so long names truncate instead of wrapping the buttons off the row. */\n flex: 1 1 0;\n min-width: 0;\n padding: 8px 12px;\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: nowrap;\n gap: 6px;\n}\n\n/* The action area is virtual: its children lay out as direct card rows. */\n.s9b5048_cardActions {\n display: contents;\n}\n\n/* The first action owns the right edge; the rest follow it. */\n.s9b5048_cardActions > :first-child {\n margin-left: auto;\n}\n\n/* A flow that outgrows the row — install/uninstall gate, log, notices, the\n * expanded body — breaks onto its own full-width line below it. Nested\n * blocks inside those panels ignore the basis. */\n.s65f383_card > .sc4f2ac_gate,\n.s65f383_card > .s4d374c_installPanel,\n.s65f383_card > .sf53ca0_installedActions,\n.s65f383_card > .s820ebf_body {\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.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 /* A wrapping pill would stretch the single-line card to two lines. */\n white-space: nowrap;\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 margin-top: 2px;\n padding: 10px 12px 12px;\n border-top: 1px solid var(--dsw-alias-border-l1);\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";
5062
5087
  if (typeof document !== "undefined" && !document.querySelector("style[data-plugin-css=\"e3675a89\"]")) {
5063
5088
  const tag = document.createElement("style");
5064
5089
  tag.dataset.pluginCss = "e3675a89";
@@ -5086,6 +5111,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5086
5111
  "versionText": "sb9b924_versionText",
5087
5112
  "updateSelfButton": "sa04455_updateSelfButton",
5088
5113
  "checkUpdateButton": "s614a38_checkUpdateButton",
5114
+ "githubLink": "sa987f7_githubLink",
5089
5115
  "selfUpdatePanel": "sb81ba5_selfUpdatePanel",
5090
5116
  "searchInput": "s232ec4_searchInput",
5091
5117
  "staleBlock": "s1555aa_staleBlock",
@@ -5101,17 +5127,22 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5101
5127
  "cardsSentry": "sd7ee01_cardsSentry",
5102
5128
  "showingLine": "s705cc6_showingLine",
5103
5129
  "cardSpine": "s4ce25d_cardSpine",
5104
- "cardCover": "s9e2bec_cardCover",
5105
- "coverLabel": "s1a7c5b_coverLabel",
5130
+ "categoryBadge": "s9c8eaa_categoryBadge",
5106
5131
  "entryHeader": "sa97234_entryHeader",
5107
5132
  "name": "sc3f149_name",
5108
5133
  "badges": "s513026_badges",
5134
+ "cardActions": "s9b5048_cardActions",
5135
+ "gate": "sc4f2ac_gate",
5136
+ "installPanel": "s4d374c_installPanel",
5137
+ "installedActions": "sf53ca0_installedActions",
5138
+ "body": "s820ebf_body",
5139
+ "cardVersion": "sc6ec6e_cardVersion",
5140
+ "sourceBadge": "s71f10f_sourceBadge",
5109
5141
  "tierBadge": "s5e82cb_tierBadge",
5110
5142
  "unclaimedBadge": "s23e3e1_unclaimedBadge",
5111
5143
  "starsBadge": "sff43b4_starsBadge",
5112
5144
  "chevron": "s73f7fb_chevron",
5113
5145
  "chevronOpen": "s61393c_chevronOpen",
5114
- "body": "s820ebf_body",
5115
5146
  "summary": "sb8fbc8_summary",
5116
5147
  "summaryZh": "se507db_summaryZh",
5117
5148
  "summaryExpanded": "s10baf0_summaryExpanded",
@@ -5123,15 +5154,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5123
5154
  "detailRows": "se98179_detailRows",
5124
5155
  "detailRow": "sfb4d2f_detailRow",
5125
5156
  "reviewedLine": "sd58c6d_reviewedLine",
5126
- "installPanel": "s4d374c_installPanel",
5127
5157
  "installButton": "se7aaed_installButton",
5128
5158
  "confirmButton": "sb43a49_confirmButton",
5129
5159
  "cancelButton": "s6e45d1_cancelButton",
5130
5160
  "installedLabel": "sb6fc74_installedLabel",
5131
- "installedActions": "sf53ca0_installedActions",
5132
5161
  "uninstallButton": "s96967f_uninstallButton",
5133
5162
  "restartButton": "s6ac237_restartButton",
5134
- "gate": "sc4f2ac_gate",
5135
5163
  "gateTitle": "s559c38_gateTitle",
5136
5164
  "gateBody": "s3c0ace_gateBody",
5137
5165
  "gateActions": "s4b2373_gateActions",
@@ -5160,6 +5188,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5160
5188
  * Everything the tab renders is text — summaries, capabilities, log lines,
5161
5189
  * details — never markup, so hostile npm descriptions cannot inject (spec
5162
5190
  * §11.3.4): no render path here may ever use dangerouslySetInnerHTML. */
5191
+ /** The project's home on GitHub, linked from the toolbar. */
5192
+ const SHOP_REPO_URL = "https://github.com/LivXue/dsh-plugin-shop";
5163
5193
  function ChevronIcon({ open }) {
5164
5194
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
5165
5195
  viewBox: "0 0 16 16",
@@ -5193,14 +5223,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5193
5223
  className: _dsh_plugin_shop_css_e3675a89_default.cardSpine,
5194
5224
  "aria-hidden": "true"
5195
5225
  }),
5196
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5197
- className: _dsh_plugin_shop_css_e3675a89_default.cardCover,
5198
- "aria-hidden": "true",
5199
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5200
- className: _dsh_plugin_shop_css_e3675a89_default.coverLabel,
5201
- children: t(categoryKey(entry))
5202
- })
5203
- }),
5204
5226
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
5205
5227
  type: "button",
5206
5228
  className: _dsh_plugin_shop_css_e3675a89_default.entryHeader,
@@ -5213,6 +5235,28 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5213
5235
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5214
5236
  className: _dsh_plugin_shop_css_e3675a89_default.badges,
5215
5237
  children: [
5238
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5239
+ className: _dsh_plugin_shop_css_e3675a89_default.categoryBadge,
5240
+ children: t(categoryKey(entry))
5241
+ }),
5242
+ entry.source === "github" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5243
+ className: _dsh_plugin_shop_css_e3675a89_default.sourceBadge,
5244
+ role: "img",
5245
+ "aria-label": t("githubSource"),
5246
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
5247
+ viewBox: "0 0 16 16",
5248
+ width: "12",
5249
+ height: "12",
5250
+ "aria-hidden": "true",
5251
+ fill: "currentColor",
5252
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z" })
5253
+ })
5254
+ }),
5255
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5256
+ className: _dsh_plugin_shop_css_e3675a89_default.cardVersion,
5257
+ "data-card-version": true,
5258
+ children: ["v", displayVersion(entry)]
5259
+ }),
5216
5260
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5217
5261
  className: _dsh_plugin_shop_css_e3675a89_default.tierBadge,
5218
5262
  "data-tier": entry.tier,
@@ -5232,15 +5276,46 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5232
5276
  ]
5233
5277
  })]
5234
5278
  }),
5235
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5279
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5280
+ className: _dsh_plugin_shop_css_e3675a89_default.cardActions,
5281
+ children: installed === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstallPanel, {
5282
+ name: entry.name,
5283
+ version: entry.version,
5284
+ tier: entry.tier,
5285
+ t,
5286
+ install,
5287
+ installStatus,
5288
+ restart
5289
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [installed.outdated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstallPanel, {
5290
+ name: entry.name,
5291
+ version: entry.version,
5292
+ tier: entry.tier,
5293
+ variant: "update",
5294
+ t,
5295
+ install,
5296
+ installStatus,
5297
+ restart
5298
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5299
+ className: _dsh_plugin_shop_css_e3675a89_default.installedLabel,
5300
+ "data-shop-installed": true,
5301
+ children: t("installed")
5302
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UninstallPanel, {
5303
+ name: entry.name,
5304
+ t,
5305
+ uninstall,
5306
+ installStatus,
5307
+ restart
5308
+ })] })
5309
+ }),
5310
+ open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5236
5311
  className: _dsh_plugin_shop_css_e3675a89_default.body,
5237
5312
  children: [
5238
5313
  summary !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5239
- className: open ? `${_dsh_plugin_shop_css_e3675a89_default.summary} ${_dsh_plugin_shop_css_e3675a89_default.summaryExpanded}` : _dsh_plugin_shop_css_e3675a89_default.summary,
5314
+ className: `${_dsh_plugin_shop_css_e3675a89_default.summary} ${_dsh_plugin_shop_css_e3675a89_default.summaryExpanded}`,
5240
5315
  children: summary.en
5241
5316
  }),
5242
5317
  summary?.zh !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5243
- className: open ? `${_dsh_plugin_shop_css_e3675a89_default.summaryZh} ${_dsh_plugin_shop_css_e3675a89_default.summaryZhExpanded}` : _dsh_plugin_shop_css_e3675a89_default.summaryZh,
5318
+ className: `${_dsh_plugin_shop_css_e3675a89_default.summaryZh} ${_dsh_plugin_shop_css_e3675a89_default.summaryZhExpanded}`,
5244
5319
  children: summary.zh
5245
5320
  }),
5246
5321
  entry.catalog !== void 0 && entry.catalog.capabilities.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -5253,7 +5328,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5253
5328
  children: entry.catalog.capabilities.map((capability) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: capability }, capability))
5254
5329
  })]
5255
5330
  }),
5256
- open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
5331
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
5257
5332
  id: detailId,
5258
5333
  className: _dsh_plugin_shop_css_e3675a89_default.detail,
5259
5334
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dl", {
@@ -5284,37 +5359,6 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5284
5359
  current: entry.version
5285
5360
  })
5286
5361
  })]
5287
- }),
5288
- installed === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstallPanel, {
5289
- name: entry.name,
5290
- version: entry.version,
5291
- tier: entry.tier,
5292
- t,
5293
- install,
5294
- installStatus,
5295
- restart
5296
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5297
- className: _dsh_plugin_shop_css_e3675a89_default.installedActions,
5298
- children: [installed.outdated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstallPanel, {
5299
- name: entry.name,
5300
- version: entry.version,
5301
- tier: entry.tier,
5302
- variant: "update",
5303
- t,
5304
- install,
5305
- installStatus,
5306
- restart
5307
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5308
- className: _dsh_plugin_shop_css_e3675a89_default.installedLabel,
5309
- "data-shop-installed": true,
5310
- children: t("installed")
5311
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UninstallPanel, {
5312
- name: entry.name,
5313
- t,
5314
- uninstall,
5315
- installStatus,
5316
- restart
5317
- })]
5318
5362
  })
5319
5363
  ]
5320
5364
  })
@@ -5597,7 +5641,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5597
5641
  * installed-state RPC behind it — the tab assumes an installed plugin is on,
5598
5642
  * so the first toggle sends the inverted value. After a successful `setEnabled`
5599
5643
  * the §8 hot note renders. */
5600
- function OutdatedRow({ row, tier, t, setEnabled, install, installStatus, restart }) {
5644
+ function OutdatedRow({ row, tier, source, t, setEnabled, install, installStatus, restart }) {
5601
5645
  const [enabled, setEnabledState] = (0, react.useState)(true);
5602
5646
  const [toggle, setToggle] = (0, react.useState)({ kind: "idle" });
5603
5647
  const onToggle = async () => {
@@ -5636,7 +5680,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5636
5680
  className: _dsh_plugin_shop_css_e3675a89_default.outdatedVersions,
5637
5681
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5638
5682
  className: _dsh_plugin_shop_css_e3675a89_default.outdatedVersion,
5639
- children: t("installedVersion", { version: row.installed })
5683
+ children: t("installedVersion", { version: source === "github" ? row.installed.slice(0, 7) : row.installed })
5640
5684
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5641
5685
  className: _dsh_plugin_shop_css_e3675a89_default.outdatedVersion,
5642
5686
  children: t("latestVersion", { version: row.latest })
@@ -5686,7 +5730,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5686
5730
  * tier for the update gate is looked up from the catalog by name (community →
5687
5731
  * acknowledgement); an entry absent from the catalog defaults to the
5688
5732
  * community gate (the safer read). */
5689
- function OutdatedSection({ state, tiers, t, setEnabled, install, installStatus, restart }) {
5733
+ function OutdatedSection({ state, tiers, sources, t, setEnabled, install, installStatus, restart }) {
5690
5734
  if (state.kind === "loading") return null;
5691
5735
  if (state.kind === "error") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5692
5736
  className: _dsh_plugin_shop_css_e3675a89_default.stateLine,
@@ -5706,6 +5750,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5706
5750
  children: outdated.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OutdatedRow, {
5707
5751
  row,
5708
5752
  tier: tiers.get(row.name) ?? "community",
5753
+ source: sources.get(row.name) ?? "npm",
5709
5754
  t,
5710
5755
  setEnabled,
5711
5756
  install,
@@ -5808,7 +5853,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5808
5853
  if (catalogState.kind !== "ready") return [];
5809
5854
  const q = query.trim().toLowerCase();
5810
5855
  return catalogState.result.plugins.filter((entry) => {
5811
- if (isShopLike(entry.name)) return false;
5856
+ if (isShopLike(entry.name) || entry.repo !== void 0 && isShopLike(entry.repo)) return false;
5812
5857
  if (category === "installed") {
5813
5858
  if (!installedByName.has(entry.name)) return false;
5814
5859
  } else if (category !== null && categoryKey(entry) !== categoryLocaleKey(category)) return false;
@@ -5844,7 +5889,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5844
5889
  const categoryCounts = (0, react.useMemo)(() => {
5845
5890
  const counts = /* @__PURE__ */ new Map();
5846
5891
  if (catalogState.kind === "ready") for (const entry of catalogState.result.plugins) {
5847
- if (isShopLike(entry.name)) continue;
5892
+ if (isShopLike(entry.name) || entry.repo !== void 0 && isShopLike(entry.repo)) continue;
5848
5893
  const key = categoryKey(entry);
5849
5894
  const bare = CATEGORY_ORDER.find((c) => categoryLocaleKey(c) === key);
5850
5895
  if (bare !== void 0) counts.set(bare, (counts.get(bare) ?? 0) + 1);
@@ -5860,6 +5905,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5860
5905
  if (catalogState.kind === "ready") for (const entry of catalogState.result.plugins) map.set(entry.name, entry.tier);
5861
5906
  return map;
5862
5907
  }, [catalogState]);
5908
+ const sources = (0, react.useMemo)(() => {
5909
+ const map = /* @__PURE__ */ new Map();
5910
+ if (catalogState.kind === "ready") for (const entry of catalogState.result.plugins) map.set(entry.name, entry.source);
5911
+ return map;
5912
+ }, [catalogState]);
5863
5913
  if (catalogState.kind === "loading") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5864
5914
  className: _dsh_plugin_shop_css_e3675a89_default.panel,
5865
5915
  "data-shop-tab": true,
@@ -5914,44 +5964,63 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5914
5964
  }
5915
5965
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5916
5966
  className: _dsh_plugin_shop_css_e3675a89_default.toolbarRight,
5917
- children: [result.stale && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5918
- className: _dsh_plugin_shop_css_e3675a89_default.staleBlock,
5919
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5920
- className: _dsh_plugin_shop_css_e3675a89_default.staleBadge,
5921
- children: t("staleLabel", { date: result.builtAt.slice(0, 10) })
5922
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5923
- type: "button",
5924
- className: _dsh_plugin_shop_css_e3675a89_default.actionButton,
5925
- onClick: () => setRequest({ kind: "refresh" }),
5926
- children: t("refresh")
5927
- })]
5928
- }), selfVersion !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5929
- className: _dsh_plugin_shop_css_e3675a89_default.versionBlock,
5930
- children: [
5931
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5932
- className: _dsh_plugin_shop_css_e3675a89_default.versionText,
5933
- "data-shop-version": true,
5934
- children: ["v", selfVersion.installed]
5935
- }),
5936
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5937
- type: "button",
5938
- className: _dsh_plugin_shop_css_e3675a89_default.checkUpdateButton,
5939
- "data-shop-check-update": true,
5940
- disabled: checkState !== "idle",
5941
- onClick: () => void checkVersion(),
5942
- children: checkState === "up-to-date" ? t("upToDate") : t("checkUpdate")
5943
- }),
5944
- selfVersion.outdated && selfVersion.latest !== null && selfUpdate.view.kind === "idle" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5967
+ children: [
5968
+ result.stale && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5969
+ className: _dsh_plugin_shop_css_e3675a89_default.staleBlock,
5970
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5971
+ className: _dsh_plugin_shop_css_e3675a89_default.staleBadge,
5972
+ children: t("staleLabel", { date: result.builtAt.slice(0, 10) })
5973
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5945
5974
  type: "button",
5946
- className: _dsh_plugin_shop_css_e3675a89_default.updateSelfButton,
5947
- "data-shop-update-self": true,
5948
- onClick: () => {
5949
- if (selfVersion.latest !== null) selfUpdate.start({ version: selfVersion.latest });
5950
- },
5951
- children: t("update")
5975
+ className: _dsh_plugin_shop_css_e3675a89_default.actionButton,
5976
+ onClick: () => setRequest({ kind: "refresh" }),
5977
+ children: t("refresh")
5978
+ })]
5979
+ }),
5980
+ selfVersion !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5981
+ className: _dsh_plugin_shop_css_e3675a89_default.versionBlock,
5982
+ children: [
5983
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5984
+ className: _dsh_plugin_shop_css_e3675a89_default.versionText,
5985
+ "data-shop-version": true,
5986
+ children: ["v", selfVersion.installed]
5987
+ }),
5988
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5989
+ type: "button",
5990
+ className: _dsh_plugin_shop_css_e3675a89_default.checkUpdateButton,
5991
+ "data-shop-check-update": true,
5992
+ disabled: checkState !== "idle",
5993
+ onClick: () => void checkVersion(),
5994
+ children: checkState === "up-to-date" ? t("upToDate") : t("checkUpdate")
5995
+ }),
5996
+ selfVersion.outdated && selfVersion.latest !== null && selfUpdate.view.kind === "idle" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5997
+ type: "button",
5998
+ className: _dsh_plugin_shop_css_e3675a89_default.updateSelfButton,
5999
+ "data-shop-update-self": true,
6000
+ onClick: () => {
6001
+ if (selfVersion.latest !== null) selfUpdate.start({ version: selfVersion.latest });
6002
+ },
6003
+ children: t("update")
6004
+ })
6005
+ ]
6006
+ }),
6007
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
6008
+ className: _dsh_plugin_shop_css_e3675a89_default.githubLink,
6009
+ href: SHOP_REPO_URL,
6010
+ target: "_blank",
6011
+ rel: "noopener noreferrer",
6012
+ "aria-label": t("github"),
6013
+ "data-shop-github": true,
6014
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
6015
+ viewBox: "0 0 16 16",
6016
+ width: "16",
6017
+ height: "16",
6018
+ "aria-hidden": "true",
6019
+ fill: "currentColor",
6020
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z" })
5952
6021
  })
5953
- ]
5954
- })]
6022
+ })
6023
+ ]
5955
6024
  })]
5956
6025
  }),
5957
6026
  selfUpdate.view.kind === "running" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
@@ -6069,7 +6138,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6069
6138
  className: _dsh_plugin_shop_css_e3675a89_default.cards,
6070
6139
  children: [visible.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EntryCard, {
6071
6140
  entry,
6072
- stars: stars[entry.name],
6141
+ stars: stars[entry.repo ?? entry.name],
6073
6142
  installed: installedByName.get(entry.name),
6074
6143
  t,
6075
6144
  install,
@@ -6093,6 +6162,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6093
6162
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OutdatedSection, {
6094
6163
  state: installedState,
6095
6164
  tiers,
6165
+ sources,
6096
6166
  t,
6097
6167
  setEnabled,
6098
6168
  install,