dsh-plugin-shop 0.8.7-beta.0 → 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 +748 -396
- package/lib/index.js +2 -1
- package/lib/typert.host.js +2 -1
- package/lib/typert.remote-client.js +1 -0
- 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 +42 -8
- package/lib/types/client/present.d.ts +111 -28
- package/lib/types/client/useFlows.d.ts +4 -0
- package/lib/types/client/useFoldableRedBlock.d.ts +36 -0
- package/lib/types/host/types.d.ts +5 -0
- package/package.json +3 -1
package/lib/client.js
CHANGED
|
@@ -4328,6 +4328,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4328
4328
|
_undefined(),
|
|
4329
4329
|
literal(false),
|
|
4330
4330
|
literal(true)
|
|
4331
|
+
]).optional(),
|
|
4332
|
+
"forked": union([
|
|
4333
|
+
_undefined(),
|
|
4334
|
+
literal(false),
|
|
4335
|
+
literal(true)
|
|
4331
4336
|
]).optional()
|
|
4332
4337
|
})),
|
|
4333
4338
|
"denied": array(object({
|
|
@@ -6478,10 +6483,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6478
6483
|
return blockerBadgeKey(blockers) === "incompatibleBadge";
|
|
6479
6484
|
}
|
|
6480
6485
|
/**
|
|
6481
|
-
* Whether this install is refused before it starts, so
|
|
6482
|
-
*
|
|
6483
|
-
* cannot proceed: by the host, for a taken name, or by
|
|
6484
|
-
* 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.
|
|
6485
6490
|
*
|
|
6486
6491
|
* The second is the one harness reason that blocks (design
|
|
6487
6492
|
* 2026-09-26-dsh-017-readiness, B1, amending "warn, never block"): dsh 0.1.7
|
|
@@ -6489,9 +6494,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6489
6494
|
* entry after it, rolling the profile back, and the shop cannot install past
|
|
6490
6495
|
* it. The verdict is dsh's own rule on the profile's own exemptions, whichever
|
|
6491
6496
|
* channel the entry comes from (design
|
|
6492
|
-
* 2026-09-28-bundle-components-and-github-peers, section 7.3), so
|
|
6493
|
-
*
|
|
6494
|
-
*
|
|
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.
|
|
6495
6500
|
*/
|
|
6496
6501
|
function refusesInstall(blockers) {
|
|
6497
6502
|
return blockers.some((blocker) => blocker.kind === "name-taken" || blocker.kind === "harness-peers");
|
|
@@ -6527,11 +6532,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6527
6532
|
if (spec === void 0) return void 0;
|
|
6528
6533
|
return specVerdict(entry, spec) === "same" ? void 0 : holderLabel(spec, entry.name);
|
|
6529
6534
|
}
|
|
6530
|
-
/**
|
|
6531
|
-
*
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
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 命令,以及读取和修改发送给模型的请求。未标「已审核」的插件未经审核。";
|
|
6535
6548
|
/** Install rejection code → locale key, for the rejected-state code label
|
|
6536
6549
|
* (§7.2): every rejection the host can return has a human label here. */
|
|
6537
6550
|
function rejectionCodeKey(code) {
|
|
@@ -6546,15 +6559,61 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6546
6559
|
case "desktop-profile": return "desktopProfileCode";
|
|
6547
6560
|
}
|
|
6548
6561
|
}
|
|
6549
|
-
/**
|
|
6550
|
-
|
|
6551
|
-
|
|
6562
|
+
/** A commit pin as the registry writes one: forty lowercase hex. */
|
|
6563
|
+
const COMMIT_PIN = /^[0-9a-f]{40}$/;
|
|
6564
|
+
/**
|
|
6565
|
+
* How a version string reads on the shelf, decided by its source and its
|
|
6566
|
+
* shape alone (market borrowings §11).
|
|
6567
|
+
*
|
|
6568
|
+
* Shape, never the entry's `tarball`, because an installed row carries only
|
|
6569
|
+
* the pin the shop recorded at install — the catalog `version` of that moment
|
|
6570
|
+
* — and no `tarball`. The registry pins a commit as forty lowercase hex and a
|
|
6571
|
+
* release by its tag, so the two shapes do not cross; `present.test.ts`
|
|
6572
|
+
* checks that against the committed live catalog.
|
|
6573
|
+
*
|
|
6574
|
+
* - An npm version takes the `v` the shelf has always shown, and only before
|
|
6575
|
+
* a digit: an installed row reports the manifest's spec, and a range there
|
|
6576
|
+
* (`^1.2.3`) is not a version to prefix.
|
|
6577
|
+
* - A commit is cut to the seven characters git abbreviates to, with no `v`:
|
|
6578
|
+
* it is not a version number.
|
|
6579
|
+
* - A release tag prints exactly as tagged, never cut and never prefixed:
|
|
6580
|
+
* cutting `v0.29.10` to seven characters printed `v0.29.1`, another
|
|
6581
|
+
* version, and the `v` this function no longer adds doubled the tag's own.
|
|
6582
|
+
*/
|
|
6583
|
+
function versionLabel(source, version) {
|
|
6584
|
+
if (source === "npm") return {
|
|
6585
|
+
kind: "npm",
|
|
6586
|
+
text: /^\d/.test(version) ? `v${version}` : version,
|
|
6587
|
+
value: version
|
|
6588
|
+
};
|
|
6589
|
+
if (COMMIT_PIN.test(version)) {
|
|
6590
|
+
const short = version.slice(0, 7);
|
|
6591
|
+
return {
|
|
6592
|
+
kind: "commit",
|
|
6593
|
+
text: short,
|
|
6594
|
+
value: short
|
|
6595
|
+
};
|
|
6596
|
+
}
|
|
6597
|
+
return {
|
|
6598
|
+
kind: "release",
|
|
6599
|
+
text: version,
|
|
6600
|
+
value: version
|
|
6601
|
+
};
|
|
6602
|
+
}
|
|
6603
|
+
/** The copy that names a version's kind, for the accessible name and the
|
|
6604
|
+
* tooltip; its `{version}` takes {@link VersionLabel.value}. */
|
|
6605
|
+
function versionLabelKey(kind) {
|
|
6606
|
+
switch (kind) {
|
|
6607
|
+
case "npm": return "npmVersionLabel";
|
|
6608
|
+
case "commit": return "commitVersionLabel";
|
|
6609
|
+
case "release": return "releaseVersionLabel";
|
|
6610
|
+
}
|
|
6552
6611
|
}
|
|
6553
6612
|
/** The hash pin a stale line names for a repo or release-rescued entry — the
|
|
6554
6613
|
* reviewed commit, or the reviewed release tarball sha256 (market borrowings
|
|
6555
|
-
* §3.1). Shortened to 7 chars like the card
|
|
6556
|
-
* hand-built fixture carrying no pin. The npm form is a version
|
|
6557
|
-
*
|
|
6614
|
+
* §3.1). Shortened to 7 chars like a commit on the card; '' only for a
|
|
6615
|
+
* hand-built fixture carrying no pin. The npm form is a version, which
|
|
6616
|
+
* {@link versionLabel} prints instead. */
|
|
6558
6617
|
function reviewHashPin(review) {
|
|
6559
6618
|
return (review.reviewedCommit ?? review.reviewedSha256 ?? "").slice(0, 7);
|
|
6560
6619
|
}
|
|
@@ -6679,6 +6738,24 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6679
6738
|
}
|
|
6680
6739
|
}
|
|
6681
6740
|
}
|
|
6741
|
+
/**
|
|
6742
|
+
* Which order the shelf is in (design 2026-10-12-shelf-orders). `stars` is
|
|
6743
|
+
* the default AND the tie-break of both date orders, so the two date orders
|
|
6744
|
+
* are stable re-sorts of it rather than orders with comparators of their own.
|
|
6745
|
+
*/
|
|
6746
|
+
const SHELF_ORDER_KEYS = {
|
|
6747
|
+
stars: "sortStars",
|
|
6748
|
+
added: "sortAdded",
|
|
6749
|
+
updated: "sortUpdated"
|
|
6750
|
+
};
|
|
6751
|
+
/** The three orders in display order (map insertion order). `readonly`: this
|
|
6752
|
+
* is a shared vocabulary, and an order list a caller can push to is a bug
|
|
6753
|
+
* waiting for its second caller. */
|
|
6754
|
+
const SHELF_ORDERS = Object.keys(SHELF_ORDER_KEYS);
|
|
6755
|
+
/** The locale key naming one order (the toggle's buttons). */
|
|
6756
|
+
function shelfOrderLocaleKey(order) {
|
|
6757
|
+
return SHELF_ORDER_KEYS[order];
|
|
6758
|
+
}
|
|
6682
6759
|
const CATEGORY_KEYS = {
|
|
6683
6760
|
tool: "categoryTool",
|
|
6684
6761
|
provider: "categoryProvider",
|
|
@@ -6825,6 +6902,77 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6825
6902
|
return NAME_COLLATOR.compare(a.name, b.name);
|
|
6826
6903
|
});
|
|
6827
6904
|
}
|
|
6905
|
+
/** The day form the registry writes into `added` (`emit.ts` E9 enforces it
|
|
6906
|
+
* on every build on its side; a catalog is untrusted input on this side). */
|
|
6907
|
+
const DAY_FORM = /^\d{4}-\d{2}-\d{2}$/;
|
|
6908
|
+
/**
|
|
6909
|
+
* The ES date-time form WITH an explicit zone. The zone is required, and not
|
|
6910
|
+
* for tidiness: a date-time string with no zone is parsed as LOCAL time, so
|
|
6911
|
+
* accepting one would make the shelf's order depend on the machine.
|
|
6912
|
+
*/
|
|
6913
|
+
const INSTANT_FORM = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
6914
|
+
/**
|
|
6915
|
+
* One date string as milliseconds, or undefined when it cannot be trusted.
|
|
6916
|
+
*
|
|
6917
|
+
* The form is checked BEFORE `Date.parse`, and that order is load-bearing:
|
|
6918
|
+
* V8 answers a real instant for `2026-02-31T00:00:00Z` (3 March) and for
|
|
6919
|
+
* `October 12` (a 2001 date), so `Date.parse` alone is not a validity test.
|
|
6920
|
+
*/
|
|
6921
|
+
function parseForm(value, form) {
|
|
6922
|
+
if (typeof value !== "string" || !form.test(value)) return void 0;
|
|
6923
|
+
const ms = Date.parse(value);
|
|
6924
|
+
return Number.isNaN(ms) ? void 0 : ms;
|
|
6925
|
+
}
|
|
6926
|
+
/** `shelfTime` with the catalog's build time already parsed, for the n calls
|
|
6927
|
+
* `orderShelf` makes in one pass. */
|
|
6928
|
+
function timeOf(entry, order, built) {
|
|
6929
|
+
if (order === "stars") return void 0;
|
|
6930
|
+
const ms = order === "added" ? parseForm(entry.added, DAY_FORM) : parseForm(entry.publishedAt, INSTANT_FORM);
|
|
6931
|
+
if (ms === void 0 || built === void 0 || ms > built) return void 0;
|
|
6932
|
+
return ms;
|
|
6933
|
+
}
|
|
6934
|
+
/**
|
|
6935
|
+
* The entry's sort key under a date order, or undefined when it has no usable
|
|
6936
|
+
* date (design §3): absent, off-form, unparseable, or later than the catalog's
|
|
6937
|
+
* own `builtAt`. Milliseconds since the epoch. `'stars'` carries no date, so
|
|
6938
|
+
* this answers undefined for it.
|
|
6939
|
+
*/
|
|
6940
|
+
function shelfTime(entry, order, builtAt) {
|
|
6941
|
+
return timeOf(entry, order, parseForm(builtAt, INSTANT_FORM));
|
|
6942
|
+
}
|
|
6943
|
+
/**
|
|
6944
|
+
* A UTC calendar day for display: "2026-10-12". Derived from the instant the
|
|
6945
|
+
* sort ranked by, so a card's label and its position cannot disagree. UTC and
|
|
6946
|
+
* never a local getter: a reader in UTC+8 must see the same days the catalog
|
|
6947
|
+
* was built in, the calendar the stats row already prints.
|
|
6948
|
+
*/
|
|
6949
|
+
function utcDay(ms) {
|
|
6950
|
+
return new Date(ms).toISOString().slice(0, 10);
|
|
6951
|
+
}
|
|
6952
|
+
/**
|
|
6953
|
+
* The shelf in `order` (design 2026-10-12-shelf-orders §2, §5). `'stars'`
|
|
6954
|
+
* returns the array it was handed, so the default path allocates nothing and
|
|
6955
|
+
* does not invalidate the memos downstream of it.
|
|
6956
|
+
*
|
|
6957
|
+
* Each key is computed ONCE per entry rather than inside the comparator, and
|
|
6958
|
+
* the sort is stable (ES2019), so entries sharing a date keep the star order
|
|
6959
|
+
* they arrived in — which is how the tie-break is defined without a second
|
|
6960
|
+
* comparator that could drift from `sortByStars`.
|
|
6961
|
+
*/
|
|
6962
|
+
function orderShelf(starOrdered, order, builtAt) {
|
|
6963
|
+
if (order === "stars") return starOrdered;
|
|
6964
|
+
const built = parseForm(builtAt, INSTANT_FORM);
|
|
6965
|
+
const keyed = starOrdered.map((entry) => ({
|
|
6966
|
+
entry,
|
|
6967
|
+
time: timeOf(entry, order, built)
|
|
6968
|
+
}));
|
|
6969
|
+
keyed.sort((a, b) => {
|
|
6970
|
+
if (a.time === void 0) return b.time === void 0 ? 0 : 1;
|
|
6971
|
+
if (b.time === void 0) return -1;
|
|
6972
|
+
return b.time - a.time;
|
|
6973
|
+
});
|
|
6974
|
+
return keyed.map((item) => item.entry);
|
|
6975
|
+
}
|
|
6828
6976
|
/** 999 → "999"; 1000 → "1k"; 1234 → "1.2k"; 1500 → "1.5k"; 99999 → "100k". */
|
|
6829
6977
|
function formatStars(n) {
|
|
6830
6978
|
if (n < 1e3) return String(n);
|
|
@@ -6897,6 +7045,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6897
7045
|
all: "全部",
|
|
6898
7046
|
catalog: "插件目录",
|
|
6899
7047
|
catalogStats: "{count} 个插件 · 构建于 {date}",
|
|
7048
|
+
/** The shelf's order control, and the date each card carries under the two
|
|
7049
|
+
* date orders (design 2026-10-12-shelf-orders §4). */
|
|
7050
|
+
sortLabel: "排序",
|
|
7051
|
+
sortStars: "星标",
|
|
7052
|
+
sortAdded: "最新上架",
|
|
7053
|
+
sortUpdated: "最近更新",
|
|
7054
|
+
listedOn: "上架 {date}",
|
|
7055
|
+
updatedOn: "更新 {date}",
|
|
6900
7056
|
stars: "{count} 星",
|
|
6901
7057
|
categoryTool: "工具",
|
|
6902
7058
|
categoryProvider: "模型服务",
|
|
@@ -6916,11 +7072,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6916
7072
|
archivedBadge: "已归档",
|
|
6917
7073
|
capabilitiesNote: "作者自述,无强制约束",
|
|
6918
7074
|
install: "安装",
|
|
6919
|
-
confirm: "确认",
|
|
6920
7075
|
cancel: "取消",
|
|
6921
7076
|
dismiss: "知道了",
|
|
6922
|
-
|
|
6923
|
-
|
|
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: "收起",
|
|
6924
7085
|
installing: "正在安装…",
|
|
6925
7086
|
downloading: "正在下载…",
|
|
6926
7087
|
installedRestartNotice: "已安装;重启 dsh 后生效",
|
|
@@ -6932,8 +7093,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6932
7093
|
* "unsupported" leaves the reader guessing which side is off. */
|
|
6933
7094
|
harnessRangeDetail: "作者声明支持的 dsh 版本:{range};本机运行的是 {running}。",
|
|
6934
7095
|
harnessProfileDetail: "作者声明支持的 profile:{declared};此 dsh 以 {running} profile 启动。",
|
|
6935
|
-
/** dsh's OWN refusal (0.1.7 on), not the author's claim: the
|
|
6936
|
-
*
|
|
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}`
|
|
6937
7099
|
* is each refused peer with the range the package declared. The remedy
|
|
6938
7100
|
* leads into the exemption command, rendered on its own line after it, and
|
|
6939
7101
|
* names the Refresh button by its label. */
|
|
@@ -6981,7 +7143,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6981
7143
|
emptyIncompatibleFiltered: "匹配的插件都标为不兼容,已被「隐藏不兼容」筛掉。",
|
|
6982
7144
|
license: "许可",
|
|
6983
7145
|
customLicense: "自定义许可",
|
|
6984
|
-
reviewedVersionLine: "已审核
|
|
7146
|
+
reviewedVersionLine: "已审核 {reviewed} / 当前 {current} 未审核",
|
|
6985
7147
|
reviewedPinLine: "已审核 {pin} / 当前 {current} 未审核",
|
|
6986
7148
|
deniedCode: "已拒绝",
|
|
6987
7149
|
notInCatalogCode: "不在目录中",
|
|
@@ -6995,6 +7157,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
6995
7157
|
nameTakenDetail: "已安装同名的另一个插件({holder})。\n两个同名插件声明相同的加载项 id,无法共存;安装它会替换掉已装的那个。",
|
|
6996
7158
|
npmSource: "npm 包",
|
|
6997
7159
|
githubSource: "源码在 GitHub",
|
|
7160
|
+
/** The accessible name and the tooltip of a card's version, which say which
|
|
7161
|
+
* of three things the printed string is (market borrowings §11): the string
|
|
7162
|
+
* alone cannot tell a release tag from a version number, and a short commit
|
|
7163
|
+
* printed with a `v` read as one. */
|
|
7164
|
+
npmVersionLabel: "npm 版本 {version}",
|
|
7165
|
+
commitVersionLabel: "GitHub 提交 {version}",
|
|
7166
|
+
releaseVersionLabel: "GitHub 发布 {version}",
|
|
6998
7167
|
/** The badge and the explanation for an installed github entry no catalog
|
|
6999
7168
|
* row claims (C10 MVP). The mechanism synthesizes a row for ANY github spec
|
|
7000
7169
|
* with no catalog row — shadowed by a same-named npm package, an unlisted
|
|
@@ -7004,8 +7173,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7004
7173
|
shadowedBadge: "目录未列出此包",
|
|
7005
7174
|
shadowedDetail: "此包已安装,但当前的插件目录没有它(可能是被同名 npm 包遮蔽、作者已下架、或目录尚未收录);它照常运行。",
|
|
7006
7175
|
updatableSection: "可更新",
|
|
7007
|
-
|
|
7008
|
-
|
|
7176
|
+
/** `{version}` is the shelf's own version text (`versionLabel`), which
|
|
7177
|
+
* carries the `v` an npm version takes and no other kind does. */
|
|
7178
|
+
installedVersion: "已安装 {version}",
|
|
7179
|
+
latestVersion: "最新 {version}",
|
|
7009
7180
|
update: "更新",
|
|
7010
7181
|
installed: "已安装",
|
|
7011
7182
|
uninstall: "卸载",
|
|
@@ -7061,6 +7232,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7061
7232
|
all: "All",
|
|
7062
7233
|
catalog: "Plugin catalog",
|
|
7063
7234
|
catalogStats: "{count} packages · built {date}",
|
|
7235
|
+
/** The shelf's order control, and the date each card carries under the two
|
|
7236
|
+
* date orders (design 2026-10-12-shelf-orders §4). */
|
|
7237
|
+
sortLabel: "Sort",
|
|
7238
|
+
sortStars: "Stars",
|
|
7239
|
+
sortAdded: "Newest",
|
|
7240
|
+
sortUpdated: "Recently updated",
|
|
7241
|
+
listedOn: "Listed {date}",
|
|
7242
|
+
updatedOn: "Updated {date}",
|
|
7064
7243
|
stars: "{count} stars",
|
|
7065
7244
|
categoryTool: "Tool",
|
|
7066
7245
|
categoryProvider: "Provider",
|
|
@@ -7080,11 +7259,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7080
7259
|
tierCommunity: "Community",
|
|
7081
7260
|
capabilitiesNote: "self-declared by the author, not enforced",
|
|
7082
7261
|
install: "Install",
|
|
7083
|
-
confirm: "Confirm",
|
|
7084
7262
|
cancel: "Cancel",
|
|
7085
7263
|
dismiss: "Dismiss",
|
|
7086
|
-
|
|
7087
|
-
|
|
7264
|
+
disclosure: DISCLOSURE_EN,
|
|
7265
|
+
moreRedLines: "Show more",
|
|
7266
|
+
lessRedLines: "Show less",
|
|
7088
7267
|
installing: "Installing…",
|
|
7089
7268
|
downloading: "Downloading…",
|
|
7090
7269
|
installedRestartNotice: "installed; restart dsh to activate",
|
|
@@ -7121,7 +7300,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7121
7300
|
emptyIncompatibleFiltered: "Every matching plugin is marked incompatible, and the incompatible filter is leaving them out.",
|
|
7122
7301
|
license: "License",
|
|
7123
7302
|
customLicense: "Custom license",
|
|
7124
|
-
reviewedVersionLine: "reviewed
|
|
7303
|
+
reviewedVersionLine: "reviewed {reviewed} / current {current} unreviewed",
|
|
7125
7304
|
reviewedPinLine: "reviewed {pin} / current {current} unreviewed",
|
|
7126
7305
|
deniedCode: "Denied",
|
|
7127
7306
|
notInCatalogCode: "Not in the catalog",
|
|
@@ -7135,11 +7314,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7135
7314
|
nameTakenDetail: "A different plugin of this name is installed ({holder}).\nTwo plugins of one name declare the same loader entry id and cannot both load; installing this one would replace it.",
|
|
7136
7315
|
npmSource: "npm package",
|
|
7137
7316
|
githubSource: "source on GitHub",
|
|
7317
|
+
npmVersionLabel: "npm version {version}",
|
|
7318
|
+
commitVersionLabel: "GitHub commit {version}",
|
|
7319
|
+
releaseVersionLabel: "GitHub release {version}",
|
|
7138
7320
|
shadowedBadge: "Not in the current catalog",
|
|
7139
7321
|
shadowedDetail: "This package is installed but the current catalog does not list it (it may be shadowed by a same-named npm package, removed by the author, or never listed); it runs as before.",
|
|
7140
7322
|
updatableSection: "Updatable",
|
|
7141
|
-
installedVersion: "installed
|
|
7142
|
-
latestVersion: "latest
|
|
7323
|
+
installedVersion: "installed {version}",
|
|
7324
|
+
latestVersion: "latest {version}",
|
|
7143
7325
|
update: "Update",
|
|
7144
7326
|
installed: "Installed",
|
|
7145
7327
|
uninstall: "Uninstall",
|
|
@@ -7693,6 +7875,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7693
7875
|
*/
|
|
7694
7876
|
function useKeyedFlows(begin, installStatus, onSettled) {
|
|
7695
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());
|
|
7696
7880
|
const settled = (0, react.useRef)(onSettled);
|
|
7697
7881
|
settled.current = onSettled;
|
|
7698
7882
|
const put = (0, react.useCallback)((key, view) => {
|
|
@@ -7713,8 +7897,21 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7713
7897
|
});
|
|
7714
7898
|
}, []);
|
|
7715
7899
|
const start = (0, react.useCallback)(async (key, args) => {
|
|
7716
|
-
|
|
7717
|
-
|
|
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
|
+
}
|
|
7718
7915
|
}, [begin, put]);
|
|
7719
7916
|
const reset = (0, react.useCallback)((key) => {
|
|
7720
7917
|
setViews((current) => {
|
|
@@ -7778,10 +7975,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7778
7975
|
};
|
|
7779
7976
|
}
|
|
7780
7977
|
const view = views.get(key) ?? IDLE_VIEW;
|
|
7978
|
+
const isStarting = starting.has(key);
|
|
7781
7979
|
const cached = flowCache.current.get(key);
|
|
7782
|
-
if (cached !== void 0 && cached.view === view) return cached;
|
|
7980
|
+
if (cached !== void 0 && cached.view === view && cached.starting === isStarting) return cached;
|
|
7783
7981
|
const flow = {
|
|
7784
7982
|
view,
|
|
7983
|
+
starting: isStarting,
|
|
7785
7984
|
start: (args) => start(key, args),
|
|
7786
7985
|
reset: () => reset(key)
|
|
7787
7986
|
};
|
|
@@ -7789,6 +7988,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
7789
7988
|
return flow;
|
|
7790
7989
|
}, [
|
|
7791
7990
|
views,
|
|
7991
|
+
starting,
|
|
7792
7992
|
start,
|
|
7793
7993
|
reset
|
|
7794
7994
|
]),
|
|
@@ -9047,129 +9247,232 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9047
9247
|
};
|
|
9048
9248
|
}
|
|
9049
9249
|
//#endregion
|
|
9050
|
-
//#region
|
|
9051
|
-
const css = "/* Shop tab styles — a market shelf inside the settings surface. The visual\n * language is the dsh theme's alias tokens ONLY (the four tokens the previous\n * styles referenced that do not exist — bg-layer-3, interactive-bg-hover,\n * state-error, state-success — are corrected to bg-layer-2 and the\n * state-*-primary set). The signature is the category spine: one HUE per\n * category (seven fixed hues, not the brand token — the brand resolves to\n * near-black in the light theme, so seven brand opacities read as seven\n * shades of gray), with the category name as the cover label in the same hue. Class names are mapped to content-derived names at bundle time\n * (tsdown.s8583e3_client.s942a79_config.s5f67aa_ts) and stubbed in tests; only the keys are relied\n * on.\n *\n * One token rule the theme forces: the background LAYERS do not carry\n * contrast. In the light theme bg-base, bg-layer-1, bg-layer-2 and\n * bg-layer-3 all resolve to the same white; only the dark theme spreads them\n * (950/875/850/800). So a layer-2 fill on a layer-1 ground is invisible for\n * half of all users, and anything whose fill is its ONLY affordance — the\n * loading skeleton, the borderless capability chips — derives its fill from\n * label-primary instead, which inverts with the theme by construction.\n * Elements that also carry a border or text may keep a layer fill.\n * `css-tokens.s8583e3_client.sfd9d8a_spec.s5f67aa_ts` pins this. */\n\n.s255e5a_panel {\n display: flex;\n flex-direction: column;\n gap: 14px;\n /* The shelf never scrolls sideways: a card too wide for its track wraps\n * its badges rather than widening the panel.\n *\n * Clipped on BOTH axes, with a margin, because `.s232ec4_searchInput` sits at\n * exactly (0, 0) of this box — it is the first child of `.s646509_toolbar`, itself\n * the first child here, and this rule carries no padding — so its focus\n * ring paints ENTIRELY outside the panel.\n *\n * Both axes is the load-bearing part: `overflow-clip-margin` is inert\n * unless the element clips on both. Clipping x alone left the margin doing\n * nothing — measured against the live app on 0.7.1, the ring reached 0 of\n * its 3px on the left at every device pixel ratio (1, 1.25, 1.5, 2), while\n * keeping the full 3px on the right, where the 280px input stops short of\n * the panel and never meets the clip edge. Raising the margin to 6px\n * changed nothing; clipping both axes with the SAME 3px restored the whole\n * ring. A test that reads declarations cannot see that difference, which is\n * why the first attempt at this shipped broken.\n *\n * `clip` is not a scroll container, so this does not bring back the\n * accidental scrolling `hidden` caused by coercing the other axis to\n * `auto`. Nothing is lost vertically: the panel's scrollHeight equals its\n * height and no descendant paints outside the clip rect (measured).\n * `overflow-x: hidden` stays as the fallback for engines without `clip`,\n * where the ring is clipped as it was before, never worse. */\n overflow-x: hidden;\n overflow: clip;\n overflow-clip-margin: 3px;\n}\n\n.s44a3ec_stateLine {\n margin: 0;\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n/* Visually hidden but screen-reader readable (the loading copy while the\n * skeleton stands in visually). */\n.s3a0ece_srOnly {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n border: 0;\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n overflow: hidden;\n white-space: nowrap;\n}\n\n/* The loading shelf: ghost cards with the same geometry as the real grid, so\n * the swap to content is a same-shape handoff instead of a jump. */\n.se0c3b4_skeletonGrid {\n display: grid;\n grid-template-columns: 1fr;\n gap: 8px;\n}\n\n.s520416_skeletonCard {\n position: relative;\n display: flex;\n flex-direction: column;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-1);\n overflow: hidden;\n}\n\n/* The shimmer sweep: one gradient band travels across each ghost card while\n * the bars breathe, so the loading state reads as \"the shelf is scanning\". */\n.s520416_skeletonCard::after {\n content: '';\n position: absolute;\n inset: 0;\n background: linear-gradient(\n 100deg,\n transparent 25%,\n color-mix(in srgb, var(--dsw-alias-label-primary) 14%, transparent) 50%,\n transparent 75%\n );\n transform: translateX(-100%);\n animation: dshShopSkeletonSweep 1.4s ease-in-out infinite;\n pointer-events: none;\n}\n\n@keyframes dshShopSkeletonSweep {\n to { transform: translateX(100%); }\n}\n\n.s8c7d6f_skeletonName {\n width: 55%;\n height: 14px;\n margin: 10px 12px 0;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.sf5179e_skeletonSummary {\n width: 92%;\n height: 12px;\n margin: 10px 12px 0;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.sbb0d25_skeletonSummaryShort {\n width: 68%;\n height: 12px;\n margin: 6px 12px 10px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n/* The ghost of the action line, right where the real buttons land. */\n.s96e910_skeletonActions {\n display: block;\n width: 84px;\n height: 24px;\n margin: 0 12px 10px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.s520416_skeletonCard span {\n animation: dshShopSkeletonPulse 1.6s ease-in-out infinite;\n}\n\n@keyframes dshShopSkeletonPulse {\n 0%, 100% { opacity: 0.5; }\n 50% { opacity: 1; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .s520416_skeletonCard span { animation: none; opacity: 0.75; }\n .s520416_skeletonCard::after { content: none; }\n}\n\n.s73242a_actionButton {\n align-self: flex-start;\n padding: 5px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.s73242a_actionButton:hover {\n border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l2));\n color: var(--dsw-alias-brand-primary);\n}\n\n.s73242a_actionButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.s646509_toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.s745e3c_toolbarRight {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n/* The shop's own version, right of the search box: `v0.4.4`, plus the\n * update button when the version check found a newer release. */\n.s4fe61f_versionBlock {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.sb9b924_versionText {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sa04455_updateSelfButton {\n padding: 2px 10px;\n border: 1px solid var(--dsw-alias-brand-primary);\n border-radius: 999px;\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n color: var(--dsw-alias-brand-primary);\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n}\n\n.sa04455_updateSelfButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 20%, transparent);\n}\n\n.sa04455_updateSelfButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The on-demand re-check next to the version number: a quiet pill; the\n * border and text carry it in the light theme where bg layers are white. */\n.s614a38_checkUpdateButton {\n padding: 2px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n cursor: pointer;\n}\n\n.s614a38_checkUpdateButton:hover {\n border-color: var(--dsw-alias-label-secondary);\n color: var(--dsw-alias-label-primary);\n}\n\n.s614a38_checkUpdateButton:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n.s614a38_checkUpdateButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The project's GitHub mark, right of the version row: a quiet icon link.\n * The octocat carries itself — color shift on hover, no text, no border. */\n.sa987f7_githubLink {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 3px;\n border-radius: 6px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sa987f7_githubLink:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sa987f7_githubLink:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The self-update's expanded states (running log / done + restart /\n * failure detail) sit under the toolbar, above the category bar. */\n.sb81ba5_selfUpdatePanel {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 8px;\n}\n\n.s232ec4_searchInput {\n width: min(280px, 100%);\n padding: 6px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n.s232ec4_searchInput:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* Subdued line under the search box when a dictionary intent fired (design\n * 2026-10-08-search-query-expansion §4). One fact, no badge. */\n.s76fb13_expansionNote {\n margin: 4px 0 0;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s3946f1_staleBadge {\n padding: 2px 8px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary) 40%, transparent);\n color: var(--dsw-alias-state-warn-primary);\n font-size: 12px;\n}\n\n.sb2c859_categoryBar {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 6px;\n margin-top: 6px;\n}\n\n.s26d58e_categoryButton {\n /* The hue this button carries when selected. `All` and `Installed` select by\n * something other than a category and keep the brand token; the six category\n * buttons override it from the shared hue table below, so a pressed Tool tab\n * turns the same blue as a tool card's spine. */\n --category-hue: var(--dsw-alias-brand-primary);\n padding: 3px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n cursor: pointer;\n}\n\n/* Blend text with the theme foreground: darker on light backgrounds, lighter\n * on dark ones. The unblended category hue belongs on the border and fill. */\n.s26d58e_categoryButton:hover {\n border-color: color-mix(in srgb, var(--category-hue) 45%, var(--dsw-alias-border-l2));\n color: color-mix(in srgb, var(--category-hue) 45%, var(--dsw-alias-label-primary) 55%);\n}\n\n/* Selected. Written `.s26d58e_categoryButton.s5f68c4_categoryButtonOn` — TWO classes — so it\n * matches `.s26d58e_categoryButton:hover`'s specificity and, coming after it, wins.\n * As a bare `.s5f68c4_categoryButtonOn` it lost both `border-color` and `color` to the\n * hover rule for as long as the pointer stayed on the tab that had just been\n * clicked, which is precisely when a reader looks for confirmation.\n *\n * EVERY declaration here is a colour or a shadow: a pressed tab must occupy\n * exactly the same box as an unpressed one, so the bar does not reflow — and\n * with ~9 pills on a wrapping row, one that grows can push the rest onto\n * another line and move the tab out from under the pointer that just clicked\n * it. That is what `font-weight: 600` did (bold metrics are wider than\n * regular), and it is why nothing may be added here that resolves to a length:\n * no padding, no border-width, no font-size, no letter-spacing.\n * `css-tokens.s8583e3_client.sfd9d8a_spec.s5f67aa_ts` fails the build on one.\n *\n * `box-shadow` is the exception that earns its place: it paints outside the\n * layout box, so an inset ring doubles the border's weight at zero cost to the\n * metrics. It is what `font-weight: 600` used to be — a second, thickness-\n * based signal that hover does not also produce — and the `other` tab needs it\n * most: its hue IS the neutral gray this file uses for \"no category\", so on\n * the light theme hue alone barely separates pressed from unpressed. */\n.s26d58e_categoryButton.s5f68c4_categoryButtonOn {\n border-color: var(--category-hue);\n background: color-mix(in srgb, var(--category-hue) 14%, transparent);\n color: color-mix(in srgb, var(--category-hue) 45%, var(--dsw-alias-label-primary) 55%);\n box-shadow: inset 0 0 0 1px var(--category-hue);\n}\n\n/* Forced colours replace every colour above with the system palette, which\n * would leave the three colour declarations saying nothing and the pressed tab\n * indistinguishable from its neighbours. `box-shadow` is dropped in this mode\n * too, so the ring has to come back as an outline. */\n@media (forced-colors: active) {\n .s26d58e_categoryButton.s5f68c4_categoryButtonOn {\n outline: 2px solid ButtonText;\n outline-offset: -4px;\n }\n}\n\n.s26d58e_categoryButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The incompatible filter, at the right edge of the category bar: `margin-left:\n * auto` pushes it past the pills, and the bar wraps, so on a narrow panel it\n * drops to its own line rather than squeezing them. It is a MODIFIER, not a\n * ninth category — the categories select what to show and this one subtracts\n * from whatever they selected — so it takes the error hue it filters on rather\n * than joining the hue table.\n *\n * And it is a SWITCH, which is the difference the shape now carries. It used\n * to wear `.s26d58e_categoryButton`: same pill, same row, same size as the eight tabs,\n * differing only in being red. That reads as a ninth tab, and it is not one —\n * the tabs are choose-one and this is on-or-off. So it drops the pill chrome\n * entirely (no border, no fill, nothing to compete with the row it sits\n * beside) and puts the state in a track, the same one every installed card\n * carries. Nothing here restates the switch geometry; `--switch-hue` is the\n * whole of what this control says about it, and it reaches the label too, so\n * one declaration keeps the word and the track the same colour. */\n.sfd887b_incompatibleFilter {\n --switch-hue: var(--dsw-alias-state-error-primary);\n margin-left: auto;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n /* Matches the pills' vertical padding so the bar keeps one line box whether\n * this control wraps onto a row of its own or shares theirs. */\n padding: 3px 0;\n border: 0;\n background: none;\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n cursor: pointer;\n}\n\n/* Hover lifts the word to full contrast and warms the track's border. Scoped\n * to the OFF track: `.sfd887b_incompatibleFilter:hover .s11c019_switch` outranks `.s7580e8_switchOn`\n * by two classes, so unscoped it would strip the hue from the border at\n * exactly the moment a reader has the pointer on it to check the state. */\n.sfd887b_incompatibleFilter:hover {\n color: var(--dsw-alias-label-primary);\n}\n\n.sfd887b_incompatibleFilter:hover .s11c019_switch:not(.s7580e8_switchOn) {\n border-color: color-mix(in srgb, var(--switch-hue) 45%, var(--dsw-alias-border-l2));\n}\n\n/* On. Written with both classes for the reason `.s26d58e_categoryButton.s5f68c4_categoryButtonOn`\n * is: it has to match `:hover`'s specificity and come after it, or the colour\n * reverts under a pointer that has not moved since the click. */\n.sfd887b_incompatibleFilter.s6e4cba_incompatibleFilterOn {\n color: color-mix(in srgb, var(--switch-hue) 45%, var(--dsw-alias-label-primary) 55%);\n}\n\n/* Off here means \"nothing is being taken away\" — the resting state — so the\n * knob steps back to the secondary label. The enable switch keeps the full\n * strength dot, where off means \"this plugin is disabled\" and is worth\n * noticing. In this bar it would be the heaviest mark in a row of hairline\n * pills, and on the light theme it is the WHOLE control: bg-layer-2 is the\n * same white as the panel there, so an off track is a hairline outline with a\n * near-black dot sitting in it. */\n.sfd887b_incompatibleFilter .s11c019_switch:not(.s7580e8_switchOn) .saab2b3_switchKnob {\n background: var(--dsw-alias-label-secondary);\n}\n\n/* The ring has no border to trace, so it takes an offset and the pill radius\n * of the tabs it sits beside rather than hugging the text. */\n.sfd887b_incompatibleFilter:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 2px;\n border-radius: 999px;\n}\n\n.sebf017_updatableHeading {\n margin: 2px 0 0;\n font-size: 16px;\n font-weight: 600;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-primary);\n}\n\n.seb0a1e_catalogStatsRow {\n display: flex;\n align-items: center;\n gap: 10px;\n margin: 6px 0 0;\n}\n\n.sccc4d1_catalogStats {\n margin: 0;\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* Sits beside the 12px stats caption, so it is quieter than `.s73242a_actionButton`:\n * this is an affordance, not a call to action — the catalog reloads on its\n * own at mount and the shelf is already usable without it. Named\n * `catalogRefresh*` (not `reload*`) so it cannot collide with the §4 page\n * reload offer below, which reloads the whole tab rather than refetching\n * the catalog data. */\n.sff243d_catalogRefreshButton {\n padding: 1px 9px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: transparent;\n color: var(--dsw-alias-label-secondary);\n font-size: 11px;\n line-height: 17px;\n cursor: pointer;\n}\n\n.sff243d_catalogRefreshButton:hover:not(:disabled) {\n border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l2));\n color: var(--dsw-alias-brand-primary);\n}\n\n.sff243d_catalogRefreshButton:disabled {\n cursor: default;\n opacity: 0.6;\n}\n\n.sff243d_catalogRefreshButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.s194168_catalogRefreshFailed {\n font-size: 11px;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n.s6de1ab_emptyLine {\n margin: 0;\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n/* The shelf: a responsive grid of cards. */\n/* Single-line rows: one full-width column, one plugin per line. */\n.sf657f5_cards {\n display: grid;\n grid-template-columns: 1fr;\n max-width: 100%;\n gap: 8px;\n list-style: none;\n margin: 0;\n padding: 0;\n animation: dshShopCardsIn 220ms ease;\n}\n\n@keyframes dshShopCardsIn {\n from { opacity: 0; transform: translateY(3px); }\n to { opacity: 1; transform: none; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .sf657f5_cards { animation: none; }\n}\n\n.s65f383_card {\n position: relative;\n display: flex;\n flex-direction: column;\n /* A grid item's min-width is auto by default: its content would widen the\n * 1fr track past the panel and overflow horizontally. */\n min-width: 0;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-1);\n overflow: hidden;\n transition: border-color 120ms ease, transform 120ms ease, box-shadow 120ms ease;\n /* Off-screen cards skip layout and paint; the placeholder keeps scrollbar\n * estimation sane before the browser has measured the real height. */\n content-visibility: auto;\n contain-intrinsic-size: auto 140px;\n}\n\n/* The sentinel that triggers the next shelf batch: zero visual footprint. */\n.sd7ee01_cardsSentry {\n height: 1px;\n margin: 0;\n padding: 0;\n}\n\n.s705cc6_showingLine {\n margin: 0;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s65f383_card:hover {\n border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l1));\n transform: translateY(-1px);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .s65f383_card { transition: none; }\n .s65f383_card:hover { transform: none; }\n}\n\n/* The signature: a spine and cover in the category's own hue. The hues are\n * fixed mid-bright colors that read on both themes; `other` deliberately\n * stays a neutral gray so the fallback category does not compete. */\n.s4ce25d_cardSpine {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 3px;\n background: var(--category-hue, #8B8E96);\n}\n\n/* The hue table, keyed by `data-category` and read by BOTH surfaces that\n * speak in category colour: a card's spine and badge, and the category bar's\n * own tab. One table, so a tab can never disagree with the cards it filters to\n * — which is the whole point of colouring the tab. `.s26d58e_categoryButton` sets the\n * brand fallback for `All`/`Installed`; these attribute selectors are one\n * specificity step above it and win wherever the attribute is present. */\n.s65f383_card[data-category='tool'], .s26d58e_categoryButton[data-category='tool'] { --category-hue: #4C8DFF; }\n.s65f383_card[data-category='provider'], .s26d58e_categoryButton[data-category='provider'] { --category-hue: #A78BFA; }\n.s65f383_card[data-category='ui'], .s26d58e_categoryButton[data-category='ui'] { --category-hue: #2DD4BF; }\n.s65f383_card[data-category='workflow'], .s26d58e_categoryButton[data-category='workflow'] { --category-hue: #F59E0B; }\n.s65f383_card[data-category='integration'], .s26d58e_categoryButton[data-category='integration'] { --category-hue: #34D399; }\n.s65f383_card[data-category='theme'], .s26d58e_categoryButton[data-category='theme'] { --category-hue: #F472B6; }\n.s65f383_card[data-category='other'], .s26d58e_categoryButton[data-category='other'] { --category-hue: #8B8E96; }\n\n/* The category sits in the badge row as a quiet chip in the category's own\n * hue — the border carries it where the light theme's white layers cannot\n * (the same lesson as the check button). */\n.s9c8eaa_categoryBadge {\n padding: 1px 7px;\n border: 1px solid color-mix(in srgb, var(--category-hue, #8B8E96) 45%, transparent);\n border-radius: 999px;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--category-hue, #8B8E96);\n white-space: nowrap;\n}\n\n.sa97234_entryHeader {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 12px;\n min-width: 0;\n padding: 8px 12px 2px;\n border: none;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n\n.sa97234_entryHeader:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n/* The name fills the row and truncates rather than wraps; the badges stay\n * intact beside it. */\n.sc3f149_name {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 13px;\n font-weight: 600;\n flex: 1 1 auto;\n min-width: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--dsw-alias-label-primary);\n}\n\n.s513026_badges {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n/* The action line: buttons side by side; an active install/uninstall flow\n * (gate, log, notices) takes the full width below them. */\n.s9b5048_cardActions {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n padding: 8px 12px 10px;\n}\n\n.s9b5048_cardActions > .sc4f2ac_gate,\n.s9b5048_cardActions > .s4d374c_installPanel,\n.s9b5048_cardActions > .sf53ca0_installedActions {\n flex-basis: 100%;\n}\n\n/* The entry's version, always visible on the card (the expanded detail\n * repeats it in the version row) — the same quiet mono as the toolbar. */\n.sc6ec6e_cardVersion {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* The octocat marking a github-sourced entry, beside its short commit. */\n.s71f10f_sourceBadge {\n display: inline-flex;\n align-items: center;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s5e82cb_tierBadge {\n padding: 1px 7px;\n border-radius: 999px;\n font-size: 11px;\n line-height: 1.6;\n}\n\n.s5e82cb_tierBadge[data-tier='verified'] {\n color: var(--dsw-alias-state-success-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-success-primary) 40%, transparent);\n}\n\n.s5e82cb_tierBadge[data-tier='verified-stale'] {\n color: var(--dsw-alias-state-warn-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary) 40%, transparent);\n}\n\n.s5e82cb_tierBadge[data-tier='community'] {\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n border: 1px solid var(--dsw-alias-border-l2);\n}\n\n/* The §C10 shadowed badge: an installed github entry the catalog refuses\n * shadowed-by-npm. Subdued/secondary rather than a warn tint — nothing is\n * wrong with the install itself; the shelf just cannot show it. */\n.s5e82cb_tierBadge[data-tier='shadowed'] {\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n border: 1px solid var(--dsw-alias-border-l2);\n}\n\n/* The §C5 archived badge: a github repository the owner has frozen.\n * Subdued register, same as shadowed — nothing is wrong with the install;\n * the pinned commit still serves, so the mark is informational only. */\n.s5e82cb_tierBadge[data-tier='archived'] {\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n border: 1px solid var(--dsw-alias-border-l2);\n}\n\n.s4bbe02_incompatibleBadge {\n flex-shrink: 0;\n padding: 3px 9px;\n border-radius: 999px;\n font-size: 12px;\n white-space: nowrap;\n color: var(--dsw-alias-state-error-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 40%, transparent);\n}\n\n.sff43b4_starsBadge {\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s73f7fb_chevron, .s61393c_chevronOpen {\n color: var(--dsw-alias-label-secondary);\n transition: transform 120ms ease;\n}\n\n.s61393c_chevronOpen { transform: rotate(180deg); }\n\n@media (prefers-reduced-motion: reduce) {\n .s73f7fb_chevron, .s61393c_chevronOpen { transition: none; }\n}\n\n.s820ebf_body {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 0 12px;\n}\n\n.sb8fbc8_summary {\n margin: 0;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.se507db_summaryZh {\n margin: -4px 0 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n display: -webkit-box;\n -webkit-line-clamp: 1;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n/* Expanded cards show the full summary: the clamp is lifted, never raised —\n * a higher number would still truncate the author's text, just later. */\n.s10baf0_summaryExpanded {\n -webkit-line-clamp: unset;\n display: block;\n}\n\n.sb9dba9_summaryZhExpanded {\n -webkit-line-clamp: unset;\n display: block;\n}\n\n/* `white-space: pre-line` so the newline in the copy is the copy's own: the\n dictionary decides where the line breaks, and a translator changes it\n without touching the component. Collapses runs of spaces like `normal`, so\n only the explicit \\n survives. */\n.s6eca19_incompatibleDetail {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n white-space: pre-line;\n color: var(--dsw-alias-state-error-primary);\n}\n\n/* The way out of dsh's own refusal: the lead-in, then the exemption command\n * on its own line. */\n.s3d0044_remedy {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.sdf203f_remedyLead {\n margin: 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n}\n\n/* The exemption command, bound for a terminal unchanged: one click selects\n * all of it, and it wraps anywhere rather than widening the card. A border,\n * not a fill, marks the box: the bg layers are one white in the light theme. */\n.sc32335_allowCommand {\n display: block;\n padding: 6px 8px;\n border-radius: 6px;\n background: var(--dsw-alias-bg-base);\n border: 1px solid var(--dsw-alias-border-l1);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px;\n line-height: 1.5;\n white-space: pre-wrap;\n word-break: break-all;\n user-select: all;\n color: var(--dsw-alias-label-primary);\n}\n\n/* A refused install on a surface that does not state its blockers — the\n * outdated row — writes them out on their own full-width line under the\n * disabled button, the command that clears dsh's refusal included. */\n.sc74941_refusal {\n display: flex;\n flex-direction: column;\n gap: 6px;\n flex-basis: 100%;\n}\n\n.s9d2029_capabilitiesBlock {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.s3bdbd5_capabilitiesNote {\n margin: 0;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s441702_capabilities {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.s441702_capabilities li {\n padding: 1px 6px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 6%, transparent);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sde3f0c_detail {\n border-top: 1px solid var(--dsw-alias-border-l1);\n padding-top: 8px;\n}\n\n.se98179_detailRows {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin: 0;\n}\n\n.sfb4d2f_detailRow {\n display: flex;\n justify-content: space-between;\n gap: 12px;\n font-size: 12px;\n}\n\n.sfb4d2f_detailRow dt {\n color: var(--dsw-alias-label-secondary);\n}\n\n.sfb4d2f_detailRow dd {\n margin: 0;\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n overflow-wrap: anywhere;\n text-align: right;\n color: var(--dsw-alias-label-primary);\n}\n\n.sfb4d2f_detailRow a {\n color: var(--dsw-alias-brand-primary);\n text-decoration: none;\n}\n\n.sfb4d2f_detailRow a:hover {\n text-decoration: underline;\n}\n\n.sfb4d2f_detailRow a:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* What the entry IS rather than what you can do with it — how big it is and\n * who published it — at the right edge of the action row. `margin-left: auto`\n * is what pushes it there, and it lives on this wrapper rather than on either\n * label so the group stays flush right when only one of the two has a value —\n * a packument that named no account has no author, and an entry the registry\n * could not measure honestly has no size. Rarer than it was (every live entry\n * now carries a size, github included), but the wrapper is not what makes it\n * rare: without one, an EMPTY meta group still costs 8px, because\n * `.s9b5048_cardActions` is a flex row with `gap: 8px` and a zero-width item is still\n * an item. The row is a wrapping flex, so an active install flow (which claims\n * the full width) drops the whole group to the next line rather than squeezing\n * it. */\n.s5fbaf9_cardMeta {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n margin-left: auto;\n min-width: 0;\n}\n\n/* The on-disk size, left of the author. Mono and tabular so the digits of one\n * figure line up with the digits of another WHEREVER the two land: `.s5fbaf9_cardMeta`\n * is flush right and the author sits outside the size, so a size's x position\n * moves with the author's width and is different again on the many entries\n * that carry no author at all. There is no column to align to, which is why\n * the reason given here is per-figure legibility rather than the column the\n * first version of this comment claimed. Secondary colour, because it\n * qualifies the entry rather than announcing it.\n *\n * `font-variant-numeric` is safe HERE and would not be on a pill: this element\n * has no state rules, so nothing it does can reflow a wrapping row. */\n.s2d3c05_size {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-variant-numeric: tabular-nums;\n font-size: 12px;\n letter-spacing: 0.02em;\n white-space: nowrap;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* The publishing account, at the outer end of `.s5fbaf9_cardMeta` on the action row —\n * NOT beside the npm link in the expanded detail, which is where it used to\n * live and what this comment described until the accurate half was deleted\n * with the `margin-left: auto` that moved to `.s5fbaf9_cardMeta`. There is no middot:\n * `.s5fbaf9_cardMeta`'s 8px gap separates it from the size.\n *\n * Secondary label colour carries the distinction — never a background fill,\n * which is invisible in the light theme where every bg-layer token is the\n * same white. */\n.s1ff66f_author {\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n}\n\n.sd58c6d_reviewedLine {\n margin: 8px 0 0;\n padding: 6px 8px;\n border-radius: 6px;\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 12%, transparent);\n font-size: 12px;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n/* Install flow. */\n.s4d374c_installPanel {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 2px;\n}\n\n.se7aaed_installButton, .sb43a49_confirmButton, .s6e45d1_cancelButton {\n align-self: flex-start;\n padding: 5px 14px;\n border-radius: 6px;\n font-size: 13px;\n cursor: pointer;\n}\n\n.se7aaed_installButton {\n border: 1px solid var(--dsw-alias-brand-primary);\n background: var(--dsw-alias-brand-primary);\n color: var(--dsw-alias-bg-base);\n}\n\n.se7aaed_installButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 88%, var(--dsw-alias-label-primary));\n}\n\n.sb43a49_confirmButton {\n border: 1px solid var(--dsw-alias-brand-primary);\n background: var(--dsw-alias-brand-primary);\n color: var(--dsw-alias-bg-base);\n}\n\n.sb43a49_confirmButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 88%, var(--dsw-alias-label-primary));\n}\n\n.s6e45d1_cancelButton {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n}\n\n.s6e45d1_cancelButton:hover {\n border-color: var(--dsw-alias-label-secondary);\n}\n\n.se7aaed_installButton:focus-visible, .sb43a49_confirmButton:focus-visible, .s6e45d1_cancelButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The shelf card of a plugin that is installed and current carries this\n * non-interactive label instead of an install button. Border + text do the\n * work in the light theme, where every bg-layer token resolves to white. */\n.sb6fc74_installedLabel {\n align-self: flex-start;\n margin: 0;\n padding: 5px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n font-size: 13px;\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n}\n\n/* An installed card's controls: the installed label or the update button,\n * plus the uninstall button. The install panel's running/failed states are\n * column layouts and sit as one item of this row. */\n.sf53ca0_installedActions {\n display: flex;\n align-items: flex-start;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n/* Uninstall revokes privilege; the quiet outline keeps it from competing\n * with the primary action, and the error tint says \"removes\". */\n.s96967f_uninstallButton {\n align-self: flex-start;\n padding: 5px 14px;\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 55%, transparent);\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-state-error-primary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.s96967f_uninstallButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);\n}\n\n.s96967f_uninstallButton:focus-visible {\n outline: 2px solid var(--dsw-alias-state-error-primary);\n outline-offset: 1px;\n}\n\n/* The restart dsh button on a done install: quiet outline, same rank as\n * the other secondary actions. */\n.sb00c36_restartSelfButton {\n /* The version row's third state. Geometry matches .s614a38_checkUpdateButton and\n * .sa04455_updateSelfButton exactly — the row is one evolving control and a\n * different pill would read as a different kind of thing. It takes the\n * emphasised fill rather than the neutral one because at that moment the\n * restart is the only thing left to do, the same reason Update has it. */\n padding: 2px 10px;\n border: 1px solid var(--dsw-alias-brand-primary);\n border-radius: 999px;\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n color: var(--dsw-alias-brand-primary);\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n}\n\n.sb00c36_restartSelfButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The two activation offers share one recipe: a border in an alias token,\n * never a `bg-layer` fill alone — all four bg-layer tokens collapse to the\n * same white under `body` in the light theme, and a fill-only button would\n * vanish there. Grouped rather than copied, because a copy is a rule that\n * can be corrected on one button and left wrong on the other, silently. */\n.s6ac237_restartButton,\n.s64a5ff_reloadButton {\n align-self: flex-start;\n padding: 5px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.s6ac237_restartButton:hover,\n.s64a5ff_reloadButton:hover {\n border-color: var(--dsw-alias-label-secondary);\n}\n\n.s6ac237_restartButton:focus-visible,\n.s64a5ff_reloadButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The §4 reload offer: a notice plus its button, one item of\n * `.sf53ca0_installedActions` the way the restart gate is — a column of its own\n * rather than inline with the row. */\n.sf79aef_reloadPanel {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 6px;\n}\n\n\n.sc4f2ac_gate {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 10px;\n border: 1px solid var(--dsw-alias-state-warn-primary);\n border-radius: 8px;\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 8%, transparent);\n}\n\n.s559c38_gateTitle {\n margin: 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n.s3c0ace_gateBody {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-primary);\n}\n\n.sc323e5_gateWarning {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n white-space: pre-line;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.s4b2373_gateActions {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n.s52b5a5_installing {\n margin: 0;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s64ab71_log {\n max-height: 160px;\n overflow: auto;\n margin: 0;\n padding: 8px;\n border-radius: 6px;\n background: var(--dsw-alias-bg-base);\n border: 1px solid var(--dsw-alias-border-l1);\n list-style: none;\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px;\n line-height: 1.5;\n}\n\n.s1531a8_logLine {\n white-space: pre-wrap;\n word-break: break-all;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sf20dda_notice {\n margin: 0;\n font-size: 12px;\n color: var(--dsw-alias-state-success-primary);\n}\n\n.seb9b08_failedHeading {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.sfd3021_failedDetail {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-primary);\n}\n\n.s50ba35_rejectedCode {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.se75489_rejectedDetail {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-primary);\n}\n\n/* The Updatable section. The class keeps the Host's field name; only the\n heading the reader sees was renamed. */\n.sa276cf_outdatedSection {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 6px;\n}\n\n.sceae6e_outdatedList {\n display: flex;\n flex-direction: column;\n gap: 8px;\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.s8d2072_outdatedRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n flex-wrap: wrap;\n padding: 10px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-1);\n}\n\n.s5c2545_outdatedInfo {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.s5c2545_outdatedInfo strong {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 13px;\n word-break: break-all;\n color: var(--dsw-alias-label-primary);\n}\n\n.s877c68_outdatedVersions {\n /* Two inline labels with nothing between them in the markup ran together as\n * `installed v1.0.0latest v2.0.0`: JSX drops the whitespace-only line, and\n * the per-label class the markup asked for was never defined (singular\n * against this rule's plural). The container owns the gap, the same idiom\n * as .s513026_badges — the labels need no class of their own. */\n display: inline-flex;\n align-items: center;\n gap: 8px;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* The §C10 shadowed section. Reuses the outdated list's visual language; only\n * the section spacing and the per-row explanation paragraph are its own. */\n.s6c4649_shadowedSection {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 6px;\n}\n\n.scc2a21_shadowedDetail {\n margin: 4px 0 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sa1f556_outdatedActions {\n display: flex;\n align-items: center;\n /* The reload offer (§4) mounts inside the switch beside the Update button\n * and is a two-line block, not a control: without wrapping, a row reading\n * [switch][notice + Reload][Update] overflows the settings dialog. */\n flex-wrap: wrap;\n gap: 10px;\n}\n\n/* The shop's one switch. It is worn by two controls that mean different\n * things — the enable/disable toggle on an installed card, and the shelf's\n * incompatible filter — so the geometry lives here once and each caller states\n * only its hue.\n *\n * `--switch-hue` is that knob, and it is read from the ANCESTOR rather than\n * declared per state: `.sfd887b_incompatibleFilter` sets it once and it reaches the\n * track and the label together, which is what keeps a red switch from sitting\n * under a neutral word. A declaration on `.s11c019_switch` itself would shadow the\n * inherited value (an element's own rule beats what it inherits), so the\n * default rides in the `var()` fallback and is resolved once into\n * `--track-hue` instead of being spelled out at each use.\n *\n * `box-sizing` and `padding` are stated because the two callers are different\n * elements — a `<button>`, which the UA gives padding, and a `<span>`, which\n * it does not. Without them the same class renders two sizes, and the knob\n * sits 2px above centre in the taller one. */\n.s11c019_switch {\n --track-hue: var(--switch-hue, var(--dsw-alias-state-success-primary));\n box-sizing: border-box;\n display: inline-block;\n position: relative;\n width: 32px;\n height: 18px;\n padding: 0;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-2);\n cursor: pointer;\n transition: background 120ms ease, border-color 120ms ease;\n}\n\n/* The knob's POSITION is the state, which is the reason this control needs no\n * `forced-colors` fallback the way the category pills do: a system palette\n * replaces every colour below and moves nothing. */\n.s7580e8_switchOn {\n border-color: var(--track-hue);\n background: color-mix(in srgb, var(--track-hue) 55%, transparent);\n}\n\n.saab2b3_switchKnob {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 12px;\n height: 12px;\n border-radius: 999px;\n background: var(--dsw-alias-label-primary);\n transition: transform 120ms ease, background 120ms ease;\n}\n\n.s7580e8_switchOn .saab2b3_switchKnob {\n transform: translateX(14px);\n background: var(--dsw-alias-bg-base);\n}\n\n.s11c019_switch:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .s11c019_switch, .saab2b3_switchKnob { transition: none; }\n}\n\n/* The hot enable/disable switch on installed rows: the knob button plus the\n * §8 hot-apply / failure notices rendered beside it. */\n.sd9f9e4_switchWrap {\n display: inline-flex;\n align-items: center;\n /* Same reason as `.sa1f556_outdatedActions`: `.sf79aef_reloadPanel`'s own comment calls\n * itself \"a column of its own rather than inline with the row\", which held\n * for `.sf53ca0_installedActions` and not here until this line. */\n flex-wrap: wrap;\n gap: 8px;\n}\n";
|
|
9052
|
-
|
|
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\"]")) {
|
|
9053
9329
|
const tag = document.createElement("style");
|
|
9054
|
-
tag.dataset.pluginCss = "
|
|
9330
|
+
tag.dataset.pluginCss = "5f753521";
|
|
9055
9331
|
tag.textContent = css;
|
|
9056
9332
|
document.head.appendChild(tag);
|
|
9057
9333
|
}
|
|
9058
|
-
var
|
|
9059
|
-
"client": "
|
|
9060
|
-
"config": "
|
|
9061
|
-
"ts": "
|
|
9062
|
-
"spec": "
|
|
9063
|
-
"panel": "
|
|
9064
|
-
"searchInput": "
|
|
9065
|
-
"toolbar": "
|
|
9066
|
-
"stateLine": "
|
|
9067
|
-
"srOnly": "
|
|
9068
|
-
"skeletonGrid": "
|
|
9069
|
-
"skeletonCard": "
|
|
9070
|
-
"skeletonName": "
|
|
9071
|
-
"skeletonSummary": "
|
|
9072
|
-
"skeletonSummaryShort": "
|
|
9073
|
-
"skeletonActions": "
|
|
9074
|
-
"actionButton": "
|
|
9075
|
-
"toolbarRight": "
|
|
9076
|
-
"versionBlock": "
|
|
9077
|
-
"versionText": "
|
|
9078
|
-
"updateSelfButton": "
|
|
9079
|
-
"checkUpdateButton": "
|
|
9080
|
-
"githubLink": "
|
|
9081
|
-
"selfUpdatePanel": "
|
|
9082
|
-
"expansionNote": "
|
|
9083
|
-
"staleBadge": "
|
|
9084
|
-
"categoryBar": "
|
|
9085
|
-
"categoryButton": "
|
|
9086
|
-
"categoryButtonOn": "
|
|
9087
|
-
"incompatibleFilter": "
|
|
9088
|
-
"switch": "
|
|
9089
|
-
"switchOn": "
|
|
9090
|
-
"incompatibleFilterOn": "
|
|
9091
|
-
"switchKnob": "
|
|
9092
|
-
"updatableHeading": "
|
|
9093
|
-
"catalogStatsRow": "
|
|
9094
|
-
"
|
|
9095
|
-
"
|
|
9096
|
-
"
|
|
9097
|
-
"
|
|
9098
|
-
"
|
|
9099
|
-
"
|
|
9100
|
-
"
|
|
9101
|
-
"
|
|
9102
|
-
"
|
|
9103
|
-
"
|
|
9104
|
-
"
|
|
9105
|
-
"
|
|
9106
|
-
"
|
|
9107
|
-
"
|
|
9108
|
-
"
|
|
9109
|
-
"
|
|
9110
|
-
"
|
|
9111
|
-
"
|
|
9112
|
-
"
|
|
9113
|
-
"
|
|
9114
|
-
"
|
|
9115
|
-
"
|
|
9116
|
-
"
|
|
9117
|
-
"
|
|
9118
|
-
"
|
|
9119
|
-
"
|
|
9120
|
-
"
|
|
9121
|
-
"
|
|
9122
|
-
"
|
|
9123
|
-
"
|
|
9124
|
-
"
|
|
9125
|
-
"
|
|
9126
|
-
"
|
|
9127
|
-
"
|
|
9128
|
-
"
|
|
9129
|
-
"
|
|
9130
|
-
"
|
|
9131
|
-
"
|
|
9132
|
-
"
|
|
9133
|
-
"
|
|
9134
|
-
"
|
|
9135
|
-
"
|
|
9136
|
-
"
|
|
9137
|
-
"
|
|
9138
|
-
"
|
|
9139
|
-
"
|
|
9140
|
-
"
|
|
9141
|
-
"
|
|
9142
|
-
"
|
|
9143
|
-
"
|
|
9144
|
-
"
|
|
9145
|
-
"
|
|
9146
|
-
"
|
|
9147
|
-
"
|
|
9148
|
-
"
|
|
9149
|
-
"
|
|
9150
|
-
"
|
|
9151
|
-
"
|
|
9152
|
-
"
|
|
9153
|
-
"
|
|
9154
|
-
"
|
|
9155
|
-
"
|
|
9156
|
-
"
|
|
9157
|
-
"
|
|
9158
|
-
"
|
|
9159
|
-
"
|
|
9160
|
-
"
|
|
9161
|
-
"
|
|
9162
|
-
"
|
|
9163
|
-
"
|
|
9164
|
-
"
|
|
9165
|
-
"
|
|
9166
|
-
"
|
|
9167
|
-
"
|
|
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"
|
|
9168
9452
|
};
|
|
9169
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
|
|
9170
9473
|
//#region src/client/ShopTab.tsx
|
|
9171
|
-
/** The shop tab: browse the catalog, detail an entry, install
|
|
9172
|
-
*
|
|
9474
|
+
/** The shop tab: browse the catalog, detail an entry, install on one click,
|
|
9475
|
+
* poll the install to its terminal state (§7.2).
|
|
9173
9476
|
* Everything the tab renders is text — summaries, capabilities, log lines,
|
|
9174
9477
|
* details — never markup, so hostile npm descriptions cannot inject (spec
|
|
9175
9478
|
* §11.3.4): no render path here may ever use dangerouslySetInnerHTML. */
|
|
@@ -9181,14 +9484,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9181
9484
|
* those nodes, and React's next commit throws on the ones it no longer finds
|
|
9182
9485
|
* (design 2026-09-26-market-borrowings §4). The tab renders no portals, so its
|
|
9183
9486
|
* root is its whole surface. */
|
|
9184
|
-
const PANEL_ROOT = `${
|
|
9487
|
+
const PANEL_ROOT = `${_dsh_plugin_shop_css_5f753521_default.panel} notranslate`;
|
|
9185
9488
|
function ChevronIcon({ open }) {
|
|
9186
9489
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
9187
9490
|
viewBox: "0 0 16 16",
|
|
9188
9491
|
width: "14",
|
|
9189
9492
|
height: "14",
|
|
9190
9493
|
"aria-hidden": "true",
|
|
9191
|
-
className: open ?
|
|
9494
|
+
className: open ? _dsh_plugin_shop_css_5f753521_default.chevronOpen : _dsh_plugin_shop_css_5f753521_default.chevron,
|
|
9192
9495
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
9193
9496
|
fill: "currentColor",
|
|
9194
9497
|
d: "M3.5 6 8 10.5 12.5 6l1 1L8 12.5 2.5 7l1-1Z"
|
|
@@ -9201,7 +9504,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9201
9504
|
* install controls. An installed plugin's card carries its installed row:
|
|
9202
9505
|
* current → the non-interactive installed label, behind → the update button;
|
|
9203
9506
|
* uninstalled → the install button. */
|
|
9204
|
-
const EntryCard = (0, react.memo)(function EntryCard({ entry, stars, installed, missing, harness, nameTakenBy, t, flow, uninstallFlow, restart, restartBlocked, reload, setEnabled, inInstalledView, restarts }) {
|
|
9507
|
+
const EntryCard = (0, react.memo)(function EntryCard({ entry, stars, order, shelfDay, installed, missing, harness, nameTakenBy, t, flow, uninstallFlow, restart, restartBlocked, reload, setEnabled, inInstalledView, restarts }) {
|
|
9205
9508
|
const [open, setOpen] = (0, react.useState)(false);
|
|
9206
9509
|
const blockers = stateBlockers(blockersOf(missing, harness, nameTakenBy), t);
|
|
9207
9510
|
const detailId = (0, react.useId)();
|
|
@@ -9209,6 +9512,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9209
9512
|
const npmUrl = npmPageUrl(entry);
|
|
9210
9513
|
const author = authorOf(entry);
|
|
9211
9514
|
const size = formatSize(entry.installSize);
|
|
9515
|
+
const version = versionLabel(entry.source, entry.version);
|
|
9516
|
+
const versionName = t(versionLabelKey(version.kind), { version: version.value });
|
|
9212
9517
|
const category = entry.catalog?.category ?? "other";
|
|
9213
9518
|
const installTarget = {
|
|
9214
9519
|
name: entry.name,
|
|
@@ -9218,32 +9523,32 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9218
9523
|
subdir: entry.subdir
|
|
9219
9524
|
};
|
|
9220
9525
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9221
|
-
className:
|
|
9526
|
+
className: _dsh_plugin_shop_css_5f753521_default.card,
|
|
9222
9527
|
"data-shop-entry": entry.name,
|
|
9223
9528
|
"data-category": category,
|
|
9224
9529
|
children: [
|
|
9225
9530
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9226
|
-
className:
|
|
9531
|
+
className: _dsh_plugin_shop_css_5f753521_default.cardSpine,
|
|
9227
9532
|
"aria-hidden": "true"
|
|
9228
9533
|
}),
|
|
9229
9534
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
9230
9535
|
type: "button",
|
|
9231
|
-
className:
|
|
9536
|
+
className: _dsh_plugin_shop_css_5f753521_default.entryHeader,
|
|
9232
9537
|
"aria-expanded": open,
|
|
9233
9538
|
"aria-controls": detailId,
|
|
9234
9539
|
onClick: () => setOpen((current) => !current),
|
|
9235
9540
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9236
|
-
className:
|
|
9541
|
+
className: _dsh_plugin_shop_css_5f753521_default.name,
|
|
9237
9542
|
children: entry.name
|
|
9238
9543
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
9239
|
-
className:
|
|
9544
|
+
className: _dsh_plugin_shop_css_5f753521_default.badges,
|
|
9240
9545
|
children: [
|
|
9241
9546
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9242
|
-
className:
|
|
9547
|
+
className: _dsh_plugin_shop_css_5f753521_default.categoryBadge,
|
|
9243
9548
|
children: t(categoryKey(entry))
|
|
9244
9549
|
}),
|
|
9245
9550
|
entry.source === "npm" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9246
|
-
className:
|
|
9551
|
+
className: _dsh_plugin_shop_css_5f753521_default.sourceBadge,
|
|
9247
9552
|
"data-shop-source-npm": true,
|
|
9248
9553
|
role: "img",
|
|
9249
9554
|
"aria-label": t("npmSource"),
|
|
@@ -9260,7 +9565,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9260
9565
|
})
|
|
9261
9566
|
}),
|
|
9262
9567
|
hasGithubHome(entry) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9263
|
-
className:
|
|
9568
|
+
className: _dsh_plugin_shop_css_5f753521_default.sourceBadge,
|
|
9264
9569
|
"data-shop-source-github": true,
|
|
9265
9570
|
role: "img",
|
|
9266
9571
|
"aria-label": t("githubSource"),
|
|
@@ -9273,23 +9578,26 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9273
9578
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8Z" })
|
|
9274
9579
|
})
|
|
9275
9580
|
}),
|
|
9276
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
9277
|
-
className:
|
|
9581
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9582
|
+
className: _dsh_plugin_shop_css_5f753521_default.cardVersion,
|
|
9278
9583
|
"data-card-version": true,
|
|
9279
|
-
|
|
9584
|
+
role: "img",
|
|
9585
|
+
"aria-label": versionName,
|
|
9586
|
+
title: versionName,
|
|
9587
|
+
children: version.text
|
|
9280
9588
|
}),
|
|
9281
9589
|
entry.tier !== "community" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9282
|
-
className:
|
|
9590
|
+
className: _dsh_plugin_shop_css_5f753521_default.tierBadge,
|
|
9283
9591
|
"data-tier": entry.tier,
|
|
9284
9592
|
children: t(tierKey(entry.tier))
|
|
9285
9593
|
}),
|
|
9286
9594
|
entry.archived === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9287
|
-
className:
|
|
9595
|
+
className: _dsh_plugin_shop_css_5f753521_default.tierBadge,
|
|
9288
9596
|
"data-tier": "archived",
|
|
9289
9597
|
children: t("archivedBadge")
|
|
9290
9598
|
}),
|
|
9291
9599
|
stars !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
9292
|
-
className:
|
|
9600
|
+
className: _dsh_plugin_shop_css_5f753521_default.starsBadge,
|
|
9293
9601
|
role: "img",
|
|
9294
9602
|
"aria-label": t("stars", { count: stars }),
|
|
9295
9603
|
children: ["★ ", formatStars(stars)]
|
|
@@ -9299,39 +9607,43 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9299
9607
|
})]
|
|
9300
9608
|
}),
|
|
9301
9609
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9302
|
-
className:
|
|
9610
|
+
className: _dsh_plugin_shop_css_5f753521_default.body,
|
|
9303
9611
|
children: [
|
|
9304
9612
|
summary !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9305
|
-
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,
|
|
9306
9614
|
children: summary.en
|
|
9307
9615
|
}),
|
|
9308
9616
|
summary?.zh !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9309
|
-
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,
|
|
9310
9618
|
children: summary.zh
|
|
9311
9619
|
}),
|
|
9312
|
-
blockers.
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
|
|
9316
|
-
|
|
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
|
+
}),
|
|
9317
9629
|
open && entry.catalog !== void 0 && entry.catalog.capabilities.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9318
|
-
className:
|
|
9630
|
+
className: _dsh_plugin_shop_css_5f753521_default.capabilitiesBlock,
|
|
9319
9631
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9320
|
-
className:
|
|
9632
|
+
className: _dsh_plugin_shop_css_5f753521_default.capabilitiesNote,
|
|
9321
9633
|
children: t("capabilitiesNote")
|
|
9322
9634
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
9323
|
-
className:
|
|
9635
|
+
className: _dsh_plugin_shop_css_5f753521_default.capabilities,
|
|
9324
9636
|
children: entry.catalog.capabilities.map((capability) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: capability }, capability))
|
|
9325
9637
|
})]
|
|
9326
9638
|
}),
|
|
9327
9639
|
open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
9328
9640
|
id: detailId,
|
|
9329
|
-
className:
|
|
9641
|
+
className: _dsh_plugin_shop_css_5f753521_default.detail,
|
|
9330
9642
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("dl", {
|
|
9331
|
-
className:
|
|
9643
|
+
className: _dsh_plugin_shop_css_5f753521_default.detailRows,
|
|
9332
9644
|
children: [
|
|
9333
9645
|
npmUrl !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9334
|
-
className:
|
|
9646
|
+
className: _dsh_plugin_shop_css_5f753521_default.detailRow,
|
|
9335
9647
|
"data-shop-npm": true,
|
|
9336
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", {
|
|
9337
9649
|
href: npmUrl,
|
|
@@ -9341,7 +9653,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9341
9653
|
}) })]
|
|
9342
9654
|
}),
|
|
9343
9655
|
entry.repository !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9344
|
-
className:
|
|
9656
|
+
className: _dsh_plugin_shop_css_5f753521_default.detailRow,
|
|
9345
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", {
|
|
9346
9658
|
href: entry.repository,
|
|
9347
9659
|
target: "_blank",
|
|
@@ -9350,25 +9662,25 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9350
9662
|
}) : entry.repository })]
|
|
9351
9663
|
}),
|
|
9352
9664
|
entry.license !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9353
|
-
className:
|
|
9665
|
+
className: _dsh_plugin_shop_css_5f753521_default.detailRow,
|
|
9354
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 })]
|
|
9355
9667
|
})
|
|
9356
9668
|
]
|
|
9357
9669
|
}), entry.tier === "verified-stale" && entry.review !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9358
|
-
className:
|
|
9670
|
+
className: _dsh_plugin_shop_css_5f753521_default.reviewedLine,
|
|
9359
9671
|
children: entry.review.reviewedVersion !== void 0 ? t("reviewedVersionLine", {
|
|
9360
|
-
reviewed: entry.review.reviewedVersion,
|
|
9361
|
-
current:
|
|
9672
|
+
reviewed: versionLabel("npm", entry.review.reviewedVersion).text,
|
|
9673
|
+
current: version.text
|
|
9362
9674
|
}) : t("reviewedPinLine", {
|
|
9363
9675
|
pin: reviewHashPin(entry.review),
|
|
9364
|
-
current:
|
|
9676
|
+
current: version.text
|
|
9365
9677
|
})
|
|
9366
9678
|
})]
|
|
9367
9679
|
})
|
|
9368
9680
|
]
|
|
9369
9681
|
}),
|
|
9370
9682
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9371
|
-
className:
|
|
9683
|
+
className: _dsh_plugin_shop_css_5f753521_default.cardActions,
|
|
9372
9684
|
"data-shop-actions": true,
|
|
9373
9685
|
children: [installed === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [!inInstalledView && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstallPanel, {
|
|
9374
9686
|
target: installTarget,
|
|
@@ -9400,7 +9712,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9400
9712
|
restartBlocked,
|
|
9401
9713
|
reload
|
|
9402
9714
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9403
|
-
className:
|
|
9715
|
+
className: _dsh_plugin_shop_css_5f753521_default.installedLabel,
|
|
9404
9716
|
"data-shop-installed": true,
|
|
9405
9717
|
children: t("installed")
|
|
9406
9718
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockerBadge, {
|
|
@@ -9424,20 +9736,28 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9424
9736
|
reload,
|
|
9425
9737
|
flow: uninstallFlow
|
|
9426
9738
|
})
|
|
9427
|
-
] }), (size !== void 0 || author !== null) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
9428
|
-
className:
|
|
9429
|
-
children: [
|
|
9430
|
-
|
|
9431
|
-
|
|
9432
|
-
|
|
9433
|
-
|
|
9434
|
-
|
|
9435
|
-
|
|
9436
|
-
|
|
9437
|
-
|
|
9438
|
-
|
|
9439
|
-
|
|
9440
|
-
|
|
9739
|
+
] }), (size !== void 0 || author !== null || shelfDay !== void 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
9740
|
+
className: _dsh_plugin_shop_css_5f753521_default.cardMeta,
|
|
9741
|
+
children: [
|
|
9742
|
+
shelfDay !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9743
|
+
className: _dsh_plugin_shop_css_5f753521_default.shelfDate,
|
|
9744
|
+
"data-shop-shelf-date": true,
|
|
9745
|
+
children: t(order === "added" ? "listedOn" : "updatedOn", { date: shelfDay })
|
|
9746
|
+
}),
|
|
9747
|
+
size !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9748
|
+
className: _dsh_plugin_shop_css_5f753521_default.size,
|
|
9749
|
+
"data-shop-size": true,
|
|
9750
|
+
role: "img",
|
|
9751
|
+
"aria-label": t("sizeLabel", { size }),
|
|
9752
|
+
title: t("sizeLabel", { size }),
|
|
9753
|
+
children: size
|
|
9754
|
+
}),
|
|
9755
|
+
author !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9756
|
+
className: _dsh_plugin_shop_css_5f753521_default.author,
|
|
9757
|
+
"data-shop-author": true,
|
|
9758
|
+
children: t("authorLine", { author })
|
|
9759
|
+
})
|
|
9760
|
+
]
|
|
9441
9761
|
})]
|
|
9442
9762
|
})
|
|
9443
9763
|
]
|
|
@@ -9483,13 +9803,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9483
9803
|
...attribute,
|
|
9484
9804
|
children: blocker.text
|
|
9485
9805
|
}), blocker.remedy !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9486
|
-
className:
|
|
9806
|
+
className: _dsh_plugin_shop_css_5f753521_default.remedy,
|
|
9487
9807
|
"data-shop-remedy": blocker.kind,
|
|
9488
9808
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9489
|
-
className:
|
|
9809
|
+
className: _dsh_plugin_shop_css_5f753521_default.remedyLead,
|
|
9490
9810
|
children: blocker.remedy.lead
|
|
9491
9811
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("code", {
|
|
9492
|
-
className:
|
|
9812
|
+
className: _dsh_plugin_shop_css_5f753521_default.allowCommand,
|
|
9493
9813
|
"data-shop-allow-command": true,
|
|
9494
9814
|
children: blocker.remedy.command
|
|
9495
9815
|
})]
|
|
@@ -9527,7 +9847,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9527
9847
|
if (lead === void 0 || word === null) return null;
|
|
9528
9848
|
const detail = blockers.map((blocker) => blocker.text).join("\n");
|
|
9529
9849
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
9530
|
-
className:
|
|
9850
|
+
className: _dsh_plugin_shop_css_5f753521_default.incompatibleBadge,
|
|
9531
9851
|
"data-shop-blocker": lead.kind,
|
|
9532
9852
|
role: "img",
|
|
9533
9853
|
"aria-label": detail,
|
|
@@ -9535,46 +9855,53 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9535
9855
|
children: t(word)
|
|
9536
9856
|
});
|
|
9537
9857
|
}
|
|
9538
|
-
/** One entry's install flow: the button
|
|
9539
|
-
*
|
|
9540
|
-
*
|
|
9541
|
-
*
|
|
9542
|
-
*
|
|
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. */
|
|
9543
9871
|
function InstallPanel({ target, tier, blockers, blockersStated = false, variant = "install", flow, t, restart, restartBlocked, reload }) {
|
|
9544
|
-
const
|
|
9545
|
-
const { view, start } = flow;
|
|
9872
|
+
const { view, start, starting } = flow;
|
|
9546
9873
|
if (view.kind === "running") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9547
|
-
className:
|
|
9874
|
+
className: _dsh_plugin_shop_css_5f753521_default.installPanel,
|
|
9548
9875
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9549
|
-
className:
|
|
9876
|
+
className: _dsh_plugin_shop_css_5f753521_default.installing,
|
|
9550
9877
|
children: t(installPhaseKey(view.phase))
|
|
9551
9878
|
}), view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9552
|
-
className:
|
|
9879
|
+
className: _dsh_plugin_shop_css_5f753521_default.log,
|
|
9553
9880
|
children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9554
|
-
className:
|
|
9881
|
+
className: _dsh_plugin_shop_css_5f753521_default.logLine,
|
|
9555
9882
|
"data-shop-log-line": true,
|
|
9556
9883
|
children: line
|
|
9557
9884
|
}, index))
|
|
9558
9885
|
})]
|
|
9559
9886
|
});
|
|
9560
9887
|
if (view.kind === "done") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9561
|
-
className:
|
|
9888
|
+
className: _dsh_plugin_shop_css_5f753521_default.installedActions,
|
|
9562
9889
|
children: [
|
|
9563
9890
|
view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9564
|
-
className:
|
|
9891
|
+
className: _dsh_plugin_shop_css_5f753521_default.log,
|
|
9565
9892
|
children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9566
|
-
className:
|
|
9893
|
+
className: _dsh_plugin_shop_css_5f753521_default.logLine,
|
|
9567
9894
|
"data-shop-log-line": true,
|
|
9568
9895
|
children: line
|
|
9569
9896
|
}, index))
|
|
9570
9897
|
}),
|
|
9571
9898
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9572
|
-
className:
|
|
9899
|
+
className: _dsh_plugin_shop_css_5f753521_default.notice,
|
|
9573
9900
|
"data-shop-restart-notice": true,
|
|
9574
9901
|
children: t(activationNoticeKey(view.activation, view.restartReason))
|
|
9575
9902
|
}),
|
|
9576
9903
|
view.detail !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9577
|
-
className:
|
|
9904
|
+
className: _dsh_plugin_shop_css_5f753521_default.notice,
|
|
9578
9905
|
"data-shop-done-note": true,
|
|
9579
9906
|
children: view.detail
|
|
9580
9907
|
}),
|
|
@@ -9589,103 +9916,81 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9589
9916
|
]
|
|
9590
9917
|
});
|
|
9591
9918
|
if (view.kind === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9592
|
-
className:
|
|
9919
|
+
className: _dsh_plugin_shop_css_5f753521_default.installPanel,
|
|
9593
9920
|
children: [
|
|
9594
9921
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9595
|
-
className:
|
|
9922
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedHeading,
|
|
9596
9923
|
children: t("installFailed")
|
|
9597
9924
|
}),
|
|
9598
9925
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9599
|
-
className:
|
|
9926
|
+
className: _dsh_plugin_shop_css_5f753521_default.log,
|
|
9600
9927
|
children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9601
|
-
className:
|
|
9928
|
+
className: _dsh_plugin_shop_css_5f753521_default.logLine,
|
|
9602
9929
|
"data-shop-log-line": true,
|
|
9603
9930
|
children: line
|
|
9604
9931
|
}, index))
|
|
9605
9932
|
}),
|
|
9606
9933
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9607
|
-
className:
|
|
9934
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedDetail,
|
|
9608
9935
|
children: view.detail === "" ? t("installTransportFailed") : view.detail
|
|
9609
9936
|
})
|
|
9610
9937
|
]
|
|
9611
9938
|
});
|
|
9612
9939
|
if (view.kind === "rejected") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9613
|
-
className:
|
|
9940
|
+
className: _dsh_plugin_shop_css_5f753521_default.installPanel,
|
|
9614
9941
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9615
|
-
className:
|
|
9942
|
+
className: _dsh_plugin_shop_css_5f753521_default.rejectedCode,
|
|
9616
9943
|
children: t(rejectionCodeKey(view.code))
|
|
9617
9944
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9618
|
-
className:
|
|
9945
|
+
className: _dsh_plugin_shop_css_5f753521_default.rejectedDetail,
|
|
9619
9946
|
children: view.detail
|
|
9620
9947
|
})]
|
|
9621
9948
|
});
|
|
9622
|
-
if (gateOpen) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9623
|
-
className: _dsh_plugin_shop_css_e3675a89_default.gate,
|
|
9624
|
-
children: [
|
|
9625
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9626
|
-
className: _dsh_plugin_shop_css_e3675a89_default.gateTitle,
|
|
9627
|
-
children: t("acknowledgementTitle")
|
|
9628
|
-
}),
|
|
9629
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9630
|
-
className: _dsh_plugin_shop_css_e3675a89_default.gateBody,
|
|
9631
|
-
children: t("acknowledgementBody")
|
|
9632
|
-
}),
|
|
9633
|
-
!blockersStated && blockers.map((blocker) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockerLine, {
|
|
9634
|
-
blocker,
|
|
9635
|
-
className: _dsh_plugin_shop_css_e3675a89_default.gateWarning,
|
|
9636
|
-
surface: "warning"
|
|
9637
|
-
}, blocker.kind)),
|
|
9638
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9639
|
-
className: _dsh_plugin_shop_css_e3675a89_default.gateActions,
|
|
9640
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
9641
|
-
type: "button",
|
|
9642
|
-
className: _dsh_plugin_shop_css_e3675a89_default.confirmButton,
|
|
9643
|
-
"data-shop-confirm": true,
|
|
9644
|
-
onClick: () => {
|
|
9645
|
-
setGateOpen(false);
|
|
9646
|
-
start({
|
|
9647
|
-
...target,
|
|
9648
|
-
acknowledged: true
|
|
9649
|
-
});
|
|
9650
|
-
},
|
|
9651
|
-
children: t("confirm")
|
|
9652
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
9653
|
-
type: "button",
|
|
9654
|
-
className: _dsh_plugin_shop_css_e3675a89_default.cancelButton,
|
|
9655
|
-
onClick: () => setGateOpen(false),
|
|
9656
|
-
children: t("cancel")
|
|
9657
|
-
})]
|
|
9658
|
-
})
|
|
9659
|
-
]
|
|
9660
|
-
});
|
|
9661
9949
|
const update = variant === "update";
|
|
9662
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
|
+
})] });
|
|
9663
9970
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
9664
9971
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
9665
9972
|
type: "button",
|
|
9666
|
-
className:
|
|
9667
|
-
disabled:
|
|
9668
|
-
...refused ? { "data-shop-refused": true } : {},
|
|
9973
|
+
className: _dsh_plugin_shop_css_5f753521_default.installButton,
|
|
9974
|
+
disabled: starting,
|
|
9669
9975
|
...update ? { "data-shop-update": true } : { "data-shop-install": true },
|
|
9670
9976
|
onClick: () => {
|
|
9671
|
-
|
|
9977
|
+
start({
|
|
9672
9978
|
...target,
|
|
9673
|
-
acknowledged: void 0
|
|
9979
|
+
acknowledged: tier === "verified" ? void 0 : true
|
|
9674
9980
|
});
|
|
9675
|
-
else setGateOpen(true);
|
|
9676
9981
|
},
|
|
9677
9982
|
children: t(update ? "update" : "install")
|
|
9678
9983
|
}),
|
|
9679
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockerBadge, {
|
|
9984
|
+
blockers.length > 0 && !refused && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockerBadge, {
|
|
9680
9985
|
blockers,
|
|
9681
9986
|
t
|
|
9682
9987
|
}),
|
|
9683
|
-
|
|
9684
|
-
|
|
9685
|
-
"data-shop-
|
|
9988
|
+
!blockersStated && blockers.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FoldableRedBlock, {
|
|
9989
|
+
t,
|
|
9990
|
+
attrName: "data-shop-blocker-lines",
|
|
9686
9991
|
children: blockers.map((blocker) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(BlockerLine, {
|
|
9687
9992
|
blocker,
|
|
9688
|
-
className:
|
|
9993
|
+
className: _dsh_plugin_shop_css_5f753521_default.gateWarning,
|
|
9689
9994
|
surface: "warning"
|
|
9690
9995
|
}, blocker.kind))
|
|
9691
9996
|
})
|
|
@@ -9700,32 +10005,32 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9700
10005
|
function UninstallPanel({ name, t, restart, restartBlocked, reload, flow }) {
|
|
9701
10006
|
const { view, start, reset } = flow;
|
|
9702
10007
|
if (view.kind === "running") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9703
|
-
className:
|
|
10008
|
+
className: _dsh_plugin_shop_css_5f753521_default.installPanel,
|
|
9704
10009
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9705
|
-
className:
|
|
10010
|
+
className: _dsh_plugin_shop_css_5f753521_default.installing,
|
|
9706
10011
|
children: t("uninstalling")
|
|
9707
10012
|
}), view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9708
|
-
className:
|
|
10013
|
+
className: _dsh_plugin_shop_css_5f753521_default.log,
|
|
9709
10014
|
children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9710
|
-
className:
|
|
10015
|
+
className: _dsh_plugin_shop_css_5f753521_default.logLine,
|
|
9711
10016
|
"data-shop-log-line": true,
|
|
9712
10017
|
children: line
|
|
9713
10018
|
}, index))
|
|
9714
10019
|
})]
|
|
9715
10020
|
});
|
|
9716
10021
|
if (view.kind === "done") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9717
|
-
className:
|
|
10022
|
+
className: _dsh_plugin_shop_css_5f753521_default.installedActions,
|
|
9718
10023
|
children: [
|
|
9719
10024
|
view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9720
|
-
className:
|
|
10025
|
+
className: _dsh_plugin_shop_css_5f753521_default.log,
|
|
9721
10026
|
children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9722
|
-
className:
|
|
10027
|
+
className: _dsh_plugin_shop_css_5f753521_default.logLine,
|
|
9723
10028
|
"data-shop-log-line": true,
|
|
9724
10029
|
children: line
|
|
9725
10030
|
}, index))
|
|
9726
10031
|
}),
|
|
9727
10032
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9728
|
-
className:
|
|
10033
|
+
className: _dsh_plugin_shop_css_5f753521_default.notice,
|
|
9729
10034
|
"data-shop-uninstall-done": true,
|
|
9730
10035
|
children: t(uninstallActivationNoticeKey(view.activation))
|
|
9731
10036
|
}),
|
|
@@ -9739,7 +10044,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9739
10044
|
}),
|
|
9740
10045
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
9741
10046
|
type: "button",
|
|
9742
|
-
className:
|
|
10047
|
+
className: _dsh_plugin_shop_css_5f753521_default.cancelButton,
|
|
9743
10048
|
"data-shop-uninstall-dismiss": true,
|
|
9744
10049
|
onClick: reset,
|
|
9745
10050
|
children: t("dismiss")
|
|
@@ -9747,27 +10052,27 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9747
10052
|
]
|
|
9748
10053
|
});
|
|
9749
10054
|
if (view.kind === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9750
|
-
className:
|
|
10055
|
+
className: _dsh_plugin_shop_css_5f753521_default.installPanel,
|
|
9751
10056
|
children: [
|
|
9752
10057
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9753
|
-
className:
|
|
10058
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedHeading,
|
|
9754
10059
|
children: t("uninstallFailed")
|
|
9755
10060
|
}),
|
|
9756
10061
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9757
|
-
className:
|
|
10062
|
+
className: _dsh_plugin_shop_css_5f753521_default.log,
|
|
9758
10063
|
children: view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
9759
|
-
className:
|
|
10064
|
+
className: _dsh_plugin_shop_css_5f753521_default.logLine,
|
|
9760
10065
|
"data-shop-log-line": true,
|
|
9761
10066
|
children: line
|
|
9762
10067
|
}, index))
|
|
9763
10068
|
}),
|
|
9764
10069
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9765
|
-
className:
|
|
10070
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedDetail,
|
|
9766
10071
|
children: view.detail === "" ? t("uninstallTransportFailed") : view.detail
|
|
9767
10072
|
}),
|
|
9768
10073
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
9769
10074
|
type: "button",
|
|
9770
|
-
className:
|
|
10075
|
+
className: _dsh_plugin_shop_css_5f753521_default.uninstallButton,
|
|
9771
10076
|
"data-shop-uninstall-retry": true,
|
|
9772
10077
|
onClick: () => void start({ name }),
|
|
9773
10078
|
children: t("retry")
|
|
@@ -9777,7 +10082,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9777
10082
|
if (view.kind !== "idle") return null;
|
|
9778
10083
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
9779
10084
|
type: "button",
|
|
9780
|
-
className:
|
|
10085
|
+
className: _dsh_plugin_shop_css_5f753521_default.uninstallButton,
|
|
9781
10086
|
"data-shop-uninstall": true,
|
|
9782
10087
|
onClick: () => void start({ name }),
|
|
9783
10088
|
children: t("uninstall")
|
|
@@ -9818,38 +10123,39 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9818
10123
|
else if (outcome.kind === "unreachable") setRefusal(t("restartTransportFailed"));
|
|
9819
10124
|
};
|
|
9820
10125
|
if (monitor?.kind === "requesting" || monitor?.kind === "restarting") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9821
|
-
className:
|
|
10126
|
+
className: _dsh_plugin_shop_css_5f753521_default.notice,
|
|
9822
10127
|
"data-shop-restarting": true,
|
|
9823
10128
|
children: t("restarting")
|
|
9824
10129
|
});
|
|
9825
10130
|
if (monitor?.kind === "failed") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9826
|
-
className:
|
|
10131
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedDetail,
|
|
9827
10132
|
"data-shop-restart-error": true,
|
|
9828
10133
|
children: monitor.logFile === void 0 ? t("restartFailedNotice") : t("restartFailedLogNotice", { log: monitor.logFile })
|
|
9829
10134
|
});
|
|
9830
10135
|
if (monitor?.kind === "not-restarted") return null;
|
|
9831
10136
|
if (gateOpen) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9832
|
-
className:
|
|
10137
|
+
className: _dsh_plugin_shop_css_5f753521_default.gate,
|
|
10138
|
+
"data-shop-restart-gate": true,
|
|
9833
10139
|
children: [
|
|
9834
10140
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9835
|
-
className:
|
|
10141
|
+
className: _dsh_plugin_shop_css_5f753521_default.gateTitle,
|
|
9836
10142
|
children: t("restartTitle")
|
|
9837
10143
|
}),
|
|
9838
10144
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9839
|
-
className:
|
|
10145
|
+
className: _dsh_plugin_shop_css_5f753521_default.gateBody,
|
|
9840
10146
|
children: t("restartBody")
|
|
9841
10147
|
}),
|
|
9842
10148
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9843
|
-
className:
|
|
10149
|
+
className: _dsh_plugin_shop_css_5f753521_default.gateActions,
|
|
9844
10150
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
9845
10151
|
type: "button",
|
|
9846
|
-
className:
|
|
10152
|
+
className: _dsh_plugin_shop_css_5f753521_default.confirmButton,
|
|
9847
10153
|
"data-shop-restart-confirm": true,
|
|
9848
10154
|
onClick: () => void onConfirm(),
|
|
9849
10155
|
children: t("restartConfirm")
|
|
9850
10156
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
9851
10157
|
type: "button",
|
|
9852
|
-
className:
|
|
10158
|
+
className: _dsh_plugin_shop_css_5f753521_default.cancelButton,
|
|
9853
10159
|
onClick: () => setGateOpen(false),
|
|
9854
10160
|
children: t("cancel")
|
|
9855
10161
|
})]
|
|
@@ -9857,14 +10163,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9857
10163
|
]
|
|
9858
10164
|
});
|
|
9859
10165
|
const refused = refusal === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9860
|
-
className:
|
|
10166
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedDetail,
|
|
9861
10167
|
"data-shop-restart-error": true,
|
|
9862
10168
|
children: refusal
|
|
9863
10169
|
});
|
|
9864
10170
|
if (gate !== void 0) return refused;
|
|
9865
10171
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [refused, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
9866
10172
|
type: "button",
|
|
9867
|
-
className:
|
|
10173
|
+
className: _dsh_plugin_shop_css_5f753521_default.restartButton,
|
|
9868
10174
|
"data-shop-restart": true,
|
|
9869
10175
|
onClick: () => setGateOpen(true),
|
|
9870
10176
|
children: t("restart")
|
|
@@ -9882,13 +10188,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9882
10188
|
*/
|
|
9883
10189
|
function NotRestartedNotice({ t, pid, logFile }) {
|
|
9884
10190
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9885
|
-
className:
|
|
10191
|
+
className: _dsh_plugin_shop_css_5f753521_default.selfUpdatePanel,
|
|
9886
10192
|
"data-shop-not-restarted": true,
|
|
9887
10193
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9888
|
-
className:
|
|
10194
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedDetail,
|
|
9889
10195
|
children: pid === void 0 ? t("notRestartedNotice") : t("notRestartedPidNotice", { pid: String(pid) })
|
|
9890
10196
|
}), logFile !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9891
|
-
className:
|
|
10197
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedDetail,
|
|
9892
10198
|
children: t("notRestartedLogNotice", { log: logFile })
|
|
9893
10199
|
})]
|
|
9894
10200
|
});
|
|
@@ -9918,7 +10224,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9918
10224
|
restart,
|
|
9919
10225
|
reload
|
|
9920
10226
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9921
|
-
className:
|
|
10227
|
+
className: _dsh_plugin_shop_css_5f753521_default.notice,
|
|
9922
10228
|
"data-shop-restart-disabled": true,
|
|
9923
10229
|
children: t(restartBlockedNoticeKey(restartBlocked))
|
|
9924
10230
|
});
|
|
@@ -9929,14 +10235,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9929
10235
|
* would help without knowing that now is a good time. */
|
|
9930
10236
|
function ReloadPanel({ t, reload, where }) {
|
|
9931
10237
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9932
|
-
className:
|
|
10238
|
+
className: _dsh_plugin_shop_css_5f753521_default.reloadPanel,
|
|
9933
10239
|
"data-shop-reload": where,
|
|
9934
10240
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
9935
|
-
className:
|
|
10241
|
+
className: _dsh_plugin_shop_css_5f753521_default.notice,
|
|
9936
10242
|
children: t("reloadNote")
|
|
9937
10243
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
9938
10244
|
type: "button",
|
|
9939
|
-
className:
|
|
10245
|
+
className: _dsh_plugin_shop_css_5f753521_default.reloadButton,
|
|
9940
10246
|
onClick: reload,
|
|
9941
10247
|
children: t("reload")
|
|
9942
10248
|
})]
|
|
@@ -9987,7 +10293,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9987
10293
|
}
|
|
9988
10294
|
};
|
|
9989
10295
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
9990
|
-
className:
|
|
10296
|
+
className: _dsh_plugin_shop_css_5f753521_default.switchWrap,
|
|
9991
10297
|
"data-shop-enabled-switch": row.name,
|
|
9992
10298
|
children: [
|
|
9993
10299
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
@@ -9996,18 +10302,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
9996
10302
|
"aria-checked": enabled,
|
|
9997
10303
|
"aria-label": t("enabledSwitch"),
|
|
9998
10304
|
"data-shop-toggle": true,
|
|
9999
|
-
className: `${
|
|
10305
|
+
className: `${_dsh_plugin_shop_css_5f753521_default.switch} ${enabled ? _dsh_plugin_shop_css_5f753521_default.switchOn : ""}`,
|
|
10000
10306
|
onClick: () => void onToggle(),
|
|
10001
10307
|
disabled: toggle.kind === "saving",
|
|
10002
|
-
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 })
|
|
10003
10309
|
}),
|
|
10004
10310
|
toggle.kind === "saved" && owed === "nothing" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10005
|
-
className:
|
|
10311
|
+
className: _dsh_plugin_shop_css_5f753521_default.notice,
|
|
10006
10312
|
"data-shop-hot-apply": true,
|
|
10007
10313
|
children: t("hotApplyNote")
|
|
10008
10314
|
}),
|
|
10009
10315
|
owed === "restart" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10010
|
-
className:
|
|
10316
|
+
className: _dsh_plugin_shop_css_5f753521_default.notice,
|
|
10011
10317
|
"data-shop-toggle-restart": true,
|
|
10012
10318
|
children: t("toggleRestartNote")
|
|
10013
10319
|
}),
|
|
@@ -10020,7 +10326,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10020
10326
|
where: "toggle"
|
|
10021
10327
|
}),
|
|
10022
10328
|
toggle.kind === "error" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10023
|
-
className:
|
|
10329
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedDetail,
|
|
10024
10330
|
"data-shop-toggle-error": true,
|
|
10025
10331
|
children: toggle.detail
|
|
10026
10332
|
})
|
|
@@ -10035,19 +10341,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10035
10341
|
* field is narrowed at the parameter. */
|
|
10036
10342
|
function OutdatedRow({ row, tier, missing, harness, t, setEnabled, flowFor, restart, restartBlocked, reload, restarts }) {
|
|
10037
10343
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10038
|
-
className:
|
|
10344
|
+
className: _dsh_plugin_shop_css_5f753521_default.outdatedRow,
|
|
10039
10345
|
"data-shop-outdated-entry": row.name,
|
|
10040
10346
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10041
|
-
className:
|
|
10347
|
+
className: _dsh_plugin_shop_css_5f753521_default.outdatedInfo,
|
|
10042
10348
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10043
|
-
className:
|
|
10349
|
+
className: _dsh_plugin_shop_css_5f753521_default.name,
|
|
10044
10350
|
children: row.name
|
|
10045
10351
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
10046
|
-
className:
|
|
10047
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("installedVersion", { version: row.source
|
|
10352
|
+
className: _dsh_plugin_shop_css_5f753521_default.outdatedVersions,
|
|
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 }) })]
|
|
10048
10354
|
})]
|
|
10049
10355
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10050
|
-
className:
|
|
10356
|
+
className: _dsh_plugin_shop_css_5f753521_default.outdatedActions,
|
|
10051
10357
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(EnabledSwitch, {
|
|
10052
10358
|
row,
|
|
10053
10359
|
t,
|
|
@@ -10090,28 +10396,30 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10090
10396
|
* "Installed" until 2026-09-16, which was the Installed filter's label
|
|
10091
10397
|
* character for character in both languages, over a strict subset of what
|
|
10092
10398
|
* that filter selects. Internals stay `outdated` — the Host field name. The
|
|
10093
|
-
* tier
|
|
10094
|
-
*
|
|
10095
|
-
*
|
|
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. */
|
|
10096
10404
|
function OutdatedSection({ state, entriesByKey, missingByKey, harnessVerdicts, t, setEnabled, flowFor, restart, restartBlocked, reload, restarts }) {
|
|
10097
10405
|
if (state.kind === "loading") return null;
|
|
10098
10406
|
if (state.kind === "error") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10099
|
-
className:
|
|
10407
|
+
className: _dsh_plugin_shop_css_5f753521_default.stateLine,
|
|
10100
10408
|
"data-shop-outdated-error": true,
|
|
10101
10409
|
children: t("installedError")
|
|
10102
10410
|
});
|
|
10103
10411
|
const outdated = state.entries.filter((entry) => entry.outdated && entry.latest !== null);
|
|
10104
10412
|
if (outdated.length === 0) return null;
|
|
10105
10413
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
10106
|
-
className:
|
|
10414
|
+
className: _dsh_plugin_shop_css_5f753521_default.outdatedSection,
|
|
10107
10415
|
"data-shop-outdated": true,
|
|
10108
10416
|
"aria-labelledby": UPDATABLE_HEADING_ID,
|
|
10109
10417
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
10110
|
-
className:
|
|
10418
|
+
className: _dsh_plugin_shop_css_5f753521_default.updatableHeading,
|
|
10111
10419
|
id: UPDATABLE_HEADING_ID,
|
|
10112
10420
|
children: t("updatableSection")
|
|
10113
10421
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
10114
|
-
className:
|
|
10422
|
+
className: _dsh_plugin_shop_css_5f753521_default.outdatedList,
|
|
10115
10423
|
children: outdated.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(OutdatedRow, {
|
|
10116
10424
|
row,
|
|
10117
10425
|
tier: entriesByKey.get(identityKey(row))?.tier ?? "community",
|
|
@@ -10139,37 +10447,37 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10139
10447
|
const shadowed = entries.filter((entry) => entry.shadowed === true);
|
|
10140
10448
|
if (shadowed.length === 0) return null;
|
|
10141
10449
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("section", {
|
|
10142
|
-
className:
|
|
10450
|
+
className: _dsh_plugin_shop_css_5f753521_default.shadowedSection,
|
|
10143
10451
|
"data-shop-shadowed": true,
|
|
10144
10452
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
10145
|
-
className:
|
|
10453
|
+
className: _dsh_plugin_shop_css_5f753521_default.outdatedList,
|
|
10146
10454
|
children: shadowed.map((row) => {
|
|
10147
10455
|
const key = identityKey(row);
|
|
10148
10456
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10149
|
-
className:
|
|
10457
|
+
className: _dsh_plugin_shop_css_5f753521_default.outdatedRow,
|
|
10150
10458
|
"data-shop-shadowed-entry": row.name,
|
|
10151
10459
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10152
|
-
className:
|
|
10460
|
+
className: _dsh_plugin_shop_css_5f753521_default.outdatedInfo,
|
|
10153
10461
|
children: [
|
|
10154
10462
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10155
|
-
className:
|
|
10463
|
+
className: _dsh_plugin_shop_css_5f753521_default.name,
|
|
10156
10464
|
children: row.name
|
|
10157
10465
|
}),
|
|
10158
10466
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10159
|
-
className:
|
|
10467
|
+
className: _dsh_plugin_shop_css_5f753521_default.badges,
|
|
10160
10468
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10161
|
-
className:
|
|
10469
|
+
className: _dsh_plugin_shop_css_5f753521_default.tierBadge,
|
|
10162
10470
|
"data-tier": "shadowed",
|
|
10163
10471
|
children: t("shadowedBadge")
|
|
10164
10472
|
})
|
|
10165
10473
|
}),
|
|
10166
10474
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10167
|
-
className:
|
|
10475
|
+
className: _dsh_plugin_shop_css_5f753521_default.shadowedDetail,
|
|
10168
10476
|
children: t("shadowedDetail")
|
|
10169
10477
|
})
|
|
10170
10478
|
]
|
|
10171
10479
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10172
|
-
className:
|
|
10480
|
+
className: _dsh_plugin_shop_css_5f753521_default.outdatedActions,
|
|
10173
10481
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UninstallPanel, {
|
|
10174
10482
|
name: row.name,
|
|
10175
10483
|
t,
|
|
@@ -10219,16 +10527,16 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10219
10527
|
role: "alert",
|
|
10220
10528
|
children: [
|
|
10221
10529
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10222
|
-
className:
|
|
10530
|
+
className: _dsh_plugin_shop_css_5f753521_default.stateLine,
|
|
10223
10531
|
children: say("tabCrashed", en.tabCrashed)
|
|
10224
10532
|
}),
|
|
10225
10533
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10226
|
-
className:
|
|
10534
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedDetail,
|
|
10227
10535
|
children: error.message
|
|
10228
10536
|
}),
|
|
10229
10537
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
10230
10538
|
type: "button",
|
|
10231
|
-
className:
|
|
10539
|
+
className: _dsh_plugin_shop_css_5f753521_default.actionButton,
|
|
10232
10540
|
onClick: () => this.setState({ error: null }),
|
|
10233
10541
|
children: say("retry", en.retry)
|
|
10234
10542
|
})
|
|
@@ -10315,11 +10623,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10315
10623
|
const [reloadFailed, setReloadFailed] = (0, react.useState)(false);
|
|
10316
10624
|
const [query, setQuery] = (0, react.useState)("");
|
|
10317
10625
|
const [category, setCategory] = (0, react.useState)(null);
|
|
10626
|
+
const [order, setOrder] = (0, react.useState)("stars");
|
|
10318
10627
|
const [hideIncompatible, setHideIncompatible] = (0, react.useState)(false);
|
|
10319
10628
|
const incremental = typeof IntersectionObserver !== "undefined";
|
|
10320
10629
|
const [visibleCount, setVisibleCount] = (0, react.useState)(48);
|
|
10321
10630
|
const sentinelRef = (0, react.useRef)(null);
|
|
10322
10631
|
const filteredLenRef = (0, react.useRef)(0);
|
|
10632
|
+
const sortLabelId = (0, react.useId)();
|
|
10323
10633
|
const pendingRefreshRef = (0, react.useRef)(null);
|
|
10324
10634
|
(0, react.useEffect)(() => {
|
|
10325
10635
|
let cancelled = false;
|
|
@@ -10414,7 +10724,13 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10414
10724
|
return map;
|
|
10415
10725
|
}, [installedState]);
|
|
10416
10726
|
const stars = (0, react.useMemo)(() => catalogState.kind === "ready" ? catalogState.result.stars : {}, [catalogState]);
|
|
10417
|
-
const
|
|
10727
|
+
const starOrder = (0, react.useMemo)(() => catalogState.kind === "ready" ? sortByStars(catalogState.result.plugins, stars) : [], [catalogState, stars]);
|
|
10728
|
+
const builtAt = catalogState.kind === "ready" ? catalogState.result.builtAt : "";
|
|
10729
|
+
const sortedCatalog = (0, react.useMemo)(() => orderShelf(starOrder, order, builtAt), [
|
|
10730
|
+
starOrder,
|
|
10731
|
+
order,
|
|
10732
|
+
builtAt
|
|
10733
|
+
]);
|
|
10418
10734
|
const browsable = (0, react.useMemo)(() => {
|
|
10419
10735
|
if (catalogState.kind !== "ready") return [];
|
|
10420
10736
|
const cleared = new Set(catalogState.result.notAShop ?? []);
|
|
@@ -10487,6 +10803,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10487
10803
|
filtered.length
|
|
10488
10804
|
]);
|
|
10489
10805
|
const visible = incremental ? filtered.slice(0, visibleCount) : filtered;
|
|
10806
|
+
/** The date a card shows under the current order, or undefined under the
|
|
10807
|
+
* star order and for an entry with no usable date. Computed from the same
|
|
10808
|
+
* `shelfTime` the order keyed on, so a label can never name a date the
|
|
10809
|
+
* order ignored. */
|
|
10810
|
+
const shelfDayOf = (entry) => {
|
|
10811
|
+
const ms = shelfTime(entry, order, builtAt);
|
|
10812
|
+
return ms === void 0 ? void 0 : utcDay(ms);
|
|
10813
|
+
};
|
|
10490
10814
|
const categoryCounts = (0, react.useMemo)(() => {
|
|
10491
10815
|
const counts = /* @__PURE__ */ new Map();
|
|
10492
10816
|
for (const entry of browsable) {
|
|
@@ -10509,19 +10833,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10509
10833
|
"data-shop-tab": true,
|
|
10510
10834
|
"aria-busy": "true",
|
|
10511
10835
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10512
|
-
className:
|
|
10836
|
+
className: _dsh_plugin_shop_css_5f753521_default.srOnly,
|
|
10513
10837
|
children: t("loading")
|
|
10514
10838
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10515
|
-
className:
|
|
10839
|
+
className: _dsh_plugin_shop_css_5f753521_default.skeletonGrid,
|
|
10516
10840
|
"data-shop-skeleton": true,
|
|
10517
10841
|
"aria-hidden": "true",
|
|
10518
10842
|
children: Array.from({ length: 8 }, (_, i) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10519
|
-
className:
|
|
10843
|
+
className: _dsh_plugin_shop_css_5f753521_default.skeletonCard,
|
|
10520
10844
|
children: [
|
|
10521
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className:
|
|
10522
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className:
|
|
10523
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className:
|
|
10524
|
-
/* @__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 })
|
|
10525
10849
|
]
|
|
10526
10850
|
}, i))
|
|
10527
10851
|
})]
|
|
@@ -10531,11 +10855,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10531
10855
|
translate: "no",
|
|
10532
10856
|
"data-shop-tab": true,
|
|
10533
10857
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10534
|
-
className:
|
|
10858
|
+
className: _dsh_plugin_shop_css_5f753521_default.stateLine,
|
|
10535
10859
|
children: t("error")
|
|
10536
10860
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
10537
10861
|
type: "button",
|
|
10538
|
-
className:
|
|
10862
|
+
className: _dsh_plugin_shop_css_5f753521_default.actionButton,
|
|
10539
10863
|
onClick: () => {
|
|
10540
10864
|
setRequest({ kind: "retry" });
|
|
10541
10865
|
setVersionReload((current) => current + 1);
|
|
@@ -10550,11 +10874,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10550
10874
|
"data-shop-tab": true,
|
|
10551
10875
|
children: [
|
|
10552
10876
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10553
|
-
className:
|
|
10877
|
+
className: _dsh_plugin_shop_css_5f753521_default.toolbar,
|
|
10554
10878
|
children: [
|
|
10555
10879
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
10556
10880
|
type: "search",
|
|
10557
|
-
className:
|
|
10881
|
+
className: _dsh_plugin_shop_css_5f753521_default.searchInput,
|
|
10558
10882
|
"aria-label": t("search"),
|
|
10559
10883
|
placeholder: t("search"),
|
|
10560
10884
|
value: query,
|
|
@@ -10565,31 +10889,31 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10565
10889
|
}),
|
|
10566
10890
|
expansionMemo.intents.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10567
10891
|
"data-shop-expansion-note": true,
|
|
10568
|
-
className:
|
|
10892
|
+
className: _dsh_plugin_shop_css_5f753521_default.expansionNote,
|
|
10569
10893
|
children: t("zhExpansionNote", { terms: expansionMemo.terms.slice(1).map((term) => term.startsWith("#") ? term.slice(1) : term).join(", ") })
|
|
10570
10894
|
}),
|
|
10571
10895
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10572
|
-
className:
|
|
10896
|
+
className: _dsh_plugin_shop_css_5f753521_default.toolbarRight,
|
|
10573
10897
|
children: [
|
|
10574
10898
|
result.stale && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10575
|
-
className:
|
|
10899
|
+
className: _dsh_plugin_shop_css_5f753521_default.staleBadge,
|
|
10576
10900
|
children: t("staleLabel", { date: result.builtAt.slice(0, 10) })
|
|
10577
10901
|
}),
|
|
10578
10902
|
selfVersion !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10579
|
-
className:
|
|
10903
|
+
className: _dsh_plugin_shop_css_5f753521_default.versionBlock,
|
|
10580
10904
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
10581
|
-
className:
|
|
10905
|
+
className: _dsh_plugin_shop_css_5f753521_default.versionText,
|
|
10582
10906
|
"data-shop-version": true,
|
|
10583
10907
|
children: ["v", selfVersion.installed]
|
|
10584
10908
|
}), restartMonitor !== null ? null : selfPending && restartBlocked === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
10585
10909
|
type: "button",
|
|
10586
|
-
className:
|
|
10910
|
+
className: _dsh_plugin_shop_css_5f753521_default.restartSelfButton,
|
|
10587
10911
|
"data-shop-restart": true,
|
|
10588
10912
|
onClick: () => setSelfRestartGate(true),
|
|
10589
10913
|
children: t("restart")
|
|
10590
10914
|
}) : !selfPending && selfVersion.outdated && selfVersion.latest !== null && selfUpdate.view.kind === "idle" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
10591
10915
|
type: "button",
|
|
10592
|
-
className:
|
|
10916
|
+
className: _dsh_plugin_shop_css_5f753521_default.updateSelfButton,
|
|
10593
10917
|
"data-shop-update-self": true,
|
|
10594
10918
|
onClick: () => {
|
|
10595
10919
|
if (selfVersion.latest !== null) selfUpdate.start({ version: selfVersion.latest });
|
|
@@ -10597,7 +10921,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10597
10921
|
children: t("update")
|
|
10598
10922
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
10599
10923
|
type: "button",
|
|
10600
|
-
className:
|
|
10924
|
+
className: _dsh_plugin_shop_css_5f753521_default.checkUpdateButton,
|
|
10601
10925
|
"data-shop-check-update": true,
|
|
10602
10926
|
title: t("checkUpdateTitle"),
|
|
10603
10927
|
disabled: checkState !== "idle",
|
|
@@ -10606,7 +10930,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10606
10930
|
})]
|
|
10607
10931
|
}),
|
|
10608
10932
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
10609
|
-
className:
|
|
10933
|
+
className: _dsh_plugin_shop_css_5f753521_default.githubLink,
|
|
10610
10934
|
href: SHOP_REPO_URL,
|
|
10611
10935
|
target: "_blank",
|
|
10612
10936
|
rel: "noopener noreferrer",
|
|
@@ -10631,28 +10955,28 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10631
10955
|
logFile: restartMonitor.logFile
|
|
10632
10956
|
}),
|
|
10633
10957
|
selfUpdate.view.kind === "running" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10634
|
-
className:
|
|
10958
|
+
className: _dsh_plugin_shop_css_5f753521_default.selfUpdatePanel,
|
|
10635
10959
|
"data-shop-self-updating": true,
|
|
10636
10960
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10637
|
-
className:
|
|
10961
|
+
className: _dsh_plugin_shop_css_5f753521_default.installing,
|
|
10638
10962
|
children: t(installPhaseKey(selfUpdate.view.phase))
|
|
10639
10963
|
}), selfUpdate.view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10640
|
-
className:
|
|
10964
|
+
className: _dsh_plugin_shop_css_5f753521_default.log,
|
|
10641
10965
|
children: selfUpdate.view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10642
|
-
className:
|
|
10966
|
+
className: _dsh_plugin_shop_css_5f753521_default.logLine,
|
|
10643
10967
|
"data-shop-log-line": true,
|
|
10644
10968
|
children: line
|
|
10645
10969
|
}, index))
|
|
10646
10970
|
})]
|
|
10647
10971
|
}),
|
|
10648
10972
|
selfPending && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10649
|
-
className:
|
|
10973
|
+
className: _dsh_plugin_shop_css_5f753521_default.selfUpdatePanel,
|
|
10650
10974
|
"data-shop-self-update-done": true,
|
|
10651
10975
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10652
|
-
className:
|
|
10976
|
+
className: _dsh_plugin_shop_css_5f753521_default.installedActions,
|
|
10653
10977
|
children: [
|
|
10654
10978
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10655
|
-
className:
|
|
10979
|
+
className: _dsh_plugin_shop_css_5f753521_default.notice,
|
|
10656
10980
|
children: t("installedRestartNotice")
|
|
10657
10981
|
}),
|
|
10658
10982
|
restartBlocked === null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RestartPanel, {
|
|
@@ -10665,7 +10989,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10665
10989
|
}
|
|
10666
10990
|
}),
|
|
10667
10991
|
restartBlocked !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10668
|
-
className:
|
|
10992
|
+
className: _dsh_plugin_shop_css_5f753521_default.notice,
|
|
10669
10993
|
"data-shop-restart-disabled": true,
|
|
10670
10994
|
children: t(restartBlockedNoticeKey(restartBlocked))
|
|
10671
10995
|
})
|
|
@@ -10673,35 +10997,35 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10673
10997
|
})
|
|
10674
10998
|
}),
|
|
10675
10999
|
selfUpdate.view.kind === "failed" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10676
|
-
className:
|
|
11000
|
+
className: _dsh_plugin_shop_css_5f753521_default.selfUpdatePanel,
|
|
10677
11001
|
"data-shop-self-update-failed": true,
|
|
10678
11002
|
children: [
|
|
10679
11003
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10680
|
-
className:
|
|
11004
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedHeading,
|
|
10681
11005
|
children: t("updateFailed")
|
|
10682
11006
|
}),
|
|
10683
11007
|
selfUpdate.view.log.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10684
|
-
className:
|
|
11008
|
+
className: _dsh_plugin_shop_css_5f753521_default.log,
|
|
10685
11009
|
children: selfUpdate.view.log.map((line, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
10686
|
-
className:
|
|
11010
|
+
className: _dsh_plugin_shop_css_5f753521_default.logLine,
|
|
10687
11011
|
"data-shop-log-line": true,
|
|
10688
11012
|
children: line
|
|
10689
11013
|
}, index))
|
|
10690
11014
|
}),
|
|
10691
11015
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10692
|
-
className:
|
|
11016
|
+
className: _dsh_plugin_shop_css_5f753521_default.failedDetail,
|
|
10693
11017
|
children: selfUpdate.view.detail === "" ? t("updateTransportFailed") : selfUpdate.view.detail
|
|
10694
11018
|
})
|
|
10695
11019
|
]
|
|
10696
11020
|
}),
|
|
10697
11021
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10698
|
-
className:
|
|
11022
|
+
className: _dsh_plugin_shop_css_5f753521_default.categoryBar,
|
|
10699
11023
|
role: "group",
|
|
10700
11024
|
"aria-label": t("catalog"),
|
|
10701
11025
|
children: [
|
|
10702
11026
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
10703
11027
|
type: "button",
|
|
10704
|
-
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,
|
|
10705
11029
|
"aria-pressed": category === null,
|
|
10706
11030
|
onClick: () => {
|
|
10707
11031
|
setCategory(null);
|
|
@@ -10716,7 +11040,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10716
11040
|
}),
|
|
10717
11041
|
CATEGORY_ORDER.map((key) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
10718
11042
|
type: "button",
|
|
10719
|
-
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,
|
|
10720
11044
|
"aria-pressed": category === key,
|
|
10721
11045
|
"data-category": key,
|
|
10722
11046
|
"data-shop-category-tab": key,
|
|
@@ -10732,7 +11056,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10732
11056
|
}, key)),
|
|
10733
11057
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
10734
11058
|
type: "button",
|
|
10735
|
-
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,
|
|
10736
11060
|
"aria-pressed": category === "installed",
|
|
10737
11061
|
"data-shop-category-installed": true,
|
|
10738
11062
|
onClick: () => {
|
|
@@ -10749,7 +11073,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10749
11073
|
type: "button",
|
|
10750
11074
|
role: "switch",
|
|
10751
11075
|
"aria-checked": hideIncompatible,
|
|
10752
|
-
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,
|
|
10753
11077
|
title: t(hideIncompatible ? "showIncompatibleTitle" : "incompatibleFilterTitle"),
|
|
10754
11078
|
"data-shop-hide-incompatible": true,
|
|
10755
11079
|
onClick: () => {
|
|
@@ -10757,18 +11081,18 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10757
11081
|
setVisibleCount(48);
|
|
10758
11082
|
},
|
|
10759
11083
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10760
|
-
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,
|
|
10761
11085
|
"aria-hidden": "true",
|
|
10762
|
-
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 })
|
|
10763
11087
|
}), t("hideIncompatible", { count: incompatibleCount })]
|
|
10764
11088
|
})
|
|
10765
11089
|
]
|
|
10766
11090
|
}),
|
|
10767
11091
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
10768
|
-
className:
|
|
11092
|
+
className: _dsh_plugin_shop_css_5f753521_default.catalogStatsRow,
|
|
10769
11093
|
children: [
|
|
10770
11094
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10771
|
-
className:
|
|
11095
|
+
className: _dsh_plugin_shop_css_5f753521_default.catalogStats,
|
|
10772
11096
|
"data-shop-catalog-stats": true,
|
|
10773
11097
|
children: t("catalogStats", {
|
|
10774
11098
|
count: String(browsable.length),
|
|
@@ -10777,7 +11101,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10777
11101
|
}),
|
|
10778
11102
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
10779
11103
|
type: "button",
|
|
10780
|
-
className:
|
|
11104
|
+
className: _dsh_plugin_shop_css_5f753521_default.catalogRefreshButton,
|
|
10781
11105
|
"data-shop-catalog-refresh": true,
|
|
10782
11106
|
disabled: reloading,
|
|
10783
11107
|
onClick: () => {
|
|
@@ -10789,26 +11113,54 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10789
11113
|
children: reloading ? t("refreshing") : t("refresh")
|
|
10790
11114
|
}),
|
|
10791
11115
|
reloadFailed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
10792
|
-
className:
|
|
11116
|
+
className: _dsh_plugin_shop_css_5f753521_default.catalogRefreshFailed,
|
|
10793
11117
|
"data-shop-catalog-refresh-failed": true,
|
|
10794
11118
|
children: t("refreshFailed")
|
|
11119
|
+
}),
|
|
11120
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
11121
|
+
className: _dsh_plugin_shop_css_5f753521_default.sortGroup,
|
|
11122
|
+
role: "group",
|
|
11123
|
+
"aria-labelledby": sortLabelId,
|
|
11124
|
+
"data-shop-sort": true,
|
|
11125
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
11126
|
+
className: _dsh_plugin_shop_css_5f753521_default.sortLabel,
|
|
11127
|
+
id: sortLabelId,
|
|
11128
|
+
children: t("sortLabel")
|
|
11129
|
+
}), SHELF_ORDERS.map((name) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
11130
|
+
type: "button",
|
|
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,
|
|
11132
|
+
"aria-pressed": order === name,
|
|
11133
|
+
"data-shop-order": name,
|
|
11134
|
+
onClick: () => {
|
|
11135
|
+
setOrder(name);
|
|
11136
|
+
setVisibleCount(48);
|
|
11137
|
+
},
|
|
11138
|
+
children: t(shelfOrderLocaleKey(name))
|
|
11139
|
+
}, name))]
|
|
10795
11140
|
})
|
|
10796
11141
|
]
|
|
10797
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
|
+
}),
|
|
10798
11148
|
result.plugins.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10799
|
-
className:
|
|
11149
|
+
className: _dsh_plugin_shop_css_5f753521_default.emptyLine,
|
|
10800
11150
|
children: t("empty")
|
|
10801
11151
|
}) : filtered.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10802
|
-
className:
|
|
11152
|
+
className: _dsh_plugin_shop_css_5f753521_default.emptyLine,
|
|
10803
11153
|
"data-shop-empty": true,
|
|
10804
11154
|
children: matched.length > 0 ? t("emptyIncompatibleFiltered") : t("emptySearch")
|
|
10805
11155
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("ul", {
|
|
10806
|
-
className:
|
|
11156
|
+
className: _dsh_plugin_shop_css_5f753521_default.cards,
|
|
10807
11157
|
children: [visible.map((entry) => {
|
|
10808
11158
|
const key = entryKey(entry);
|
|
10809
11159
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EntryCard, {
|
|
10810
11160
|
entry,
|
|
10811
11161
|
stars: starsOf(entry, stars),
|
|
11162
|
+
order,
|
|
11163
|
+
shelfDay: shelfDayOf(entry),
|
|
10812
11164
|
installed: installedByKey.get(key),
|
|
10813
11165
|
missing: missingByKey.get(key) ?? [],
|
|
10814
11166
|
harness: harnessVerdictOf(harnessVerdicts, key),
|
|
@@ -10825,12 +11177,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
10825
11177
|
}) }, key);
|
|
10826
11178
|
}), incremental && visibleCount < filtered.length && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
|
|
10827
11179
|
ref: sentinelRef,
|
|
10828
|
-
className:
|
|
11180
|
+
className: _dsh_plugin_shop_css_5f753521_default.cardsSentry,
|
|
10829
11181
|
"data-shop-sentry": true,
|
|
10830
11182
|
"aria-hidden": "true"
|
|
10831
11183
|
})]
|
|
10832
11184
|
}), incremental && visibleCount < filtered.length && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
10833
|
-
className:
|
|
11185
|
+
className: _dsh_plugin_shop_css_5f753521_default.showingLine,
|
|
10834
11186
|
"aria-live": "polite",
|
|
10835
11187
|
children: t("showing", {
|
|
10836
11188
|
shown: String(visibleCount),
|