dsh-plugin-shop 0.8.7-beta.1 → 0.8.7-beta.2
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/assets/icon.png +0 -0
- package/lib/client.js +501 -372
- package/lib/types/client/FoldableRedBlock.d.ts +27 -0
- package/lib/types/client/ShopTab.d.ts +2 -2
- package/lib/types/client/locales.d.ts +14 -8
- package/lib/types/client/present.d.ts +28 -20
- package/lib/types/client/useFlows.d.ts +4 -0
- package/lib/types/client/useFoldableRedBlock.d.ts +36 -0
- package/package.json +3 -1
package/lib/client.js
CHANGED
|
@@ -6483,10 +6483,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6483
6483
|
return blockerBadgeKey(blockers) === "incompatibleBadge";
|
|
6484
6484
|
}
|
|
6485
6485
|
/**
|
|
6486
|
-
* Whether this install is refused before it starts, so
|
|
6487
|
-
*
|
|
6488
|
-
* cannot proceed: by the host, for a taken name, or by
|
|
6489
|
-
* its harness peers.
|
|
6486
|
+
* Whether this install is refused before it starts, so the card shows the
|
|
6487
|
+
* refused label where its button would be, rather than a button for an
|
|
6488
|
+
* install that cannot proceed: refused by the host, for a taken name, or by
|
|
6489
|
+
* the running dsh, for its harness peers.
|
|
6490
6490
|
*
|
|
6491
6491
|
* The second is the one harness reason that blocks (design
|
|
6492
6492
|
* 2026-09-26-dsh-017-readiness, B1, amending "warn, never block"): dsh 0.1.7
|
|
@@ -6494,9 +6494,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6494
6494
|
* entry after it, rolling the profile back, and the shop cannot install past
|
|
6495
6495
|
* it. The verdict is dsh's own rule on the profile's own exemptions, whichever
|
|
6496
6496
|
* channel the entry comes from (design
|
|
6497
|
-
* 2026-09-28-bundle-components-and-github-peers, section 7.3), so
|
|
6498
|
-
*
|
|
6499
|
-
*
|
|
6497
|
+
* 2026-09-28-bundle-components-and-github-peers, section 7.3), so the label
|
|
6498
|
+
* is dsh's answer given early, never the shop's opinion — and recording the
|
|
6499
|
+
* exemption the card shows is what brings the button back.
|
|
6500
6500
|
*/
|
|
6501
6501
|
function refusesInstall(blockers) {
|
|
6502
6502
|
return blockers.some((blocker) => blocker.kind === "name-taken" || blocker.kind === "harness-peers");
|
|
@@ -6532,11 +6532,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6532
6532
|
if (spec === void 0) return void 0;
|
|
6533
6533
|
return specVerdict(entry, spec) === "same" ? void 0 : holderLabel(spec, entry.name);
|
|
6534
6534
|
}
|
|
6535
|
-
/**
|
|
6536
|
-
*
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6535
|
+
/** The disclosure every install now stands under (spec §9.3, amended
|
|
6536
|
+
* 2026-10-14). It used to be the acknowledgement dialog's body: the gate asked
|
|
6537
|
+
* a reader to accept THESE privileges before each community install. The gate
|
|
6538
|
+
* is gone, and the same facts stand instead as one quiet line on the page, so
|
|
6539
|
+
* they cannot be missed and cannot be dismissed unread.
|
|
6540
|
+
*
|
|
6541
|
+
* Stated in the general case — "a plugin", not "this plugin" — because it now
|
|
6542
|
+
* describes the tier system rather than one entry, and because the community
|
|
6543
|
+
* tier is never badged on a card: naming a badge that is not rendered would
|
|
6544
|
+
* send a reader looking for it. */
|
|
6545
|
+
const DISCLOSURE_EN = "Once installed, a plugin holds the same privileges as a built-in one: reading and writing your files, running shell commands, and reading and modifying the requests sent to the model. Plugins not marked Verified have not been reviewed.";
|
|
6546
|
+
/** The same facts in the zh register, with the same force. */
|
|
6547
|
+
const DISCLOSURE_ZH = "安装后,插件拥有与内置插件相同的权限:读写你的文件、执行 shell 命令,以及读取和修改发送给模型的请求。未标「已审核」的插件未经审核。";
|
|
6540
6548
|
/** Install rejection code → locale key, for the rejected-state code label
|
|
6541
6549
|
* (§7.2): every rejection the host can return has a human label here. */
|
|
6542
6550
|
function rejectionCodeKey(code) {
|
|
@@ -7064,11 +7072,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7064
7072
|
archivedBadge: "已归档",
|
|
7065
7073
|
capabilitiesNote: "作者自述,无强制约束",
|
|
7066
7074
|
install: "安装",
|
|
7067
|
-
confirm: "确认",
|
|
7068
7075
|
cancel: "取消",
|
|
7069
7076
|
dismiss: "知道了",
|
|
7070
|
-
|
|
7071
|
-
|
|
7077
|
+
/** What every install now stands under, on the page rather than in a dialog
|
|
7078
|
+
* (spec §9.2/§9.3, amended 2026-10-14). */
|
|
7079
|
+
disclosure: DISCLOSURE_ZH,
|
|
7080
|
+
/** The fold toggle under a card's collapsed red block. `aria-expanded` plus:
|
|
7081
|
+
* open shows the whole block; closed shows two lines of it. This toggle is
|
|
7082
|
+
* independent of the card's own header-expand. */
|
|
7083
|
+
moreRedLines: "展开",
|
|
7084
|
+
lessRedLines: "收起",
|
|
7072
7085
|
installing: "正在安装…",
|
|
7073
7086
|
downloading: "正在下载…",
|
|
7074
7087
|
installedRestartNotice: "已安装;重启 dsh 后生效",
|
|
@@ -7080,8 +7093,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7080
7093
|
* "unsupported" leaves the reader guessing which side is off. */
|
|
7081
7094
|
harnessRangeDetail: "作者声明支持的 dsh 版本:{range};本机运行的是 {running}。",
|
|
7082
7095
|
harnessProfileDetail: "作者声明支持的 profile:{declared};此 dsh 以 {running} profile 启动。",
|
|
7083
|
-
/** dsh's OWN refusal (0.1.7 on), not the author's claim: the
|
|
7084
|
-
*
|
|
7096
|
+
/** dsh's OWN refusal (0.1.7 on), not the author's claim: the card shows the
|
|
7097
|
+
* refused label in the install button's place (design
|
|
7098
|
+
* 2026-09-26-dsh-017-readiness, B1, amended 2026-10-14). `{peers}`
|
|
7085
7099
|
* is each refused peer with the range the package declared. The remedy
|
|
7086
7100
|
* leads into the exemption command, rendered on its own line after it, and
|
|
7087
7101
|
* names the Refresh button by its label. */
|
|
@@ -7245,11 +7259,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7245
7259
|
tierCommunity: "Community",
|
|
7246
7260
|
capabilitiesNote: "self-declared by the author, not enforced",
|
|
7247
7261
|
install: "Install",
|
|
7248
|
-
confirm: "Confirm",
|
|
7249
7262
|
cancel: "Cancel",
|
|
7250
7263
|
dismiss: "Dismiss",
|
|
7251
|
-
|
|
7252
|
-
|
|
7264
|
+
disclosure: DISCLOSURE_EN,
|
|
7265
|
+
moreRedLines: "Show more",
|
|
7266
|
+
lessRedLines: "Show less",
|
|
7253
7267
|
installing: "Installing…",
|
|
7254
7268
|
downloading: "Downloading…",
|
|
7255
7269
|
installedRestartNotice: "installed; restart dsh to activate",
|
|
@@ -7861,6 +7875,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7861
7875
|
*/
|
|
7862
7876
|
function useKeyedFlows(begin, installStatus, onSettled) {
|
|
7863
7877
|
const [views, setViews] = (0, react.useState)(() => /* @__PURE__ */ new Map());
|
|
7878
|
+
const [starting, setStarting] = (0, react.useState)(() => /* @__PURE__ */ new Set());
|
|
7879
|
+
const inFlight = (0, react.useRef)(/* @__PURE__ */ new Set());
|
|
7864
7880
|
const settled = (0, react.useRef)(onSettled);
|
|
7865
7881
|
settled.current = onSettled;
|
|
7866
7882
|
const put = (0, react.useCallback)((key, view) => {
|
|
@@ -7881,8 +7897,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7881
7897
|
});
|
|
7882
7898
|
}, []);
|
|
7883
7899
|
const start = (0, react.useCallback)(async (key, args) => {
|
|
7884
|
-
|
|
7885
|
-
|
|
7900
|
+
if (inFlight.current.has(key)) return;
|
|
7901
|
+
inFlight.current.add(key);
|
|
7902
|
+
setStarting((current) => new Set(current).add(key));
|
|
7903
|
+
try {
|
|
7904
|
+
put(key, { kind: "idle" });
|
|
7905
|
+
put(key, await begin(args));
|
|
7906
|
+
} finally {
|
|
7907
|
+
inFlight.current.delete(key);
|
|
7908
|
+
setStarting((current) => {
|
|
7909
|
+
if (!current.has(key)) return current;
|
|
7910
|
+
const next = new Set(current);
|
|
7911
|
+
next.delete(key);
|
|
7912
|
+
return next;
|
|
7913
|
+
});
|
|
7914
|
+
}
|
|
7886
7915
|
}, [begin, put]);
|
|
7887
7916
|
const reset = (0, react.useCallback)((key) => {
|
|
7888
7917
|
setViews((current) => {
|
|
@@ -7946,10 +7975,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7946
7975
|
};
|
|
7947
7976
|
}
|
|
7948
7977
|
const view = views.get(key) ?? IDLE_VIEW;
|
|
7978
|
+
const isStarting = starting.has(key);
|
|
7949
7979
|
const cached = flowCache.current.get(key);
|
|
7950
|
-
if (cached !== void 0 && cached.view === view) return cached;
|
|
7980
|
+
if (cached !== void 0 && cached.view === view && cached.starting === isStarting) return cached;
|
|
7951
7981
|
const flow = {
|
|
7952
7982
|
view,
|
|
7983
|
+
starting: isStarting,
|
|
7953
7984
|
start: (args) => start(key, args),
|
|
7954
7985
|
reset: () => reset(key)
|
|
7955
7986
|
};
|
|
@@ -7957,6 +7988,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7957
7988
|
return flow;
|
|
7958
7989
|
}, [
|
|
7959
7990
|
views,
|
|
7991
|
+
starting,
|
|
7960
7992
|
start,
|
|
7961
7993
|
reset
|
|
7962
7994
|
]),
|
|
@@ -9215,132 +9247,232 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9215
9247
|
};
|
|
9216
9248
|
}
|
|
9217
9249
|
//#endregion
|
|
9218
|
-
//#region
|
|
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
|
-
|
|
9250
|
+
//#region src/client/useFoldableRedBlock.ts
|
|
9251
|
+
/**
|
|
9252
|
+
* The fold under a card's whole red block: two lines by default, a small pill
|
|
9253
|
+
* that expands it when there is more, and the whole block again on a second
|
|
9254
|
+
* press. Three rules keep the fold honest:
|
|
9255
|
+
*
|
|
9256
|
+
* - It is measured, not declared. The toggle is offered only where the block
|
|
9257
|
+
* itself shows it holds more than the clamp lets through —
|
|
9258
|
+
* `scrollHeight > clientHeight`, read while the block is collapsed. A count
|
|
9259
|
+
* of lines or characters would lie on exactly the narrow window the fold
|
|
9260
|
+
* matters most for.
|
|
9261
|
+
* - It is independent of the card's own expand. The header's chevron opens
|
|
9262
|
+
* the author description; the pill opens the red block.
|
|
9263
|
+
* - It is visual only. The label's accessible name and tooltip carry the
|
|
9264
|
+
* WHOLE block, folded or open, so a screen reader meets the same facts
|
|
9265
|
+
* either way.
|
|
9266
|
+
*
|
|
9267
|
+
* The measure runs wherever its answer can change: at mount, on every
|
|
9268
|
+
* collapse, when the block's box resizes (a ResizeObserver: the panel
|
|
9269
|
+
* narrowing, a font arriving), and when its text changes (a MutationObserver:
|
|
9270
|
+
* a verdict that lands after the first paint, or one a refresh withdraws).
|
|
9271
|
+
* Both observers are needed. A block already clamped at two lines keeps its
|
|
9272
|
+
* height when its content grows, so a ResizeObserver alone never hears the
|
|
9273
|
+
* line that pushes it over; and a block that first mounts with no box (a
|
|
9274
|
+
* hidden ancestor) changes no text when it is shown.
|
|
9275
|
+
*
|
|
9276
|
+
* While the block is open nothing is measured: an open block is as tall as
|
|
9277
|
+
* its content and fits by construction. Reading that as "nothing to fold" is
|
|
9278
|
+
* what once let the press that opened the fold unmount its own toggle.
|
|
9279
|
+
*/
|
|
9280
|
+
function useFoldableRedBlock() {
|
|
9281
|
+
const rootRef = (0, react.useRef)(null);
|
|
9282
|
+
const [expandable, setExpandable] = (0, react.useState)(false);
|
|
9283
|
+
const [expanded, setExpanded] = (0, react.useState)(false);
|
|
9284
|
+
const expandedRef = (0, react.useRef)(false);
|
|
9285
|
+
const measure = (0, react.useCallback)(() => {
|
|
9286
|
+
const el = rootRef.current;
|
|
9287
|
+
if (el === null || expandedRef.current) return;
|
|
9288
|
+
setExpandable(el.scrollHeight > el.clientHeight);
|
|
9289
|
+
}, []);
|
|
9290
|
+
(0, react.useEffect)(() => {
|
|
9291
|
+
if (!expanded) measure();
|
|
9292
|
+
}, [expanded, measure]);
|
|
9293
|
+
(0, react.useEffect)(() => {
|
|
9294
|
+
const el = rootRef.current;
|
|
9295
|
+
if (el === null) return;
|
|
9296
|
+
const observers = [];
|
|
9297
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
9298
|
+
const resize = new ResizeObserver(measure);
|
|
9299
|
+
resize.observe(el);
|
|
9300
|
+
observers.push(resize);
|
|
9301
|
+
}
|
|
9302
|
+
if (typeof MutationObserver !== "undefined") {
|
|
9303
|
+
const mutation = new MutationObserver(measure);
|
|
9304
|
+
mutation.observe(el, {
|
|
9305
|
+
childList: true,
|
|
9306
|
+
subtree: true,
|
|
9307
|
+
characterData: true
|
|
9308
|
+
});
|
|
9309
|
+
observers.push(mutation);
|
|
9310
|
+
}
|
|
9311
|
+
return () => {
|
|
9312
|
+
for (const observer of observers) observer.disconnect();
|
|
9313
|
+
};
|
|
9314
|
+
}, [measure]);
|
|
9315
|
+
return {
|
|
9316
|
+
rootRef,
|
|
9317
|
+
expandable,
|
|
9318
|
+
expanded,
|
|
9319
|
+
toggle: () => {
|
|
9320
|
+
expandedRef.current = !expandedRef.current;
|
|
9321
|
+
setExpanded(expandedRef.current);
|
|
9322
|
+
}
|
|
9323
|
+
};
|
|
9324
|
+
}
|
|
9325
|
+
//#endregion
|
|
9326
|
+
//#region \0dsh-plugin-shop-css:5f753521
|
|
9327
|
+
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.sa85938_client.s94d4e5_config.s91da2e_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.sa85938_client.sbb5706_spec.s91da2e_ts` pins this. */\n\n.s6a01b8_panel {\n display: flex;\n flex-direction: column;\n gap: 14px;\n /* The two reds every red text in this tab is painted with. dsh's error red\n * is #ec1313 in the light theme, which measures 4.498:1 as text on white —\n * a miss of two thousandths — and 3.57:1 on the badge's own 14% red fill\n * (4.00:1 in dark). Mixing it toward label-primary fixes both, because\n * label-primary is the theme's foreground pole: it darkens the red on light\n * surfaces and lightens it on dark ones. Measured in the live app: 90% of\n * the red is 5.30:1 on white and 5.29:1 on the dark card, and 80% is\n * 4.98:1 on the badge's tint, 4.92:1 in dark. Computed: 90% is 4.69:1 on\n * the dark panel itself (#2c2c2e), where the self-update's failure heading\n * sits, and 80% is 5.23:1 on the uninstall button's 10% hover fill.\n * Declared here, on the tab root, because a custom property inherits: one\n * declaration reaches every red element, and a new one cannot be\n * forgotten. */\n --shop-error: color-mix(in srgb, var(--dsw-alias-state-error-primary) 90%, var(--dsw-alias-label-primary));\n --shop-error-on-tint: color-mix(in srgb, var(--dsw-alias-state-error-primary) 80%, var(--dsw-alias-label-primary));\n /* The amber and the green, mixed the same way for the same reason. dsh's\n * warn #f59e0b and success #22c55e are one value in both themes and pass\n * in the dark one, but as text on white they measure 2.15:1 and 2.28:1.\n * Each derived colour keeps the largest multiple of ten of its hue that\n * clears every surface it paints, as the reds do. Computed from the live\n * tokens over every bg layer in both themes: amber at 60% is 4.95:1 on a\n * plain surface but 4.44:1 on its own 14% tint, so text on an amber tint\n * takes 50% (5.66:1); green at 60% is 5.18:1 plain and 4.60:1 on its 14%\n * tint, so one green serves both. */\n --shop-warn: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 60%, var(--dsw-alias-label-primary));\n --shop-warn-on-tint: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 50%, var(--dsw-alias-label-primary));\n --shop-success: color-mix(in srgb, var(--dsw-alias-state-success-primary) 60%, var(--dsw-alias-label-primary));\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 `.sa16cbe_searchInput` sits at\n * exactly (0, 0) of this box — it is the first child of `.sbc1497_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.s352f7a_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.s60f2f5_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.s34d5f5_skeletonGrid {\n display: grid;\n grid-template-columns: 1fr;\n gap: 8px;\n}\n\n.s11e315_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.s11e315_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.s227d7c_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.s83025b_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.sdb7845_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.sa61117_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.s11e315_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 .s11e315_skeletonCard span { animation: none; opacity: 0.75; }\n .s11e315_skeletonCard::after { content: none; }\n}\n\n.s757132_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.s757132_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.s757132_actionButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.sbc1497_toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.s22d15d_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.s76d3c9_versionBlock {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.s390e34_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.s79450a_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.s79450a_updateSelfButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 20%, transparent);\n}\n\n.s79450a_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.sb48cbc_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.sb48cbc_checkUpdateButton:hover {\n border-color: var(--dsw-alias-label-secondary);\n color: var(--dsw-alias-label-primary);\n}\n\n.sb48cbc_checkUpdateButton:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n.sb48cbc_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.s946346_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.s946346_githubLink:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.s946346_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.s861599_selfUpdatePanel {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 8px;\n}\n\n.sa16cbe_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.sa16cbe_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.seb8329_expansionNote {\n margin: 4px 0 0;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s31a2f0_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(--shop-warn-on-tint);\n font-size: 12px;\n}\n\n.sab4844_categoryBar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n margin-top: 6px;\n}\n\n.s512425_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.s512425_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 `.s512425_categoryButton.s598384_categoryButtonOn` — TWO classes — so it\n * matches `.s512425_categoryButton:hover`'s specificity and, coming after it, wins.\n * As a bare `.s598384_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: a pressed tab must occupy exactly the\n * same box as an unpressed one, so the bar does not reflow — and with ~9 pills\n * on a wrapping row, one that grows can push the rest onto another line and\n * move the tab out from under the pointer that just clicked it. That is what\n * `font-weight: 600` did (bold metrics are wider than regular), and it is why\n * nothing may be added here that resolves to a length: no padding, no\n * border-width, no font-size, no letter-spacing. `css-tokens.sa85938_client.sbb5706_spec.s91da2e_ts`\n * fails the build on one.\n *\n * Nor a second line. An inset ring once doubled this border, and every control\n * in the stylesheet now draws exactly one 1px line, which is also the thinnest\n * the host paints (Chromium draws 0.5px as 1px). What separates pressed from\n * unpressed is lightness instead of thickness: the border is the hue mixed 30%\n * toward label-primary, the theme's foreground pole, so it clears 3:1 against\n * the bar in both themes for every hue — the raw hue measured 1.86:1 on white\n * for `ui` — and the fill under it is 22% of the hue. The `other` tab is the\n * case this is for: its hue IS the neutral gray this file uses for \"no\n * category\", so on the light theme the hue alone barely separates the two. */\n.s512425_categoryButton.s598384_categoryButtonOn {\n border-color: color-mix(in srgb, var(--category-hue) 70%, var(--dsw-alias-label-primary));\n background: color-mix(in srgb, var(--category-hue) 22%, transparent);\n color: color-mix(in srgb, var(--category-hue) 45%, var(--dsw-alias-label-primary) 55%);\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, so this mode alone gets an outline. */\n@media (forced-colors: active) {\n .s512425_categoryButton.s598384_categoryButtonOn {\n outline: 2px solid ButtonText;\n outline-offset: -4px;\n }\n}\n\n.s512425_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 `.s512425_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.s7ce437_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: `.s7ce437_incompatibleFilter:hover .s3a8382_switch` outranks `.s1e392e_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.s7ce437_incompatibleFilter:hover {\n color: var(--dsw-alias-label-primary);\n}\n\n.s7ce437_incompatibleFilter:hover .s3a8382_switch:not(.s1e392e_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 `.s512425_categoryButton.s598384_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.s7ce437_incompatibleFilter.sd9fa8d_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.s7ce437_incompatibleFilter .s3a8382_switch:not(.s1e392e_switchOn) .s419ba1_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.s7ce437_incompatibleFilter:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 2px;\n border-radius: 999px;\n}\n\n.s320079_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.s923c82_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.s3edd9f_sortGroup {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n margin-left: auto;\n}\n\n.s73feb1_sortLabel {\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n}\n\n.sad5aba_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/* The install disclosure: one grey line under the stats row, always on the\n * page. It carries what the §9.3 dialog used to say before every community\n * install, which is why it must not be dismissible or collapsed — a reader\n * who never installs anything still learns what installing means here.\n * Secondary label, the same register as the stats line it sits under: the\n * disclosure is a fact about the shelf, not a warning about one entry. It\n * wraps to a few lines at a narrow width, which the panel already allows. */\n.s58eb5b_disclosure {\n margin: 6px 0 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* Sits beside the 12px stats caption, so it is quieter than `.s757132_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.s4cca13_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.s4cca13_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.s4cca13_catalogRefreshButton:disabled {\n cursor: default;\n opacity: 0.6;\n}\n\n.s4cca13_catalogRefreshButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.sc24c97_catalogRefreshFailed {\n font-size: 11px;\n color: var(--shop-warn);\n}\n\n.s1aa6bf_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.sa036b0_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 .sa036b0_cards { animation: none; }\n}\n\n.s88b200_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.s853040_cardsSentry {\n height: 1px;\n margin: 0;\n padding: 0;\n}\n\n.s239c39_showingLine {\n margin: 0;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s88b200_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 .s88b200_card { transition: none; }\n .s88b200_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.sc1c25e_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. `.s512425_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.s88b200_card[data-category='tool'], .s512425_categoryButton[data-category='tool'] { --category-hue: #4C8DFF; }\n.s88b200_card[data-category='provider'], .s512425_categoryButton[data-category='provider'] { --category-hue: #A78BFA; }\n.s88b200_card[data-category='ui'], .s512425_categoryButton[data-category='ui'] { --category-hue: #2DD4BF; }\n.s88b200_card[data-category='workflow'], .s512425_categoryButton[data-category='workflow'] { --category-hue: #F59E0B; }\n.s88b200_card[data-category='integration'], .s512425_categoryButton[data-category='integration'] { --category-hue: #34D399; }\n.s88b200_card[data-category='theme'], .s512425_categoryButton[data-category='theme'] { --category-hue: #F472B6; }\n.s88b200_card[data-category='other'], .s512425_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.s433c76_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.s6bc968_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.s6bc968_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.s704f0d_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.sba9311_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.s9b33f4_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.s9b33f4_cardActions > .sc99cff_gate,\n.s9b33f4_cardActions > .sbd0905_installPanel,\n.s9b33f4_cardActions > .sd562d9_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.s83e5d6_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.s93d06c_sourceBadge {\n display: inline-flex;\n align-items: center;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s1fe784_tierBadge {\n padding: 1px 7px;\n border-radius: 999px;\n font-size: 11px;\n line-height: 1.6;\n}\n\n.s1fe784_tierBadge[data-tier='verified'] {\n color: var(--shop-success);\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.s1fe784_tierBadge[data-tier='verified-stale'] {\n color: var(--shop-warn-on-tint);\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.s1fe784_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.s1fe784_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.s1fe784_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.s2f456e_incompatibleBadge {\n flex-shrink: 0;\n padding: 3px 9px;\n border-radius: 999px;\n font-size: 12px;\n white-space: nowrap;\n /* On the tint, not on the plain surface: this text reads on its own 14% red\n * fill, where the 90% red would measure 4.21:1 (light) and 4.43:1 (dark). */\n color: var(--shop-error-on-tint);\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.sc6c968_starsBadge {\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s287fda_chevron, .s20b60f_chevronOpen {\n color: var(--dsw-alias-label-secondary);\n transition: transform 120ms ease;\n}\n\n.s20b60f_chevronOpen { transform: rotate(180deg); }\n\n@media (prefers-reduced-motion: reduce) {\n .s287fda_chevron, .s20b60f_chevronOpen { transition: none; }\n}\n\n.s87fcee_body {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 0 12px;\n}\n\n.sf5d0d8_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.sa0923f_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.s20eea8_summaryExpanded {\n -webkit-line-clamp: unset;\n display: block;\n}\n\n.sb53b34_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.sc2cc78_incompatibleDetail {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n white-space: pre-line;\n color: var(--shop-error);\n}\n\n/* The way out of dsh's own refusal: the lead-in, then the exemption command\n * on its own line. */\n/* Block layout rather than flex: Firefox 153 will not line-clamp a container\n * that holds a display:flex child (Chromium 151 does), and the fold's\n * measured goal — \"the whole red block, remedy included, as one unit\" —\n * needs the container to clamp in BOTH engines the shop targets. The 4px\n * rhythm between the lead-in and the command is kept by the margins of the\n * children. */\n.sc7602c_remedy {\n display: block;\n}\n\n.sc7602c_remedy > * + * { margin-top: 4px; }\n\n/* The fold under a card's whole red block: two lines by default, a small\n * Refresh-pill-shaped toggle under it. Two containers, one rhythm: the shelf\n * uses `.s895dae_foldBlock` collapsed and `.s6b136d_foldBlockExpanded` open, and they share\n * one `display: block` (Firefox clamps no flex child inside a -webkit-box).\n * The clamp is on the BLOCK rather than any one line, so a 43-line remedy\n * counts as a unit instead of squashing the harness-peers line to a sliver\n * and hiding the command the reader actually came for. Both containers\n * still render the full block to a screen reader — `white-space: normal`\n * instead of pre-line keeps the copy's own `\\n` visible without forcing the\n * browser to lay the block's pieces like a text node. */\n.s895dae_foldBlock {\n display: block;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n display: -webkit-box;\n overflow: hidden;\n}\n\n.s6b136d_foldBlockExpanded { display: block; }\n\n/* The toggle. Geometry and quiet voice are the Refresh pill's\n * (`catalogStatsRow` right beside it on the same screen): one 1px border,\n * 11px text, secondary label, no fill. It is the only one of its kind here,\n * so a class of its own means one function of its own. */\n.s8c3792_foldToggle {\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.s8c3792_foldToggle:hover { border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l2)); }\n.s8c3792_foldToggle:focus-visible { outline: 2px solid var(--dsw-alias-brand-primary); outline-offset: 1px; }\n\n.s935bd9_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.se75f45_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 * refused label, the command that clears dsh's refusal included. Block,\n * rather than flex: Firefox will not line-clamp a -webkit-box that holds a\n * display:flex child, and the fold this element can sit inside of carries\n * that clamp. The 6px rhythm between the block's lines is carried by the\n * children's margins. */\n.se69fd7_refusal {\n display: block;\n flex-basis: 100%;\n}\n\n.se69fd7_refusal > * + * { margin-top: 6px; }\n\n.sb2edea_capabilitiesBlock {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.sa5c8b2_capabilitiesNote {\n margin: 0;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sb95e1e_capabilities {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.sb95e1e_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.s1f4436_detail {\n border-top: 1px solid var(--dsw-alias-border-l1);\n padding-top: 8px;\n}\n\n.sf6f153_detailRows {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin: 0;\n}\n\n.sbfe178_detailRow {\n display: flex;\n justify-content: space-between;\n gap: 12px;\n font-size: 12px;\n}\n\n.sbfe178_detailRow dt {\n color: var(--dsw-alias-label-secondary);\n}\n\n.sbfe178_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.sbfe178_detailRow a {\n color: var(--dsw-alias-brand-primary);\n text-decoration: none;\n}\n\n.sbfe178_detailRow a:hover {\n text-decoration: underline;\n}\n\n.sbfe178_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 * `.s9b33f4_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.s92d907_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: `.s92d907_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 * `.s6119f4_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.s5889e2_size,\n.s6119f4_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 `.s92d907_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 `.s92d907_cardMeta`. There is no middot:\n * `.s92d907_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.s5efcbe_author {\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n}\n\n.sdd3957_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(--shop-warn-on-tint);\n}\n\n/* Install flow. */\n.sbd0905_installPanel {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 2px;\n}\n\n/* ONE geometry for everything that occupies the install button's place: the\n * button itself, the confirm/cancel pair beside the restart gate, the installed\n * label, and the refused label. Stated once because a refused label HAS to look\n * like the button it replaces apart from its colour and its text, and four\n * copies of the same five declarations is how they stop looking alike.\n * `cursor: pointer` is NOT here: only the controls that can be pressed take it,\n * while a label renders with the default cursor. */\n.se20cee_installButton,\n.sdf88f9_confirmButton,\n.s330a03_cancelButton,\n.sad029f_installedLabel,\n.s7b7f2d_refusedLabel {\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}\n\n.se20cee_installButton, .sdf88f9_confirmButton, .s330a03_cancelButton {\n cursor: pointer;\n}\n\n.se20cee_installButton {\n border-color: var(--dsw-alias-brand-primary);\n background: var(--dsw-alias-brand-primary);\n color: var(--dsw-alias-bg-base);\n}\n\n/* Between the click and the host's answer. The flow ignores a repeat start for\n * the same identity, so this is the visible half of the same fact: a control\n * that says \"not now\" rather than one that looks ready to send a second\n * install. Only the button can be in this state — a label is never pressed. */\n.se20cee_installButton:disabled {\n cursor: wait;\n}\n\n.se20cee_installButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 88%, var(--dsw-alias-label-primary));\n}\n\n.sdf88f9_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.sdf88f9_confirmButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 88%, var(--dsw-alias-label-primary));\n}\n\n.s330a03_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.s330a03_cancelButton:hover {\n border-color: var(--dsw-alias-label-secondary);\n}\n\n.se20cee_installButton:focus-visible, .sdf88f9_confirmButton:focus-visible, .s330a03_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.sad029f_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/* The refused Install/Update button's replacement: the same box as the button\n * (the geometry rule above), filled solid red, not pressable. The fill is the\n * derived red rather than the raw token because text sits on it — bg-base on\n * `--shop-error` measures 5.31:1 light and 6.14:1 dark — and the raw error red\n * under bg-base text measures 4.498:1, the miss this whole family of fixes\n * exists for. Nothing here changes on hover: it is a statement, not a control,\n * so it keeps the default cursor and its colours. */\n.s7b7f2d_refusedLabel {\n border-color: var(--shop-error);\n background: var(--shop-error);\n color: var(--dsw-alias-bg-base);\n white-space: nowrap;\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.sd562d9_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.s1ef16e_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 /* The on-tint red: hovering this button paints a 10% red fill under the\n * word, where the plain red measures 4.50:1 (light) — the same two\n * thousandths short of the floor the raw token shipped at. */\n color: var(--shop-error-on-tint);\n font-size: 13px;\n cursor: pointer;\n}\n\n.s1ef16e_uninstallButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);\n}\n\n.s1ef16e_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.s912ec7_restartSelfButton {\n /* The version row's third state. Geometry matches .sb48cbc_checkUpdateButton and\n * .s79450a_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.s912ec7_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.sa5d8c1_restartButton,\n.se5bd2c_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.sa5d8c1_restartButton:hover,\n.se5bd2c_reloadButton:hover {\n border-color: var(--dsw-alias-label-secondary);\n}\n\n.sa5d8c1_restartButton:focus-visible,\n.se5bd2c_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 * `.sd562d9_installedActions` the way the restart gate is — a column of its own\n * rather than inline with the row. */\n.s76690e_reloadPanel {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 6px;\n}\n\n\n.sc99cff_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.s58fe9b_gateTitle {\n margin: 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--shop-warn-on-tint);\n}\n\n.sd510f5_gateBody {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-primary);\n}\n\n.s943dbd_gateWarning {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n white-space: pre-line;\n color: var(--shop-error);\n}\n\n.sb2e35c_gateActions {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n.s407016_installing {\n margin: 0;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s56c1a5_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.s3d7aca_logLine {\n white-space: pre-wrap;\n word-break: break-all;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s3e1ea0_notice {\n margin: 0;\n font-size: 12px;\n color: var(--shop-success);\n}\n\n.s71965d_failedHeading {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n color: var(--shop-error);\n}\n\n.s41bb43_failedDetail {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-primary);\n}\n\n.s3b880d_rejectedCode {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n color: var(--shop-error);\n}\n\n.s465924_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.sfccf6f_outdatedSection {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 6px;\n}\n\n.s403f9e_outdatedList {\n display: flex;\n flex-direction: column;\n gap: 8px;\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.sd9cb61_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.s7b2d89_outdatedInfo {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.s7b2d89_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.sc33878_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 .sba9311_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.sa88998_shadowedSection {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 6px;\n}\n\n.s98799e_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.saaf7ce_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: `.s7ce437_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 `.s3a8382_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.s3a8382_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.s1e392e_switchOn {\n border-color: var(--track-hue);\n background: color-mix(in srgb, var(--track-hue) 55%, transparent);\n}\n\n.s419ba1_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.s1e392e_switchOn .s419ba1_switchKnob {\n transform: translateX(14px);\n background: var(--dsw-alias-bg-base);\n}\n\n.s3a8382_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 .s3a8382_switch, .s419ba1_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.saa9c93_switchWrap {\n display: inline-flex;\n align-items: center;\n /* Same reason as `.saaf7ce_outdatedActions`: `.s76690e_reloadPanel`'s own comment calls\n * itself \"a column of its own rather than inline with the row\", which held\n * for `.sd562d9_installedActions` and not here until this line. */\n flex-wrap: wrap;\n gap: 8px;\n}\n";
|
|
9328
|
+
if (typeof document !== "undefined" && !document.querySelector("style[data-plugin-css=\"5f753521\"]")) {
|
|
9221
9329
|
const tag = document.createElement("style");
|
|
9222
|
-
tag.dataset.pluginCss = "
|
|
9330
|
+
tag.dataset.pluginCss = "5f753521";
|
|
9223
9331
|
tag.textContent = css;
|
|
9224
9332
|
document.head.appendChild(tag);
|
|
9225
9333
|
}
|
|
9226
|
-
var
|
|
9227
|
-
"client": "
|
|
9228
|
-
"config": "
|
|
9229
|
-
"ts": "
|
|
9230
|
-
"spec": "
|
|
9231
|
-
"panel": "
|
|
9232
|
-
"searchInput": "
|
|
9233
|
-
"toolbar": "
|
|
9234
|
-
"stateLine": "
|
|
9235
|
-
"srOnly": "
|
|
9236
|
-
"skeletonGrid": "
|
|
9237
|
-
"skeletonCard": "
|
|
9238
|
-
"skeletonName": "
|
|
9239
|
-
"skeletonSummary": "
|
|
9240
|
-
"skeletonSummaryShort": "
|
|
9241
|
-
"skeletonActions": "
|
|
9242
|
-
"actionButton": "
|
|
9243
|
-
"toolbarRight": "
|
|
9244
|
-
"versionBlock": "
|
|
9245
|
-
"versionText": "
|
|
9246
|
-
"updateSelfButton": "
|
|
9247
|
-
"checkUpdateButton": "
|
|
9248
|
-
"githubLink": "
|
|
9249
|
-
"selfUpdatePanel": "
|
|
9250
|
-
"expansionNote": "
|
|
9251
|
-
"staleBadge": "
|
|
9252
|
-
"categoryBar": "
|
|
9253
|
-
"categoryButton": "
|
|
9254
|
-
"categoryButtonOn": "
|
|
9255
|
-
"incompatibleFilter": "
|
|
9256
|
-
"switch": "
|
|
9257
|
-
"switchOn": "
|
|
9258
|
-
"incompatibleFilterOn": "
|
|
9259
|
-
"switchKnob": "
|
|
9260
|
-
"updatableHeading": "
|
|
9261
|
-
"catalogStatsRow": "
|
|
9262
|
-
"sortGroup": "
|
|
9263
|
-
"sortLabel": "
|
|
9264
|
-
"catalogStats": "
|
|
9265
|
-
"
|
|
9266
|
-
"
|
|
9267
|
-
"
|
|
9268
|
-
"
|
|
9269
|
-
"
|
|
9270
|
-
"
|
|
9271
|
-
"
|
|
9272
|
-
"
|
|
9273
|
-
"
|
|
9274
|
-
"
|
|
9275
|
-
"
|
|
9276
|
-
"
|
|
9277
|
-
"
|
|
9278
|
-
"
|
|
9279
|
-
"
|
|
9280
|
-
"
|
|
9281
|
-
"
|
|
9282
|
-
"
|
|
9283
|
-
"
|
|
9284
|
-
"
|
|
9285
|
-
"
|
|
9286
|
-
"
|
|
9287
|
-
"
|
|
9288
|
-
"
|
|
9289
|
-
"
|
|
9290
|
-
"
|
|
9291
|
-
"
|
|
9292
|
-
"
|
|
9293
|
-
"
|
|
9294
|
-
"
|
|
9295
|
-
"
|
|
9296
|
-
"
|
|
9297
|
-
"
|
|
9298
|
-
"
|
|
9299
|
-
"
|
|
9300
|
-
"
|
|
9301
|
-
"
|
|
9302
|
-
"
|
|
9303
|
-
"
|
|
9304
|
-
"
|
|
9305
|
-
"
|
|
9306
|
-
"
|
|
9307
|
-
"
|
|
9308
|
-
"
|
|
9309
|
-
"
|
|
9310
|
-
"
|
|
9311
|
-
"
|
|
9312
|
-
"
|
|
9313
|
-
"
|
|
9314
|
-
"
|
|
9315
|
-
"
|
|
9316
|
-
"
|
|
9317
|
-
"
|
|
9318
|
-
"
|
|
9319
|
-
"
|
|
9320
|
-
"
|
|
9321
|
-
"
|
|
9322
|
-
"
|
|
9323
|
-
"
|
|
9324
|
-
"
|
|
9325
|
-
"
|
|
9326
|
-
"
|
|
9327
|
-
"
|
|
9328
|
-
"
|
|
9329
|
-
"
|
|
9330
|
-
"
|
|
9331
|
-
"
|
|
9332
|
-
"
|
|
9333
|
-
"
|
|
9334
|
-
"
|
|
9335
|
-
"
|
|
9336
|
-
"
|
|
9337
|
-
"
|
|
9338
|
-
"
|
|
9334
|
+
var _dsh_plugin_shop_css_5f753521_default = {
|
|
9335
|
+
"client": "sa85938_client",
|
|
9336
|
+
"config": "s94d4e5_config",
|
|
9337
|
+
"ts": "s91da2e_ts",
|
|
9338
|
+
"spec": "sbb5706_spec",
|
|
9339
|
+
"panel": "s6a01b8_panel",
|
|
9340
|
+
"searchInput": "sa16cbe_searchInput",
|
|
9341
|
+
"toolbar": "sbc1497_toolbar",
|
|
9342
|
+
"stateLine": "s352f7a_stateLine",
|
|
9343
|
+
"srOnly": "s60f2f5_srOnly",
|
|
9344
|
+
"skeletonGrid": "s34d5f5_skeletonGrid",
|
|
9345
|
+
"skeletonCard": "s11e315_skeletonCard",
|
|
9346
|
+
"skeletonName": "s227d7c_skeletonName",
|
|
9347
|
+
"skeletonSummary": "s83025b_skeletonSummary",
|
|
9348
|
+
"skeletonSummaryShort": "sdb7845_skeletonSummaryShort",
|
|
9349
|
+
"skeletonActions": "sa61117_skeletonActions",
|
|
9350
|
+
"actionButton": "s757132_actionButton",
|
|
9351
|
+
"toolbarRight": "s22d15d_toolbarRight",
|
|
9352
|
+
"versionBlock": "s76d3c9_versionBlock",
|
|
9353
|
+
"versionText": "s390e34_versionText",
|
|
9354
|
+
"updateSelfButton": "s79450a_updateSelfButton",
|
|
9355
|
+
"checkUpdateButton": "sb48cbc_checkUpdateButton",
|
|
9356
|
+
"githubLink": "s946346_githubLink",
|
|
9357
|
+
"selfUpdatePanel": "s861599_selfUpdatePanel",
|
|
9358
|
+
"expansionNote": "seb8329_expansionNote",
|
|
9359
|
+
"staleBadge": "s31a2f0_staleBadge",
|
|
9360
|
+
"categoryBar": "sab4844_categoryBar",
|
|
9361
|
+
"categoryButton": "s512425_categoryButton",
|
|
9362
|
+
"categoryButtonOn": "s598384_categoryButtonOn",
|
|
9363
|
+
"incompatibleFilter": "s7ce437_incompatibleFilter",
|
|
9364
|
+
"switch": "s3a8382_switch",
|
|
9365
|
+
"switchOn": "s1e392e_switchOn",
|
|
9366
|
+
"incompatibleFilterOn": "sd9fa8d_incompatibleFilterOn",
|
|
9367
|
+
"switchKnob": "s419ba1_switchKnob",
|
|
9368
|
+
"updatableHeading": "s320079_updatableHeading",
|
|
9369
|
+
"catalogStatsRow": "s923c82_catalogStatsRow",
|
|
9370
|
+
"sortGroup": "s3edd9f_sortGroup",
|
|
9371
|
+
"sortLabel": "s73feb1_sortLabel",
|
|
9372
|
+
"catalogStats": "sad5aba_catalogStats",
|
|
9373
|
+
"disclosure": "s58eb5b_disclosure",
|
|
9374
|
+
"catalogRefreshButton": "s4cca13_catalogRefreshButton",
|
|
9375
|
+
"catalogRefreshFailed": "sc24c97_catalogRefreshFailed",
|
|
9376
|
+
"emptyLine": "s1aa6bf_emptyLine",
|
|
9377
|
+
"cards": "sa036b0_cards",
|
|
9378
|
+
"card": "s88b200_card",
|
|
9379
|
+
"cardsSentry": "s853040_cardsSentry",
|
|
9380
|
+
"showingLine": "s239c39_showingLine",
|
|
9381
|
+
"cardSpine": "sc1c25e_cardSpine",
|
|
9382
|
+
"categoryBadge": "s433c76_categoryBadge",
|
|
9383
|
+
"entryHeader": "s6bc968_entryHeader",
|
|
9384
|
+
"name": "s704f0d_name",
|
|
9385
|
+
"badges": "sba9311_badges",
|
|
9386
|
+
"cardActions": "s9b33f4_cardActions",
|
|
9387
|
+
"gate": "sc99cff_gate",
|
|
9388
|
+
"installPanel": "sbd0905_installPanel",
|
|
9389
|
+
"installedActions": "sd562d9_installedActions",
|
|
9390
|
+
"cardVersion": "s83e5d6_cardVersion",
|
|
9391
|
+
"sourceBadge": "s93d06c_sourceBadge",
|
|
9392
|
+
"tierBadge": "s1fe784_tierBadge",
|
|
9393
|
+
"incompatibleBadge": "s2f456e_incompatibleBadge",
|
|
9394
|
+
"starsBadge": "sc6c968_starsBadge",
|
|
9395
|
+
"chevron": "s287fda_chevron",
|
|
9396
|
+
"chevronOpen": "s20b60f_chevronOpen",
|
|
9397
|
+
"body": "s87fcee_body",
|
|
9398
|
+
"summary": "sf5d0d8_summary",
|
|
9399
|
+
"summaryZh": "sa0923f_summaryZh",
|
|
9400
|
+
"summaryExpanded": "s20eea8_summaryExpanded",
|
|
9401
|
+
"summaryZhExpanded": "sb53b34_summaryZhExpanded",
|
|
9402
|
+
"incompatibleDetail": "sc2cc78_incompatibleDetail",
|
|
9403
|
+
"remedy": "sc7602c_remedy",
|
|
9404
|
+
"foldBlock": "s895dae_foldBlock",
|
|
9405
|
+
"foldBlockExpanded": "s6b136d_foldBlockExpanded",
|
|
9406
|
+
"foldToggle": "s8c3792_foldToggle",
|
|
9407
|
+
"remedyLead": "s935bd9_remedyLead",
|
|
9408
|
+
"allowCommand": "se75f45_allowCommand",
|
|
9409
|
+
"refusal": "se69fd7_refusal",
|
|
9410
|
+
"capabilitiesBlock": "sb2edea_capabilitiesBlock",
|
|
9411
|
+
"capabilitiesNote": "sa5c8b2_capabilitiesNote",
|
|
9412
|
+
"capabilities": "sb95e1e_capabilities",
|
|
9413
|
+
"detail": "s1f4436_detail",
|
|
9414
|
+
"detailRows": "sf6f153_detailRows",
|
|
9415
|
+
"detailRow": "sbfe178_detailRow",
|
|
9416
|
+
"cardMeta": "s92d907_cardMeta",
|
|
9417
|
+
"shelfDate": "s6119f4_shelfDate",
|
|
9418
|
+
"size": "s5889e2_size",
|
|
9419
|
+
"author": "s5efcbe_author",
|
|
9420
|
+
"reviewedLine": "sdd3957_reviewedLine",
|
|
9421
|
+
"installButton": "se20cee_installButton",
|
|
9422
|
+
"confirmButton": "sdf88f9_confirmButton",
|
|
9423
|
+
"cancelButton": "s330a03_cancelButton",
|
|
9424
|
+
"installedLabel": "sad029f_installedLabel",
|
|
9425
|
+
"refusedLabel": "s7b7f2d_refusedLabel",
|
|
9426
|
+
"uninstallButton": "s1ef16e_uninstallButton",
|
|
9427
|
+
"restartSelfButton": "s912ec7_restartSelfButton",
|
|
9428
|
+
"restartButton": "sa5d8c1_restartButton",
|
|
9429
|
+
"reloadButton": "se5bd2c_reloadButton",
|
|
9430
|
+
"reloadPanel": "s76690e_reloadPanel",
|
|
9431
|
+
"gateTitle": "s58fe9b_gateTitle",
|
|
9432
|
+
"gateBody": "sd510f5_gateBody",
|
|
9433
|
+
"gateWarning": "s943dbd_gateWarning",
|
|
9434
|
+
"gateActions": "sb2e35c_gateActions",
|
|
9435
|
+
"installing": "s407016_installing",
|
|
9436
|
+
"log": "s56c1a5_log",
|
|
9437
|
+
"logLine": "s3d7aca_logLine",
|
|
9438
|
+
"notice": "s3e1ea0_notice",
|
|
9439
|
+
"failedHeading": "s71965d_failedHeading",
|
|
9440
|
+
"failedDetail": "s41bb43_failedDetail",
|
|
9441
|
+
"rejectedCode": "s3b880d_rejectedCode",
|
|
9442
|
+
"rejectedDetail": "s465924_rejectedDetail",
|
|
9443
|
+
"outdatedSection": "sfccf6f_outdatedSection",
|
|
9444
|
+
"outdatedList": "s403f9e_outdatedList",
|
|
9445
|
+
"outdatedRow": "sd9cb61_outdatedRow",
|
|
9446
|
+
"outdatedInfo": "s7b2d89_outdatedInfo",
|
|
9447
|
+
"outdatedVersions": "sc33878_outdatedVersions",
|
|
9448
|
+
"shadowedSection": "sa88998_shadowedSection",
|
|
9449
|
+
"shadowedDetail": "s98799e_shadowedDetail",
|
|
9450
|
+
"outdatedActions": "saaf7ce_outdatedActions",
|
|
9451
|
+
"switchWrap": "saa9c93_switchWrap"
|
|
9339
9452
|
};
|
|
9340
9453
|
//#endregion
|
|
9454
|
+
//#region src/client/FoldableRedBlock.tsx
|
|
9455
|
+
function FoldableRedBlock({ children, t, attrName }) {
|
|
9456
|
+
const { rootRef, expandable, expanded, toggle } = useFoldableRedBlock();
|
|
9457
|
+
const kindName = attrName.replace(/^data-shop-/, "");
|
|
9458
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9459
|
+
className: expanded ? _dsh_plugin_shop_css_5f753521_default.foldBlockExpanded : _dsh_plugin_shop_css_5f753521_default.foldBlock,
|
|
9460
|
+
[attrName]: true,
|
|
9461
|
+
ref: rootRef,
|
|
9462
|
+
children
|
|
9463
|
+
}), expandable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
9464
|
+
type: "button",
|
|
9465
|
+
className: _dsh_plugin_shop_css_5f753521_default.foldToggle,
|
|
9466
|
+
"data-shop-fold": kindName,
|
|
9467
|
+
"aria-expanded": expanded,
|
|
9468
|
+
onClick: toggle,
|
|
9469
|
+
children: expanded ? t("lessRedLines") : t("moreRedLines")
|
|
9470
|
+
})] });
|
|
9471
|
+
}
|
|
9472
|
+
//#endregion
|
|
9341
9473
|
//#region src/client/ShopTab.tsx
|
|
9342
|
-
/** The shop tab: browse the catalog, detail an entry, install
|
|
9343
|
-
*
|
|
9474
|
+
/** The shop tab: browse the catalog, detail an entry, install on one click,
|
|
9475
|
+
* poll the install to its terminal state (§7.2).
|
|
9344
9476
|
* Everything the tab renders is text — summaries, capabilities, log lines,
|
|
9345
9477
|
* details — never markup, so hostile npm descriptions cannot inject (spec
|
|
9346
9478
|
* §11.3.4): no render path here may ever use dangerouslySetInnerHTML. */
|
|
@@ -9352,14 +9484,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9352
9484
|
* those nodes, and React's next commit throws on the ones it no longer finds
|
|
9353
9485
|
* (design 2026-09-26-market-borrowings §4). The tab renders no portals, so its
|
|
9354
9486
|
* root is its whole surface. */
|
|
9355
|
-
const PANEL_ROOT = `${
|
|
9487
|
+
const PANEL_ROOT = `${_dsh_plugin_shop_css_5f753521_default.panel} notranslate`;
|
|
9356
9488
|
function ChevronIcon({ open }) {
|
|
9357
9489
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
9358
9490
|
viewBox: "0 0 16 16",
|
|
9359
9491
|
width: "14",
|
|
9360
9492
|
height: "14",
|
|
9361
9493
|
"aria-hidden": "true",
|
|
9362
|
-
className: open ?
|
|
9494
|
+
className: open ? _dsh_plugin_shop_css_5f753521_default.chevronOpen : _dsh_plugin_shop_css_5f753521_default.chevron,
|
|
9363
9495
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
9364
9496
|
fill: "currentColor",
|
|
9365
9497
|
d: "M3.5 6 8 10.5 12.5 6l1 1L8 12.5 2.5 7l1-1Z"
|
|
@@ -9391,32 +9523,32 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9391
9523
|
subdir: entry.subdir
|
|
9392
9524
|
};
|
|
9393
9525
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9394
|
-
className:
|
|
9526
|
+
className: _dsh_plugin_shop_css_5f753521_default.card,
|
|
9395
9527
|
"data-shop-entry": entry.name,
|
|
9396
9528
|
"data-category": category,
|
|
9397
9529
|
children: [
|
|
9398
9530
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9399
|
-
className:
|
|
9531
|
+
className: _dsh_plugin_shop_css_5f753521_default.cardSpine,
|
|
9400
9532
|
"aria-hidden": "true"
|
|
9401
9533
|
}),
|
|
9402
9534
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
9403
9535
|
type: "button",
|
|
9404
|
-
className:
|
|
9536
|
+
className: _dsh_plugin_shop_css_5f753521_default.entryHeader,
|
|
9405
9537
|
"aria-expanded": open,
|
|
9406
9538
|
"aria-controls": detailId,
|
|
9407
9539
|
onClick: () => setOpen((current) => !current),
|
|
9408
9540
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9409
|
-
className:
|
|
9541
|
+
className: _dsh_plugin_shop_css_5f753521_default.name,
|
|
9410
9542
|
children: entry.name
|
|
9411
9543
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
9412
|
-
className:
|
|
9544
|
+
className: _dsh_plugin_shop_css_5f753521_default.badges,
|
|
9413
9545
|
children: [
|
|
9414
9546
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9415
|
-
className:
|
|
9547
|
+
className: _dsh_plugin_shop_css_5f753521_default.categoryBadge,
|
|
9416
9548
|
children: t(categoryKey(entry))
|
|
9417
9549
|
}),
|
|
9418
9550
|
entry.source === "npm" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9419
|
-
className:
|
|
9551
|
+
className: _dsh_plugin_shop_css_5f753521_default.sourceBadge,
|
|
9420
9552
|
"data-shop-source-npm": true,
|
|
9421
9553
|
role: "img",
|
|
9422
9554
|
"aria-label": t("npmSource"),
|
|
@@ -9433,7 +9565,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9433
9565
|
})
|
|
9434
9566
|
}),
|
|
9435
9567
|
hasGithubHome(entry) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9436
|
-
className:
|
|
9568
|
+
className: _dsh_plugin_shop_css_5f753521_default.sourceBadge,
|
|
9437
9569
|
"data-shop-source-github": true,
|
|
9438
9570
|
role: "img",
|
|
9439
9571
|
"aria-label": t("githubSource"),
|
|
@@ -9447,7 +9579,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9447
9579
|
})
|
|
9448
9580
|
}),
|
|
9449
9581
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9450
|
-
className:
|
|
9582
|
+
className: _dsh_plugin_shop_css_5f753521_default.cardVersion,
|
|
9451
9583
|
"data-card-version": true,
|
|
9452
9584
|
role: "img",
|
|
9453
9585
|
"aria-label": versionName,
|
|
@@ -9455,17 +9587,17 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9455
9587
|
children: version.text
|
|
9456
9588
|
}),
|
|
9457
9589
|
entry.tier !== "community" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9458
|
-
className:
|
|
9590
|
+
className: _dsh_plugin_shop_css_5f753521_default.tierBadge,
|
|
9459
9591
|
"data-tier": entry.tier,
|
|
9460
9592
|
children: t(tierKey(entry.tier))
|
|
9461
9593
|
}),
|
|
9462
9594
|
entry.archived === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9463
|
-
className:
|
|
9595
|
+
className: _dsh_plugin_shop_css_5f753521_default.tierBadge,
|
|
9464
9596
|
"data-tier": "archived",
|
|
9465
9597
|
children: t("archivedBadge")
|
|
9466
9598
|
}),
|
|
9467
9599
|
stars !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
9468
|
-
className:
|
|
9600
|
+
className: _dsh_plugin_shop_css_5f753521_default.starsBadge,
|
|
9469
9601
|
role: "img",
|
|
9470
9602
|
"aria-label": t("stars", { count: stars }),
|
|
9471
9603
|
children: ["★ ", formatStars(stars)]
|
|
@@ -9475,39 +9607,43 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9475
9607
|
})]
|
|
9476
9608
|
}),
|
|
9477
9609
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9478
|
-
className:
|
|
9610
|
+
className: _dsh_plugin_shop_css_5f753521_default.body,
|
|
9479
9611
|
children: [
|
|
9480
9612
|
summary !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9481
|
-
className: open ? `${
|
|
9613
|
+
className: open ? `${_dsh_plugin_shop_css_5f753521_default.summary} ${_dsh_plugin_shop_css_5f753521_default.summaryExpanded}` : _dsh_plugin_shop_css_5f753521_default.summary,
|
|
9482
9614
|
children: summary.en
|
|
9483
9615
|
}),
|
|
9484
9616
|
summary?.zh !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9485
|
-
className: open ? `${
|
|
9617
|
+
className: open ? `${_dsh_plugin_shop_css_5f753521_default.summaryZh} ${_dsh_plugin_shop_css_5f753521_default.summaryZhExpanded}` : _dsh_plugin_shop_css_5f753521_default.summaryZh,
|
|
9486
9618
|
children: summary.zh
|
|
9487
9619
|
}),
|
|
9488
|
-
blockers.
|
|
9489
|
-
|
|
9490
|
-
|
|
9491
|
-
|
|
9492
|
-
|
|
9620
|
+
blockers.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FoldableRedBlock, {
|
|
9621
|
+
t,
|
|
9622
|
+
attrName: "data-shop-remedy",
|
|
9623
|
+
children: blockers.map((blocker) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockerLine, {
|
|
9624
|
+
blocker,
|
|
9625
|
+
className: _dsh_plugin_shop_css_5f753521_default.incompatibleDetail,
|
|
9626
|
+
surface: "detail"
|
|
9627
|
+
}, blocker.kind))
|
|
9628
|
+
}),
|
|
9493
9629
|
open && entry.catalog !== void 0 && entry.catalog.capabilities.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9494
|
-
className:
|
|
9630
|
+
className: _dsh_plugin_shop_css_5f753521_default.capabilitiesBlock,
|
|
9495
9631
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9496
|
-
className:
|
|
9632
|
+
className: _dsh_plugin_shop_css_5f753521_default.capabilitiesNote,
|
|
9497
9633
|
children: t("capabilitiesNote")
|
|
9498
9634
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
9499
|
-
className:
|
|
9635
|
+
className: _dsh_plugin_shop_css_5f753521_default.capabilities,
|
|
9500
9636
|
children: entry.catalog.capabilities.map((capability) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: capability }, capability))
|
|
9501
9637
|
})]
|
|
9502
9638
|
}),
|
|
9503
9639
|
open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
9504
9640
|
id: detailId,
|
|
9505
|
-
className:
|
|
9641
|
+
className: _dsh_plugin_shop_css_5f753521_default.detail,
|
|
9506
9642
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dl", {
|
|
9507
|
-
className:
|
|
9643
|
+
className: _dsh_plugin_shop_css_5f753521_default.detailRows,
|
|
9508
9644
|
children: [
|
|
9509
9645
|
npmUrl !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9510
|
-
className:
|
|
9646
|
+
className: _dsh_plugin_shop_css_5f753521_default.detailRow,
|
|
9511
9647
|
"data-shop-npm": true,
|
|
9512
9648
|
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", {
|
|
9513
9649
|
href: npmUrl,
|
|
@@ -9517,7 +9653,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9517
9653
|
}) })]
|
|
9518
9654
|
}),
|
|
9519
9655
|
entry.repository !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9520
|
-
className:
|
|
9656
|
+
className: _dsh_plugin_shop_css_5f753521_default.detailRow,
|
|
9521
9657
|
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", {
|
|
9522
9658
|
href: entry.repository,
|
|
9523
9659
|
target: "_blank",
|
|
@@ -9526,12 +9662,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9526
9662
|
}) : entry.repository })]
|
|
9527
9663
|
}),
|
|
9528
9664
|
entry.license !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9529
|
-
className:
|
|
9665
|
+
className: _dsh_plugin_shop_css_5f753521_default.detailRow,
|
|
9530
9666
|
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 })]
|
|
9531
9667
|
})
|
|
9532
9668
|
]
|
|
9533
9669
|
}), entry.tier === "verified-stale" && entry.review !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9534
|
-
className:
|
|
9670
|
+
className: _dsh_plugin_shop_css_5f753521_default.reviewedLine,
|
|
9535
9671
|
children: entry.review.reviewedVersion !== void 0 ? t("reviewedVersionLine", {
|
|
9536
9672
|
reviewed: versionLabel("npm", entry.review.reviewedVersion).text,
|
|
9537
9673
|
current: version.text
|
|
@@ -9544,7 +9680,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9544
9680
|
]
|
|
9545
9681
|
}),
|
|
9546
9682
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9547
|
-
className:
|
|
9683
|
+
className: _dsh_plugin_shop_css_5f753521_default.cardActions,
|
|
9548
9684
|
"data-shop-actions": true,
|
|
9549
9685
|
children: [installed === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [!inInstalledView && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstallPanel, {
|
|
9550
9686
|
target: installTarget,
|
|
@@ -9576,7 +9712,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9576
9712
|
restartBlocked,
|
|
9577
9713
|
reload
|
|
9578
9714
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9579
|
-
className:
|
|
9715
|
+
className: _dsh_plugin_shop_css_5f753521_default.installedLabel,
|
|
9580
9716
|
"data-shop-installed": true,
|
|
9581
9717
|
children: t("installed")
|
|
9582
9718
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockerBadge, {
|
|
@@ -9601,15 +9737,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9601
9737
|
flow: uninstallFlow
|
|
9602
9738
|
})
|
|
9603
9739
|
] }), (size !== void 0 || author !== null || shelfDay !== void 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
9604
|
-
className:
|
|
9740
|
+
className: _dsh_plugin_shop_css_5f753521_default.cardMeta,
|
|
9605
9741
|
children: [
|
|
9606
9742
|
shelfDay !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9607
|
-
className:
|
|
9743
|
+
className: _dsh_plugin_shop_css_5f753521_default.shelfDate,
|
|
9608
9744
|
"data-shop-shelf-date": true,
|
|
9609
9745
|
children: t(order === "added" ? "listedOn" : "updatedOn", { date: shelfDay })
|
|
9610
9746
|
}),
|
|
9611
9747
|
size !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9612
|
-
className:
|
|
9748
|
+
className: _dsh_plugin_shop_css_5f753521_default.size,
|
|
9613
9749
|
"data-shop-size": true,
|
|
9614
9750
|
role: "img",
|
|
9615
9751
|
"aria-label": t("sizeLabel", { size }),
|
|
@@ -9617,7 +9753,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9617
9753
|
children: size
|
|
9618
9754
|
}),
|
|
9619
9755
|
author !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9620
|
-
className:
|
|
9756
|
+
className: _dsh_plugin_shop_css_5f753521_default.author,
|
|
9621
9757
|
"data-shop-author": true,
|
|
9622
9758
|
children: t("authorLine", { author })
|
|
9623
9759
|
})
|
|
@@ -9667,13 +9803,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9667
9803
|
...attribute,
|
|
9668
9804
|
children: blocker.text
|
|
9669
9805
|
}), blocker.remedy !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9670
|
-
className:
|
|
9806
|
+
className: _dsh_plugin_shop_css_5f753521_default.remedy,
|
|
9671
9807
|
"data-shop-remedy": blocker.kind,
|
|
9672
9808
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9673
|
-
className:
|
|
9809
|
+
className: _dsh_plugin_shop_css_5f753521_default.remedyLead,
|
|
9674
9810
|
children: blocker.remedy.lead
|
|
9675
9811
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
9676
|
-
className:
|
|
9812
|
+
className: _dsh_plugin_shop_css_5f753521_default.allowCommand,
|
|
9677
9813
|
"data-shop-allow-command": true,
|
|
9678
9814
|
children: blocker.remedy.command
|
|
9679
9815
|
})]
|
|
@@ -9711,7 +9847,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9711
9847
|
if (lead === void 0 || word === null) return null;
|
|
9712
9848
|
const detail = blockers.map((blocker) => blocker.text).join("\n");
|
|
9713
9849
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9714
|
-
className:
|
|
9850
|
+
className: _dsh_plugin_shop_css_5f753521_default.incompatibleBadge,
|
|
9715
9851
|
"data-shop-blocker": lead.kind,
|
|
9716
9852
|
role: "img",
|
|
9717
9853
|
"aria-label": detail,
|
|
@@ -9719,46 +9855,53 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9719
9855
|
children: t(word)
|
|
9720
9856
|
});
|
|
9721
9857
|
}
|
|
9722
|
-
/** One entry's install flow: the button
|
|
9723
|
-
*
|
|
9724
|
-
*
|
|
9725
|
-
*
|
|
9726
|
-
*
|
|
9858
|
+
/** One entry's install flow: the button (or the refused LABEL that stands in
|
|
9859
|
+
* its place), and the live view — running log, restart notice, failure detail,
|
|
9860
|
+
* rejection detail — driven by `useInstallFlows`. Shared by the catalog cards
|
|
9861
|
+
* (`variant: 'install'`) and the outdated rows' update button (`variant:
|
|
9862
|
+
* 'update'`, which drives the same install flow for `name@latest`).
|
|
9863
|
+
*
|
|
9864
|
+
* There is no §9.3 dialog. Until 2026-10-14 a community entry opened an
|
|
9865
|
+
* acknowledgement gate first, and that gate was also the only thing standing
|
|
9866
|
+
* between a second press and a second RPC — it reopened instead of asking
|
|
9867
|
+
* again. What replaced it is one click that carries `acknowledged: true`, the
|
|
9868
|
+
* disclosure standing on the page where a reader cannot dismiss it unread, and
|
|
9869
|
+
* the in-flight guard in `useKeyedFlows`, which is what makes the missing
|
|
9870
|
+
* dialog safe. */
|
|
9727
9871
|
function InstallPanel({ target, tier, blockers, blockersStated = false, variant = "install", flow, t, restart, restartBlocked, reload }) {
|
|
9728
|
-
const
|
|
9729
|
-
const { view, start } = flow;
|
|
9872
|
+
const { view, start, starting } = flow;
|
|
9730
9873
|
if (view.kind === "running") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9731
|
-
className:
|
|
9874
|
+
className: _dsh_plugin_shop_css_5f753521_default.installPanel,
|
|
9732
9875
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9733
|
-
className:
|
|
9876
|
+
className: _dsh_plugin_shop_css_5f753521_default.installing,
|
|
9734
9877
|
children: t(installPhaseKey(view.phase))
|
|
9735
9878
|
}), view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9736
|
-
className:
|
|
9879
|
+
className: _dsh_plugin_shop_css_5f753521_default.log,
|
|
9737
9880
|
children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9738
|
-
className:
|
|
9881
|
+
className: _dsh_plugin_shop_css_5f753521_default.logLine,
|
|
9739
9882
|
"data-shop-log-line": true,
|
|
9740
9883
|
children: line
|
|
9741
9884
|
}, index))
|
|
9742
9885
|
})]
|
|
9743
9886
|
});
|
|
9744
9887
|
if (view.kind === "done") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9745
|
-
className:
|
|
9888
|
+
className: _dsh_plugin_shop_css_5f753521_default.installedActions,
|
|
9746
9889
|
children: [
|
|
9747
9890
|
view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9748
|
-
className:
|
|
9891
|
+
className: _dsh_plugin_shop_css_5f753521_default.log,
|
|
9749
9892
|
children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9750
|
-
className:
|
|
9893
|
+
className: _dsh_plugin_shop_css_5f753521_default.logLine,
|
|
9751
9894
|
"data-shop-log-line": true,
|
|
9752
9895
|
children: line
|
|
9753
9896
|
}, index))
|
|
9754
9897
|
}),
|
|
9755
9898
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9756
|
-
className:
|
|
9899
|
+
className: _dsh_plugin_shop_css_5f753521_default.notice,
|
|
9757
9900
|
"data-shop-restart-notice": true,
|
|
9758
9901
|
children: t(activationNoticeKey(view.activation, view.restartReason))
|
|
9759
9902
|
}),
|
|
9760
9903
|
view.detail !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9761
|
-
className:
|
|
9904
|
+
className: _dsh_plugin_shop_css_5f753521_default.notice,
|
|
9762
9905
|
"data-shop-done-note": true,
|
|
9763
9906
|
children: view.detail
|
|
9764
9907
|
}),
|
|
@@ -9773,103 +9916,81 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9773
9916
|
]
|
|
9774
9917
|
});
|
|
9775
9918
|
if (view.kind === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9776
|
-
className:
|
|
9919
|
+
className: _dsh_plugin_shop_css_5f753521_default.installPanel,
|
|
9777
9920
|
children: [
|
|
9778
9921
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9779
|
-
className:
|
|
9922
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedHeading,
|
|
9780
9923
|
children: t("installFailed")
|
|
9781
9924
|
}),
|
|
9782
9925
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9783
|
-
className:
|
|
9926
|
+
className: _dsh_plugin_shop_css_5f753521_default.log,
|
|
9784
9927
|
children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9785
|
-
className:
|
|
9928
|
+
className: _dsh_plugin_shop_css_5f753521_default.logLine,
|
|
9786
9929
|
"data-shop-log-line": true,
|
|
9787
9930
|
children: line
|
|
9788
9931
|
}, index))
|
|
9789
9932
|
}),
|
|
9790
9933
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9791
|
-
className:
|
|
9934
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedDetail,
|
|
9792
9935
|
children: view.detail === "" ? t("installTransportFailed") : view.detail
|
|
9793
9936
|
})
|
|
9794
9937
|
]
|
|
9795
9938
|
});
|
|
9796
9939
|
if (view.kind === "rejected") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9797
|
-
className:
|
|
9940
|
+
className: _dsh_plugin_shop_css_5f753521_default.installPanel,
|
|
9798
9941
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9799
|
-
className:
|
|
9942
|
+
className: _dsh_plugin_shop_css_5f753521_default.rejectedCode,
|
|
9800
9943
|
children: t(rejectionCodeKey(view.code))
|
|
9801
9944
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9802
|
-
className:
|
|
9945
|
+
className: _dsh_plugin_shop_css_5f753521_default.rejectedDetail,
|
|
9803
9946
|
children: view.detail
|
|
9804
9947
|
})]
|
|
9805
9948
|
});
|
|
9806
|
-
if (gateOpen) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9807
|
-
className: _dsh_plugin_shop_css_d74581c2_default.gate,
|
|
9808
|
-
children: [
|
|
9809
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9810
|
-
className: _dsh_plugin_shop_css_d74581c2_default.gateTitle,
|
|
9811
|
-
children: t("acknowledgementTitle")
|
|
9812
|
-
}),
|
|
9813
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9814
|
-
className: _dsh_plugin_shop_css_d74581c2_default.gateBody,
|
|
9815
|
-
children: t("acknowledgementBody")
|
|
9816
|
-
}),
|
|
9817
|
-
!blockersStated && blockers.map((blocker) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockerLine, {
|
|
9818
|
-
blocker,
|
|
9819
|
-
className: _dsh_plugin_shop_css_d74581c2_default.gateWarning,
|
|
9820
|
-
surface: "warning"
|
|
9821
|
-
}, blocker.kind)),
|
|
9822
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9823
|
-
className: _dsh_plugin_shop_css_d74581c2_default.gateActions,
|
|
9824
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
9825
|
-
type: "button",
|
|
9826
|
-
className: _dsh_plugin_shop_css_d74581c2_default.confirmButton,
|
|
9827
|
-
"data-shop-confirm": true,
|
|
9828
|
-
onClick: () => {
|
|
9829
|
-
setGateOpen(false);
|
|
9830
|
-
start({
|
|
9831
|
-
...target,
|
|
9832
|
-
acknowledged: true
|
|
9833
|
-
});
|
|
9834
|
-
},
|
|
9835
|
-
children: t("confirm")
|
|
9836
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
9837
|
-
type: "button",
|
|
9838
|
-
className: _dsh_plugin_shop_css_d74581c2_default.cancelButton,
|
|
9839
|
-
onClick: () => setGateOpen(false),
|
|
9840
|
-
children: t("cancel")
|
|
9841
|
-
})]
|
|
9842
|
-
})
|
|
9843
|
-
]
|
|
9844
|
-
});
|
|
9845
9949
|
const update = variant === "update";
|
|
9846
9950
|
const refused = refusesInstall(blockers);
|
|
9951
|
+
const word = blockerBadgeKey(blockers);
|
|
9952
|
+
const detail = blockers.map((blocker) => blocker.text).join("\n");
|
|
9953
|
+
if (refused && word !== null) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9954
|
+
className: _dsh_plugin_shop_css_5f753521_default.refusedLabel,
|
|
9955
|
+
"data-shop-refused": true,
|
|
9956
|
+
"data-shop-blocker-kind": blockers[0]?.kind,
|
|
9957
|
+
role: "img",
|
|
9958
|
+
"aria-label": detail,
|
|
9959
|
+
title: detail,
|
|
9960
|
+
children: t(word)
|
|
9961
|
+
}), !blockersStated && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FoldableRedBlock, {
|
|
9962
|
+
t,
|
|
9963
|
+
attrName: "data-shop-refusal",
|
|
9964
|
+
children: blockers.map((blocker) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockerLine, {
|
|
9965
|
+
blocker,
|
|
9966
|
+
className: _dsh_plugin_shop_css_5f753521_default.gateWarning,
|
|
9967
|
+
surface: "warning"
|
|
9968
|
+
}, blocker.kind))
|
|
9969
|
+
})] });
|
|
9847
9970
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
9848
9971
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
9849
9972
|
type: "button",
|
|
9850
|
-
className:
|
|
9851
|
-
disabled:
|
|
9852
|
-
...refused ? { "data-shop-refused": true } : {},
|
|
9973
|
+
className: _dsh_plugin_shop_css_5f753521_default.installButton,
|
|
9974
|
+
disabled: starting,
|
|
9853
9975
|
...update ? { "data-shop-update": true } : { "data-shop-install": true },
|
|
9854
9976
|
onClick: () => {
|
|
9855
|
-
|
|
9977
|
+
start({
|
|
9856
9978
|
...target,
|
|
9857
|
-
acknowledged: void 0
|
|
9979
|
+
acknowledged: tier === "verified" ? void 0 : true
|
|
9858
9980
|
});
|
|
9859
|
-
else setGateOpen(true);
|
|
9860
9981
|
},
|
|
9861
9982
|
children: t(update ? "update" : "install")
|
|
9862
9983
|
}),
|
|
9863
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockerBadge, {
|
|
9984
|
+
blockers.length > 0 && !refused && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockerBadge, {
|
|
9864
9985
|
blockers,
|
|
9865
9986
|
t
|
|
9866
9987
|
}),
|
|
9867
|
-
|
|
9868
|
-
|
|
9869
|
-
"data-shop-
|
|
9988
|
+
!blockersStated && blockers.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FoldableRedBlock, {
|
|
9989
|
+
t,
|
|
9990
|
+
attrName: "data-shop-blocker-lines",
|
|
9870
9991
|
children: blockers.map((blocker) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockerLine, {
|
|
9871
9992
|
blocker,
|
|
9872
|
-
className:
|
|
9993
|
+
className: _dsh_plugin_shop_css_5f753521_default.gateWarning,
|
|
9873
9994
|
surface: "warning"
|
|
9874
9995
|
}, blocker.kind))
|
|
9875
9996
|
})
|
|
@@ -9884,32 +10005,32 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9884
10005
|
function UninstallPanel({ name, t, restart, restartBlocked, reload, flow }) {
|
|
9885
10006
|
const { view, start, reset } = flow;
|
|
9886
10007
|
if (view.kind === "running") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9887
|
-
className:
|
|
10008
|
+
className: _dsh_plugin_shop_css_5f753521_default.installPanel,
|
|
9888
10009
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9889
|
-
className:
|
|
10010
|
+
className: _dsh_plugin_shop_css_5f753521_default.installing,
|
|
9890
10011
|
children: t("uninstalling")
|
|
9891
10012
|
}), view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9892
|
-
className:
|
|
10013
|
+
className: _dsh_plugin_shop_css_5f753521_default.log,
|
|
9893
10014
|
children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9894
|
-
className:
|
|
10015
|
+
className: _dsh_plugin_shop_css_5f753521_default.logLine,
|
|
9895
10016
|
"data-shop-log-line": true,
|
|
9896
10017
|
children: line
|
|
9897
10018
|
}, index))
|
|
9898
10019
|
})]
|
|
9899
10020
|
});
|
|
9900
10021
|
if (view.kind === "done") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9901
|
-
className:
|
|
10022
|
+
className: _dsh_plugin_shop_css_5f753521_default.installedActions,
|
|
9902
10023
|
children: [
|
|
9903
10024
|
view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9904
|
-
className:
|
|
10025
|
+
className: _dsh_plugin_shop_css_5f753521_default.log,
|
|
9905
10026
|
children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9906
|
-
className:
|
|
10027
|
+
className: _dsh_plugin_shop_css_5f753521_default.logLine,
|
|
9907
10028
|
"data-shop-log-line": true,
|
|
9908
10029
|
children: line
|
|
9909
10030
|
}, index))
|
|
9910
10031
|
}),
|
|
9911
10032
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9912
|
-
className:
|
|
10033
|
+
className: _dsh_plugin_shop_css_5f753521_default.notice,
|
|
9913
10034
|
"data-shop-uninstall-done": true,
|
|
9914
10035
|
children: t(uninstallActivationNoticeKey(view.activation))
|
|
9915
10036
|
}),
|
|
@@ -9923,7 +10044,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9923
10044
|
}),
|
|
9924
10045
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
9925
10046
|
type: "button",
|
|
9926
|
-
className:
|
|
10047
|
+
className: _dsh_plugin_shop_css_5f753521_default.cancelButton,
|
|
9927
10048
|
"data-shop-uninstall-dismiss": true,
|
|
9928
10049
|
onClick: reset,
|
|
9929
10050
|
children: t("dismiss")
|
|
@@ -9931,27 +10052,27 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9931
10052
|
]
|
|
9932
10053
|
});
|
|
9933
10054
|
if (view.kind === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9934
|
-
className:
|
|
10055
|
+
className: _dsh_plugin_shop_css_5f753521_default.installPanel,
|
|
9935
10056
|
children: [
|
|
9936
10057
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9937
|
-
className:
|
|
10058
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedHeading,
|
|
9938
10059
|
children: t("uninstallFailed")
|
|
9939
10060
|
}),
|
|
9940
10061
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9941
|
-
className:
|
|
10062
|
+
className: _dsh_plugin_shop_css_5f753521_default.log,
|
|
9942
10063
|
children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9943
|
-
className:
|
|
10064
|
+
className: _dsh_plugin_shop_css_5f753521_default.logLine,
|
|
9944
10065
|
"data-shop-log-line": true,
|
|
9945
10066
|
children: line
|
|
9946
10067
|
}, index))
|
|
9947
10068
|
}),
|
|
9948
10069
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9949
|
-
className:
|
|
10070
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedDetail,
|
|
9950
10071
|
children: view.detail === "" ? t("uninstallTransportFailed") : view.detail
|
|
9951
10072
|
}),
|
|
9952
10073
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
9953
10074
|
type: "button",
|
|
9954
|
-
className:
|
|
10075
|
+
className: _dsh_plugin_shop_css_5f753521_default.uninstallButton,
|
|
9955
10076
|
"data-shop-uninstall-retry": true,
|
|
9956
10077
|
onClick: () => void start({ name }),
|
|
9957
10078
|
children: t("retry")
|
|
@@ -9961,7 +10082,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9961
10082
|
if (view.kind !== "idle") return null;
|
|
9962
10083
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
9963
10084
|
type: "button",
|
|
9964
|
-
className:
|
|
10085
|
+
className: _dsh_plugin_shop_css_5f753521_default.uninstallButton,
|
|
9965
10086
|
"data-shop-uninstall": true,
|
|
9966
10087
|
onClick: () => void start({ name }),
|
|
9967
10088
|
children: t("uninstall")
|
|
@@ -10002,38 +10123,39 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10002
10123
|
else if (outcome.kind === "unreachable") setRefusal(t("restartTransportFailed"));
|
|
10003
10124
|
};
|
|
10004
10125
|
if (monitor?.kind === "requesting" || monitor?.kind === "restarting") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10005
|
-
className:
|
|
10126
|
+
className: _dsh_plugin_shop_css_5f753521_default.notice,
|
|
10006
10127
|
"data-shop-restarting": true,
|
|
10007
10128
|
children: t("restarting")
|
|
10008
10129
|
});
|
|
10009
10130
|
if (monitor?.kind === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10010
|
-
className:
|
|
10131
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedDetail,
|
|
10011
10132
|
"data-shop-restart-error": true,
|
|
10012
10133
|
children: monitor.logFile === void 0 ? t("restartFailedNotice") : t("restartFailedLogNotice", { log: monitor.logFile })
|
|
10013
10134
|
});
|
|
10014
10135
|
if (monitor?.kind === "not-restarted") return null;
|
|
10015
10136
|
if (gateOpen) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10016
|
-
className:
|
|
10137
|
+
className: _dsh_plugin_shop_css_5f753521_default.gate,
|
|
10138
|
+
"data-shop-restart-gate": true,
|
|
10017
10139
|
children: [
|
|
10018
10140
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10019
|
-
className:
|
|
10141
|
+
className: _dsh_plugin_shop_css_5f753521_default.gateTitle,
|
|
10020
10142
|
children: t("restartTitle")
|
|
10021
10143
|
}),
|
|
10022
10144
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10023
|
-
className:
|
|
10145
|
+
className: _dsh_plugin_shop_css_5f753521_default.gateBody,
|
|
10024
10146
|
children: t("restartBody")
|
|
10025
10147
|
}),
|
|
10026
10148
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10027
|
-
className:
|
|
10149
|
+
className: _dsh_plugin_shop_css_5f753521_default.gateActions,
|
|
10028
10150
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
10029
10151
|
type: "button",
|
|
10030
|
-
className:
|
|
10152
|
+
className: _dsh_plugin_shop_css_5f753521_default.confirmButton,
|
|
10031
10153
|
"data-shop-restart-confirm": true,
|
|
10032
10154
|
onClick: () => void onConfirm(),
|
|
10033
10155
|
children: t("restartConfirm")
|
|
10034
10156
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
10035
10157
|
type: "button",
|
|
10036
|
-
className:
|
|
10158
|
+
className: _dsh_plugin_shop_css_5f753521_default.cancelButton,
|
|
10037
10159
|
onClick: () => setGateOpen(false),
|
|
10038
10160
|
children: t("cancel")
|
|
10039
10161
|
})]
|
|
@@ -10041,14 +10163,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10041
10163
|
]
|
|
10042
10164
|
});
|
|
10043
10165
|
const refused = refusal === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10044
|
-
className:
|
|
10166
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedDetail,
|
|
10045
10167
|
"data-shop-restart-error": true,
|
|
10046
10168
|
children: refusal
|
|
10047
10169
|
});
|
|
10048
10170
|
if (gate !== void 0) return refused;
|
|
10049
10171
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [refused, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
10050
10172
|
type: "button",
|
|
10051
|
-
className:
|
|
10173
|
+
className: _dsh_plugin_shop_css_5f753521_default.restartButton,
|
|
10052
10174
|
"data-shop-restart": true,
|
|
10053
10175
|
onClick: () => setGateOpen(true),
|
|
10054
10176
|
children: t("restart")
|
|
@@ -10066,13 +10188,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10066
10188
|
*/
|
|
10067
10189
|
function NotRestartedNotice({ t, pid, logFile }) {
|
|
10068
10190
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10069
|
-
className:
|
|
10191
|
+
className: _dsh_plugin_shop_css_5f753521_default.selfUpdatePanel,
|
|
10070
10192
|
"data-shop-not-restarted": true,
|
|
10071
10193
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10072
|
-
className:
|
|
10194
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedDetail,
|
|
10073
10195
|
children: pid === void 0 ? t("notRestartedNotice") : t("notRestartedPidNotice", { pid: String(pid) })
|
|
10074
10196
|
}), logFile !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10075
|
-
className:
|
|
10197
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedDetail,
|
|
10076
10198
|
children: t("notRestartedLogNotice", { log: logFile })
|
|
10077
10199
|
})]
|
|
10078
10200
|
});
|
|
@@ -10102,7 +10224,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10102
10224
|
restart,
|
|
10103
10225
|
reload
|
|
10104
10226
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10105
|
-
className:
|
|
10227
|
+
className: _dsh_plugin_shop_css_5f753521_default.notice,
|
|
10106
10228
|
"data-shop-restart-disabled": true,
|
|
10107
10229
|
children: t(restartBlockedNoticeKey(restartBlocked))
|
|
10108
10230
|
});
|
|
@@ -10113,14 +10235,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10113
10235
|
* would help without knowing that now is a good time. */
|
|
10114
10236
|
function ReloadPanel({ t, reload, where }) {
|
|
10115
10237
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10116
|
-
className:
|
|
10238
|
+
className: _dsh_plugin_shop_css_5f753521_default.reloadPanel,
|
|
10117
10239
|
"data-shop-reload": where,
|
|
10118
10240
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10119
|
-
className:
|
|
10241
|
+
className: _dsh_plugin_shop_css_5f753521_default.notice,
|
|
10120
10242
|
children: t("reloadNote")
|
|
10121
10243
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
10122
10244
|
type: "button",
|
|
10123
|
-
className:
|
|
10245
|
+
className: _dsh_plugin_shop_css_5f753521_default.reloadButton,
|
|
10124
10246
|
onClick: reload,
|
|
10125
10247
|
children: t("reload")
|
|
10126
10248
|
})]
|
|
@@ -10171,7 +10293,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10171
10293
|
}
|
|
10172
10294
|
};
|
|
10173
10295
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10174
|
-
className:
|
|
10296
|
+
className: _dsh_plugin_shop_css_5f753521_default.switchWrap,
|
|
10175
10297
|
"data-shop-enabled-switch": row.name,
|
|
10176
10298
|
children: [
|
|
10177
10299
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -10180,18 +10302,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10180
10302
|
"aria-checked": enabled,
|
|
10181
10303
|
"aria-label": t("enabledSwitch"),
|
|
10182
10304
|
"data-shop-toggle": true,
|
|
10183
|
-
className: `${
|
|
10305
|
+
className: `${_dsh_plugin_shop_css_5f753521_default.switch} ${enabled ? _dsh_plugin_shop_css_5f753521_default.switchOn : ""}`,
|
|
10184
10306
|
onClick: () => void onToggle(),
|
|
10185
10307
|
disabled: toggle.kind === "saving",
|
|
10186
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className:
|
|
10308
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_5f753521_default.switchKnob })
|
|
10187
10309
|
}),
|
|
10188
10310
|
toggle.kind === "saved" && owed === "nothing" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10189
|
-
className:
|
|
10311
|
+
className: _dsh_plugin_shop_css_5f753521_default.notice,
|
|
10190
10312
|
"data-shop-hot-apply": true,
|
|
10191
10313
|
children: t("hotApplyNote")
|
|
10192
10314
|
}),
|
|
10193
10315
|
owed === "restart" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10194
|
-
className:
|
|
10316
|
+
className: _dsh_plugin_shop_css_5f753521_default.notice,
|
|
10195
10317
|
"data-shop-toggle-restart": true,
|
|
10196
10318
|
children: t("toggleRestartNote")
|
|
10197
10319
|
}),
|
|
@@ -10204,7 +10326,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10204
10326
|
where: "toggle"
|
|
10205
10327
|
}),
|
|
10206
10328
|
toggle.kind === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10207
|
-
className:
|
|
10329
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedDetail,
|
|
10208
10330
|
"data-shop-toggle-error": true,
|
|
10209
10331
|
children: toggle.detail
|
|
10210
10332
|
})
|
|
@@ -10219,19 +10341,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10219
10341
|
* field is narrowed at the parameter. */
|
|
10220
10342
|
function OutdatedRow({ row, tier, missing, harness, t, setEnabled, flowFor, restart, restartBlocked, reload, restarts }) {
|
|
10221
10343
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10222
|
-
className:
|
|
10344
|
+
className: _dsh_plugin_shop_css_5f753521_default.outdatedRow,
|
|
10223
10345
|
"data-shop-outdated-entry": row.name,
|
|
10224
10346
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10225
|
-
className:
|
|
10347
|
+
className: _dsh_plugin_shop_css_5f753521_default.outdatedInfo,
|
|
10226
10348
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10227
|
-
className:
|
|
10349
|
+
className: _dsh_plugin_shop_css_5f753521_default.name,
|
|
10228
10350
|
children: row.name
|
|
10229
10351
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
10230
|
-
className:
|
|
10352
|
+
className: _dsh_plugin_shop_css_5f753521_default.outdatedVersions,
|
|
10231
10353
|
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 }) })]
|
|
10232
10354
|
})]
|
|
10233
10355
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10234
|
-
className:
|
|
10356
|
+
className: _dsh_plugin_shop_css_5f753521_default.outdatedActions,
|
|
10235
10357
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(EnabledSwitch, {
|
|
10236
10358
|
row,
|
|
10237
10359
|
t,
|
|
@@ -10274,28 +10396,30 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10274
10396
|
* "Installed" until 2026-09-16, which was the Installed filter's label
|
|
10275
10397
|
* character for character in both languages, over a strict subset of what
|
|
10276
10398
|
* that filter selects. Internals stay `outdated` — the Host field name. The
|
|
10277
|
-
* tier
|
|
10278
|
-
*
|
|
10279
|
-
*
|
|
10399
|
+
* tier is looked up from the catalog by install IDENTITY, never by name, and
|
|
10400
|
+
* decides whether the update's request carries the acknowledgement (a
|
|
10401
|
+
* community entry's does); an entry absent from the catalog is read as
|
|
10402
|
+
* community, so its request acknowledges, which the host accepts for any
|
|
10403
|
+
* tier. */
|
|
10280
10404
|
function OutdatedSection({ state, entriesByKey, missingByKey, harnessVerdicts, t, setEnabled, flowFor, restart, restartBlocked, reload, restarts }) {
|
|
10281
10405
|
if (state.kind === "loading") return null;
|
|
10282
10406
|
if (state.kind === "error") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10283
|
-
className:
|
|
10407
|
+
className: _dsh_plugin_shop_css_5f753521_default.stateLine,
|
|
10284
10408
|
"data-shop-outdated-error": true,
|
|
10285
10409
|
children: t("installedError")
|
|
10286
10410
|
});
|
|
10287
10411
|
const outdated = state.entries.filter((entry) => entry.outdated && entry.latest !== null);
|
|
10288
10412
|
if (outdated.length === 0) return null;
|
|
10289
10413
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
10290
|
-
className:
|
|
10414
|
+
className: _dsh_plugin_shop_css_5f753521_default.outdatedSection,
|
|
10291
10415
|
"data-shop-outdated": true,
|
|
10292
10416
|
"aria-labelledby": UPDATABLE_HEADING_ID,
|
|
10293
10417
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
10294
|
-
className:
|
|
10418
|
+
className: _dsh_plugin_shop_css_5f753521_default.updatableHeading,
|
|
10295
10419
|
id: UPDATABLE_HEADING_ID,
|
|
10296
10420
|
children: t("updatableSection")
|
|
10297
10421
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
10298
|
-
className:
|
|
10422
|
+
className: _dsh_plugin_shop_css_5f753521_default.outdatedList,
|
|
10299
10423
|
children: outdated.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OutdatedRow, {
|
|
10300
10424
|
row,
|
|
10301
10425
|
tier: entriesByKey.get(identityKey(row))?.tier ?? "community",
|
|
@@ -10323,37 +10447,37 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10323
10447
|
const shadowed = entries.filter((entry) => entry.shadowed === true);
|
|
10324
10448
|
if (shadowed.length === 0) return null;
|
|
10325
10449
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
|
|
10326
|
-
className:
|
|
10450
|
+
className: _dsh_plugin_shop_css_5f753521_default.shadowedSection,
|
|
10327
10451
|
"data-shop-shadowed": true,
|
|
10328
10452
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
10329
|
-
className:
|
|
10453
|
+
className: _dsh_plugin_shop_css_5f753521_default.outdatedList,
|
|
10330
10454
|
children: shadowed.map((row) => {
|
|
10331
10455
|
const key = identityKey(row);
|
|
10332
10456
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10333
|
-
className:
|
|
10457
|
+
className: _dsh_plugin_shop_css_5f753521_default.outdatedRow,
|
|
10334
10458
|
"data-shop-shadowed-entry": row.name,
|
|
10335
10459
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10336
|
-
className:
|
|
10460
|
+
className: _dsh_plugin_shop_css_5f753521_default.outdatedInfo,
|
|
10337
10461
|
children: [
|
|
10338
10462
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10339
|
-
className:
|
|
10463
|
+
className: _dsh_plugin_shop_css_5f753521_default.name,
|
|
10340
10464
|
children: row.name
|
|
10341
10465
|
}),
|
|
10342
10466
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10343
|
-
className:
|
|
10467
|
+
className: _dsh_plugin_shop_css_5f753521_default.badges,
|
|
10344
10468
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10345
|
-
className:
|
|
10469
|
+
className: _dsh_plugin_shop_css_5f753521_default.tierBadge,
|
|
10346
10470
|
"data-tier": "shadowed",
|
|
10347
10471
|
children: t("shadowedBadge")
|
|
10348
10472
|
})
|
|
10349
10473
|
}),
|
|
10350
10474
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10351
|
-
className:
|
|
10475
|
+
className: _dsh_plugin_shop_css_5f753521_default.shadowedDetail,
|
|
10352
10476
|
children: t("shadowedDetail")
|
|
10353
10477
|
})
|
|
10354
10478
|
]
|
|
10355
10479
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10356
|
-
className:
|
|
10480
|
+
className: _dsh_plugin_shop_css_5f753521_default.outdatedActions,
|
|
10357
10481
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UninstallPanel, {
|
|
10358
10482
|
name: row.name,
|
|
10359
10483
|
t,
|
|
@@ -10403,16 +10527,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10403
10527
|
role: "alert",
|
|
10404
10528
|
children: [
|
|
10405
10529
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10406
|
-
className:
|
|
10530
|
+
className: _dsh_plugin_shop_css_5f753521_default.stateLine,
|
|
10407
10531
|
children: say("tabCrashed", en.tabCrashed)
|
|
10408
10532
|
}),
|
|
10409
10533
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10410
|
-
className:
|
|
10534
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedDetail,
|
|
10411
10535
|
children: error.message
|
|
10412
10536
|
}),
|
|
10413
10537
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
10414
10538
|
type: "button",
|
|
10415
|
-
className:
|
|
10539
|
+
className: _dsh_plugin_shop_css_5f753521_default.actionButton,
|
|
10416
10540
|
onClick: () => this.setState({ error: null }),
|
|
10417
10541
|
children: say("retry", en.retry)
|
|
10418
10542
|
})
|
|
@@ -10709,19 +10833,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10709
10833
|
"data-shop-tab": true,
|
|
10710
10834
|
"aria-busy": "true",
|
|
10711
10835
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10712
|
-
className:
|
|
10836
|
+
className: _dsh_plugin_shop_css_5f753521_default.srOnly,
|
|
10713
10837
|
children: t("loading")
|
|
10714
10838
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10715
|
-
className:
|
|
10839
|
+
className: _dsh_plugin_shop_css_5f753521_default.skeletonGrid,
|
|
10716
10840
|
"data-shop-skeleton": true,
|
|
10717
10841
|
"aria-hidden": "true",
|
|
10718
10842
|
children: Array.from({ length: 8 }, (_, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10719
|
-
className:
|
|
10843
|
+
className: _dsh_plugin_shop_css_5f753521_default.skeletonCard,
|
|
10720
10844
|
children: [
|
|
10721
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className:
|
|
10722
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className:
|
|
10723
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className:
|
|
10724
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className:
|
|
10845
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_5f753521_default.skeletonName }),
|
|
10846
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_5f753521_default.skeletonSummary }),
|
|
10847
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_5f753521_default.skeletonSummaryShort }),
|
|
10848
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_5f753521_default.skeletonActions })
|
|
10725
10849
|
]
|
|
10726
10850
|
}, i))
|
|
10727
10851
|
})]
|
|
@@ -10731,11 +10855,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10731
10855
|
translate: "no",
|
|
10732
10856
|
"data-shop-tab": true,
|
|
10733
10857
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10734
|
-
className:
|
|
10858
|
+
className: _dsh_plugin_shop_css_5f753521_default.stateLine,
|
|
10735
10859
|
children: t("error")
|
|
10736
10860
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
10737
10861
|
type: "button",
|
|
10738
|
-
className:
|
|
10862
|
+
className: _dsh_plugin_shop_css_5f753521_default.actionButton,
|
|
10739
10863
|
onClick: () => {
|
|
10740
10864
|
setRequest({ kind: "retry" });
|
|
10741
10865
|
setVersionReload((current) => current + 1);
|
|
@@ -10750,11 +10874,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10750
10874
|
"data-shop-tab": true,
|
|
10751
10875
|
children: [
|
|
10752
10876
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10753
|
-
className:
|
|
10877
|
+
className: _dsh_plugin_shop_css_5f753521_default.toolbar,
|
|
10754
10878
|
children: [
|
|
10755
10879
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
10756
10880
|
type: "search",
|
|
10757
|
-
className:
|
|
10881
|
+
className: _dsh_plugin_shop_css_5f753521_default.searchInput,
|
|
10758
10882
|
"aria-label": t("search"),
|
|
10759
10883
|
placeholder: t("search"),
|
|
10760
10884
|
value: query,
|
|
@@ -10765,31 +10889,31 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10765
10889
|
}),
|
|
10766
10890
|
expansionMemo.intents.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10767
10891
|
"data-shop-expansion-note": true,
|
|
10768
|
-
className:
|
|
10892
|
+
className: _dsh_plugin_shop_css_5f753521_default.expansionNote,
|
|
10769
10893
|
children: t("zhExpansionNote", { terms: expansionMemo.terms.slice(1).map((term) => term.startsWith("#") ? term.slice(1) : term).join(", ") })
|
|
10770
10894
|
}),
|
|
10771
10895
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10772
|
-
className:
|
|
10896
|
+
className: _dsh_plugin_shop_css_5f753521_default.toolbarRight,
|
|
10773
10897
|
children: [
|
|
10774
10898
|
result.stale && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10775
|
-
className:
|
|
10899
|
+
className: _dsh_plugin_shop_css_5f753521_default.staleBadge,
|
|
10776
10900
|
children: t("staleLabel", { date: result.builtAt.slice(0, 10) })
|
|
10777
10901
|
}),
|
|
10778
10902
|
selfVersion !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10779
|
-
className:
|
|
10903
|
+
className: _dsh_plugin_shop_css_5f753521_default.versionBlock,
|
|
10780
10904
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
10781
|
-
className:
|
|
10905
|
+
className: _dsh_plugin_shop_css_5f753521_default.versionText,
|
|
10782
10906
|
"data-shop-version": true,
|
|
10783
10907
|
children: ["v", selfVersion.installed]
|
|
10784
10908
|
}), restartMonitor !== null ? null : selfPending && restartBlocked === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
10785
10909
|
type: "button",
|
|
10786
|
-
className:
|
|
10910
|
+
className: _dsh_plugin_shop_css_5f753521_default.restartSelfButton,
|
|
10787
10911
|
"data-shop-restart": true,
|
|
10788
10912
|
onClick: () => setSelfRestartGate(true),
|
|
10789
10913
|
children: t("restart")
|
|
10790
10914
|
}) : !selfPending && selfVersion.outdated && selfVersion.latest !== null && selfUpdate.view.kind === "idle" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
10791
10915
|
type: "button",
|
|
10792
|
-
className:
|
|
10916
|
+
className: _dsh_plugin_shop_css_5f753521_default.updateSelfButton,
|
|
10793
10917
|
"data-shop-update-self": true,
|
|
10794
10918
|
onClick: () => {
|
|
10795
10919
|
if (selfVersion.latest !== null) selfUpdate.start({ version: selfVersion.latest });
|
|
@@ -10797,7 +10921,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10797
10921
|
children: t("update")
|
|
10798
10922
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
10799
10923
|
type: "button",
|
|
10800
|
-
className:
|
|
10924
|
+
className: _dsh_plugin_shop_css_5f753521_default.checkUpdateButton,
|
|
10801
10925
|
"data-shop-check-update": true,
|
|
10802
10926
|
title: t("checkUpdateTitle"),
|
|
10803
10927
|
disabled: checkState !== "idle",
|
|
@@ -10806,7 +10930,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10806
10930
|
})]
|
|
10807
10931
|
}),
|
|
10808
10932
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
10809
|
-
className:
|
|
10933
|
+
className: _dsh_plugin_shop_css_5f753521_default.githubLink,
|
|
10810
10934
|
href: SHOP_REPO_URL,
|
|
10811
10935
|
target: "_blank",
|
|
10812
10936
|
rel: "noopener noreferrer",
|
|
@@ -10831,28 +10955,28 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10831
10955
|
logFile: restartMonitor.logFile
|
|
10832
10956
|
}),
|
|
10833
10957
|
selfUpdate.view.kind === "running" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10834
|
-
className:
|
|
10958
|
+
className: _dsh_plugin_shop_css_5f753521_default.selfUpdatePanel,
|
|
10835
10959
|
"data-shop-self-updating": true,
|
|
10836
10960
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10837
|
-
className:
|
|
10961
|
+
className: _dsh_plugin_shop_css_5f753521_default.installing,
|
|
10838
10962
|
children: t(installPhaseKey(selfUpdate.view.phase))
|
|
10839
10963
|
}), selfUpdate.view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10840
|
-
className:
|
|
10964
|
+
className: _dsh_plugin_shop_css_5f753521_default.log,
|
|
10841
10965
|
children: selfUpdate.view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10842
|
-
className:
|
|
10966
|
+
className: _dsh_plugin_shop_css_5f753521_default.logLine,
|
|
10843
10967
|
"data-shop-log-line": true,
|
|
10844
10968
|
children: line
|
|
10845
10969
|
}, index))
|
|
10846
10970
|
})]
|
|
10847
10971
|
}),
|
|
10848
10972
|
selfPending && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10849
|
-
className:
|
|
10973
|
+
className: _dsh_plugin_shop_css_5f753521_default.selfUpdatePanel,
|
|
10850
10974
|
"data-shop-self-update-done": true,
|
|
10851
10975
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10852
|
-
className:
|
|
10976
|
+
className: _dsh_plugin_shop_css_5f753521_default.installedActions,
|
|
10853
10977
|
children: [
|
|
10854
10978
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10855
|
-
className:
|
|
10979
|
+
className: _dsh_plugin_shop_css_5f753521_default.notice,
|
|
10856
10980
|
children: t("installedRestartNotice")
|
|
10857
10981
|
}),
|
|
10858
10982
|
restartBlocked === null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RestartPanel, {
|
|
@@ -10865,7 +10989,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10865
10989
|
}
|
|
10866
10990
|
}),
|
|
10867
10991
|
restartBlocked !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10868
|
-
className:
|
|
10992
|
+
className: _dsh_plugin_shop_css_5f753521_default.notice,
|
|
10869
10993
|
"data-shop-restart-disabled": true,
|
|
10870
10994
|
children: t(restartBlockedNoticeKey(restartBlocked))
|
|
10871
10995
|
})
|
|
@@ -10873,35 +10997,35 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10873
10997
|
})
|
|
10874
10998
|
}),
|
|
10875
10999
|
selfUpdate.view.kind === "failed" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10876
|
-
className:
|
|
11000
|
+
className: _dsh_plugin_shop_css_5f753521_default.selfUpdatePanel,
|
|
10877
11001
|
"data-shop-self-update-failed": true,
|
|
10878
11002
|
children: [
|
|
10879
11003
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10880
|
-
className:
|
|
11004
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedHeading,
|
|
10881
11005
|
children: t("updateFailed")
|
|
10882
11006
|
}),
|
|
10883
11007
|
selfUpdate.view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10884
|
-
className:
|
|
11008
|
+
className: _dsh_plugin_shop_css_5f753521_default.log,
|
|
10885
11009
|
children: selfUpdate.view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10886
|
-
className:
|
|
11010
|
+
className: _dsh_plugin_shop_css_5f753521_default.logLine,
|
|
10887
11011
|
"data-shop-log-line": true,
|
|
10888
11012
|
children: line
|
|
10889
11013
|
}, index))
|
|
10890
11014
|
}),
|
|
10891
11015
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10892
|
-
className:
|
|
11016
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedDetail,
|
|
10893
11017
|
children: selfUpdate.view.detail === "" ? t("updateTransportFailed") : selfUpdate.view.detail
|
|
10894
11018
|
})
|
|
10895
11019
|
]
|
|
10896
11020
|
}),
|
|
10897
11021
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10898
|
-
className:
|
|
11022
|
+
className: _dsh_plugin_shop_css_5f753521_default.categoryBar,
|
|
10899
11023
|
role: "group",
|
|
10900
11024
|
"aria-label": t("catalog"),
|
|
10901
11025
|
children: [
|
|
10902
11026
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
10903
11027
|
type: "button",
|
|
10904
|
-
className: category === null ? `${
|
|
11028
|
+
className: category === null ? `${_dsh_plugin_shop_css_5f753521_default.categoryButton} ${_dsh_plugin_shop_css_5f753521_default.categoryButtonOn}` : _dsh_plugin_shop_css_5f753521_default.categoryButton,
|
|
10905
11029
|
"aria-pressed": category === null,
|
|
10906
11030
|
onClick: () => {
|
|
10907
11031
|
setCategory(null);
|
|
@@ -10916,7 +11040,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10916
11040
|
}),
|
|
10917
11041
|
CATEGORY_ORDER.map((key) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
10918
11042
|
type: "button",
|
|
10919
|
-
className: category === key ? `${
|
|
11043
|
+
className: category === key ? `${_dsh_plugin_shop_css_5f753521_default.categoryButton} ${_dsh_plugin_shop_css_5f753521_default.categoryButtonOn}` : _dsh_plugin_shop_css_5f753521_default.categoryButton,
|
|
10920
11044
|
"aria-pressed": category === key,
|
|
10921
11045
|
"data-category": key,
|
|
10922
11046
|
"data-shop-category-tab": key,
|
|
@@ -10932,7 +11056,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10932
11056
|
}, key)),
|
|
10933
11057
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
10934
11058
|
type: "button",
|
|
10935
|
-
className: category === "installed" ? `${
|
|
11059
|
+
className: category === "installed" ? `${_dsh_plugin_shop_css_5f753521_default.categoryButton} ${_dsh_plugin_shop_css_5f753521_default.categoryButtonOn}` : _dsh_plugin_shop_css_5f753521_default.categoryButton,
|
|
10936
11060
|
"aria-pressed": category === "installed",
|
|
10937
11061
|
"data-shop-category-installed": true,
|
|
10938
11062
|
onClick: () => {
|
|
@@ -10949,7 +11073,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10949
11073
|
type: "button",
|
|
10950
11074
|
role: "switch",
|
|
10951
11075
|
"aria-checked": hideIncompatible,
|
|
10952
|
-
className: hideIncompatible ? `${
|
|
11076
|
+
className: hideIncompatible ? `${_dsh_plugin_shop_css_5f753521_default.incompatibleFilter} ${_dsh_plugin_shop_css_5f753521_default.incompatibleFilterOn}` : _dsh_plugin_shop_css_5f753521_default.incompatibleFilter,
|
|
10953
11077
|
title: t(hideIncompatible ? "showIncompatibleTitle" : "incompatibleFilterTitle"),
|
|
10954
11078
|
"data-shop-hide-incompatible": true,
|
|
10955
11079
|
onClick: () => {
|
|
@@ -10957,18 +11081,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10957
11081
|
setVisibleCount(48);
|
|
10958
11082
|
},
|
|
10959
11083
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10960
|
-
className: hideIncompatible ? `${
|
|
11084
|
+
className: hideIncompatible ? `${_dsh_plugin_shop_css_5f753521_default.switch} ${_dsh_plugin_shop_css_5f753521_default.switchOn}` : _dsh_plugin_shop_css_5f753521_default.switch,
|
|
10961
11085
|
"aria-hidden": "true",
|
|
10962
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className:
|
|
11086
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_5f753521_default.switchKnob })
|
|
10963
11087
|
}), t("hideIncompatible", { count: incompatibleCount })]
|
|
10964
11088
|
})
|
|
10965
11089
|
]
|
|
10966
11090
|
}),
|
|
10967
11091
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10968
|
-
className:
|
|
11092
|
+
className: _dsh_plugin_shop_css_5f753521_default.catalogStatsRow,
|
|
10969
11093
|
children: [
|
|
10970
11094
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10971
|
-
className:
|
|
11095
|
+
className: _dsh_plugin_shop_css_5f753521_default.catalogStats,
|
|
10972
11096
|
"data-shop-catalog-stats": true,
|
|
10973
11097
|
children: t("catalogStats", {
|
|
10974
11098
|
count: String(browsable.length),
|
|
@@ -10977,7 +11101,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10977
11101
|
}),
|
|
10978
11102
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
10979
11103
|
type: "button",
|
|
10980
|
-
className:
|
|
11104
|
+
className: _dsh_plugin_shop_css_5f753521_default.catalogRefreshButton,
|
|
10981
11105
|
"data-shop-catalog-refresh": true,
|
|
10982
11106
|
disabled: reloading,
|
|
10983
11107
|
onClick: () => {
|
|
@@ -10989,22 +11113,22 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10989
11113
|
children: reloading ? t("refreshing") : t("refresh")
|
|
10990
11114
|
}),
|
|
10991
11115
|
reloadFailed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10992
|
-
className:
|
|
11116
|
+
className: _dsh_plugin_shop_css_5f753521_default.catalogRefreshFailed,
|
|
10993
11117
|
"data-shop-catalog-refresh-failed": true,
|
|
10994
11118
|
children: t("refreshFailed")
|
|
10995
11119
|
}),
|
|
10996
11120
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
10997
|
-
className:
|
|
11121
|
+
className: _dsh_plugin_shop_css_5f753521_default.sortGroup,
|
|
10998
11122
|
role: "group",
|
|
10999
11123
|
"aria-labelledby": sortLabelId,
|
|
11000
11124
|
"data-shop-sort": true,
|
|
11001
11125
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
11002
|
-
className:
|
|
11126
|
+
className: _dsh_plugin_shop_css_5f753521_default.sortLabel,
|
|
11003
11127
|
id: sortLabelId,
|
|
11004
11128
|
children: t("sortLabel")
|
|
11005
11129
|
}), SHELF_ORDERS.map((name) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
11006
11130
|
type: "button",
|
|
11007
|
-
className: order === name ? `${
|
|
11131
|
+
className: order === name ? `${_dsh_plugin_shop_css_5f753521_default.categoryButton} ${_dsh_plugin_shop_css_5f753521_default.categoryButtonOn}` : _dsh_plugin_shop_css_5f753521_default.categoryButton,
|
|
11008
11132
|
"aria-pressed": order === name,
|
|
11009
11133
|
"data-shop-order": name,
|
|
11010
11134
|
onClick: () => {
|
|
@@ -11016,15 +11140,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
11016
11140
|
})
|
|
11017
11141
|
]
|
|
11018
11142
|
}),
|
|
11143
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
11144
|
+
className: _dsh_plugin_shop_css_5f753521_default.disclosure,
|
|
11145
|
+
"data-shop-disclosure": true,
|
|
11146
|
+
children: t("disclosure")
|
|
11147
|
+
}),
|
|
11019
11148
|
result.plugins.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
11020
|
-
className:
|
|
11149
|
+
className: _dsh_plugin_shop_css_5f753521_default.emptyLine,
|
|
11021
11150
|
children: t("empty")
|
|
11022
11151
|
}) : filtered.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
11023
|
-
className:
|
|
11152
|
+
className: _dsh_plugin_shop_css_5f753521_default.emptyLine,
|
|
11024
11153
|
"data-shop-empty": true,
|
|
11025
11154
|
children: matched.length > 0 ? t("emptyIncompatibleFiltered") : t("emptySearch")
|
|
11026
11155
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ul", {
|
|
11027
|
-
className:
|
|
11156
|
+
className: _dsh_plugin_shop_css_5f753521_default.cards,
|
|
11028
11157
|
children: [visible.map((entry) => {
|
|
11029
11158
|
const key = entryKey(entry);
|
|
11030
11159
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EntryCard, {
|
|
@@ -11048,12 +11177,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
11048
11177
|
}) }, key);
|
|
11049
11178
|
}), incremental && visibleCount < filtered.length && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
|
|
11050
11179
|
ref: sentinelRef,
|
|
11051
|
-
className:
|
|
11180
|
+
className: _dsh_plugin_shop_css_5f753521_default.cardsSentry,
|
|
11052
11181
|
"data-shop-sentry": true,
|
|
11053
11182
|
"aria-hidden": "true"
|
|
11054
11183
|
})]
|
|
11055
11184
|
}), incremental && visibleCount < filtered.length && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
11056
|
-
className:
|
|
11185
|
+
className: _dsh_plugin_shop_css_5f753521_default.showingLine,
|
|
11057
11186
|
"aria-live": "polite",
|
|
11058
11187
|
children: t("showing", {
|
|
11059
11188
|
shown: String(visibleCount),
|