dsh-plugin-shop 0.8.6 → 0.8.7-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -4321,12 +4321,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4321
4321
  "profiles": union([_undefined(), array(string())]).optional()
4322
4322
  })]).optional(),
4323
4323
  "dshPeers": union([_undefined(), record(string(), string())]).optional(),
4324
+ "manifestVersion": union([_undefined(), string()]).optional(),
4324
4325
  "unpackedSize": union([_undefined(), number()]).optional(),
4325
4326
  "installSize": union([_undefined(), number()]).optional(),
4326
4327
  "archived": union([
4327
4328
  _undefined(),
4328
4329
  literal(false),
4329
4330
  literal(true)
4331
+ ]).optional(),
4332
+ "forked": union([
4333
+ _undefined(),
4334
+ literal(false),
4335
+ literal(true)
4330
4336
  ]).optional()
4331
4337
  })),
4332
4338
  "denied": array(object({
@@ -6484,10 +6490,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6484
6490
  *
6485
6491
  * The second is the one harness reason that blocks (design
6486
6492
  * 2026-09-26-dsh-017-readiness, B1, amending "warn, never block"): dsh 0.1.7
6487
- * refuses the install itself, before pnpm runs, and the shop cannot install
6488
- * past it. The verdict is dsh's own rule on the profile's own exemptions, so
6489
- * a disabled button is dsh's answer given early, never the shop's opinion —
6490
- * and recording the exemption the card shows is what enables it again.
6493
+ * refuses the install itself, an npm entry before pnpm runs and a github
6494
+ * entry after it, rolling the profile back, and the shop cannot install past
6495
+ * it. The verdict is dsh's own rule on the profile's own exemptions, whichever
6496
+ * channel the entry comes from (design
6497
+ * 2026-09-28-bundle-components-and-github-peers, section 7.3), so a disabled
6498
+ * button is dsh's answer given early, never the shop's opinion — and
6499
+ * recording the exemption the card shows is what enables it again.
6491
6500
  */
6492
6501
  function refusesInstall(blockers) {
6493
6502
  return blockers.some((blocker) => blocker.kind === "name-taken" || blocker.kind === "harness-peers");
@@ -6542,15 +6551,61 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6542
6551
  case "desktop-profile": return "desktopProfileCode";
6543
6552
  }
6544
6553
  }
6545
- /** The version a card displays: full for npm, the short commit for github. */
6546
- function displayVersion(entry) {
6547
- return entry.source === "github" ? entry.version.slice(0, 7) : entry.version;
6554
+ /** A commit pin as the registry writes one: forty lowercase hex. */
6555
+ const COMMIT_PIN = /^[0-9a-f]{40}$/;
6556
+ /**
6557
+ * How a version string reads on the shelf, decided by its source and its
6558
+ * shape alone (market borrowings §11).
6559
+ *
6560
+ * Shape, never the entry's `tarball`, because an installed row carries only
6561
+ * the pin the shop recorded at install — the catalog `version` of that moment
6562
+ * — and no `tarball`. The registry pins a commit as forty lowercase hex and a
6563
+ * release by its tag, so the two shapes do not cross; `present.test.ts`
6564
+ * checks that against the committed live catalog.
6565
+ *
6566
+ * - An npm version takes the `v` the shelf has always shown, and only before
6567
+ * a digit: an installed row reports the manifest's spec, and a range there
6568
+ * (`^1.2.3`) is not a version to prefix.
6569
+ * - A commit is cut to the seven characters git abbreviates to, with no `v`:
6570
+ * it is not a version number.
6571
+ * - A release tag prints exactly as tagged, never cut and never prefixed:
6572
+ * cutting `v0.29.10` to seven characters printed `v0.29.1`, another
6573
+ * version, and the `v` this function no longer adds doubled the tag's own.
6574
+ */
6575
+ function versionLabel(source, version) {
6576
+ if (source === "npm") return {
6577
+ kind: "npm",
6578
+ text: /^\d/.test(version) ? `v${version}` : version,
6579
+ value: version
6580
+ };
6581
+ if (COMMIT_PIN.test(version)) {
6582
+ const short = version.slice(0, 7);
6583
+ return {
6584
+ kind: "commit",
6585
+ text: short,
6586
+ value: short
6587
+ };
6588
+ }
6589
+ return {
6590
+ kind: "release",
6591
+ text: version,
6592
+ value: version
6593
+ };
6594
+ }
6595
+ /** The copy that names a version's kind, for the accessible name and the
6596
+ * tooltip; its `{version}` takes {@link VersionLabel.value}. */
6597
+ function versionLabelKey(kind) {
6598
+ switch (kind) {
6599
+ case "npm": return "npmVersionLabel";
6600
+ case "commit": return "commitVersionLabel";
6601
+ case "release": return "releaseVersionLabel";
6602
+ }
6548
6603
  }
6549
6604
  /** The hash pin a stale line names for a repo or release-rescued entry — the
6550
6605
  * reviewed commit, or the reviewed release tarball sha256 (market borrowings
6551
- * §3.1). Shortened to 7 chars like the card's own version; '' only for a
6552
- * hand-built fixture carrying no pin. The npm form is a version and keeps the
6553
- * version line's `v` prefix instead. */
6606
+ * §3.1). Shortened to 7 chars like a commit on the card; '' only for a
6607
+ * hand-built fixture carrying no pin. The npm form is a version, which
6608
+ * {@link versionLabel} prints instead. */
6554
6609
  function reviewHashPin(review) {
6555
6610
  return (review.reviewedCommit ?? review.reviewedSha256 ?? "").slice(0, 7);
6556
6611
  }
@@ -6675,6 +6730,24 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6675
6730
  }
6676
6731
  }
6677
6732
  }
6733
+ /**
6734
+ * Which order the shelf is in (design 2026-10-12-shelf-orders). `stars` is
6735
+ * the default AND the tie-break of both date orders, so the two date orders
6736
+ * are stable re-sorts of it rather than orders with comparators of their own.
6737
+ */
6738
+ const SHELF_ORDER_KEYS = {
6739
+ stars: "sortStars",
6740
+ added: "sortAdded",
6741
+ updated: "sortUpdated"
6742
+ };
6743
+ /** The three orders in display order (map insertion order). `readonly`: this
6744
+ * is a shared vocabulary, and an order list a caller can push to is a bug
6745
+ * waiting for its second caller. */
6746
+ const SHELF_ORDERS = Object.keys(SHELF_ORDER_KEYS);
6747
+ /** The locale key naming one order (the toggle's buttons). */
6748
+ function shelfOrderLocaleKey(order) {
6749
+ return SHELF_ORDER_KEYS[order];
6750
+ }
6678
6751
  const CATEGORY_KEYS = {
6679
6752
  tool: "categoryTool",
6680
6753
  provider: "categoryProvider",
@@ -6821,6 +6894,77 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6821
6894
  return NAME_COLLATOR.compare(a.name, b.name);
6822
6895
  });
6823
6896
  }
6897
+ /** The day form the registry writes into `added` (`emit.ts` E9 enforces it
6898
+ * on every build on its side; a catalog is untrusted input on this side). */
6899
+ const DAY_FORM = /^\d{4}-\d{2}-\d{2}$/;
6900
+ /**
6901
+ * The ES date-time form WITH an explicit zone. The zone is required, and not
6902
+ * for tidiness: a date-time string with no zone is parsed as LOCAL time, so
6903
+ * accepting one would make the shelf's order depend on the machine.
6904
+ */
6905
+ const INSTANT_FORM = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?(?:Z|[+-]\d{2}:\d{2})$/;
6906
+ /**
6907
+ * One date string as milliseconds, or undefined when it cannot be trusted.
6908
+ *
6909
+ * The form is checked BEFORE `Date.parse`, and that order is load-bearing:
6910
+ * V8 answers a real instant for `2026-02-31T00:00:00Z` (3 March) and for
6911
+ * `October 12` (a 2001 date), so `Date.parse` alone is not a validity test.
6912
+ */
6913
+ function parseForm(value, form) {
6914
+ if (typeof value !== "string" || !form.test(value)) return void 0;
6915
+ const ms = Date.parse(value);
6916
+ return Number.isNaN(ms) ? void 0 : ms;
6917
+ }
6918
+ /** `shelfTime` with the catalog's build time already parsed, for the n calls
6919
+ * `orderShelf` makes in one pass. */
6920
+ function timeOf(entry, order, built) {
6921
+ if (order === "stars") return void 0;
6922
+ const ms = order === "added" ? parseForm(entry.added, DAY_FORM) : parseForm(entry.publishedAt, INSTANT_FORM);
6923
+ if (ms === void 0 || built === void 0 || ms > built) return void 0;
6924
+ return ms;
6925
+ }
6926
+ /**
6927
+ * The entry's sort key under a date order, or undefined when it has no usable
6928
+ * date (design §3): absent, off-form, unparseable, or later than the catalog's
6929
+ * own `builtAt`. Milliseconds since the epoch. `'stars'` carries no date, so
6930
+ * this answers undefined for it.
6931
+ */
6932
+ function shelfTime(entry, order, builtAt) {
6933
+ return timeOf(entry, order, parseForm(builtAt, INSTANT_FORM));
6934
+ }
6935
+ /**
6936
+ * A UTC calendar day for display: "2026-10-12". Derived from the instant the
6937
+ * sort ranked by, so a card's label and its position cannot disagree. UTC and
6938
+ * never a local getter: a reader in UTC+8 must see the same days the catalog
6939
+ * was built in, the calendar the stats row already prints.
6940
+ */
6941
+ function utcDay(ms) {
6942
+ return new Date(ms).toISOString().slice(0, 10);
6943
+ }
6944
+ /**
6945
+ * The shelf in `order` (design 2026-10-12-shelf-orders §2, §5). `'stars'`
6946
+ * returns the array it was handed, so the default path allocates nothing and
6947
+ * does not invalidate the memos downstream of it.
6948
+ *
6949
+ * Each key is computed ONCE per entry rather than inside the comparator, and
6950
+ * the sort is stable (ES2019), so entries sharing a date keep the star order
6951
+ * they arrived in — which is how the tie-break is defined without a second
6952
+ * comparator that could drift from `sortByStars`.
6953
+ */
6954
+ function orderShelf(starOrdered, order, builtAt) {
6955
+ if (order === "stars") return starOrdered;
6956
+ const built = parseForm(builtAt, INSTANT_FORM);
6957
+ const keyed = starOrdered.map((entry) => ({
6958
+ entry,
6959
+ time: timeOf(entry, order, built)
6960
+ }));
6961
+ keyed.sort((a, b) => {
6962
+ if (a.time === void 0) return b.time === void 0 ? 0 : 1;
6963
+ if (b.time === void 0) return -1;
6964
+ return b.time - a.time;
6965
+ });
6966
+ return keyed.map((item) => item.entry);
6967
+ }
6824
6968
  /** 999 → "999"; 1000 → "1k"; 1234 → "1.2k"; 1500 → "1.5k"; 99999 → "100k". */
6825
6969
  function formatStars(n) {
6826
6970
  if (n < 1e3) return String(n);
@@ -6893,6 +7037,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6893
7037
  all: "全部",
6894
7038
  catalog: "插件目录",
6895
7039
  catalogStats: "{count} 个插件 · 构建于 {date}",
7040
+ /** The shelf's order control, and the date each card carries under the two
7041
+ * date orders (design 2026-10-12-shelf-orders §4). */
7042
+ sortLabel: "排序",
7043
+ sortStars: "星标",
7044
+ sortAdded: "最新上架",
7045
+ sortUpdated: "最近更新",
7046
+ listedOn: "上架 {date}",
7047
+ updatedOn: "更新 {date}",
6896
7048
  stars: "{count} 星",
6897
7049
  categoryTool: "工具",
6898
7050
  categoryProvider: "模型服务",
@@ -6977,7 +7129,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6977
7129
  emptyIncompatibleFiltered: "匹配的插件都标为不兼容,已被「隐藏不兼容」筛掉。",
6978
7130
  license: "许可",
6979
7131
  customLicense: "自定义许可",
6980
- reviewedVersionLine: "已审核 v{reviewed} / 当前 v{current} 未审核",
7132
+ reviewedVersionLine: "已审核 {reviewed} / 当前 {current} 未审核",
6981
7133
  reviewedPinLine: "已审核 {pin} / 当前 {current} 未审核",
6982
7134
  deniedCode: "已拒绝",
6983
7135
  notInCatalogCode: "不在目录中",
@@ -6991,6 +7143,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6991
7143
  nameTakenDetail: "已安装同名的另一个插件({holder})。\n两个同名插件声明相同的加载项 id,无法共存;安装它会替换掉已装的那个。",
6992
7144
  npmSource: "npm 包",
6993
7145
  githubSource: "源码在 GitHub",
7146
+ /** The accessible name and the tooltip of a card's version, which say which
7147
+ * of three things the printed string is (market borrowings §11): the string
7148
+ * alone cannot tell a release tag from a version number, and a short commit
7149
+ * printed with a `v` read as one. */
7150
+ npmVersionLabel: "npm 版本 {version}",
7151
+ commitVersionLabel: "GitHub 提交 {version}",
7152
+ releaseVersionLabel: "GitHub 发布 {version}",
6994
7153
  /** The badge and the explanation for an installed github entry no catalog
6995
7154
  * row claims (C10 MVP). The mechanism synthesizes a row for ANY github spec
6996
7155
  * with no catalog row — shadowed by a same-named npm package, an unlisted
@@ -7000,8 +7159,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7000
7159
  shadowedBadge: "目录未列出此包",
7001
7160
  shadowedDetail: "此包已安装,但当前的插件目录没有它(可能是被同名 npm 包遮蔽、作者已下架、或目录尚未收录);它照常运行。",
7002
7161
  updatableSection: "可更新",
7003
- installedVersion: "已安装 v{version}",
7004
- latestVersion: "最新 v{version}",
7162
+ /** `{version}` is the shelf's own version text (`versionLabel`), which
7163
+ * carries the `v` an npm version takes and no other kind does. */
7164
+ installedVersion: "已安装 {version}",
7165
+ latestVersion: "最新 {version}",
7005
7166
  update: "更新",
7006
7167
  installed: "已安装",
7007
7168
  uninstall: "卸载",
@@ -7057,6 +7218,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7057
7218
  all: "All",
7058
7219
  catalog: "Plugin catalog",
7059
7220
  catalogStats: "{count} packages · built {date}",
7221
+ /** The shelf's order control, and the date each card carries under the two
7222
+ * date orders (design 2026-10-12-shelf-orders §4). */
7223
+ sortLabel: "Sort",
7224
+ sortStars: "Stars",
7225
+ sortAdded: "Newest",
7226
+ sortUpdated: "Recently updated",
7227
+ listedOn: "Listed {date}",
7228
+ updatedOn: "Updated {date}",
7060
7229
  stars: "{count} stars",
7061
7230
  categoryTool: "Tool",
7062
7231
  categoryProvider: "Provider",
@@ -7117,7 +7286,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7117
7286
  emptyIncompatibleFiltered: "Every matching plugin is marked incompatible, and the incompatible filter is leaving them out.",
7118
7287
  license: "License",
7119
7288
  customLicense: "Custom license",
7120
- reviewedVersionLine: "reviewed v{reviewed} / current v{current} unreviewed",
7289
+ reviewedVersionLine: "reviewed {reviewed} / current {current} unreviewed",
7121
7290
  reviewedPinLine: "reviewed {pin} / current {current} unreviewed",
7122
7291
  deniedCode: "Denied",
7123
7292
  notInCatalogCode: "Not in the catalog",
@@ -7131,11 +7300,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
7131
7300
  nameTakenDetail: "A different plugin of this name is installed ({holder}).\nTwo plugins of one name declare the same loader entry id and cannot both load; installing this one would replace it.",
7132
7301
  npmSource: "npm package",
7133
7302
  githubSource: "source on GitHub",
7303
+ npmVersionLabel: "npm version {version}",
7304
+ commitVersionLabel: "GitHub commit {version}",
7305
+ releaseVersionLabel: "GitHub release {version}",
7134
7306
  shadowedBadge: "Not in the current catalog",
7135
7307
  shadowedDetail: "This package is installed but the current catalog does not list it (it may be shadowed by a same-named npm package, removed by the author, or never listed); it runs as before.",
7136
7308
  updatableSection: "Updatable",
7137
- installedVersion: "installed v{version}",
7138
- latestVersion: "latest v{version}",
7309
+ installedVersion: "installed {version}",
7310
+ latestVersion: "latest {version}",
7139
7311
  update: "Update",
7140
7312
  installed: "Installed",
7141
7313
  uninstall: "Uninstall",
@@ -9043,124 +9215,127 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9043
9215
  };
9044
9216
  }
9045
9217
  //#endregion
9046
- //#region \0dsh-plugin-shop-css:e3675a89
9047
- const css = "/* Shop tab styles — a market shelf inside the settings surface. The visual\n * language is the dsh theme's alias tokens ONLY (the four tokens the previous\n * styles referenced that do not exist — bg-layer-3, interactive-bg-hover,\n * state-error, state-success — are corrected to bg-layer-2 and the\n * state-*-primary set). The signature is the category spine: one HUE per\n * category (seven fixed hues, not the brand token — the brand resolves to\n * near-black in the light theme, so seven brand opacities read as seven\n * shades of gray), with the category name as the cover label in the same hue. Class names are mapped to content-derived names at bundle time\n * (tsdown.s8583e3_client.s942a79_config.s5f67aa_ts) and stubbed in tests; only the keys are relied\n * on.\n *\n * One token rule the theme forces: the background LAYERS do not carry\n * contrast. In the light theme bg-base, bg-layer-1, bg-layer-2 and\n * bg-layer-3 all resolve to the same white; only the dark theme spreads them\n * (950/875/850/800). So a layer-2 fill on a layer-1 ground is invisible for\n * half of all users, and anything whose fill is its ONLY affordance — the\n * loading skeleton, the borderless capability chips — derives its fill from\n * label-primary instead, which inverts with the theme by construction.\n * Elements that also carry a border or text may keep a layer fill.\n * `css-tokens.s8583e3_client.sfd9d8a_spec.s5f67aa_ts` pins this. */\n\n.s255e5a_panel {\n display: flex;\n flex-direction: column;\n gap: 14px;\n /* The shelf never scrolls sideways: a card too wide for its track wraps\n * its badges rather than widening the panel.\n *\n * Clipped on BOTH axes, with a margin, because `.s232ec4_searchInput` sits at\n * exactly (0, 0) of this box — it is the first child of `.s646509_toolbar`, itself\n * the first child here, and this rule carries no padding — so its focus\n * ring paints ENTIRELY outside the panel.\n *\n * Both axes is the load-bearing part: `overflow-clip-margin` is inert\n * unless the element clips on both. Clipping x alone left the margin doing\n * nothing — measured against the live app on 0.7.1, the ring reached 0 of\n * its 3px on the left at every device pixel ratio (1, 1.25, 1.5, 2), while\n * keeping the full 3px on the right, where the 280px input stops short of\n * the panel and never meets the clip edge. Raising the margin to 6px\n * changed nothing; clipping both axes with the SAME 3px restored the whole\n * ring. A test that reads declarations cannot see that difference, which is\n * why the first attempt at this shipped broken.\n *\n * `clip` is not a scroll container, so this does not bring back the\n * accidental scrolling `hidden` caused by coercing the other axis to\n * `auto`. Nothing is lost vertically: the panel's scrollHeight equals its\n * height and no descendant paints outside the clip rect (measured).\n * `overflow-x: hidden` stays as the fallback for engines without `clip`,\n * where the ring is clipped as it was before, never worse. */\n overflow-x: hidden;\n overflow: clip;\n overflow-clip-margin: 3px;\n}\n\n.s44a3ec_stateLine {\n margin: 0;\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n/* Visually hidden but screen-reader readable (the loading copy while the\n * skeleton stands in visually). */\n.s3a0ece_srOnly {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n border: 0;\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n overflow: hidden;\n white-space: nowrap;\n}\n\n/* The loading shelf: ghost cards with the same geometry as the real grid, so\n * the swap to content is a same-shape handoff instead of a jump. */\n.se0c3b4_skeletonGrid {\n display: grid;\n grid-template-columns: 1fr;\n gap: 8px;\n}\n\n.s520416_skeletonCard {\n position: relative;\n display: flex;\n flex-direction: column;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-1);\n overflow: hidden;\n}\n\n/* The shimmer sweep: one gradient band travels across each ghost card while\n * the bars breathe, so the loading state reads as \"the shelf is scanning\". */\n.s520416_skeletonCard::after {\n content: '';\n position: absolute;\n inset: 0;\n background: linear-gradient(\n 100deg,\n transparent 25%,\n color-mix(in srgb, var(--dsw-alias-label-primary) 14%, transparent) 50%,\n transparent 75%\n );\n transform: translateX(-100%);\n animation: dshShopSkeletonSweep 1.4s ease-in-out infinite;\n pointer-events: none;\n}\n\n@keyframes dshShopSkeletonSweep {\n to { transform: translateX(100%); }\n}\n\n.s8c7d6f_skeletonName {\n width: 55%;\n height: 14px;\n margin: 10px 12px 0;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.sf5179e_skeletonSummary {\n width: 92%;\n height: 12px;\n margin: 10px 12px 0;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.sbb0d25_skeletonSummaryShort {\n width: 68%;\n height: 12px;\n margin: 6px 12px 10px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n/* The ghost of the action line, right where the real buttons land. */\n.s96e910_skeletonActions {\n display: block;\n width: 84px;\n height: 24px;\n margin: 0 12px 10px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.s520416_skeletonCard span {\n animation: dshShopSkeletonPulse 1.6s ease-in-out infinite;\n}\n\n@keyframes dshShopSkeletonPulse {\n 0%, 100% { opacity: 0.5; }\n 50% { opacity: 1; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .s520416_skeletonCard span { animation: none; opacity: 0.75; }\n .s520416_skeletonCard::after { content: none; }\n}\n\n.s73242a_actionButton {\n align-self: flex-start;\n padding: 5px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.s73242a_actionButton:hover {\n border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l2));\n color: var(--dsw-alias-brand-primary);\n}\n\n.s73242a_actionButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.s646509_toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.s745e3c_toolbarRight {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n/* The shop's own version, right of the search box: `v0.4.4`, plus the\n * update button when the version check found a newer release. */\n.s4fe61f_versionBlock {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.sb9b924_versionText {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sa04455_updateSelfButton {\n padding: 2px 10px;\n border: 1px solid var(--dsw-alias-brand-primary);\n border-radius: 999px;\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n color: var(--dsw-alias-brand-primary);\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n}\n\n.sa04455_updateSelfButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 20%, transparent);\n}\n\n.sa04455_updateSelfButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The on-demand re-check next to the version number: a quiet pill; the\n * border and text carry it in the light theme where bg layers are white. */\n.s614a38_checkUpdateButton {\n padding: 2px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n cursor: pointer;\n}\n\n.s614a38_checkUpdateButton:hover {\n border-color: var(--dsw-alias-label-secondary);\n color: var(--dsw-alias-label-primary);\n}\n\n.s614a38_checkUpdateButton:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n.s614a38_checkUpdateButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The project's GitHub mark, right of the version row: a quiet icon link.\n * The octocat carries itself — color shift on hover, no text, no border. */\n.sa987f7_githubLink {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 3px;\n border-radius: 6px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sa987f7_githubLink:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sa987f7_githubLink:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The self-update's expanded states (running log / done + restart /\n * failure detail) sit under the toolbar, above the category bar. */\n.sb81ba5_selfUpdatePanel {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 8px;\n}\n\n.s232ec4_searchInput {\n width: min(280px, 100%);\n padding: 6px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n.s232ec4_searchInput:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* Subdued line under the search box when a dictionary intent fired (design\n * 2026-10-08-search-query-expansion §4). One fact, no badge. */\n.s76fb13_expansionNote {\n margin: 4px 0 0;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s3946f1_staleBadge {\n padding: 2px 8px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary) 40%, transparent);\n color: var(--dsw-alias-state-warn-primary);\n font-size: 12px;\n}\n\n.sb2c859_categoryBar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n margin-top: 6px;\n}\n\n.s26d58e_categoryButton {\n /* The hue this button carries when selected. `All` and `Installed` select by\n * something other than a category and keep the brand token; the six category\n * buttons override it from the shared hue table below, so a pressed Tool tab\n * turns the same blue as a tool card's spine. */\n --category-hue: var(--dsw-alias-brand-primary);\n padding: 3px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n cursor: pointer;\n}\n\n/* Blend text with the theme foreground: darker on light backgrounds, lighter\n * on dark ones. The unblended category hue belongs on the border and fill. */\n.s26d58e_categoryButton:hover {\n border-color: color-mix(in srgb, var(--category-hue) 45%, var(--dsw-alias-border-l2));\n color: color-mix(in srgb, var(--category-hue) 45%, var(--dsw-alias-label-primary) 55%);\n}\n\n/* Selected. Written `.s26d58e_categoryButton.s5f68c4_categoryButtonOn` — TWO classes — so it\n * matches `.s26d58e_categoryButton:hover`'s specificity and, coming after it, wins.\n * As a bare `.s5f68c4_categoryButtonOn` it lost both `border-color` and `color` to the\n * hover rule for as long as the pointer stayed on the tab that had just been\n * clicked, which is precisely when a reader looks for confirmation.\n *\n * EVERY declaration here is a colour or a shadow: a pressed tab must occupy\n * exactly the same box as an unpressed one, so the bar does not reflow — and\n * with ~9 pills on a wrapping row, one that grows can push the rest onto\n * another line and move the tab out from under the pointer that just clicked\n * it. That is what `font-weight: 600` did (bold metrics are wider than\n * regular), and it is why nothing may be added here that resolves to a length:\n * no padding, no border-width, no font-size, no letter-spacing.\n * `css-tokens.s8583e3_client.sfd9d8a_spec.s5f67aa_ts` fails the build on one.\n *\n * `box-shadow` is the exception that earns its place: it paints outside the\n * layout box, so an inset ring doubles the border's weight at zero cost to the\n * metrics. It is what `font-weight: 600` used to be — a second, thickness-\n * based signal that hover does not also produce — and the `other` tab needs it\n * most: its hue IS the neutral gray this file uses for \"no category\", so on\n * the light theme hue alone barely separates pressed from unpressed. */\n.s26d58e_categoryButton.s5f68c4_categoryButtonOn {\n border-color: var(--category-hue);\n background: color-mix(in srgb, var(--category-hue) 14%, transparent);\n color: color-mix(in srgb, var(--category-hue) 45%, var(--dsw-alias-label-primary) 55%);\n box-shadow: inset 0 0 0 1px var(--category-hue);\n}\n\n/* Forced colours replace every colour above with the system palette, which\n * would leave the three colour declarations saying nothing and the pressed tab\n * indistinguishable from its neighbours. `box-shadow` is dropped in this mode\n * too, so the ring has to come back as an outline. */\n@media (forced-colors: active) {\n .s26d58e_categoryButton.s5f68c4_categoryButtonOn {\n outline: 2px solid ButtonText;\n outline-offset: -4px;\n }\n}\n\n.s26d58e_categoryButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The incompatible filter, at the right edge of the category bar: `margin-left:\n * auto` pushes it past the pills, and the bar wraps, so on a narrow panel it\n * drops to its own line rather than squeezing them. It is a MODIFIER, not a\n * ninth category — the categories select what to show and this one subtracts\n * from whatever they selected — so it takes the error hue it filters on rather\n * than joining the hue table.\n *\n * And it is a SWITCH, which is the difference the shape now carries. It used\n * to wear `.s26d58e_categoryButton`: same pill, same row, same size as the eight tabs,\n * differing only in being red. That reads as a ninth tab, and it is not one —\n * the tabs are choose-one and this is on-or-off. So it drops the pill chrome\n * entirely (no border, no fill, nothing to compete with the row it sits\n * beside) and puts the state in a track, the same one every installed card\n * carries. Nothing here restates the switch geometry; `--switch-hue` is the\n * whole of what this control says about it, and it reaches the label too, so\n * one declaration keeps the word and the track the same colour. */\n.sfd887b_incompatibleFilter {\n --switch-hue: var(--dsw-alias-state-error-primary);\n margin-left: auto;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n /* Matches the pills' vertical padding so the bar keeps one line box whether\n * this control wraps onto a row of its own or shares theirs. */\n padding: 3px 0;\n border: 0;\n background: none;\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n cursor: pointer;\n}\n\n/* Hover lifts the word to full contrast and warms the track's border. Scoped\n * to the OFF track: `.sfd887b_incompatibleFilter:hover .s11c019_switch` outranks `.s7580e8_switchOn`\n * by two classes, so unscoped it would strip the hue from the border at\n * exactly the moment a reader has the pointer on it to check the state. */\n.sfd887b_incompatibleFilter:hover {\n color: var(--dsw-alias-label-primary);\n}\n\n.sfd887b_incompatibleFilter:hover .s11c019_switch:not(.s7580e8_switchOn) {\n border-color: color-mix(in srgb, var(--switch-hue) 45%, var(--dsw-alias-border-l2));\n}\n\n/* On. Written with both classes for the reason `.s26d58e_categoryButton.s5f68c4_categoryButtonOn`\n * is: it has to match `:hover`'s specificity and come after it, or the colour\n * reverts under a pointer that has not moved since the click. */\n.sfd887b_incompatibleFilter.s6e4cba_incompatibleFilterOn {\n color: color-mix(in srgb, var(--switch-hue) 45%, var(--dsw-alias-label-primary) 55%);\n}\n\n/* Off here means \"nothing is being taken away\" — the resting state — so the\n * knob steps back to the secondary label. The enable switch keeps the full\n * strength dot, where off means \"this plugin is disabled\" and is worth\n * noticing. In this bar it would be the heaviest mark in a row of hairline\n * pills, and on the light theme it is the WHOLE control: bg-layer-2 is the\n * same white as the panel there, so an off track is a hairline outline with a\n * near-black dot sitting in it. */\n.sfd887b_incompatibleFilter .s11c019_switch:not(.s7580e8_switchOn) .saab2b3_switchKnob {\n background: var(--dsw-alias-label-secondary);\n}\n\n/* The ring has no border to trace, so it takes an offset and the pill radius\n * of the tabs it sits beside rather than hugging the text. */\n.sfd887b_incompatibleFilter:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 2px;\n border-radius: 999px;\n}\n\n.sebf017_updatableHeading {\n margin: 2px 0 0;\n font-size: 16px;\n font-weight: 600;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-primary);\n}\n\n.seb0a1e_catalogStatsRow {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 6px 0 0;\n}\n\n.sccc4d1_catalogStats {\n margin: 0;\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* Sits beside the 12px stats caption, so it is quieter than `.s73242a_actionButton`:\n * this is an affordance, not a call to action — the catalog reloads on its\n * own at mount and the shelf is already usable without it. Named\n * `catalogRefresh*` (not `reload*`) so it cannot collide with the §4 page\n * reload offer below, which reloads the whole tab rather than refetching\n * the catalog data. */\n.sff243d_catalogRefreshButton {\n padding: 1px 9px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n line-height: 17px;\n cursor: pointer;\n}\n\n.sff243d_catalogRefreshButton:hover:not(:disabled) {\n border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l2));\n color: var(--dsw-alias-brand-primary);\n}\n\n.sff243d_catalogRefreshButton:disabled {\n cursor: default;\n opacity: 0.6;\n}\n\n.sff243d_catalogRefreshButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.s194168_catalogRefreshFailed {\n font-size: 11px;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n.s6de1ab_emptyLine {\n margin: 0;\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n/* The shelf: a responsive grid of cards. */\n/* Single-line rows: one full-width column, one plugin per line. */\n.sf657f5_cards {\n display: grid;\n grid-template-columns: 1fr;\n max-width: 100%;\n gap: 8px;\n list-style: none;\n margin: 0;\n padding: 0;\n animation: dshShopCardsIn 220ms ease;\n}\n\n@keyframes dshShopCardsIn {\n from { opacity: 0; transform: translateY(3px); }\n to { opacity: 1; transform: none; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .sf657f5_cards { animation: none; }\n}\n\n.s65f383_card {\n position: relative;\n display: flex;\n flex-direction: column;\n /* A grid item's min-width is auto by default: its content would widen the\n * 1fr track past the panel and overflow horizontally. */\n min-width: 0;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-1);\n overflow: hidden;\n transition: border-color 120ms ease, transform 120ms ease, box-shadow 120ms ease;\n /* Off-screen cards skip layout and paint; the placeholder keeps scrollbar\n * estimation sane before the browser has measured the real height. */\n content-visibility: auto;\n contain-intrinsic-size: auto 140px;\n}\n\n/* The sentinel that triggers the next shelf batch: zero visual footprint. */\n.sd7ee01_cardsSentry {\n height: 1px;\n margin: 0;\n padding: 0;\n}\n\n.s705cc6_showingLine {\n margin: 0;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s65f383_card:hover {\n border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l1));\n transform: translateY(-1px);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .s65f383_card { transition: none; }\n .s65f383_card:hover { transform: none; }\n}\n\n/* The signature: a spine and cover in the category's own hue. The hues are\n * fixed mid-bright colors that read on both themes; `other` deliberately\n * stays a neutral gray so the fallback category does not compete. */\n.s4ce25d_cardSpine {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 3px;\n background: var(--category-hue, #8B8E96);\n}\n\n/* The hue table, keyed by `data-category` and read by BOTH surfaces that\n * speak in category colour: a card's spine and badge, and the category bar's\n * own tab. One table, so a tab can never disagree with the cards it filters to\n * — which is the whole point of colouring the tab. `.s26d58e_categoryButton` sets the\n * brand fallback for `All`/`Installed`; these attribute selectors are one\n * specificity step above it and win wherever the attribute is present. */\n.s65f383_card[data-category='tool'], .s26d58e_categoryButton[data-category='tool'] { --category-hue: #4C8DFF; }\n.s65f383_card[data-category='provider'], .s26d58e_categoryButton[data-category='provider'] { --category-hue: #A78BFA; }\n.s65f383_card[data-category='ui'], .s26d58e_categoryButton[data-category='ui'] { --category-hue: #2DD4BF; }\n.s65f383_card[data-category='workflow'], .s26d58e_categoryButton[data-category='workflow'] { --category-hue: #F59E0B; }\n.s65f383_card[data-category='integration'], .s26d58e_categoryButton[data-category='integration'] { --category-hue: #34D399; }\n.s65f383_card[data-category='theme'], .s26d58e_categoryButton[data-category='theme'] { --category-hue: #F472B6; }\n.s65f383_card[data-category='other'], .s26d58e_categoryButton[data-category='other'] { --category-hue: #8B8E96; }\n\n/* The category sits in the badge row as a quiet chip in the category's own\n * hue — the border carries it where the light theme's white layers cannot\n * (the same lesson as the check button). */\n.s9c8eaa_categoryBadge {\n padding: 1px 7px;\n border: 1px solid color-mix(in srgb, var(--category-hue, #8B8E96) 45%, transparent);\n border-radius: 999px;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--category-hue, #8B8E96);\n white-space: nowrap;\n}\n\n.sa97234_entryHeader {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 12px;\n min-width: 0;\n padding: 8px 12px 2px;\n border: none;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n\n.sa97234_entryHeader:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n/* The name fills the row and truncates rather than wraps; the badges stay\n * intact beside it. */\n.sc3f149_name {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 13px;\n font-weight: 600;\n flex: 1 1 auto;\n min-width: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--dsw-alias-label-primary);\n}\n\n.s513026_badges {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n/* The action line: buttons side by side; an active install/uninstall flow\n * (gate, log, notices) takes the full width below them. */\n.s9b5048_cardActions {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n padding: 8px 12px 10px;\n}\n\n.s9b5048_cardActions > .sc4f2ac_gate,\n.s9b5048_cardActions > .s4d374c_installPanel,\n.s9b5048_cardActions > .sf53ca0_installedActions {\n flex-basis: 100%;\n}\n\n/* The entry's version, always visible on the card (the expanded detail\n * repeats it in the version row) — the same quiet mono as the toolbar. */\n.sc6ec6e_cardVersion {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* The octocat marking a github-sourced entry, beside its short commit. */\n.s71f10f_sourceBadge {\n display: inline-flex;\n align-items: center;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s5e82cb_tierBadge {\n padding: 1px 7px;\n border-radius: 999px;\n font-size: 11px;\n line-height: 1.6;\n}\n\n.s5e82cb_tierBadge[data-tier='verified'] {\n color: var(--dsw-alias-state-success-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-success-primary) 40%, transparent);\n}\n\n.s5e82cb_tierBadge[data-tier='verified-stale'] {\n color: var(--dsw-alias-state-warn-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary) 40%, transparent);\n}\n\n.s5e82cb_tierBadge[data-tier='community'] {\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n border: 1px solid var(--dsw-alias-border-l2);\n}\n\n/* The §C10 shadowed badge: an installed github entry the catalog refuses\n * shadowed-by-npm. Subdued/secondary rather than a warn tint — nothing is\n * wrong with the install itself; the shelf just cannot show it. */\n.s5e82cb_tierBadge[data-tier='shadowed'] {\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n border: 1px solid var(--dsw-alias-border-l2);\n}\n\n/* The §C5 archived badge: a github repository the owner has frozen.\n * Subdued register, same as shadowed — nothing is wrong with the install;\n * the pinned commit still serves, so the mark is informational only. */\n.s5e82cb_tierBadge[data-tier='archived'] {\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n border: 1px solid var(--dsw-alias-border-l2);\n}\n\n.s4bbe02_incompatibleBadge {\n flex-shrink: 0;\n padding: 3px 9px;\n border-radius: 999px;\n font-size: 12px;\n white-space: nowrap;\n color: var(--dsw-alias-state-error-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 40%, transparent);\n}\n\n.sff43b4_starsBadge {\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s73f7fb_chevron, .s61393c_chevronOpen {\n color: var(--dsw-alias-label-secondary);\n transition: transform 120ms ease;\n}\n\n.s61393c_chevronOpen { transform: rotate(180deg); }\n\n@media (prefers-reduced-motion: reduce) {\n .s73f7fb_chevron, .s61393c_chevronOpen { transition: none; }\n}\n\n.s820ebf_body {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 0 12px;\n}\n\n.sb8fbc8_summary {\n margin: 0;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.se507db_summaryZh {\n margin: -4px 0 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n display: -webkit-box;\n -webkit-line-clamp: 1;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n/* Expanded cards show the full summary: the clamp is lifted, never raised —\n * a higher number would still truncate the author's text, just later. */\n.s10baf0_summaryExpanded {\n -webkit-line-clamp: unset;\n display: block;\n}\n\n.sb9dba9_summaryZhExpanded {\n -webkit-line-clamp: unset;\n display: block;\n}\n\n/* `white-space: pre-line` so the newline in the copy is the copy's own: the\n dictionary decides where the line breaks, and a translator changes it\n without touching the component. Collapses runs of spaces like `normal`, so\n only the explicit \\n survives. */\n.s6eca19_incompatibleDetail {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n white-space: pre-line;\n color: var(--dsw-alias-state-error-primary);\n}\n\n/* The way out of dsh's own refusal: the lead-in, then the exemption command\n * on its own line. */\n.s3d0044_remedy {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.sdf203f_remedyLead {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n}\n\n/* The exemption command, bound for a terminal unchanged: one click selects\n * all of it, and it wraps anywhere rather than widening the card. A border,\n * not a fill, marks the box: the bg layers are one white in the light theme. */\n.sc32335_allowCommand {\n display: block;\n padding: 6px 8px;\n border-radius: 6px;\n background: var(--dsw-alias-bg-base);\n border: 1px solid var(--dsw-alias-border-l1);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px;\n line-height: 1.5;\n white-space: pre-wrap;\n word-break: break-all;\n user-select: all;\n color: var(--dsw-alias-label-primary);\n}\n\n/* A refused install on a surface that does not state its blockers — the\n * outdated row — writes them out on their own full-width line under the\n * disabled button, the command that clears dsh's refusal included. */\n.sc74941_refusal {\n display: flex;\n flex-direction: column;\n gap: 6px;\n flex-basis: 100%;\n}\n\n.s9d2029_capabilitiesBlock {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.s3bdbd5_capabilitiesNote {\n margin: 0;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s441702_capabilities {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.s441702_capabilities li {\n padding: 1px 6px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 6%, transparent);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sde3f0c_detail {\n border-top: 1px solid var(--dsw-alias-border-l1);\n padding-top: 8px;\n}\n\n.se98179_detailRows {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin: 0;\n}\n\n.sfb4d2f_detailRow {\n display: flex;\n justify-content: space-between;\n gap: 12px;\n font-size: 12px;\n}\n\n.sfb4d2f_detailRow dt {\n color: var(--dsw-alias-label-secondary);\n}\n\n.sfb4d2f_detailRow dd {\n margin: 0;\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n overflow-wrap: anywhere;\n text-align: right;\n color: var(--dsw-alias-label-primary);\n}\n\n.sfb4d2f_detailRow a {\n color: var(--dsw-alias-brand-primary);\n text-decoration: none;\n}\n\n.sfb4d2f_detailRow a:hover {\n text-decoration: underline;\n}\n\n.sfb4d2f_detailRow a:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* What the entry IS rather than what you can do with it — how big it is and\n * who published it — at the right edge of the action row. `margin-left: auto`\n * is what pushes it there, and it lives on this wrapper rather than on either\n * label so the group stays flush right when only one of the two has a value —\n * a packument that named no account has no author, and an entry the registry\n * could not measure honestly has no size. Rarer than it was (every live entry\n * now carries a size, github included), but the wrapper is not what makes it\n * rare: without one, an EMPTY meta group still costs 8px, because\n * `.s9b5048_cardActions` is a flex row with `gap: 8px` and a zero-width item is still\n * an item. The row is a wrapping flex, so an active install flow (which claims\n * the full width) drops the whole group to the next line rather than squeezing\n * it. */\n.s5fbaf9_cardMeta {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n margin-left: auto;\n min-width: 0;\n}\n\n/* The on-disk size, left of the author. Mono and tabular so the digits of one\n * figure line up with the digits of another WHEREVER the two land: `.s5fbaf9_cardMeta`\n * is flush right and the author sits outside the size, so a size's x position\n * moves with the author's width and is different again on the many entries\n * that carry no author at all. There is no column to align to, which is why\n * the reason given here is per-figure legibility rather than the column the\n * first version of this comment claimed. Secondary colour, because it\n * qualifies the entry rather than announcing it.\n *\n * `font-variant-numeric` is safe HERE and would not be on a pill: this element\n * has no state rules, so nothing it does can reflow a wrapping row. */\n.s2d3c05_size {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-variant-numeric: tabular-nums;\n font-size: 12px;\n letter-spacing: 0.02em;\n white-space: nowrap;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* The publishing account, at the outer end of `.s5fbaf9_cardMeta` on the action row —\n * NOT beside the npm link in the expanded detail, which is where it used to\n * live and what this comment described until the accurate half was deleted\n * with the `margin-left: auto` that moved to `.s5fbaf9_cardMeta`. There is no middot:\n * `.s5fbaf9_cardMeta`'s 8px gap separates it from the size.\n *\n * Secondary label colour carries the distinction — never a background fill,\n * which is invisible in the light theme where every bg-layer token is the\n * same white. */\n.s1ff66f_author {\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n}\n\n.sd58c6d_reviewedLine {\n margin: 8px 0 0;\n padding: 6px 8px;\n border-radius: 6px;\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 12%, transparent);\n font-size: 12px;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n/* Install flow. */\n.s4d374c_installPanel {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 2px;\n}\n\n.se7aaed_installButton, .sb43a49_confirmButton, .s6e45d1_cancelButton {\n align-self: flex-start;\n padding: 5px 14px;\n border-radius: 6px;\n font-size: 13px;\n cursor: pointer;\n}\n\n.se7aaed_installButton {\n border: 1px solid var(--dsw-alias-brand-primary);\n background: var(--dsw-alias-brand-primary);\n color: var(--dsw-alias-bg-base);\n}\n\n.se7aaed_installButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 88%, var(--dsw-alias-label-primary));\n}\n\n.sb43a49_confirmButton {\n border: 1px solid var(--dsw-alias-brand-primary);\n background: var(--dsw-alias-brand-primary);\n color: var(--dsw-alias-bg-base);\n}\n\n.sb43a49_confirmButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 88%, var(--dsw-alias-label-primary));\n}\n\n.s6e45d1_cancelButton {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n}\n\n.s6e45d1_cancelButton:hover {\n border-color: var(--dsw-alias-label-secondary);\n}\n\n.se7aaed_installButton:focus-visible, .sb43a49_confirmButton:focus-visible, .s6e45d1_cancelButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The shelf card of a plugin that is installed and current carries this\n * non-interactive label instead of an install button. Border + text do the\n * work in the light theme, where every bg-layer token resolves to white. */\n.sb6fc74_installedLabel {\n align-self: flex-start;\n margin: 0;\n padding: 5px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n font-size: 13px;\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n}\n\n/* An installed card's controls: the installed label or the update button,\n * plus the uninstall button. The install panel's running/failed states are\n * column layouts and sit as one item of this row. */\n.sf53ca0_installedActions {\n display: flex;\n align-items: flex-start;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n/* Uninstall revokes privilege; the quiet outline keeps it from competing\n * with the primary action, and the error tint says \"removes\". */\n.s96967f_uninstallButton {\n align-self: flex-start;\n padding: 5px 14px;\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 55%, transparent);\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-state-error-primary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.s96967f_uninstallButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);\n}\n\n.s96967f_uninstallButton:focus-visible {\n outline: 2px solid var(--dsw-alias-state-error-primary);\n outline-offset: 1px;\n}\n\n/* The restart dsh button on a done install: quiet outline, same rank as\n * the other secondary actions. */\n.sb00c36_restartSelfButton {\n /* The version row's third state. Geometry matches .s614a38_checkUpdateButton and\n * .sa04455_updateSelfButton exactly — the row is one evolving control and a\n * different pill would read as a different kind of thing. It takes the\n * emphasised fill rather than the neutral one because at that moment the\n * restart is the only thing left to do, the same reason Update has it. */\n padding: 2px 10px;\n border: 1px solid var(--dsw-alias-brand-primary);\n border-radius: 999px;\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n color: var(--dsw-alias-brand-primary);\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n}\n\n.sb00c36_restartSelfButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The two activation offers share one recipe: a border in an alias token,\n * never a `bg-layer` fill alone — all four bg-layer tokens collapse to the\n * same white under `body` in the light theme, and a fill-only button would\n * vanish there. Grouped rather than copied, because a copy is a rule that\n * can be corrected on one button and left wrong on the other, silently. */\n.s6ac237_restartButton,\n.s64a5ff_reloadButton {\n align-self: flex-start;\n padding: 5px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.s6ac237_restartButton:hover,\n.s64a5ff_reloadButton:hover {\n border-color: var(--dsw-alias-label-secondary);\n}\n\n.s6ac237_restartButton:focus-visible,\n.s64a5ff_reloadButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The §4 reload offer: a notice plus its button, one item of\n * `.sf53ca0_installedActions` the way the restart gate is — a column of its own\n * rather than inline with the row. */\n.sf79aef_reloadPanel {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 6px;\n}\n\n\n.sc4f2ac_gate {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 10px;\n border: 1px solid var(--dsw-alias-state-warn-primary);\n border-radius: 8px;\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 8%, transparent);\n}\n\n.s559c38_gateTitle {\n margin: 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n.s3c0ace_gateBody {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-primary);\n}\n\n.sc323e5_gateWarning {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n white-space: pre-line;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.s4b2373_gateActions {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n.s52b5a5_installing {\n margin: 0;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s64ab71_log {\n max-height: 160px;\n overflow: auto;\n margin: 0;\n padding: 8px;\n border-radius: 6px;\n background: var(--dsw-alias-bg-base);\n border: 1px solid var(--dsw-alias-border-l1);\n list-style: none;\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px;\n line-height: 1.5;\n}\n\n.s1531a8_logLine {\n white-space: pre-wrap;\n word-break: break-all;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sf20dda_notice {\n margin: 0;\n font-size: 12px;\n color: var(--dsw-alias-state-success-primary);\n}\n\n.seb9b08_failedHeading {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.sfd3021_failedDetail {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-primary);\n}\n\n.s50ba35_rejectedCode {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.se75489_rejectedDetail {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-primary);\n}\n\n/* The Updatable section. The class keeps the Host's field name; only the\n heading the reader sees was renamed. */\n.sa276cf_outdatedSection {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 6px;\n}\n\n.sceae6e_outdatedList {\n display: flex;\n flex-direction: column;\n gap: 8px;\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.s8d2072_outdatedRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n flex-wrap: wrap;\n padding: 10px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-1);\n}\n\n.s5c2545_outdatedInfo {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.s5c2545_outdatedInfo strong {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 13px;\n word-break: break-all;\n color: var(--dsw-alias-label-primary);\n}\n\n.s877c68_outdatedVersions {\n /* Two inline labels with nothing between them in the markup ran together as\n * `installed v1.0.0latest v2.0.0`: JSX drops the whitespace-only line, and\n * the per-label class the markup asked for was never defined (singular\n * against this rule's plural). The container owns the gap, the same idiom\n * as .s513026_badges — the labels need no class of their own. */\n display: inline-flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* The §C10 shadowed section. Reuses the outdated list's visual language; only\n * the section spacing and the per-row explanation paragraph are its own. */\n.s6c4649_shadowedSection {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 6px;\n}\n\n.scc2a21_shadowedDetail {\n margin: 4px 0 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sa1f556_outdatedActions {\n display: flex;\n align-items: center;\n /* The reload offer (§4) mounts inside the switch beside the Update button\n * and is a two-line block, not a control: without wrapping, a row reading\n * [switch][notice + Reload][Update] overflows the settings dialog. */\n flex-wrap: wrap;\n gap: 10px;\n}\n\n/* The shop's one switch. It is worn by two controls that mean different\n * things — the enable/disable toggle on an installed card, and the shelf's\n * incompatible filter — so the geometry lives here once and each caller states\n * only its hue.\n *\n * `--switch-hue` is that knob, and it is read from the ANCESTOR rather than\n * declared per state: `.sfd887b_incompatibleFilter` sets it once and it reaches the\n * track and the label together, which is what keeps a red switch from sitting\n * under a neutral word. A declaration on `.s11c019_switch` itself would shadow the\n * inherited value (an element's own rule beats what it inherits), so the\n * default rides in the `var()` fallback and is resolved once into\n * `--track-hue` instead of being spelled out at each use.\n *\n * `box-sizing` and `padding` are stated because the two callers are different\n * elements — a `<button>`, which the UA gives padding, and a `<span>`, which\n * it does not. Without them the same class renders two sizes, and the knob\n * sits 2px above centre in the taller one. */\n.s11c019_switch {\n --track-hue: var(--switch-hue, var(--dsw-alias-state-success-primary));\n box-sizing: border-box;\n display: inline-block;\n position: relative;\n width: 32px;\n height: 18px;\n padding: 0;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-2);\n cursor: pointer;\n transition: background 120ms ease, border-color 120ms ease;\n}\n\n/* The knob's POSITION is the state, which is the reason this control needs no\n * `forced-colors` fallback the way the category pills do: a system palette\n * replaces every colour below and moves nothing. */\n.s7580e8_switchOn {\n border-color: var(--track-hue);\n background: color-mix(in srgb, var(--track-hue) 55%, transparent);\n}\n\n.saab2b3_switchKnob {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 12px;\n height: 12px;\n border-radius: 999px;\n background: var(--dsw-alias-label-primary);\n transition: transform 120ms ease, background 120ms ease;\n}\n\n.s7580e8_switchOn .saab2b3_switchKnob {\n transform: translateX(14px);\n background: var(--dsw-alias-bg-base);\n}\n\n.s11c019_switch:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .s11c019_switch, .saab2b3_switchKnob { transition: none; }\n}\n\n/* The hot enable/disable switch on installed rows: the knob button plus the\n * §8 hot-apply / failure notices rendered beside it. */\n.sd9f9e4_switchWrap {\n display: inline-flex;\n align-items: center;\n /* Same reason as `.sa1f556_outdatedActions`: `.sf79aef_reloadPanel`'s own comment calls\n * itself \"a column of its own rather than inline with the row\", which held\n * for `.sf53ca0_installedActions` and not here until this line. */\n flex-wrap: wrap;\n gap: 8px;\n}\n";
9048
- if (typeof document !== "undefined" && !document.querySelector("style[data-plugin-css=\"e3675a89\"]")) {
9218
+ //#region \0dsh-plugin-shop-css:d74581c2
9219
+ 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.s73a3ff_client.s45aee2_config.s8a51a7_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.s73a3ff_client.s117e2f_spec.s8a51a7_ts` pins this. */\n\n.sa2d7b5_panel {\n display: flex;\n flex-direction: column;\n gap: 14px;\n /* The shelf never scrolls sideways: a card too wide for its track wraps\n * its badges rather than widening the panel.\n *\n * Clipped on BOTH axes, with a margin, because `.s7c10d7_searchInput` sits at\n * exactly (0, 0) of this box — it is the first child of `.sbee2c3_toolbar`, itself\n * the first child here, and this rule carries no padding — so its focus\n * ring paints ENTIRELY outside the panel.\n *\n * Both axes is the load-bearing part: `overflow-clip-margin` is inert\n * unless the element clips on both. Clipping x alone left the margin doing\n * nothing — measured against the live app on 0.7.1, the ring reached 0 of\n * its 3px on the left at every device pixel ratio (1, 1.25, 1.5, 2), while\n * keeping the full 3px on the right, where the 280px input stops short of\n * the panel and never meets the clip edge. Raising the margin to 6px\n * changed nothing; clipping both axes with the SAME 3px restored the whole\n * ring. A test that reads declarations cannot see that difference, which is\n * why the first attempt at this shipped broken.\n *\n * `clip` is not a scroll container, so this does not bring back the\n * accidental scrolling `hidden` caused by coercing the other axis to\n * `auto`. Nothing is lost vertically: the panel's scrollHeight equals its\n * height and no descendant paints outside the clip rect (measured).\n * `overflow-x: hidden` stays as the fallback for engines without `clip`,\n * where the ring is clipped as it was before, never worse. */\n overflow-x: hidden;\n overflow: clip;\n overflow-clip-margin: 3px;\n}\n\n.sa89499_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.s90f53b_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.sd5b66e_skeletonGrid {\n display: grid;\n grid-template-columns: 1fr;\n gap: 8px;\n}\n\n.sfb41cd_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.sfb41cd_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.sc9bc63_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.s75708b_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.s772f28_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.s6ce39d_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.sfb41cd_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 .sfb41cd_skeletonCard span { animation: none; opacity: 0.75; }\n .sfb41cd_skeletonCard::after { content: none; }\n}\n\n.s1547e4_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.s1547e4_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.s1547e4_actionButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.sbee2c3_toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.sfb98ad_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.s15df74_versionBlock {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.s9d78e5_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.scb0503_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.scb0503_updateSelfButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 20%, transparent);\n}\n\n.scb0503_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.seab5ae_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.seab5ae_checkUpdateButton:hover {\n border-color: var(--dsw-alias-label-secondary);\n color: var(--dsw-alias-label-primary);\n}\n\n.seab5ae_checkUpdateButton:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n.seab5ae_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.s6932e1_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.s6932e1_githubLink:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.s6932e1_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.sb1e444_selfUpdatePanel {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 8px;\n}\n\n.s7c10d7_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.s7c10d7_searchInput:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* Subdued line under the search box when a dictionary intent fired (design\n * 2026-10-08-search-query-expansion §4). One fact, no badge. */\n.s1dee54_expansionNote {\n margin: 4px 0 0;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s5823b0_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.s52084f_categoryBar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n margin-top: 6px;\n}\n\n.sd07f01_categoryButton {\n /* The hue this button carries when selected. `All` and `Installed` select by\n * something other than a category and keep the brand token; the six category\n * buttons override it from the shared hue table below, so a pressed Tool tab\n * turns the same blue as a tool card's spine. */\n --category-hue: var(--dsw-alias-brand-primary);\n padding: 3px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n cursor: pointer;\n}\n\n/* Blend text with the theme foreground: darker on light backgrounds, lighter\n * on dark ones. The unblended category hue belongs on the border and fill. */\n.sd07f01_categoryButton:hover {\n border-color: color-mix(in srgb, var(--category-hue) 45%, var(--dsw-alias-border-l2));\n color: color-mix(in srgb, var(--category-hue) 45%, var(--dsw-alias-label-primary) 55%);\n}\n\n/* Selected. Written `.sd07f01_categoryButton.s7d231e_categoryButtonOn` — TWO classes — so it\n * matches `.sd07f01_categoryButton:hover`'s specificity and, coming after it, wins.\n * As a bare `.s7d231e_categoryButtonOn` it lost both `border-color` and `color` to the\n * hover rule for as long as the pointer stayed on the tab that had just been\n * clicked, which is precisely when a reader looks for confirmation.\n *\n * EVERY declaration here is a colour or a shadow: a pressed tab must occupy\n * exactly the same box as an unpressed one, so the bar does not reflow — and\n * with ~9 pills on a wrapping row, one that grows can push the rest onto\n * another line and move the tab out from under the pointer that just clicked\n * it. That is what `font-weight: 600` did (bold metrics are wider than\n * regular), and it is why nothing may be added here that resolves to a length:\n * no padding, no border-width, no font-size, no letter-spacing.\n * `css-tokens.s73a3ff_client.s117e2f_spec.s8a51a7_ts` fails the build on one.\n *\n * `box-shadow` is the exception that earns its place: it paints outside the\n * layout box, so an inset ring doubles the border's weight at zero cost to the\n * metrics. It is what `font-weight: 600` used to be — a second, thickness-\n * based signal that hover does not also produce — and the `other` tab needs it\n * most: its hue IS the neutral gray this file uses for \"no category\", so on\n * the light theme hue alone barely separates pressed from unpressed. */\n.sd07f01_categoryButton.s7d231e_categoryButtonOn {\n border-color: var(--category-hue);\n background: color-mix(in srgb, var(--category-hue) 14%, transparent);\n color: color-mix(in srgb, var(--category-hue) 45%, var(--dsw-alias-label-primary) 55%);\n box-shadow: inset 0 0 0 1px var(--category-hue);\n}\n\n/* Forced colours replace every colour above with the system palette, which\n * would leave the three colour declarations saying nothing and the pressed tab\n * indistinguishable from its neighbours. `box-shadow` is dropped in this mode\n * too, so the ring has to come back as an outline. */\n@media (forced-colors: active) {\n .sd07f01_categoryButton.s7d231e_categoryButtonOn {\n outline: 2px solid ButtonText;\n outline-offset: -4px;\n }\n}\n\n.sd07f01_categoryButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The incompatible filter, at the right edge of the category bar: `margin-left:\n * auto` pushes it past the pills, and the bar wraps, so on a narrow panel it\n * drops to its own line rather than squeezing them. It is a MODIFIER, not a\n * ninth category — the categories select what to show and this one subtracts\n * from whatever they selected — so it takes the error hue it filters on rather\n * than joining the hue table.\n *\n * And it is a SWITCH, which is the difference the shape now carries. It used\n * to wear `.sd07f01_categoryButton`: same pill, same row, same size as the eight tabs,\n * differing only in being red. That reads as a ninth tab, and it is not one —\n * the tabs are choose-one and this is on-or-off. So it drops the pill chrome\n * entirely (no border, no fill, nothing to compete with the row it sits\n * beside) and puts the state in a track, the same one every installed card\n * carries. Nothing here restates the switch geometry; `--switch-hue` is the\n * whole of what this control says about it, and it reaches the label too, so\n * one declaration keeps the word and the track the same colour. */\n.s7abba7_incompatibleFilter {\n --switch-hue: var(--dsw-alias-state-error-primary);\n margin-left: auto;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n /* Matches the pills' vertical padding so the bar keeps one line box whether\n * this control wraps onto a row of its own or shares theirs. */\n padding: 3px 0;\n border: 0;\n background: none;\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n cursor: pointer;\n}\n\n/* Hover lifts the word to full contrast and warms the track's border. Scoped\n * to the OFF track: `.s7abba7_incompatibleFilter:hover .sa4bed2_switch` outranks `.s97d8ec_switchOn`\n * by two classes, so unscoped it would strip the hue from the border at\n * exactly the moment a reader has the pointer on it to check the state. */\n.s7abba7_incompatibleFilter:hover {\n color: var(--dsw-alias-label-primary);\n}\n\n.s7abba7_incompatibleFilter:hover .sa4bed2_switch:not(.s97d8ec_switchOn) {\n border-color: color-mix(in srgb, var(--switch-hue) 45%, var(--dsw-alias-border-l2));\n}\n\n/* On. Written with both classes for the reason `.sd07f01_categoryButton.s7d231e_categoryButtonOn`\n * is: it has to match `:hover`'s specificity and come after it, or the colour\n * reverts under a pointer that has not moved since the click. */\n.s7abba7_incompatibleFilter.sa24b4e_incompatibleFilterOn {\n color: color-mix(in srgb, var(--switch-hue) 45%, var(--dsw-alias-label-primary) 55%);\n}\n\n/* Off here means \"nothing is being taken away\" — the resting state — so the\n * knob steps back to the secondary label. The enable switch keeps the full\n * strength dot, where off means \"this plugin is disabled\" and is worth\n * noticing. In this bar it would be the heaviest mark in a row of hairline\n * pills, and on the light theme it is the WHOLE control: bg-layer-2 is the\n * same white as the panel there, so an off track is a hairline outline with a\n * near-black dot sitting in it. */\n.s7abba7_incompatibleFilter .sa4bed2_switch:not(.s97d8ec_switchOn) .s24e1f5_switchKnob {\n background: var(--dsw-alias-label-secondary);\n}\n\n/* The ring has no border to trace, so it takes an offset and the pill radius\n * of the tabs it sits beside rather than hugging the text. */\n.s7abba7_incompatibleFilter:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 2px;\n border-radius: 999px;\n}\n\n.s3ffb2a_updatableHeading {\n margin: 2px 0 0;\n font-size: 16px;\n font-weight: 600;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-primary);\n}\n\n.sfd3ed8_catalogStatsRow {\n display: flex;\n align-items: center;\n /* The row holds the catalog line, the refresh button and the shelf order's\n * three pills (design 2026-10-12-shelf-orders §4). Without wrapping, the\n * last of them overflows the tab at a narrow width. */\n flex-wrap: wrap;\n gap: 10px;\n margin: 6px 0 0;\n}\n\n/* The shelf's order control, at the right end of the stats row. `margin-left:\n * auto` is what puts it there while the line and the refresh button stay\n * left — and it is safe on a wrapping row, unlike a width. */\n.sc5a073_sortGroup {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n margin-left: auto;\n}\n\n.s275eb9_sortLabel {\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n}\n\n.s3c7a5d_catalogStats {\n margin: 0;\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* Sits beside the 12px stats caption, so it is quieter than `.s1547e4_actionButton`:\n * this is an affordance, not a call to action — the catalog reloads on its\n * own at mount and the shelf is already usable without it. Named\n * `catalogRefresh*` (not `reload*`) so it cannot collide with the §4 page\n * reload offer below, which reloads the whole tab rather than refetching\n * the catalog data. */\n.s6d141d_catalogRefreshButton {\n padding: 1px 9px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n line-height: 17px;\n cursor: pointer;\n}\n\n.s6d141d_catalogRefreshButton:hover:not(:disabled) {\n border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l2));\n color: var(--dsw-alias-brand-primary);\n}\n\n.s6d141d_catalogRefreshButton:disabled {\n cursor: default;\n opacity: 0.6;\n}\n\n.s6d141d_catalogRefreshButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.s8b8ed4_catalogRefreshFailed {\n font-size: 11px;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n.s918b21_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.sf66d65_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 .sf66d65_cards { animation: none; }\n}\n\n.sa6c0bf_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.s21cef7_cardsSentry {\n height: 1px;\n margin: 0;\n padding: 0;\n}\n\n.s9babb8_showingLine {\n margin: 0;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sa6c0bf_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 .sa6c0bf_card { transition: none; }\n .sa6c0bf_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.sce2063_cardSpine {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 3px;\n background: var(--category-hue, #8B8E96);\n}\n\n/* The hue table, keyed by `data-category` and read by BOTH surfaces that\n * speak in category colour: a card's spine and badge, and the category bar's\n * own tab. One table, so a tab can never disagree with the cards it filters to\n * — which is the whole point of colouring the tab. `.sd07f01_categoryButton` sets the\n * brand fallback for `All`/`Installed`; these attribute selectors are one\n * specificity step above it and win wherever the attribute is present. */\n.sa6c0bf_card[data-category='tool'], .sd07f01_categoryButton[data-category='tool'] { --category-hue: #4C8DFF; }\n.sa6c0bf_card[data-category='provider'], .sd07f01_categoryButton[data-category='provider'] { --category-hue: #A78BFA; }\n.sa6c0bf_card[data-category='ui'], .sd07f01_categoryButton[data-category='ui'] { --category-hue: #2DD4BF; }\n.sa6c0bf_card[data-category='workflow'], .sd07f01_categoryButton[data-category='workflow'] { --category-hue: #F59E0B; }\n.sa6c0bf_card[data-category='integration'], .sd07f01_categoryButton[data-category='integration'] { --category-hue: #34D399; }\n.sa6c0bf_card[data-category='theme'], .sd07f01_categoryButton[data-category='theme'] { --category-hue: #F472B6; }\n.sa6c0bf_card[data-category='other'], .sd07f01_categoryButton[data-category='other'] { --category-hue: #8B8E96; }\n\n/* The category sits in the badge row as a quiet chip in the category's own\n * hue — the border carries it where the light theme's white layers cannot\n * (the same lesson as the check button). */\n.sdc7cd8_categoryBadge {\n padding: 1px 7px;\n border: 1px solid color-mix(in srgb, var(--category-hue, #8B8E96) 45%, transparent);\n border-radius: 999px;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--category-hue, #8B8E96);\n white-space: nowrap;\n}\n\n.s6ae6ba_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.s6ae6ba_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.saf3e98_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.s93d243_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.s763b75_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.s763b75_cardActions > .sff1df6_gate,\n.s763b75_cardActions > .s6d7bee_installPanel,\n.s763b75_cardActions > .s10ddb2_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.s9d1d9e_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 /* A release tag prints as tagged, never cut to seven characters (market\n * borrowings §11), and a tag can run to any length. The cap keeps every\n * tag of up to fourteen characters whole, prerelease suffix included\n * (`v0.1.0-alpha.0`): fourteen glyphs plus the letter-spacing above come to\n * about 14.5ch, so the cap is 15ch, not 14. A longer tag ends in an ellipsis,\n * and the tooltip carries the whole of it. */\n max-width: 15ch;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* The octocat marking a source the reader can read on GitHub, beside the\n * card's version. */\n.sad456f_sourceBadge {\n display: inline-flex;\n align-items: center;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s7f352e_tierBadge {\n padding: 1px 7px;\n border-radius: 999px;\n font-size: 11px;\n line-height: 1.6;\n}\n\n.s7f352e_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.s7f352e_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.s7f352e_tierBadge[data-tier='community'] {\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n border: 1px solid var(--dsw-alias-border-l2);\n}\n\n/* The §C10 shadowed badge: an installed github entry the catalog refuses\n * shadowed-by-npm. Subdued/secondary rather than a warn tint — nothing is\n * wrong with the install itself; the shelf just cannot show it. */\n.s7f352e_tierBadge[data-tier='shadowed'] {\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n border: 1px solid var(--dsw-alias-border-l2);\n}\n\n/* The §C5 archived badge: a github repository the owner has frozen.\n * Subdued register, same as shadowed — nothing is wrong with the install;\n * the pinned commit still serves, so the mark is informational only. */\n.s7f352e_tierBadge[data-tier='archived'] {\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n border: 1px solid var(--dsw-alias-border-l2);\n}\n\n.s649a66_incompatibleBadge {\n flex-shrink: 0;\n padding: 3px 9px;\n border-radius: 999px;\n font-size: 12px;\n white-space: nowrap;\n color: var(--dsw-alias-state-error-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 40%, transparent);\n}\n\n.se83753_starsBadge {\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sc7129d_chevron, .s3e170e_chevronOpen {\n color: var(--dsw-alias-label-secondary);\n transition: transform 120ms ease;\n}\n\n.s3e170e_chevronOpen { transform: rotate(180deg); }\n\n@media (prefers-reduced-motion: reduce) {\n .sc7129d_chevron, .s3e170e_chevronOpen { transition: none; }\n}\n\n.s249e15_body {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 0 12px;\n}\n\n.sdd4d7a_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.s1a6809_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.sc924dd_summaryExpanded {\n -webkit-line-clamp: unset;\n display: block;\n}\n\n.sab7503_summaryZhExpanded {\n -webkit-line-clamp: unset;\n display: block;\n}\n\n/* `white-space: pre-line` so the newline in the copy is the copy's own: the\n dictionary decides where the line breaks, and a translator changes it\n without touching the component. Collapses runs of spaces like `normal`, so\n only the explicit \\n survives. */\n.s4625f5_incompatibleDetail {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n white-space: pre-line;\n color: var(--dsw-alias-state-error-primary);\n}\n\n/* The way out of dsh's own refusal: the lead-in, then the exemption command\n * on its own line. */\n.s632e71_remedy {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.s360025_remedyLead {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n}\n\n/* The exemption command, bound for a terminal unchanged: one click selects\n * all of it, and it wraps anywhere rather than widening the card. A border,\n * not a fill, marks the box: the bg layers are one white in the light theme. */\n.s12ec11_allowCommand {\n display: block;\n padding: 6px 8px;\n border-radius: 6px;\n background: var(--dsw-alias-bg-base);\n border: 1px solid var(--dsw-alias-border-l1);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px;\n line-height: 1.5;\n white-space: pre-wrap;\n word-break: break-all;\n user-select: all;\n color: var(--dsw-alias-label-primary);\n}\n\n/* A refused install on a surface that does not state its blockers — the\n * outdated row — writes them out on their own full-width line under the\n * disabled button, the command that clears dsh's refusal included. */\n.sa92311_refusal {\n display: flex;\n flex-direction: column;\n gap: 6px;\n flex-basis: 100%;\n}\n\n.s99a4f5_capabilitiesBlock {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.scf0a21_capabilitiesNote {\n margin: 0;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sc760c8_capabilities {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.sc760c8_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.s48edfc_detail {\n border-top: 1px solid var(--dsw-alias-border-l1);\n padding-top: 8px;\n}\n\n.sce5502_detailRows {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin: 0;\n}\n\n.scc4b2b_detailRow {\n display: flex;\n justify-content: space-between;\n gap: 12px;\n font-size: 12px;\n}\n\n.scc4b2b_detailRow dt {\n color: var(--dsw-alias-label-secondary);\n}\n\n.scc4b2b_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.scc4b2b_detailRow a {\n color: var(--dsw-alias-brand-primary);\n text-decoration: none;\n}\n\n.scc4b2b_detailRow a:hover {\n text-decoration: underline;\n}\n\n.scc4b2b_detailRow a:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* What the entry IS rather than what you can do with it — how big it is and\n * who published it — at the right edge of the action row. `margin-left: auto`\n * is what pushes it there, and it lives on this wrapper rather than on either\n * label so the group stays flush right when only one of the two has a value —\n * a packument that named no account has no author, and an entry the registry\n * could not measure honestly has no size. Rarer than it was (every live entry\n * now carries a size, github included), but the wrapper is not what makes it\n * rare: without one, an EMPTY meta group still costs 8px, because\n * `.s763b75_cardActions` is a flex row with `gap: 8px` and a zero-width item is still\n * an item. The row is a wrapping flex, so an active install flow (which claims\n * the full width) drops the whole group to the next line rather than squeezing\n * it. */\n.s8fb80a_cardMeta {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n margin-left: auto;\n min-width: 0;\n}\n\n/* The on-disk size, left of the author. Mono and tabular so the digits of one\n * figure line up with the digits of another WHEREVER the two land: `.s8fb80a_cardMeta`\n * is flush right and the author sits outside the size, so a size's x position\n * moves with the author's width and is different again on the many entries\n * that carry no author at all. There is no column to align to, which is why\n * the reason given here is per-figure legibility rather than the column the\n * first version of this comment claimed. Secondary colour, because it\n * qualifies the entry rather than announcing it.\n *\n * `font-variant-numeric` is safe HERE and would not be on a pill: this element\n * has no state rules, so nothing it does can reflow a wrapping row.\n *\n * `.s72b045_shelfDate` shares this rule because it is the same kind of fact — a\n * fixed-width figure a reader compares down the shelf (design\n * 2026-10-12-shelf-orders §4) — and two copies of these declarations would\n * drift. */\n.s24611f_size,\n.s72b045_shelfDate {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-variant-numeric: tabular-nums;\n font-size: 12px;\n letter-spacing: 0.02em;\n white-space: nowrap;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* The publishing account, at the outer end of `.s8fb80a_cardMeta` on the action row —\n * NOT beside the npm link in the expanded detail, which is where it used to\n * live and what this comment described until the accurate half was deleted\n * with the `margin-left: auto` that moved to `.s8fb80a_cardMeta`. There is no middot:\n * `.s8fb80a_cardMeta`'s 8px gap separates it from the size.\n *\n * Secondary label colour carries the distinction — never a background fill,\n * which is invisible in the light theme where every bg-layer token is the\n * same white. */\n.seefbaa_author {\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n}\n\n.sa79085_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.s6d7bee_installPanel {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 2px;\n}\n\n.sdddc5f_installButton, .safd4a8_confirmButton, .sfcf768_cancelButton {\n align-self: flex-start;\n padding: 5px 14px;\n border-radius: 6px;\n font-size: 13px;\n cursor: pointer;\n}\n\n.sdddc5f_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.sdddc5f_installButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 88%, var(--dsw-alias-label-primary));\n}\n\n.safd4a8_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.safd4a8_confirmButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 88%, var(--dsw-alias-label-primary));\n}\n\n.sfcf768_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.sfcf768_cancelButton:hover {\n border-color: var(--dsw-alias-label-secondary);\n}\n\n.sdddc5f_installButton:focus-visible, .safd4a8_confirmButton:focus-visible, .sfcf768_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.s563674_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.s10ddb2_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.s767a97_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.s767a97_uninstallButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);\n}\n\n.s767a97_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.sa6b105_restartSelfButton {\n /* The version row's third state. Geometry matches .seab5ae_checkUpdateButton and\n * .scb0503_updateSelfButton exactly — the row is one evolving control and a\n * different pill would read as a different kind of thing. It takes the\n * emphasised fill rather than the neutral one because at that moment the\n * restart is the only thing left to do, the same reason Update has it. */\n padding: 2px 10px;\n border: 1px solid var(--dsw-alias-brand-primary);\n border-radius: 999px;\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n color: var(--dsw-alias-brand-primary);\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n}\n\n.sa6b105_restartSelfButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The two activation offers share one recipe: a border in an alias token,\n * never a `bg-layer` fill alone — all four bg-layer tokens collapse to the\n * same white under `body` in the light theme, and a fill-only button would\n * vanish there. Grouped rather than copied, because a copy is a rule that\n * can be corrected on one button and left wrong on the other, silently. */\n.s426aa0_restartButton,\n.sa283f9_reloadButton {\n align-self: flex-start;\n padding: 5px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.s426aa0_restartButton:hover,\n.sa283f9_reloadButton:hover {\n border-color: var(--dsw-alias-label-secondary);\n}\n\n.s426aa0_restartButton:focus-visible,\n.sa283f9_reloadButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The §4 reload offer: a notice plus its button, one item of\n * `.s10ddb2_installedActions` the way the restart gate is — a column of its own\n * rather than inline with the row. */\n.sf20a99_reloadPanel {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 6px;\n}\n\n\n.sff1df6_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.see0e3d_gateTitle {\n margin: 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n.sd04ef9_gateBody {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-primary);\n}\n\n.s7fd5d3_gateWarning {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n white-space: pre-line;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.sf6b0aa_gateActions {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n.sa32b72_installing {\n margin: 0;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sf9b6a4_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.s9e4d00_logLine {\n white-space: pre-wrap;\n word-break: break-all;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s7cd7db_notice {\n margin: 0;\n font-size: 12px;\n color: var(--dsw-alias-state-success-primary);\n}\n\n.sfb1bae_failedHeading {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.s9591b1_failedDetail {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-primary);\n}\n\n.saa0605_rejectedCode {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.sbd5c7c_rejectedDetail {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-primary);\n}\n\n/* The Updatable section. The class keeps the Host's field name; only the\n heading the reader sees was renamed. */\n.s7c248d_outdatedSection {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 6px;\n}\n\n.se144d3_outdatedList {\n display: flex;\n flex-direction: column;\n gap: 8px;\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.s6fbe58_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.s8af496_outdatedInfo {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.s8af496_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.sf82d51_outdatedVersions {\n /* Two inline labels with nothing between them in the markup ran together as\n * `installed v1.0.0latest v2.0.0`: JSX drops the whitespace-only line, and\n * the per-label class the markup asked for was never defined (singular\n * against this rule's plural). The container owns the gap, the same idiom\n * as .s93d243_badges — the labels need no class of their own. */\n display: inline-flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* The §C10 shadowed section. Reuses the outdated list's visual language; only\n * the section spacing and the per-row explanation paragraph are its own. */\n.s31b89a_shadowedSection {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 6px;\n}\n\n.scf838f_shadowedDetail {\n margin: 4px 0 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s109c95_outdatedActions {\n display: flex;\n align-items: center;\n /* The reload offer (§4) mounts inside the switch beside the Update button\n * and is a two-line block, not a control: without wrapping, a row reading\n * [switch][notice + Reload][Update] overflows the settings dialog. */\n flex-wrap: wrap;\n gap: 10px;\n}\n\n/* The shop's one switch. It is worn by two controls that mean different\n * things — the enable/disable toggle on an installed card, and the shelf's\n * incompatible filter — so the geometry lives here once and each caller states\n * only its hue.\n *\n * `--switch-hue` is that knob, and it is read from the ANCESTOR rather than\n * declared per state: `.s7abba7_incompatibleFilter` sets it once and it reaches the\n * track and the label together, which is what keeps a red switch from sitting\n * under a neutral word. A declaration on `.sa4bed2_switch` itself would shadow the\n * inherited value (an element's own rule beats what it inherits), so the\n * default rides in the `var()` fallback and is resolved once into\n * `--track-hue` instead of being spelled out at each use.\n *\n * `box-sizing` and `padding` are stated because the two callers are different\n * elements — a `<button>`, which the UA gives padding, and a `<span>`, which\n * it does not. Without them the same class renders two sizes, and the knob\n * sits 2px above centre in the taller one. */\n.sa4bed2_switch {\n --track-hue: var(--switch-hue, var(--dsw-alias-state-success-primary));\n box-sizing: border-box;\n display: inline-block;\n position: relative;\n width: 32px;\n height: 18px;\n padding: 0;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-2);\n cursor: pointer;\n transition: background 120ms ease, border-color 120ms ease;\n}\n\n/* The knob's POSITION is the state, which is the reason this control needs no\n * `forced-colors` fallback the way the category pills do: a system palette\n * replaces every colour below and moves nothing. */\n.s97d8ec_switchOn {\n border-color: var(--track-hue);\n background: color-mix(in srgb, var(--track-hue) 55%, transparent);\n}\n\n.s24e1f5_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.s97d8ec_switchOn .s24e1f5_switchKnob {\n transform: translateX(14px);\n background: var(--dsw-alias-bg-base);\n}\n\n.sa4bed2_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 .sa4bed2_switch, .s24e1f5_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.s5c21f4_switchWrap {\n display: inline-flex;\n align-items: center;\n /* Same reason as `.s109c95_outdatedActions`: `.sf20a99_reloadPanel`'s own comment calls\n * itself \"a column of its own rather than inline with the row\", which held\n * for `.s10ddb2_installedActions` and not here until this line. */\n flex-wrap: wrap;\n gap: 8px;\n}\n";
9220
+ if (typeof document !== "undefined" && !document.querySelector("style[data-plugin-css=\"d74581c2\"]")) {
9049
9221
  const tag = document.createElement("style");
9050
- tag.dataset.pluginCss = "e3675a89";
9222
+ tag.dataset.pluginCss = "d74581c2";
9051
9223
  tag.textContent = css;
9052
9224
  document.head.appendChild(tag);
9053
9225
  }
9054
- var _dsh_plugin_shop_css_e3675a89_default = {
9055
- "client": "s8583e3_client",
9056
- "config": "s942a79_config",
9057
- "ts": "s5f67aa_ts",
9058
- "spec": "sfd9d8a_spec",
9059
- "panel": "s255e5a_panel",
9060
- "searchInput": "s232ec4_searchInput",
9061
- "toolbar": "s646509_toolbar",
9062
- "stateLine": "s44a3ec_stateLine",
9063
- "srOnly": "s3a0ece_srOnly",
9064
- "skeletonGrid": "se0c3b4_skeletonGrid",
9065
- "skeletonCard": "s520416_skeletonCard",
9066
- "skeletonName": "s8c7d6f_skeletonName",
9067
- "skeletonSummary": "sf5179e_skeletonSummary",
9068
- "skeletonSummaryShort": "sbb0d25_skeletonSummaryShort",
9069
- "skeletonActions": "s96e910_skeletonActions",
9070
- "actionButton": "s73242a_actionButton",
9071
- "toolbarRight": "s745e3c_toolbarRight",
9072
- "versionBlock": "s4fe61f_versionBlock",
9073
- "versionText": "sb9b924_versionText",
9074
- "updateSelfButton": "sa04455_updateSelfButton",
9075
- "checkUpdateButton": "s614a38_checkUpdateButton",
9076
- "githubLink": "sa987f7_githubLink",
9077
- "selfUpdatePanel": "sb81ba5_selfUpdatePanel",
9078
- "expansionNote": "s76fb13_expansionNote",
9079
- "staleBadge": "s3946f1_staleBadge",
9080
- "categoryBar": "sb2c859_categoryBar",
9081
- "categoryButton": "s26d58e_categoryButton",
9082
- "categoryButtonOn": "s5f68c4_categoryButtonOn",
9083
- "incompatibleFilter": "sfd887b_incompatibleFilter",
9084
- "switch": "s11c019_switch",
9085
- "switchOn": "s7580e8_switchOn",
9086
- "incompatibleFilterOn": "s6e4cba_incompatibleFilterOn",
9087
- "switchKnob": "saab2b3_switchKnob",
9088
- "updatableHeading": "sebf017_updatableHeading",
9089
- "catalogStatsRow": "seb0a1e_catalogStatsRow",
9090
- "catalogStats": "sccc4d1_catalogStats",
9091
- "catalogRefreshButton": "sff243d_catalogRefreshButton",
9092
- "catalogRefreshFailed": "s194168_catalogRefreshFailed",
9093
- "emptyLine": "s6de1ab_emptyLine",
9094
- "cards": "sf657f5_cards",
9095
- "card": "s65f383_card",
9096
- "cardsSentry": "sd7ee01_cardsSentry",
9097
- "showingLine": "s705cc6_showingLine",
9098
- "cardSpine": "s4ce25d_cardSpine",
9099
- "categoryBadge": "s9c8eaa_categoryBadge",
9100
- "entryHeader": "sa97234_entryHeader",
9101
- "name": "sc3f149_name",
9102
- "badges": "s513026_badges",
9103
- "cardActions": "s9b5048_cardActions",
9104
- "gate": "sc4f2ac_gate",
9105
- "installPanel": "s4d374c_installPanel",
9106
- "installedActions": "sf53ca0_installedActions",
9107
- "cardVersion": "sc6ec6e_cardVersion",
9108
- "sourceBadge": "s71f10f_sourceBadge",
9109
- "tierBadge": "s5e82cb_tierBadge",
9110
- "incompatibleBadge": "s4bbe02_incompatibleBadge",
9111
- "starsBadge": "sff43b4_starsBadge",
9112
- "chevron": "s73f7fb_chevron",
9113
- "chevronOpen": "s61393c_chevronOpen",
9114
- "body": "s820ebf_body",
9115
- "summary": "sb8fbc8_summary",
9116
- "summaryZh": "se507db_summaryZh",
9117
- "summaryExpanded": "s10baf0_summaryExpanded",
9118
- "summaryZhExpanded": "sb9dba9_summaryZhExpanded",
9119
- "incompatibleDetail": "s6eca19_incompatibleDetail",
9120
- "remedy": "s3d0044_remedy",
9121
- "remedyLead": "sdf203f_remedyLead",
9122
- "allowCommand": "sc32335_allowCommand",
9123
- "refusal": "sc74941_refusal",
9124
- "capabilitiesBlock": "s9d2029_capabilitiesBlock",
9125
- "capabilitiesNote": "s3bdbd5_capabilitiesNote",
9126
- "capabilities": "s441702_capabilities",
9127
- "detail": "sde3f0c_detail",
9128
- "detailRows": "se98179_detailRows",
9129
- "detailRow": "sfb4d2f_detailRow",
9130
- "cardMeta": "s5fbaf9_cardMeta",
9131
- "size": "s2d3c05_size",
9132
- "author": "s1ff66f_author",
9133
- "reviewedLine": "sd58c6d_reviewedLine",
9134
- "installButton": "se7aaed_installButton",
9135
- "confirmButton": "sb43a49_confirmButton",
9136
- "cancelButton": "s6e45d1_cancelButton",
9137
- "installedLabel": "sb6fc74_installedLabel",
9138
- "uninstallButton": "s96967f_uninstallButton",
9139
- "restartSelfButton": "sb00c36_restartSelfButton",
9140
- "restartButton": "s6ac237_restartButton",
9141
- "reloadButton": "s64a5ff_reloadButton",
9142
- "reloadPanel": "sf79aef_reloadPanel",
9143
- "gateTitle": "s559c38_gateTitle",
9144
- "gateBody": "s3c0ace_gateBody",
9145
- "gateWarning": "sc323e5_gateWarning",
9146
- "gateActions": "s4b2373_gateActions",
9147
- "installing": "s52b5a5_installing",
9148
- "log": "s64ab71_log",
9149
- "logLine": "s1531a8_logLine",
9150
- "notice": "sf20dda_notice",
9151
- "failedHeading": "seb9b08_failedHeading",
9152
- "failedDetail": "sfd3021_failedDetail",
9153
- "rejectedCode": "s50ba35_rejectedCode",
9154
- "rejectedDetail": "se75489_rejectedDetail",
9155
- "outdatedSection": "sa276cf_outdatedSection",
9156
- "outdatedList": "sceae6e_outdatedList",
9157
- "outdatedRow": "s8d2072_outdatedRow",
9158
- "outdatedInfo": "s5c2545_outdatedInfo",
9159
- "outdatedVersions": "s877c68_outdatedVersions",
9160
- "shadowedSection": "s6c4649_shadowedSection",
9161
- "shadowedDetail": "scc2a21_shadowedDetail",
9162
- "outdatedActions": "sa1f556_outdatedActions",
9163
- "switchWrap": "sd9f9e4_switchWrap"
9226
+ var _dsh_plugin_shop_css_d74581c2_default = {
9227
+ "client": "s73a3ff_client",
9228
+ "config": "s45aee2_config",
9229
+ "ts": "s8a51a7_ts",
9230
+ "spec": "s117e2f_spec",
9231
+ "panel": "sa2d7b5_panel",
9232
+ "searchInput": "s7c10d7_searchInput",
9233
+ "toolbar": "sbee2c3_toolbar",
9234
+ "stateLine": "sa89499_stateLine",
9235
+ "srOnly": "s90f53b_srOnly",
9236
+ "skeletonGrid": "sd5b66e_skeletonGrid",
9237
+ "skeletonCard": "sfb41cd_skeletonCard",
9238
+ "skeletonName": "sc9bc63_skeletonName",
9239
+ "skeletonSummary": "s75708b_skeletonSummary",
9240
+ "skeletonSummaryShort": "s772f28_skeletonSummaryShort",
9241
+ "skeletonActions": "s6ce39d_skeletonActions",
9242
+ "actionButton": "s1547e4_actionButton",
9243
+ "toolbarRight": "sfb98ad_toolbarRight",
9244
+ "versionBlock": "s15df74_versionBlock",
9245
+ "versionText": "s9d78e5_versionText",
9246
+ "updateSelfButton": "scb0503_updateSelfButton",
9247
+ "checkUpdateButton": "seab5ae_checkUpdateButton",
9248
+ "githubLink": "s6932e1_githubLink",
9249
+ "selfUpdatePanel": "sb1e444_selfUpdatePanel",
9250
+ "expansionNote": "s1dee54_expansionNote",
9251
+ "staleBadge": "s5823b0_staleBadge",
9252
+ "categoryBar": "s52084f_categoryBar",
9253
+ "categoryButton": "sd07f01_categoryButton",
9254
+ "categoryButtonOn": "s7d231e_categoryButtonOn",
9255
+ "incompatibleFilter": "s7abba7_incompatibleFilter",
9256
+ "switch": "sa4bed2_switch",
9257
+ "switchOn": "s97d8ec_switchOn",
9258
+ "incompatibleFilterOn": "sa24b4e_incompatibleFilterOn",
9259
+ "switchKnob": "s24e1f5_switchKnob",
9260
+ "updatableHeading": "s3ffb2a_updatableHeading",
9261
+ "catalogStatsRow": "sfd3ed8_catalogStatsRow",
9262
+ "sortGroup": "sc5a073_sortGroup",
9263
+ "sortLabel": "s275eb9_sortLabel",
9264
+ "catalogStats": "s3c7a5d_catalogStats",
9265
+ "catalogRefreshButton": "s6d141d_catalogRefreshButton",
9266
+ "catalogRefreshFailed": "s8b8ed4_catalogRefreshFailed",
9267
+ "emptyLine": "s918b21_emptyLine",
9268
+ "cards": "sf66d65_cards",
9269
+ "card": "sa6c0bf_card",
9270
+ "cardsSentry": "s21cef7_cardsSentry",
9271
+ "showingLine": "s9babb8_showingLine",
9272
+ "cardSpine": "sce2063_cardSpine",
9273
+ "categoryBadge": "sdc7cd8_categoryBadge",
9274
+ "entryHeader": "s6ae6ba_entryHeader",
9275
+ "name": "saf3e98_name",
9276
+ "badges": "s93d243_badges",
9277
+ "cardActions": "s763b75_cardActions",
9278
+ "gate": "sff1df6_gate",
9279
+ "installPanel": "s6d7bee_installPanel",
9280
+ "installedActions": "s10ddb2_installedActions",
9281
+ "cardVersion": "s9d1d9e_cardVersion",
9282
+ "sourceBadge": "sad456f_sourceBadge",
9283
+ "tierBadge": "s7f352e_tierBadge",
9284
+ "incompatibleBadge": "s649a66_incompatibleBadge",
9285
+ "starsBadge": "se83753_starsBadge",
9286
+ "chevron": "sc7129d_chevron",
9287
+ "chevronOpen": "s3e170e_chevronOpen",
9288
+ "body": "s249e15_body",
9289
+ "summary": "sdd4d7a_summary",
9290
+ "summaryZh": "s1a6809_summaryZh",
9291
+ "summaryExpanded": "sc924dd_summaryExpanded",
9292
+ "summaryZhExpanded": "sab7503_summaryZhExpanded",
9293
+ "incompatibleDetail": "s4625f5_incompatibleDetail",
9294
+ "remedy": "s632e71_remedy",
9295
+ "remedyLead": "s360025_remedyLead",
9296
+ "allowCommand": "s12ec11_allowCommand",
9297
+ "refusal": "sa92311_refusal",
9298
+ "capabilitiesBlock": "s99a4f5_capabilitiesBlock",
9299
+ "capabilitiesNote": "scf0a21_capabilitiesNote",
9300
+ "capabilities": "sc760c8_capabilities",
9301
+ "detail": "s48edfc_detail",
9302
+ "detailRows": "sce5502_detailRows",
9303
+ "detailRow": "scc4b2b_detailRow",
9304
+ "cardMeta": "s8fb80a_cardMeta",
9305
+ "shelfDate": "s72b045_shelfDate",
9306
+ "size": "s24611f_size",
9307
+ "author": "seefbaa_author",
9308
+ "reviewedLine": "sa79085_reviewedLine",
9309
+ "installButton": "sdddc5f_installButton",
9310
+ "confirmButton": "safd4a8_confirmButton",
9311
+ "cancelButton": "sfcf768_cancelButton",
9312
+ "installedLabel": "s563674_installedLabel",
9313
+ "uninstallButton": "s767a97_uninstallButton",
9314
+ "restartSelfButton": "sa6b105_restartSelfButton",
9315
+ "restartButton": "s426aa0_restartButton",
9316
+ "reloadButton": "sa283f9_reloadButton",
9317
+ "reloadPanel": "sf20a99_reloadPanel",
9318
+ "gateTitle": "see0e3d_gateTitle",
9319
+ "gateBody": "sd04ef9_gateBody",
9320
+ "gateWarning": "s7fd5d3_gateWarning",
9321
+ "gateActions": "sf6b0aa_gateActions",
9322
+ "installing": "sa32b72_installing",
9323
+ "log": "sf9b6a4_log",
9324
+ "logLine": "s9e4d00_logLine",
9325
+ "notice": "s7cd7db_notice",
9326
+ "failedHeading": "sfb1bae_failedHeading",
9327
+ "failedDetail": "s9591b1_failedDetail",
9328
+ "rejectedCode": "saa0605_rejectedCode",
9329
+ "rejectedDetail": "sbd5c7c_rejectedDetail",
9330
+ "outdatedSection": "s7c248d_outdatedSection",
9331
+ "outdatedList": "se144d3_outdatedList",
9332
+ "outdatedRow": "s6fbe58_outdatedRow",
9333
+ "outdatedInfo": "s8af496_outdatedInfo",
9334
+ "outdatedVersions": "sf82d51_outdatedVersions",
9335
+ "shadowedSection": "s31b89a_shadowedSection",
9336
+ "shadowedDetail": "scf838f_shadowedDetail",
9337
+ "outdatedActions": "s109c95_outdatedActions",
9338
+ "switchWrap": "s5c21f4_switchWrap"
9164
9339
  };
9165
9340
  //#endregion
9166
9341
  //#region src/client/ShopTab.tsx
@@ -9177,14 +9352,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9177
9352
  * those nodes, and React's next commit throws on the ones it no longer finds
9178
9353
  * (design 2026-09-26-market-borrowings §4). The tab renders no portals, so its
9179
9354
  * root is its whole surface. */
9180
- const PANEL_ROOT = `${_dsh_plugin_shop_css_e3675a89_default.panel} notranslate`;
9355
+ const PANEL_ROOT = `${_dsh_plugin_shop_css_d74581c2_default.panel} notranslate`;
9181
9356
  function ChevronIcon({ open }) {
9182
9357
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
9183
9358
  viewBox: "0 0 16 16",
9184
9359
  width: "14",
9185
9360
  height: "14",
9186
9361
  "aria-hidden": "true",
9187
- className: open ? _dsh_plugin_shop_css_e3675a89_default.chevronOpen : _dsh_plugin_shop_css_e3675a89_default.chevron,
9362
+ className: open ? _dsh_plugin_shop_css_d74581c2_default.chevronOpen : _dsh_plugin_shop_css_d74581c2_default.chevron,
9188
9363
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
9189
9364
  fill: "currentColor",
9190
9365
  d: "M3.5 6 8 10.5 12.5 6l1 1L8 12.5 2.5 7l1-1Z"
@@ -9197,7 +9372,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9197
9372
  * install controls. An installed plugin's card carries its installed row:
9198
9373
  * current → the non-interactive installed label, behind → the update button;
9199
9374
  * uninstalled → the install button. */
9200
- const EntryCard = (0, react.memo)(function EntryCard({ entry, stars, installed, missing, harness, nameTakenBy, t, flow, uninstallFlow, restart, restartBlocked, reload, setEnabled, inInstalledView, restarts }) {
9375
+ const EntryCard = (0, react.memo)(function EntryCard({ entry, stars, order, shelfDay, installed, missing, harness, nameTakenBy, t, flow, uninstallFlow, restart, restartBlocked, reload, setEnabled, inInstalledView, restarts }) {
9201
9376
  const [open, setOpen] = (0, react.useState)(false);
9202
9377
  const blockers = stateBlockers(blockersOf(missing, harness, nameTakenBy), t);
9203
9378
  const detailId = (0, react.useId)();
@@ -9205,6 +9380,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9205
9380
  const npmUrl = npmPageUrl(entry);
9206
9381
  const author = authorOf(entry);
9207
9382
  const size = formatSize(entry.installSize);
9383
+ const version = versionLabel(entry.source, entry.version);
9384
+ const versionName = t(versionLabelKey(version.kind), { version: version.value });
9208
9385
  const category = entry.catalog?.category ?? "other";
9209
9386
  const installTarget = {
9210
9387
  name: entry.name,
@@ -9214,32 +9391,32 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9214
9391
  subdir: entry.subdir
9215
9392
  };
9216
9393
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9217
- className: _dsh_plugin_shop_css_e3675a89_default.card,
9394
+ className: _dsh_plugin_shop_css_d74581c2_default.card,
9218
9395
  "data-shop-entry": entry.name,
9219
9396
  "data-category": category,
9220
9397
  children: [
9221
9398
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9222
- className: _dsh_plugin_shop_css_e3675a89_default.cardSpine,
9399
+ className: _dsh_plugin_shop_css_d74581c2_default.cardSpine,
9223
9400
  "aria-hidden": "true"
9224
9401
  }),
9225
9402
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
9226
9403
  type: "button",
9227
- className: _dsh_plugin_shop_css_e3675a89_default.entryHeader,
9404
+ className: _dsh_plugin_shop_css_d74581c2_default.entryHeader,
9228
9405
  "aria-expanded": open,
9229
9406
  "aria-controls": detailId,
9230
9407
  onClick: () => setOpen((current) => !current),
9231
9408
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9232
- className: _dsh_plugin_shop_css_e3675a89_default.name,
9409
+ className: _dsh_plugin_shop_css_d74581c2_default.name,
9233
9410
  children: entry.name
9234
9411
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
9235
- className: _dsh_plugin_shop_css_e3675a89_default.badges,
9412
+ className: _dsh_plugin_shop_css_d74581c2_default.badges,
9236
9413
  children: [
9237
9414
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9238
- className: _dsh_plugin_shop_css_e3675a89_default.categoryBadge,
9415
+ className: _dsh_plugin_shop_css_d74581c2_default.categoryBadge,
9239
9416
  children: t(categoryKey(entry))
9240
9417
  }),
9241
9418
  entry.source === "npm" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9242
- className: _dsh_plugin_shop_css_e3675a89_default.sourceBadge,
9419
+ className: _dsh_plugin_shop_css_d74581c2_default.sourceBadge,
9243
9420
  "data-shop-source-npm": true,
9244
9421
  role: "img",
9245
9422
  "aria-label": t("npmSource"),
@@ -9256,7 +9433,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9256
9433
  })
9257
9434
  }),
9258
9435
  hasGithubHome(entry) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9259
- className: _dsh_plugin_shop_css_e3675a89_default.sourceBadge,
9436
+ className: _dsh_plugin_shop_css_d74581c2_default.sourceBadge,
9260
9437
  "data-shop-source-github": true,
9261
9438
  role: "img",
9262
9439
  "aria-label": t("githubSource"),
@@ -9269,23 +9446,26 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9269
9446
  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" })
9270
9447
  })
9271
9448
  }),
9272
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
9273
- className: _dsh_plugin_shop_css_e3675a89_default.cardVersion,
9449
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9450
+ className: _dsh_plugin_shop_css_d74581c2_default.cardVersion,
9274
9451
  "data-card-version": true,
9275
- children: ["v", displayVersion(entry)]
9452
+ role: "img",
9453
+ "aria-label": versionName,
9454
+ title: versionName,
9455
+ children: version.text
9276
9456
  }),
9277
9457
  entry.tier !== "community" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9278
- className: _dsh_plugin_shop_css_e3675a89_default.tierBadge,
9458
+ className: _dsh_plugin_shop_css_d74581c2_default.tierBadge,
9279
9459
  "data-tier": entry.tier,
9280
9460
  children: t(tierKey(entry.tier))
9281
9461
  }),
9282
9462
  entry.archived === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9283
- className: _dsh_plugin_shop_css_e3675a89_default.tierBadge,
9463
+ className: _dsh_plugin_shop_css_d74581c2_default.tierBadge,
9284
9464
  "data-tier": "archived",
9285
9465
  children: t("archivedBadge")
9286
9466
  }),
9287
9467
  stars !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
9288
- className: _dsh_plugin_shop_css_e3675a89_default.starsBadge,
9468
+ className: _dsh_plugin_shop_css_d74581c2_default.starsBadge,
9289
9469
  role: "img",
9290
9470
  "aria-label": t("stars", { count: stars }),
9291
9471
  children: ["★ ", formatStars(stars)]
@@ -9295,39 +9475,39 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9295
9475
  })]
9296
9476
  }),
9297
9477
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9298
- className: _dsh_plugin_shop_css_e3675a89_default.body,
9478
+ className: _dsh_plugin_shop_css_d74581c2_default.body,
9299
9479
  children: [
9300
9480
  summary !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9301
- className: open ? `${_dsh_plugin_shop_css_e3675a89_default.summary} ${_dsh_plugin_shop_css_e3675a89_default.summaryExpanded}` : _dsh_plugin_shop_css_e3675a89_default.summary,
9481
+ className: open ? `${_dsh_plugin_shop_css_d74581c2_default.summary} ${_dsh_plugin_shop_css_d74581c2_default.summaryExpanded}` : _dsh_plugin_shop_css_d74581c2_default.summary,
9302
9482
  children: summary.en
9303
9483
  }),
9304
9484
  summary?.zh !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9305
- className: open ? `${_dsh_plugin_shop_css_e3675a89_default.summaryZh} ${_dsh_plugin_shop_css_e3675a89_default.summaryZhExpanded}` : _dsh_plugin_shop_css_e3675a89_default.summaryZh,
9485
+ className: open ? `${_dsh_plugin_shop_css_d74581c2_default.summaryZh} ${_dsh_plugin_shop_css_d74581c2_default.summaryZhExpanded}` : _dsh_plugin_shop_css_d74581c2_default.summaryZh,
9306
9486
  children: summary.zh
9307
9487
  }),
9308
9488
  blockers.map((blocker) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockerLine, {
9309
9489
  blocker,
9310
- className: _dsh_plugin_shop_css_e3675a89_default.incompatibleDetail,
9490
+ className: _dsh_plugin_shop_css_d74581c2_default.incompatibleDetail,
9311
9491
  surface: "detail"
9312
9492
  }, blocker.kind)),
9313
9493
  open && entry.catalog !== void 0 && entry.catalog.capabilities.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9314
- className: _dsh_plugin_shop_css_e3675a89_default.capabilitiesBlock,
9494
+ className: _dsh_plugin_shop_css_d74581c2_default.capabilitiesBlock,
9315
9495
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9316
- className: _dsh_plugin_shop_css_e3675a89_default.capabilitiesNote,
9496
+ className: _dsh_plugin_shop_css_d74581c2_default.capabilitiesNote,
9317
9497
  children: t("capabilitiesNote")
9318
9498
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
9319
- className: _dsh_plugin_shop_css_e3675a89_default.capabilities,
9499
+ className: _dsh_plugin_shop_css_d74581c2_default.capabilities,
9320
9500
  children: entry.catalog.capabilities.map((capability) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: capability }, capability))
9321
9501
  })]
9322
9502
  }),
9323
9503
  open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
9324
9504
  id: detailId,
9325
- className: _dsh_plugin_shop_css_e3675a89_default.detail,
9505
+ className: _dsh_plugin_shop_css_d74581c2_default.detail,
9326
9506
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dl", {
9327
- className: _dsh_plugin_shop_css_e3675a89_default.detailRows,
9507
+ className: _dsh_plugin_shop_css_d74581c2_default.detailRows,
9328
9508
  children: [
9329
9509
  npmUrl !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9330
- className: _dsh_plugin_shop_css_e3675a89_default.detailRow,
9510
+ className: _dsh_plugin_shop_css_d74581c2_default.detailRow,
9331
9511
  "data-shop-npm": true,
9332
9512
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("npmPage") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
9333
9513
  href: npmUrl,
@@ -9337,7 +9517,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9337
9517
  }) })]
9338
9518
  }),
9339
9519
  entry.repository !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9340
- className: _dsh_plugin_shop_css_e3675a89_default.detailRow,
9520
+ className: _dsh_plugin_shop_css_d74581c2_default.detailRow,
9341
9521
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("repository") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: /^https?:\/\//.test(entry.repository) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
9342
9522
  href: entry.repository,
9343
9523
  target: "_blank",
@@ -9346,25 +9526,25 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9346
9526
  }) : entry.repository })]
9347
9527
  }),
9348
9528
  entry.license !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9349
- className: _dsh_plugin_shop_css_e3675a89_default.detailRow,
9529
+ className: _dsh_plugin_shop_css_d74581c2_default.detailRow,
9350
9530
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("license") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: isCustomLicense(entry.license) ? t("customLicense") : entry.license })]
9351
9531
  })
9352
9532
  ]
9353
9533
  }), entry.tier === "verified-stale" && entry.review !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9354
- className: _dsh_plugin_shop_css_e3675a89_default.reviewedLine,
9534
+ className: _dsh_plugin_shop_css_d74581c2_default.reviewedLine,
9355
9535
  children: entry.review.reviewedVersion !== void 0 ? t("reviewedVersionLine", {
9356
- reviewed: entry.review.reviewedVersion,
9357
- current: entry.version
9536
+ reviewed: versionLabel("npm", entry.review.reviewedVersion).text,
9537
+ current: version.text
9358
9538
  }) : t("reviewedPinLine", {
9359
9539
  pin: reviewHashPin(entry.review),
9360
- current: displayVersion(entry)
9540
+ current: version.text
9361
9541
  })
9362
9542
  })]
9363
9543
  })
9364
9544
  ]
9365
9545
  }),
9366
9546
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9367
- className: _dsh_plugin_shop_css_e3675a89_default.cardActions,
9547
+ className: _dsh_plugin_shop_css_d74581c2_default.cardActions,
9368
9548
  "data-shop-actions": true,
9369
9549
  children: [installed === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [!inInstalledView && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstallPanel, {
9370
9550
  target: installTarget,
@@ -9396,7 +9576,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9396
9576
  restartBlocked,
9397
9577
  reload
9398
9578
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9399
- className: _dsh_plugin_shop_css_e3675a89_default.installedLabel,
9579
+ className: _dsh_plugin_shop_css_d74581c2_default.installedLabel,
9400
9580
  "data-shop-installed": true,
9401
9581
  children: t("installed")
9402
9582
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockerBadge, {
@@ -9420,20 +9600,28 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9420
9600
  reload,
9421
9601
  flow: uninstallFlow
9422
9602
  })
9423
- ] }), (size !== void 0 || author !== null) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
9424
- className: _dsh_plugin_shop_css_e3675a89_default.cardMeta,
9425
- children: [size !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9426
- className: _dsh_plugin_shop_css_e3675a89_default.size,
9427
- "data-shop-size": true,
9428
- role: "img",
9429
- "aria-label": t("sizeLabel", { size }),
9430
- title: t("sizeLabel", { size }),
9431
- children: size
9432
- }), author !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9433
- className: _dsh_plugin_shop_css_e3675a89_default.author,
9434
- "data-shop-author": true,
9435
- children: t("authorLine", { author })
9436
- })]
9603
+ ] }), (size !== void 0 || author !== null || shelfDay !== void 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
9604
+ className: _dsh_plugin_shop_css_d74581c2_default.cardMeta,
9605
+ children: [
9606
+ shelfDay !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9607
+ className: _dsh_plugin_shop_css_d74581c2_default.shelfDate,
9608
+ "data-shop-shelf-date": true,
9609
+ children: t(order === "added" ? "listedOn" : "updatedOn", { date: shelfDay })
9610
+ }),
9611
+ size !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9612
+ className: _dsh_plugin_shop_css_d74581c2_default.size,
9613
+ "data-shop-size": true,
9614
+ role: "img",
9615
+ "aria-label": t("sizeLabel", { size }),
9616
+ title: t("sizeLabel", { size }),
9617
+ children: size
9618
+ }),
9619
+ author !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9620
+ className: _dsh_plugin_shop_css_d74581c2_default.author,
9621
+ "data-shop-author": true,
9622
+ children: t("authorLine", { author })
9623
+ })
9624
+ ]
9437
9625
  })]
9438
9626
  })
9439
9627
  ]
@@ -9479,13 +9667,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9479
9667
  ...attribute,
9480
9668
  children: blocker.text
9481
9669
  }), blocker.remedy !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9482
- className: _dsh_plugin_shop_css_e3675a89_default.remedy,
9670
+ className: _dsh_plugin_shop_css_d74581c2_default.remedy,
9483
9671
  "data-shop-remedy": blocker.kind,
9484
9672
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9485
- className: _dsh_plugin_shop_css_e3675a89_default.remedyLead,
9673
+ className: _dsh_plugin_shop_css_d74581c2_default.remedyLead,
9486
9674
  children: blocker.remedy.lead
9487
9675
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
9488
- className: _dsh_plugin_shop_css_e3675a89_default.allowCommand,
9676
+ className: _dsh_plugin_shop_css_d74581c2_default.allowCommand,
9489
9677
  "data-shop-allow-command": true,
9490
9678
  children: blocker.remedy.command
9491
9679
  })]
@@ -9523,7 +9711,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9523
9711
  if (lead === void 0 || word === null) return null;
9524
9712
  const detail = blockers.map((blocker) => blocker.text).join("\n");
9525
9713
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
9526
- className: _dsh_plugin_shop_css_e3675a89_default.incompatibleBadge,
9714
+ className: _dsh_plugin_shop_css_d74581c2_default.incompatibleBadge,
9527
9715
  "data-shop-blocker": lead.kind,
9528
9716
  role: "img",
9529
9717
  "aria-label": detail,
@@ -9540,37 +9728,37 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9540
9728
  const [gateOpen, setGateOpen] = (0, react.useState)(false);
9541
9729
  const { view, start } = flow;
9542
9730
  if (view.kind === "running") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9543
- className: _dsh_plugin_shop_css_e3675a89_default.installPanel,
9731
+ className: _dsh_plugin_shop_css_d74581c2_default.installPanel,
9544
9732
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9545
- className: _dsh_plugin_shop_css_e3675a89_default.installing,
9733
+ className: _dsh_plugin_shop_css_d74581c2_default.installing,
9546
9734
  children: t(installPhaseKey(view.phase))
9547
9735
  }), view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9548
- className: _dsh_plugin_shop_css_e3675a89_default.log,
9736
+ className: _dsh_plugin_shop_css_d74581c2_default.log,
9549
9737
  children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9550
- className: _dsh_plugin_shop_css_e3675a89_default.logLine,
9738
+ className: _dsh_plugin_shop_css_d74581c2_default.logLine,
9551
9739
  "data-shop-log-line": true,
9552
9740
  children: line
9553
9741
  }, index))
9554
9742
  })]
9555
9743
  });
9556
9744
  if (view.kind === "done") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9557
- className: _dsh_plugin_shop_css_e3675a89_default.installedActions,
9745
+ className: _dsh_plugin_shop_css_d74581c2_default.installedActions,
9558
9746
  children: [
9559
9747
  view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9560
- className: _dsh_plugin_shop_css_e3675a89_default.log,
9748
+ className: _dsh_plugin_shop_css_d74581c2_default.log,
9561
9749
  children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9562
- className: _dsh_plugin_shop_css_e3675a89_default.logLine,
9750
+ className: _dsh_plugin_shop_css_d74581c2_default.logLine,
9563
9751
  "data-shop-log-line": true,
9564
9752
  children: line
9565
9753
  }, index))
9566
9754
  }),
9567
9755
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9568
- className: _dsh_plugin_shop_css_e3675a89_default.notice,
9756
+ className: _dsh_plugin_shop_css_d74581c2_default.notice,
9569
9757
  "data-shop-restart-notice": true,
9570
9758
  children: t(activationNoticeKey(view.activation, view.restartReason))
9571
9759
  }),
9572
9760
  view.detail !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9573
- className: _dsh_plugin_shop_css_e3675a89_default.notice,
9761
+ className: _dsh_plugin_shop_css_d74581c2_default.notice,
9574
9762
  "data-shop-done-note": true,
9575
9763
  children: view.detail
9576
9764
  }),
@@ -9585,57 +9773,57 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9585
9773
  ]
9586
9774
  });
9587
9775
  if (view.kind === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9588
- className: _dsh_plugin_shop_css_e3675a89_default.installPanel,
9776
+ className: _dsh_plugin_shop_css_d74581c2_default.installPanel,
9589
9777
  children: [
9590
9778
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9591
- className: _dsh_plugin_shop_css_e3675a89_default.failedHeading,
9779
+ className: _dsh_plugin_shop_css_d74581c2_default.failedHeading,
9592
9780
  children: t("installFailed")
9593
9781
  }),
9594
9782
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9595
- className: _dsh_plugin_shop_css_e3675a89_default.log,
9783
+ className: _dsh_plugin_shop_css_d74581c2_default.log,
9596
9784
  children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9597
- className: _dsh_plugin_shop_css_e3675a89_default.logLine,
9785
+ className: _dsh_plugin_shop_css_d74581c2_default.logLine,
9598
9786
  "data-shop-log-line": true,
9599
9787
  children: line
9600
9788
  }, index))
9601
9789
  }),
9602
9790
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9603
- className: _dsh_plugin_shop_css_e3675a89_default.failedDetail,
9791
+ className: _dsh_plugin_shop_css_d74581c2_default.failedDetail,
9604
9792
  children: view.detail === "" ? t("installTransportFailed") : view.detail
9605
9793
  })
9606
9794
  ]
9607
9795
  });
9608
9796
  if (view.kind === "rejected") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9609
- className: _dsh_plugin_shop_css_e3675a89_default.installPanel,
9797
+ className: _dsh_plugin_shop_css_d74581c2_default.installPanel,
9610
9798
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9611
- className: _dsh_plugin_shop_css_e3675a89_default.rejectedCode,
9799
+ className: _dsh_plugin_shop_css_d74581c2_default.rejectedCode,
9612
9800
  children: t(rejectionCodeKey(view.code))
9613
9801
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9614
- className: _dsh_plugin_shop_css_e3675a89_default.rejectedDetail,
9802
+ className: _dsh_plugin_shop_css_d74581c2_default.rejectedDetail,
9615
9803
  children: view.detail
9616
9804
  })]
9617
9805
  });
9618
9806
  if (gateOpen) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9619
- className: _dsh_plugin_shop_css_e3675a89_default.gate,
9807
+ className: _dsh_plugin_shop_css_d74581c2_default.gate,
9620
9808
  children: [
9621
9809
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9622
- className: _dsh_plugin_shop_css_e3675a89_default.gateTitle,
9810
+ className: _dsh_plugin_shop_css_d74581c2_default.gateTitle,
9623
9811
  children: t("acknowledgementTitle")
9624
9812
  }),
9625
9813
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9626
- className: _dsh_plugin_shop_css_e3675a89_default.gateBody,
9814
+ className: _dsh_plugin_shop_css_d74581c2_default.gateBody,
9627
9815
  children: t("acknowledgementBody")
9628
9816
  }),
9629
9817
  !blockersStated && blockers.map((blocker) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockerLine, {
9630
9818
  blocker,
9631
- className: _dsh_plugin_shop_css_e3675a89_default.gateWarning,
9819
+ className: _dsh_plugin_shop_css_d74581c2_default.gateWarning,
9632
9820
  surface: "warning"
9633
9821
  }, blocker.kind)),
9634
9822
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9635
- className: _dsh_plugin_shop_css_e3675a89_default.gateActions,
9823
+ className: _dsh_plugin_shop_css_d74581c2_default.gateActions,
9636
9824
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
9637
9825
  type: "button",
9638
- className: _dsh_plugin_shop_css_e3675a89_default.confirmButton,
9826
+ className: _dsh_plugin_shop_css_d74581c2_default.confirmButton,
9639
9827
  "data-shop-confirm": true,
9640
9828
  onClick: () => {
9641
9829
  setGateOpen(false);
@@ -9647,7 +9835,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9647
9835
  children: t("confirm")
9648
9836
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
9649
9837
  type: "button",
9650
- className: _dsh_plugin_shop_css_e3675a89_default.cancelButton,
9838
+ className: _dsh_plugin_shop_css_d74581c2_default.cancelButton,
9651
9839
  onClick: () => setGateOpen(false),
9652
9840
  children: t("cancel")
9653
9841
  })]
@@ -9659,7 +9847,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9659
9847
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
9660
9848
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
9661
9849
  type: "button",
9662
- className: _dsh_plugin_shop_css_e3675a89_default.installButton,
9850
+ className: _dsh_plugin_shop_css_d74581c2_default.installButton,
9663
9851
  disabled: refused,
9664
9852
  ...refused ? { "data-shop-refused": true } : {},
9665
9853
  ...update ? { "data-shop-update": true } : { "data-shop-install": true },
@@ -9677,11 +9865,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9677
9865
  t
9678
9866
  }),
9679
9867
  refused && !blockersStated && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9680
- className: _dsh_plugin_shop_css_e3675a89_default.refusal,
9868
+ className: _dsh_plugin_shop_css_d74581c2_default.refusal,
9681
9869
  "data-shop-refusal": true,
9682
9870
  children: blockers.map((blocker) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockerLine, {
9683
9871
  blocker,
9684
- className: _dsh_plugin_shop_css_e3675a89_default.gateWarning,
9872
+ className: _dsh_plugin_shop_css_d74581c2_default.gateWarning,
9685
9873
  surface: "warning"
9686
9874
  }, blocker.kind))
9687
9875
  })
@@ -9696,32 +9884,32 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9696
9884
  function UninstallPanel({ name, t, restart, restartBlocked, reload, flow }) {
9697
9885
  const { view, start, reset } = flow;
9698
9886
  if (view.kind === "running") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9699
- className: _dsh_plugin_shop_css_e3675a89_default.installPanel,
9887
+ className: _dsh_plugin_shop_css_d74581c2_default.installPanel,
9700
9888
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9701
- className: _dsh_plugin_shop_css_e3675a89_default.installing,
9889
+ className: _dsh_plugin_shop_css_d74581c2_default.installing,
9702
9890
  children: t("uninstalling")
9703
9891
  }), view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9704
- className: _dsh_plugin_shop_css_e3675a89_default.log,
9892
+ className: _dsh_plugin_shop_css_d74581c2_default.log,
9705
9893
  children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9706
- className: _dsh_plugin_shop_css_e3675a89_default.logLine,
9894
+ className: _dsh_plugin_shop_css_d74581c2_default.logLine,
9707
9895
  "data-shop-log-line": true,
9708
9896
  children: line
9709
9897
  }, index))
9710
9898
  })]
9711
9899
  });
9712
9900
  if (view.kind === "done") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9713
- className: _dsh_plugin_shop_css_e3675a89_default.installedActions,
9901
+ className: _dsh_plugin_shop_css_d74581c2_default.installedActions,
9714
9902
  children: [
9715
9903
  view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9716
- className: _dsh_plugin_shop_css_e3675a89_default.log,
9904
+ className: _dsh_plugin_shop_css_d74581c2_default.log,
9717
9905
  children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9718
- className: _dsh_plugin_shop_css_e3675a89_default.logLine,
9906
+ className: _dsh_plugin_shop_css_d74581c2_default.logLine,
9719
9907
  "data-shop-log-line": true,
9720
9908
  children: line
9721
9909
  }, index))
9722
9910
  }),
9723
9911
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9724
- className: _dsh_plugin_shop_css_e3675a89_default.notice,
9912
+ className: _dsh_plugin_shop_css_d74581c2_default.notice,
9725
9913
  "data-shop-uninstall-done": true,
9726
9914
  children: t(uninstallActivationNoticeKey(view.activation))
9727
9915
  }),
@@ -9735,7 +9923,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9735
9923
  }),
9736
9924
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
9737
9925
  type: "button",
9738
- className: _dsh_plugin_shop_css_e3675a89_default.cancelButton,
9926
+ className: _dsh_plugin_shop_css_d74581c2_default.cancelButton,
9739
9927
  "data-shop-uninstall-dismiss": true,
9740
9928
  onClick: reset,
9741
9929
  children: t("dismiss")
@@ -9743,27 +9931,27 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9743
9931
  ]
9744
9932
  });
9745
9933
  if (view.kind === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9746
- className: _dsh_plugin_shop_css_e3675a89_default.installPanel,
9934
+ className: _dsh_plugin_shop_css_d74581c2_default.installPanel,
9747
9935
  children: [
9748
9936
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9749
- className: _dsh_plugin_shop_css_e3675a89_default.failedHeading,
9937
+ className: _dsh_plugin_shop_css_d74581c2_default.failedHeading,
9750
9938
  children: t("uninstallFailed")
9751
9939
  }),
9752
9940
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9753
- className: _dsh_plugin_shop_css_e3675a89_default.log,
9941
+ className: _dsh_plugin_shop_css_d74581c2_default.log,
9754
9942
  children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
9755
- className: _dsh_plugin_shop_css_e3675a89_default.logLine,
9943
+ className: _dsh_plugin_shop_css_d74581c2_default.logLine,
9756
9944
  "data-shop-log-line": true,
9757
9945
  children: line
9758
9946
  }, index))
9759
9947
  }),
9760
9948
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9761
- className: _dsh_plugin_shop_css_e3675a89_default.failedDetail,
9949
+ className: _dsh_plugin_shop_css_d74581c2_default.failedDetail,
9762
9950
  children: view.detail === "" ? t("uninstallTransportFailed") : view.detail
9763
9951
  }),
9764
9952
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
9765
9953
  type: "button",
9766
- className: _dsh_plugin_shop_css_e3675a89_default.uninstallButton,
9954
+ className: _dsh_plugin_shop_css_d74581c2_default.uninstallButton,
9767
9955
  "data-shop-uninstall-retry": true,
9768
9956
  onClick: () => void start({ name }),
9769
9957
  children: t("retry")
@@ -9773,7 +9961,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9773
9961
  if (view.kind !== "idle") return null;
9774
9962
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
9775
9963
  type: "button",
9776
- className: _dsh_plugin_shop_css_e3675a89_default.uninstallButton,
9964
+ className: _dsh_plugin_shop_css_d74581c2_default.uninstallButton,
9777
9965
  "data-shop-uninstall": true,
9778
9966
  onClick: () => void start({ name }),
9779
9967
  children: t("uninstall")
@@ -9814,38 +10002,38 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9814
10002
  else if (outcome.kind === "unreachable") setRefusal(t("restartTransportFailed"));
9815
10003
  };
9816
10004
  if (monitor?.kind === "requesting" || monitor?.kind === "restarting") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9817
- className: _dsh_plugin_shop_css_e3675a89_default.notice,
10005
+ className: _dsh_plugin_shop_css_d74581c2_default.notice,
9818
10006
  "data-shop-restarting": true,
9819
10007
  children: t("restarting")
9820
10008
  });
9821
10009
  if (monitor?.kind === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9822
- className: _dsh_plugin_shop_css_e3675a89_default.failedDetail,
10010
+ className: _dsh_plugin_shop_css_d74581c2_default.failedDetail,
9823
10011
  "data-shop-restart-error": true,
9824
10012
  children: monitor.logFile === void 0 ? t("restartFailedNotice") : t("restartFailedLogNotice", { log: monitor.logFile })
9825
10013
  });
9826
10014
  if (monitor?.kind === "not-restarted") return null;
9827
10015
  if (gateOpen) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9828
- className: _dsh_plugin_shop_css_e3675a89_default.gate,
10016
+ className: _dsh_plugin_shop_css_d74581c2_default.gate,
9829
10017
  children: [
9830
10018
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9831
- className: _dsh_plugin_shop_css_e3675a89_default.gateTitle,
10019
+ className: _dsh_plugin_shop_css_d74581c2_default.gateTitle,
9832
10020
  children: t("restartTitle")
9833
10021
  }),
9834
10022
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9835
- className: _dsh_plugin_shop_css_e3675a89_default.gateBody,
10023
+ className: _dsh_plugin_shop_css_d74581c2_default.gateBody,
9836
10024
  children: t("restartBody")
9837
10025
  }),
9838
10026
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9839
- className: _dsh_plugin_shop_css_e3675a89_default.gateActions,
10027
+ className: _dsh_plugin_shop_css_d74581c2_default.gateActions,
9840
10028
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
9841
10029
  type: "button",
9842
- className: _dsh_plugin_shop_css_e3675a89_default.confirmButton,
10030
+ className: _dsh_plugin_shop_css_d74581c2_default.confirmButton,
9843
10031
  "data-shop-restart-confirm": true,
9844
10032
  onClick: () => void onConfirm(),
9845
10033
  children: t("restartConfirm")
9846
10034
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
9847
10035
  type: "button",
9848
- className: _dsh_plugin_shop_css_e3675a89_default.cancelButton,
10036
+ className: _dsh_plugin_shop_css_d74581c2_default.cancelButton,
9849
10037
  onClick: () => setGateOpen(false),
9850
10038
  children: t("cancel")
9851
10039
  })]
@@ -9853,14 +10041,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9853
10041
  ]
9854
10042
  });
9855
10043
  const refused = refusal === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9856
- className: _dsh_plugin_shop_css_e3675a89_default.failedDetail,
10044
+ className: _dsh_plugin_shop_css_d74581c2_default.failedDetail,
9857
10045
  "data-shop-restart-error": true,
9858
10046
  children: refusal
9859
10047
  });
9860
10048
  if (gate !== void 0) return refused;
9861
10049
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [refused, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
9862
10050
  type: "button",
9863
- className: _dsh_plugin_shop_css_e3675a89_default.restartButton,
10051
+ className: _dsh_plugin_shop_css_d74581c2_default.restartButton,
9864
10052
  "data-shop-restart": true,
9865
10053
  onClick: () => setGateOpen(true),
9866
10054
  children: t("restart")
@@ -9878,13 +10066,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9878
10066
  */
9879
10067
  function NotRestartedNotice({ t, pid, logFile }) {
9880
10068
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9881
- className: _dsh_plugin_shop_css_e3675a89_default.selfUpdatePanel,
10069
+ className: _dsh_plugin_shop_css_d74581c2_default.selfUpdatePanel,
9882
10070
  "data-shop-not-restarted": true,
9883
10071
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9884
- className: _dsh_plugin_shop_css_e3675a89_default.failedDetail,
10072
+ className: _dsh_plugin_shop_css_d74581c2_default.failedDetail,
9885
10073
  children: pid === void 0 ? t("notRestartedNotice") : t("notRestartedPidNotice", { pid: String(pid) })
9886
10074
  }), logFile !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9887
- className: _dsh_plugin_shop_css_e3675a89_default.failedDetail,
10075
+ className: _dsh_plugin_shop_css_d74581c2_default.failedDetail,
9888
10076
  children: t("notRestartedLogNotice", { log: logFile })
9889
10077
  })]
9890
10078
  });
@@ -9914,7 +10102,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9914
10102
  restart,
9915
10103
  reload
9916
10104
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9917
- className: _dsh_plugin_shop_css_e3675a89_default.notice,
10105
+ className: _dsh_plugin_shop_css_d74581c2_default.notice,
9918
10106
  "data-shop-restart-disabled": true,
9919
10107
  children: t(restartBlockedNoticeKey(restartBlocked))
9920
10108
  });
@@ -9925,14 +10113,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9925
10113
  * would help without knowing that now is a good time. */
9926
10114
  function ReloadPanel({ t, reload, where }) {
9927
10115
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9928
- className: _dsh_plugin_shop_css_e3675a89_default.reloadPanel,
10116
+ className: _dsh_plugin_shop_css_d74581c2_default.reloadPanel,
9929
10117
  "data-shop-reload": where,
9930
10118
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
9931
- className: _dsh_plugin_shop_css_e3675a89_default.notice,
10119
+ className: _dsh_plugin_shop_css_d74581c2_default.notice,
9932
10120
  children: t("reloadNote")
9933
10121
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
9934
10122
  type: "button",
9935
- className: _dsh_plugin_shop_css_e3675a89_default.reloadButton,
10123
+ className: _dsh_plugin_shop_css_d74581c2_default.reloadButton,
9936
10124
  onClick: reload,
9937
10125
  children: t("reload")
9938
10126
  })]
@@ -9983,7 +10171,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9983
10171
  }
9984
10172
  };
9985
10173
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
9986
- className: _dsh_plugin_shop_css_e3675a89_default.switchWrap,
10174
+ className: _dsh_plugin_shop_css_d74581c2_default.switchWrap,
9987
10175
  "data-shop-enabled-switch": row.name,
9988
10176
  children: [
9989
10177
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
@@ -9992,18 +10180,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
9992
10180
  "aria-checked": enabled,
9993
10181
  "aria-label": t("enabledSwitch"),
9994
10182
  "data-shop-toggle": true,
9995
- className: `${_dsh_plugin_shop_css_e3675a89_default.switch} ${enabled ? _dsh_plugin_shop_css_e3675a89_default.switchOn : ""}`,
10183
+ className: `${_dsh_plugin_shop_css_d74581c2_default.switch} ${enabled ? _dsh_plugin_shop_css_d74581c2_default.switchOn : ""}`,
9996
10184
  onClick: () => void onToggle(),
9997
10185
  disabled: toggle.kind === "saving",
9998
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_e3675a89_default.switchKnob })
10186
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_d74581c2_default.switchKnob })
9999
10187
  }),
10000
10188
  toggle.kind === "saved" && owed === "nothing" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10001
- className: _dsh_plugin_shop_css_e3675a89_default.notice,
10189
+ className: _dsh_plugin_shop_css_d74581c2_default.notice,
10002
10190
  "data-shop-hot-apply": true,
10003
10191
  children: t("hotApplyNote")
10004
10192
  }),
10005
10193
  owed === "restart" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10006
- className: _dsh_plugin_shop_css_e3675a89_default.notice,
10194
+ className: _dsh_plugin_shop_css_d74581c2_default.notice,
10007
10195
  "data-shop-toggle-restart": true,
10008
10196
  children: t("toggleRestartNote")
10009
10197
  }),
@@ -10016,7 +10204,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10016
10204
  where: "toggle"
10017
10205
  }),
10018
10206
  toggle.kind === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10019
- className: _dsh_plugin_shop_css_e3675a89_default.failedDetail,
10207
+ className: _dsh_plugin_shop_css_d74581c2_default.failedDetail,
10020
10208
  "data-shop-toggle-error": true,
10021
10209
  children: toggle.detail
10022
10210
  })
@@ -10031,19 +10219,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10031
10219
  * field is narrowed at the parameter. */
10032
10220
  function OutdatedRow({ row, tier, missing, harness, t, setEnabled, flowFor, restart, restartBlocked, reload, restarts }) {
10033
10221
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10034
- className: _dsh_plugin_shop_css_e3675a89_default.outdatedRow,
10222
+ className: _dsh_plugin_shop_css_d74581c2_default.outdatedRow,
10035
10223
  "data-shop-outdated-entry": row.name,
10036
10224
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10037
- className: _dsh_plugin_shop_css_e3675a89_default.outdatedInfo,
10225
+ className: _dsh_plugin_shop_css_d74581c2_default.outdatedInfo,
10038
10226
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10039
- className: _dsh_plugin_shop_css_e3675a89_default.name,
10227
+ className: _dsh_plugin_shop_css_d74581c2_default.name,
10040
10228
  children: row.name
10041
10229
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
10042
- className: _dsh_plugin_shop_css_e3675a89_default.outdatedVersions,
10043
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("installedVersion", { version: row.source === "github" ? row.installed.slice(0, 7) : row.installed }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("latestVersion", { version: row.latest }) })]
10230
+ className: _dsh_plugin_shop_css_d74581c2_default.outdatedVersions,
10231
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("installedVersion", { version: versionLabel(row.source, row.installed).text }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("latestVersion", { version: versionLabel(row.source, row.latest).text }) })]
10044
10232
  })]
10045
10233
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10046
- className: _dsh_plugin_shop_css_e3675a89_default.outdatedActions,
10234
+ className: _dsh_plugin_shop_css_d74581c2_default.outdatedActions,
10047
10235
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(EnabledSwitch, {
10048
10236
  row,
10049
10237
  t,
@@ -10092,22 +10280,22 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10092
10280
  function OutdatedSection({ state, entriesByKey, missingByKey, harnessVerdicts, t, setEnabled, flowFor, restart, restartBlocked, reload, restarts }) {
10093
10281
  if (state.kind === "loading") return null;
10094
10282
  if (state.kind === "error") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10095
- className: _dsh_plugin_shop_css_e3675a89_default.stateLine,
10283
+ className: _dsh_plugin_shop_css_d74581c2_default.stateLine,
10096
10284
  "data-shop-outdated-error": true,
10097
10285
  children: t("installedError")
10098
10286
  });
10099
10287
  const outdated = state.entries.filter((entry) => entry.outdated && entry.latest !== null);
10100
10288
  if (outdated.length === 0) return null;
10101
10289
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
10102
- className: _dsh_plugin_shop_css_e3675a89_default.outdatedSection,
10290
+ className: _dsh_plugin_shop_css_d74581c2_default.outdatedSection,
10103
10291
  "data-shop-outdated": true,
10104
10292
  "aria-labelledby": UPDATABLE_HEADING_ID,
10105
10293
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
10106
- className: _dsh_plugin_shop_css_e3675a89_default.updatableHeading,
10294
+ className: _dsh_plugin_shop_css_d74581c2_default.updatableHeading,
10107
10295
  id: UPDATABLE_HEADING_ID,
10108
10296
  children: t("updatableSection")
10109
10297
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
10110
- className: _dsh_plugin_shop_css_e3675a89_default.outdatedList,
10298
+ className: _dsh_plugin_shop_css_d74581c2_default.outdatedList,
10111
10299
  children: outdated.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OutdatedRow, {
10112
10300
  row,
10113
10301
  tier: entriesByKey.get(identityKey(row))?.tier ?? "community",
@@ -10135,37 +10323,37 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10135
10323
  const shadowed = entries.filter((entry) => entry.shadowed === true);
10136
10324
  if (shadowed.length === 0) return null;
10137
10325
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
10138
- className: _dsh_plugin_shop_css_e3675a89_default.shadowedSection,
10326
+ className: _dsh_plugin_shop_css_d74581c2_default.shadowedSection,
10139
10327
  "data-shop-shadowed": true,
10140
10328
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
10141
- className: _dsh_plugin_shop_css_e3675a89_default.outdatedList,
10329
+ className: _dsh_plugin_shop_css_d74581c2_default.outdatedList,
10142
10330
  children: shadowed.map((row) => {
10143
10331
  const key = identityKey(row);
10144
10332
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10145
- className: _dsh_plugin_shop_css_e3675a89_default.outdatedRow,
10333
+ className: _dsh_plugin_shop_css_d74581c2_default.outdatedRow,
10146
10334
  "data-shop-shadowed-entry": row.name,
10147
10335
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10148
- className: _dsh_plugin_shop_css_e3675a89_default.outdatedInfo,
10336
+ className: _dsh_plugin_shop_css_d74581c2_default.outdatedInfo,
10149
10337
  children: [
10150
10338
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10151
- className: _dsh_plugin_shop_css_e3675a89_default.name,
10339
+ className: _dsh_plugin_shop_css_d74581c2_default.name,
10152
10340
  children: row.name
10153
10341
  }),
10154
10342
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10155
- className: _dsh_plugin_shop_css_e3675a89_default.badges,
10343
+ className: _dsh_plugin_shop_css_d74581c2_default.badges,
10156
10344
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10157
- className: _dsh_plugin_shop_css_e3675a89_default.tierBadge,
10345
+ className: _dsh_plugin_shop_css_d74581c2_default.tierBadge,
10158
10346
  "data-tier": "shadowed",
10159
10347
  children: t("shadowedBadge")
10160
10348
  })
10161
10349
  }),
10162
10350
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10163
- className: _dsh_plugin_shop_css_e3675a89_default.shadowedDetail,
10351
+ className: _dsh_plugin_shop_css_d74581c2_default.shadowedDetail,
10164
10352
  children: t("shadowedDetail")
10165
10353
  })
10166
10354
  ]
10167
10355
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10168
- className: _dsh_plugin_shop_css_e3675a89_default.outdatedActions,
10356
+ className: _dsh_plugin_shop_css_d74581c2_default.outdatedActions,
10169
10357
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UninstallPanel, {
10170
10358
  name: row.name,
10171
10359
  t,
@@ -10215,16 +10403,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10215
10403
  role: "alert",
10216
10404
  children: [
10217
10405
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10218
- className: _dsh_plugin_shop_css_e3675a89_default.stateLine,
10406
+ className: _dsh_plugin_shop_css_d74581c2_default.stateLine,
10219
10407
  children: say("tabCrashed", en.tabCrashed)
10220
10408
  }),
10221
10409
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10222
- className: _dsh_plugin_shop_css_e3675a89_default.failedDetail,
10410
+ className: _dsh_plugin_shop_css_d74581c2_default.failedDetail,
10223
10411
  children: error.message
10224
10412
  }),
10225
10413
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
10226
10414
  type: "button",
10227
- className: _dsh_plugin_shop_css_e3675a89_default.actionButton,
10415
+ className: _dsh_plugin_shop_css_d74581c2_default.actionButton,
10228
10416
  onClick: () => this.setState({ error: null }),
10229
10417
  children: say("retry", en.retry)
10230
10418
  })
@@ -10311,11 +10499,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10311
10499
  const [reloadFailed, setReloadFailed] = (0, react.useState)(false);
10312
10500
  const [query, setQuery] = (0, react.useState)("");
10313
10501
  const [category, setCategory] = (0, react.useState)(null);
10502
+ const [order, setOrder] = (0, react.useState)("stars");
10314
10503
  const [hideIncompatible, setHideIncompatible] = (0, react.useState)(false);
10315
10504
  const incremental = typeof IntersectionObserver !== "undefined";
10316
10505
  const [visibleCount, setVisibleCount] = (0, react.useState)(48);
10317
10506
  const sentinelRef = (0, react.useRef)(null);
10318
10507
  const filteredLenRef = (0, react.useRef)(0);
10508
+ const sortLabelId = (0, react.useId)();
10319
10509
  const pendingRefreshRef = (0, react.useRef)(null);
10320
10510
  (0, react.useEffect)(() => {
10321
10511
  let cancelled = false;
@@ -10410,7 +10600,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10410
10600
  return map;
10411
10601
  }, [installedState]);
10412
10602
  const stars = (0, react.useMemo)(() => catalogState.kind === "ready" ? catalogState.result.stars : {}, [catalogState]);
10413
- const sortedCatalog = (0, react.useMemo)(() => catalogState.kind === "ready" ? sortByStars(catalogState.result.plugins, stars) : [], [catalogState, stars]);
10603
+ const starOrder = (0, react.useMemo)(() => catalogState.kind === "ready" ? sortByStars(catalogState.result.plugins, stars) : [], [catalogState, stars]);
10604
+ const builtAt = catalogState.kind === "ready" ? catalogState.result.builtAt : "";
10605
+ const sortedCatalog = (0, react.useMemo)(() => orderShelf(starOrder, order, builtAt), [
10606
+ starOrder,
10607
+ order,
10608
+ builtAt
10609
+ ]);
10414
10610
  const browsable = (0, react.useMemo)(() => {
10415
10611
  if (catalogState.kind !== "ready") return [];
10416
10612
  const cleared = new Set(catalogState.result.notAShop ?? []);
@@ -10483,6 +10679,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10483
10679
  filtered.length
10484
10680
  ]);
10485
10681
  const visible = incremental ? filtered.slice(0, visibleCount) : filtered;
10682
+ /** The date a card shows under the current order, or undefined under the
10683
+ * star order and for an entry with no usable date. Computed from the same
10684
+ * `shelfTime` the order keyed on, so a label can never name a date the
10685
+ * order ignored. */
10686
+ const shelfDayOf = (entry) => {
10687
+ const ms = shelfTime(entry, order, builtAt);
10688
+ return ms === void 0 ? void 0 : utcDay(ms);
10689
+ };
10486
10690
  const categoryCounts = (0, react.useMemo)(() => {
10487
10691
  const counts = /* @__PURE__ */ new Map();
10488
10692
  for (const entry of browsable) {
@@ -10505,19 +10709,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10505
10709
  "data-shop-tab": true,
10506
10710
  "aria-busy": "true",
10507
10711
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10508
- className: _dsh_plugin_shop_css_e3675a89_default.srOnly,
10712
+ className: _dsh_plugin_shop_css_d74581c2_default.srOnly,
10509
10713
  children: t("loading")
10510
10714
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10511
- className: _dsh_plugin_shop_css_e3675a89_default.skeletonGrid,
10715
+ className: _dsh_plugin_shop_css_d74581c2_default.skeletonGrid,
10512
10716
  "data-shop-skeleton": true,
10513
10717
  "aria-hidden": "true",
10514
10718
  children: Array.from({ length: 8 }, (_, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10515
- className: _dsh_plugin_shop_css_e3675a89_default.skeletonCard,
10719
+ className: _dsh_plugin_shop_css_d74581c2_default.skeletonCard,
10516
10720
  children: [
10517
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_e3675a89_default.skeletonName }),
10518
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_e3675a89_default.skeletonSummary }),
10519
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_e3675a89_default.skeletonSummaryShort }),
10520
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_e3675a89_default.skeletonActions })
10721
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_d74581c2_default.skeletonName }),
10722
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_d74581c2_default.skeletonSummary }),
10723
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_d74581c2_default.skeletonSummaryShort }),
10724
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_d74581c2_default.skeletonActions })
10521
10725
  ]
10522
10726
  }, i))
10523
10727
  })]
@@ -10527,11 +10731,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10527
10731
  translate: "no",
10528
10732
  "data-shop-tab": true,
10529
10733
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10530
- className: _dsh_plugin_shop_css_e3675a89_default.stateLine,
10734
+ className: _dsh_plugin_shop_css_d74581c2_default.stateLine,
10531
10735
  children: t("error")
10532
10736
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
10533
10737
  type: "button",
10534
- className: _dsh_plugin_shop_css_e3675a89_default.actionButton,
10738
+ className: _dsh_plugin_shop_css_d74581c2_default.actionButton,
10535
10739
  onClick: () => {
10536
10740
  setRequest({ kind: "retry" });
10537
10741
  setVersionReload((current) => current + 1);
@@ -10546,11 +10750,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10546
10750
  "data-shop-tab": true,
10547
10751
  children: [
10548
10752
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10549
- className: _dsh_plugin_shop_css_e3675a89_default.toolbar,
10753
+ className: _dsh_plugin_shop_css_d74581c2_default.toolbar,
10550
10754
  children: [
10551
10755
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
10552
10756
  type: "search",
10553
- className: _dsh_plugin_shop_css_e3675a89_default.searchInput,
10757
+ className: _dsh_plugin_shop_css_d74581c2_default.searchInput,
10554
10758
  "aria-label": t("search"),
10555
10759
  placeholder: t("search"),
10556
10760
  value: query,
@@ -10561,31 +10765,31 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10561
10765
  }),
10562
10766
  expansionMemo.intents.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10563
10767
  "data-shop-expansion-note": true,
10564
- className: _dsh_plugin_shop_css_e3675a89_default.expansionNote,
10768
+ className: _dsh_plugin_shop_css_d74581c2_default.expansionNote,
10565
10769
  children: t("zhExpansionNote", { terms: expansionMemo.terms.slice(1).map((term) => term.startsWith("#") ? term.slice(1) : term).join(", ") })
10566
10770
  }),
10567
10771
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10568
- className: _dsh_plugin_shop_css_e3675a89_default.toolbarRight,
10772
+ className: _dsh_plugin_shop_css_d74581c2_default.toolbarRight,
10569
10773
  children: [
10570
10774
  result.stale && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10571
- className: _dsh_plugin_shop_css_e3675a89_default.staleBadge,
10775
+ className: _dsh_plugin_shop_css_d74581c2_default.staleBadge,
10572
10776
  children: t("staleLabel", { date: result.builtAt.slice(0, 10) })
10573
10777
  }),
10574
10778
  selfVersion !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10575
- className: _dsh_plugin_shop_css_e3675a89_default.versionBlock,
10779
+ className: _dsh_plugin_shop_css_d74581c2_default.versionBlock,
10576
10780
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
10577
- className: _dsh_plugin_shop_css_e3675a89_default.versionText,
10781
+ className: _dsh_plugin_shop_css_d74581c2_default.versionText,
10578
10782
  "data-shop-version": true,
10579
10783
  children: ["v", selfVersion.installed]
10580
10784
  }), restartMonitor !== null ? null : selfPending && restartBlocked === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
10581
10785
  type: "button",
10582
- className: _dsh_plugin_shop_css_e3675a89_default.restartSelfButton,
10786
+ className: _dsh_plugin_shop_css_d74581c2_default.restartSelfButton,
10583
10787
  "data-shop-restart": true,
10584
10788
  onClick: () => setSelfRestartGate(true),
10585
10789
  children: t("restart")
10586
10790
  }) : !selfPending && selfVersion.outdated && selfVersion.latest !== null && selfUpdate.view.kind === "idle" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
10587
10791
  type: "button",
10588
- className: _dsh_plugin_shop_css_e3675a89_default.updateSelfButton,
10792
+ className: _dsh_plugin_shop_css_d74581c2_default.updateSelfButton,
10589
10793
  "data-shop-update-self": true,
10590
10794
  onClick: () => {
10591
10795
  if (selfVersion.latest !== null) selfUpdate.start({ version: selfVersion.latest });
@@ -10593,7 +10797,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10593
10797
  children: t("update")
10594
10798
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
10595
10799
  type: "button",
10596
- className: _dsh_plugin_shop_css_e3675a89_default.checkUpdateButton,
10800
+ className: _dsh_plugin_shop_css_d74581c2_default.checkUpdateButton,
10597
10801
  "data-shop-check-update": true,
10598
10802
  title: t("checkUpdateTitle"),
10599
10803
  disabled: checkState !== "idle",
@@ -10602,7 +10806,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10602
10806
  })]
10603
10807
  }),
10604
10808
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
10605
- className: _dsh_plugin_shop_css_e3675a89_default.githubLink,
10809
+ className: _dsh_plugin_shop_css_d74581c2_default.githubLink,
10606
10810
  href: SHOP_REPO_URL,
10607
10811
  target: "_blank",
10608
10812
  rel: "noopener noreferrer",
@@ -10627,28 +10831,28 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10627
10831
  logFile: restartMonitor.logFile
10628
10832
  }),
10629
10833
  selfUpdate.view.kind === "running" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10630
- className: _dsh_plugin_shop_css_e3675a89_default.selfUpdatePanel,
10834
+ className: _dsh_plugin_shop_css_d74581c2_default.selfUpdatePanel,
10631
10835
  "data-shop-self-updating": true,
10632
10836
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10633
- className: _dsh_plugin_shop_css_e3675a89_default.installing,
10837
+ className: _dsh_plugin_shop_css_d74581c2_default.installing,
10634
10838
  children: t(installPhaseKey(selfUpdate.view.phase))
10635
10839
  }), selfUpdate.view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10636
- className: _dsh_plugin_shop_css_e3675a89_default.log,
10840
+ className: _dsh_plugin_shop_css_d74581c2_default.log,
10637
10841
  children: selfUpdate.view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10638
- className: _dsh_plugin_shop_css_e3675a89_default.logLine,
10842
+ className: _dsh_plugin_shop_css_d74581c2_default.logLine,
10639
10843
  "data-shop-log-line": true,
10640
10844
  children: line
10641
10845
  }, index))
10642
10846
  })]
10643
10847
  }),
10644
10848
  selfPending && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10645
- className: _dsh_plugin_shop_css_e3675a89_default.selfUpdatePanel,
10849
+ className: _dsh_plugin_shop_css_d74581c2_default.selfUpdatePanel,
10646
10850
  "data-shop-self-update-done": true,
10647
10851
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10648
- className: _dsh_plugin_shop_css_e3675a89_default.installedActions,
10852
+ className: _dsh_plugin_shop_css_d74581c2_default.installedActions,
10649
10853
  children: [
10650
10854
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10651
- className: _dsh_plugin_shop_css_e3675a89_default.notice,
10855
+ className: _dsh_plugin_shop_css_d74581c2_default.notice,
10652
10856
  children: t("installedRestartNotice")
10653
10857
  }),
10654
10858
  restartBlocked === null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RestartPanel, {
@@ -10661,7 +10865,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10661
10865
  }
10662
10866
  }),
10663
10867
  restartBlocked !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10664
- className: _dsh_plugin_shop_css_e3675a89_default.notice,
10868
+ className: _dsh_plugin_shop_css_d74581c2_default.notice,
10665
10869
  "data-shop-restart-disabled": true,
10666
10870
  children: t(restartBlockedNoticeKey(restartBlocked))
10667
10871
  })
@@ -10669,35 +10873,35 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10669
10873
  })
10670
10874
  }),
10671
10875
  selfUpdate.view.kind === "failed" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10672
- className: _dsh_plugin_shop_css_e3675a89_default.selfUpdatePanel,
10876
+ className: _dsh_plugin_shop_css_d74581c2_default.selfUpdatePanel,
10673
10877
  "data-shop-self-update-failed": true,
10674
10878
  children: [
10675
10879
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10676
- className: _dsh_plugin_shop_css_e3675a89_default.failedHeading,
10880
+ className: _dsh_plugin_shop_css_d74581c2_default.failedHeading,
10677
10881
  children: t("updateFailed")
10678
10882
  }),
10679
10883
  selfUpdate.view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10680
- className: _dsh_plugin_shop_css_e3675a89_default.log,
10884
+ className: _dsh_plugin_shop_css_d74581c2_default.log,
10681
10885
  children: selfUpdate.view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
10682
- className: _dsh_plugin_shop_css_e3675a89_default.logLine,
10886
+ className: _dsh_plugin_shop_css_d74581c2_default.logLine,
10683
10887
  "data-shop-log-line": true,
10684
10888
  children: line
10685
10889
  }, index))
10686
10890
  }),
10687
10891
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10688
- className: _dsh_plugin_shop_css_e3675a89_default.failedDetail,
10892
+ className: _dsh_plugin_shop_css_d74581c2_default.failedDetail,
10689
10893
  children: selfUpdate.view.detail === "" ? t("updateTransportFailed") : selfUpdate.view.detail
10690
10894
  })
10691
10895
  ]
10692
10896
  }),
10693
10897
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10694
- className: _dsh_plugin_shop_css_e3675a89_default.categoryBar,
10898
+ className: _dsh_plugin_shop_css_d74581c2_default.categoryBar,
10695
10899
  role: "group",
10696
10900
  "aria-label": t("catalog"),
10697
10901
  children: [
10698
10902
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
10699
10903
  type: "button",
10700
- className: category === null ? `${_dsh_plugin_shop_css_e3675a89_default.categoryButton} ${_dsh_plugin_shop_css_e3675a89_default.categoryButtonOn}` : _dsh_plugin_shop_css_e3675a89_default.categoryButton,
10904
+ className: category === null ? `${_dsh_plugin_shop_css_d74581c2_default.categoryButton} ${_dsh_plugin_shop_css_d74581c2_default.categoryButtonOn}` : _dsh_plugin_shop_css_d74581c2_default.categoryButton,
10701
10905
  "aria-pressed": category === null,
10702
10906
  onClick: () => {
10703
10907
  setCategory(null);
@@ -10712,7 +10916,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10712
10916
  }),
10713
10917
  CATEGORY_ORDER.map((key) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
10714
10918
  type: "button",
10715
- className: category === key ? `${_dsh_plugin_shop_css_e3675a89_default.categoryButton} ${_dsh_plugin_shop_css_e3675a89_default.categoryButtonOn}` : _dsh_plugin_shop_css_e3675a89_default.categoryButton,
10919
+ className: category === key ? `${_dsh_plugin_shop_css_d74581c2_default.categoryButton} ${_dsh_plugin_shop_css_d74581c2_default.categoryButtonOn}` : _dsh_plugin_shop_css_d74581c2_default.categoryButton,
10716
10920
  "aria-pressed": category === key,
10717
10921
  "data-category": key,
10718
10922
  "data-shop-category-tab": key,
@@ -10728,7 +10932,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10728
10932
  }, key)),
10729
10933
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
10730
10934
  type: "button",
10731
- className: category === "installed" ? `${_dsh_plugin_shop_css_e3675a89_default.categoryButton} ${_dsh_plugin_shop_css_e3675a89_default.categoryButtonOn}` : _dsh_plugin_shop_css_e3675a89_default.categoryButton,
10935
+ className: category === "installed" ? `${_dsh_plugin_shop_css_d74581c2_default.categoryButton} ${_dsh_plugin_shop_css_d74581c2_default.categoryButtonOn}` : _dsh_plugin_shop_css_d74581c2_default.categoryButton,
10732
10936
  "aria-pressed": category === "installed",
10733
10937
  "data-shop-category-installed": true,
10734
10938
  onClick: () => {
@@ -10745,7 +10949,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10745
10949
  type: "button",
10746
10950
  role: "switch",
10747
10951
  "aria-checked": hideIncompatible,
10748
- className: hideIncompatible ? `${_dsh_plugin_shop_css_e3675a89_default.incompatibleFilter} ${_dsh_plugin_shop_css_e3675a89_default.incompatibleFilterOn}` : _dsh_plugin_shop_css_e3675a89_default.incompatibleFilter,
10952
+ className: hideIncompatible ? `${_dsh_plugin_shop_css_d74581c2_default.incompatibleFilter} ${_dsh_plugin_shop_css_d74581c2_default.incompatibleFilterOn}` : _dsh_plugin_shop_css_d74581c2_default.incompatibleFilter,
10749
10953
  title: t(hideIncompatible ? "showIncompatibleTitle" : "incompatibleFilterTitle"),
10750
10954
  "data-shop-hide-incompatible": true,
10751
10955
  onClick: () => {
@@ -10753,18 +10957,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10753
10957
  setVisibleCount(48);
10754
10958
  },
10755
10959
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10756
- className: hideIncompatible ? `${_dsh_plugin_shop_css_e3675a89_default.switch} ${_dsh_plugin_shop_css_e3675a89_default.switchOn}` : _dsh_plugin_shop_css_e3675a89_default.switch,
10960
+ className: hideIncompatible ? `${_dsh_plugin_shop_css_d74581c2_default.switch} ${_dsh_plugin_shop_css_d74581c2_default.switchOn}` : _dsh_plugin_shop_css_d74581c2_default.switch,
10757
10961
  "aria-hidden": "true",
10758
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_e3675a89_default.switchKnob })
10962
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_d74581c2_default.switchKnob })
10759
10963
  }), t("hideIncompatible", { count: incompatibleCount })]
10760
10964
  })
10761
10965
  ]
10762
10966
  }),
10763
10967
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
10764
- className: _dsh_plugin_shop_css_e3675a89_default.catalogStatsRow,
10968
+ className: _dsh_plugin_shop_css_d74581c2_default.catalogStatsRow,
10765
10969
  children: [
10766
10970
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10767
- className: _dsh_plugin_shop_css_e3675a89_default.catalogStats,
10971
+ className: _dsh_plugin_shop_css_d74581c2_default.catalogStats,
10768
10972
  "data-shop-catalog-stats": true,
10769
10973
  children: t("catalogStats", {
10770
10974
  count: String(browsable.length),
@@ -10773,7 +10977,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10773
10977
  }),
10774
10978
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
10775
10979
  type: "button",
10776
- className: _dsh_plugin_shop_css_e3675a89_default.catalogRefreshButton,
10980
+ className: _dsh_plugin_shop_css_d74581c2_default.catalogRefreshButton,
10777
10981
  "data-shop-catalog-refresh": true,
10778
10982
  disabled: reloading,
10779
10983
  onClick: () => {
@@ -10785,26 +10989,49 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10785
10989
  children: reloading ? t("refreshing") : t("refresh")
10786
10990
  }),
10787
10991
  reloadFailed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
10788
- className: _dsh_plugin_shop_css_e3675a89_default.catalogRefreshFailed,
10992
+ className: _dsh_plugin_shop_css_d74581c2_default.catalogRefreshFailed,
10789
10993
  "data-shop-catalog-refresh-failed": true,
10790
10994
  children: t("refreshFailed")
10995
+ }),
10996
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
10997
+ className: _dsh_plugin_shop_css_d74581c2_default.sortGroup,
10998
+ role: "group",
10999
+ "aria-labelledby": sortLabelId,
11000
+ "data-shop-sort": true,
11001
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
11002
+ className: _dsh_plugin_shop_css_d74581c2_default.sortLabel,
11003
+ id: sortLabelId,
11004
+ children: t("sortLabel")
11005
+ }), SHELF_ORDERS.map((name) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
11006
+ type: "button",
11007
+ className: order === name ? `${_dsh_plugin_shop_css_d74581c2_default.categoryButton} ${_dsh_plugin_shop_css_d74581c2_default.categoryButtonOn}` : _dsh_plugin_shop_css_d74581c2_default.categoryButton,
11008
+ "aria-pressed": order === name,
11009
+ "data-shop-order": name,
11010
+ onClick: () => {
11011
+ setOrder(name);
11012
+ setVisibleCount(48);
11013
+ },
11014
+ children: t(shelfOrderLocaleKey(name))
11015
+ }, name))]
10791
11016
  })
10792
11017
  ]
10793
11018
  }),
10794
11019
  result.plugins.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10795
- className: _dsh_plugin_shop_css_e3675a89_default.emptyLine,
11020
+ className: _dsh_plugin_shop_css_d74581c2_default.emptyLine,
10796
11021
  children: t("empty")
10797
11022
  }) : filtered.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10798
- className: _dsh_plugin_shop_css_e3675a89_default.emptyLine,
11023
+ className: _dsh_plugin_shop_css_d74581c2_default.emptyLine,
10799
11024
  "data-shop-empty": true,
10800
11025
  children: matched.length > 0 ? t("emptyIncompatibleFiltered") : t("emptySearch")
10801
11026
  }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ul", {
10802
- className: _dsh_plugin_shop_css_e3675a89_default.cards,
11027
+ className: _dsh_plugin_shop_css_d74581c2_default.cards,
10803
11028
  children: [visible.map((entry) => {
10804
11029
  const key = entryKey(entry);
10805
11030
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EntryCard, {
10806
11031
  entry,
10807
11032
  stars: starsOf(entry, stars),
11033
+ order,
11034
+ shelfDay: shelfDayOf(entry),
10808
11035
  installed: installedByKey.get(key),
10809
11036
  missing: missingByKey.get(key) ?? [],
10810
11037
  harness: harnessVerdictOf(harnessVerdicts, key),
@@ -10821,12 +11048,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
10821
11048
  }) }, key);
10822
11049
  }), incremental && visibleCount < filtered.length && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
10823
11050
  ref: sentinelRef,
10824
- className: _dsh_plugin_shop_css_e3675a89_default.cardsSentry,
11051
+ className: _dsh_plugin_shop_css_d74581c2_default.cardsSentry,
10825
11052
  "data-shop-sentry": true,
10826
11053
  "aria-hidden": "true"
10827
11054
  })]
10828
11055
  }), incremental && visibleCount < filtered.length && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
10829
- className: _dsh_plugin_shop_css_e3675a89_default.showingLine,
11056
+ className: _dsh_plugin_shop_css_d74581c2_default.showingLine,
10830
11057
  "aria-live": "polite",
10831
11058
  children: t("showing", {
10832
11059
  shown: String(visibleCount),