dsh-plugin-shop 0.3.1 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/client.js +282 -13
- package/lib/index.js +43 -5
- package/lib/typert.host.js +7 -6
- package/lib/typert.remote-client.js +6 -5
- package/lib/types/client/locales.d.ts +4 -0
- package/lib/types/client/present.d.ts +22 -0
- package/lib/types/host/catalog.d.ts +6 -1
- package/lib/types/host/index.d.ts +3 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -1890,6 +1890,115 @@ window.__ModuleLoader__.load({
|
|
|
1890
1890
|
result.value = merged.data;
|
|
1891
1891
|
return result;
|
|
1892
1892
|
}
|
|
1893
|
+
const $ZodRecord = /*@__PURE__*/ $constructor("$ZodRecord", (inst, def) => {
|
|
1894
|
+
$ZodType.init(inst, def);
|
|
1895
|
+
inst._zod.parse = (payload, ctx) => {
|
|
1896
|
+
const input = payload.value;
|
|
1897
|
+
if (!isPlainObject(input)) {
|
|
1898
|
+
payload.issues.push({
|
|
1899
|
+
expected: "record",
|
|
1900
|
+
code: "invalid_type",
|
|
1901
|
+
input,
|
|
1902
|
+
inst
|
|
1903
|
+
});
|
|
1904
|
+
return payload;
|
|
1905
|
+
}
|
|
1906
|
+
const proms = [];
|
|
1907
|
+
const values = def.keyType._zod.values;
|
|
1908
|
+
if (values) {
|
|
1909
|
+
payload.value = {};
|
|
1910
|
+
const recordKeys = /* @__PURE__ */ new Set();
|
|
1911
|
+
for (const key of values) if (typeof key === "string" || typeof key === "number" || typeof key === "symbol") {
|
|
1912
|
+
recordKeys.add(typeof key === "number" ? key.toString() : key);
|
|
1913
|
+
const keyResult = def.keyType._zod.run({
|
|
1914
|
+
value: key,
|
|
1915
|
+
issues: []
|
|
1916
|
+
}, ctx);
|
|
1917
|
+
if (keyResult instanceof Promise) throw new Error("Async schemas not supported in object keys currently");
|
|
1918
|
+
if (keyResult.issues.length) {
|
|
1919
|
+
payload.issues.push({
|
|
1920
|
+
code: "invalid_key",
|
|
1921
|
+
origin: "record",
|
|
1922
|
+
issues: keyResult.issues.map((iss) => finalizeIssue(iss, ctx, config())),
|
|
1923
|
+
input: key,
|
|
1924
|
+
path: [key],
|
|
1925
|
+
inst
|
|
1926
|
+
});
|
|
1927
|
+
continue;
|
|
1928
|
+
}
|
|
1929
|
+
const outKey = keyResult.value;
|
|
1930
|
+
const result = def.valueType._zod.run({
|
|
1931
|
+
value: input[key],
|
|
1932
|
+
issues: []
|
|
1933
|
+
}, ctx);
|
|
1934
|
+
if (result instanceof Promise) proms.push(result.then((result) => {
|
|
1935
|
+
if (result.issues.length) payload.issues.push(...prefixIssues(key, result.issues));
|
|
1936
|
+
payload.value[outKey] = result.value;
|
|
1937
|
+
}));
|
|
1938
|
+
else {
|
|
1939
|
+
if (result.issues.length) payload.issues.push(...prefixIssues(key, result.issues));
|
|
1940
|
+
payload.value[outKey] = result.value;
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
let unrecognized;
|
|
1944
|
+
for (const key in input) if (!recordKeys.has(key)) {
|
|
1945
|
+
unrecognized = unrecognized ?? [];
|
|
1946
|
+
unrecognized.push(key);
|
|
1947
|
+
}
|
|
1948
|
+
if (unrecognized && unrecognized.length > 0) payload.issues.push({
|
|
1949
|
+
code: "unrecognized_keys",
|
|
1950
|
+
input,
|
|
1951
|
+
inst,
|
|
1952
|
+
keys: unrecognized
|
|
1953
|
+
});
|
|
1954
|
+
} else {
|
|
1955
|
+
payload.value = {};
|
|
1956
|
+
for (const key of Reflect.ownKeys(input)) {
|
|
1957
|
+
if (key === "__proto__") continue;
|
|
1958
|
+
if (!Object.prototype.propertyIsEnumerable.call(input, key)) continue;
|
|
1959
|
+
let keyResult = def.keyType._zod.run({
|
|
1960
|
+
value: key,
|
|
1961
|
+
issues: []
|
|
1962
|
+
}, ctx);
|
|
1963
|
+
if (keyResult instanceof Promise) throw new Error("Async schemas not supported in object keys currently");
|
|
1964
|
+
if (typeof key === "string" && number$1.test(key) && keyResult.issues.length) {
|
|
1965
|
+
const retryResult = def.keyType._zod.run({
|
|
1966
|
+
value: Number(key),
|
|
1967
|
+
issues: []
|
|
1968
|
+
}, ctx);
|
|
1969
|
+
if (retryResult instanceof Promise) throw new Error("Async schemas not supported in object keys currently");
|
|
1970
|
+
if (retryResult.issues.length === 0) keyResult = retryResult;
|
|
1971
|
+
}
|
|
1972
|
+
if (keyResult.issues.length) {
|
|
1973
|
+
if (def.mode === "loose") payload.value[key] = input[key];
|
|
1974
|
+
else payload.issues.push({
|
|
1975
|
+
code: "invalid_key",
|
|
1976
|
+
origin: "record",
|
|
1977
|
+
issues: keyResult.issues.map((iss) => finalizeIssue(iss, ctx, config())),
|
|
1978
|
+
input: key,
|
|
1979
|
+
path: [key],
|
|
1980
|
+
inst
|
|
1981
|
+
});
|
|
1982
|
+
continue;
|
|
1983
|
+
}
|
|
1984
|
+
const result = def.valueType._zod.run({
|
|
1985
|
+
value: input[key],
|
|
1986
|
+
issues: []
|
|
1987
|
+
}, ctx);
|
|
1988
|
+
if (result instanceof Promise) proms.push(result.then((result) => {
|
|
1989
|
+
if (result.issues.length) payload.issues.push(...prefixIssues(key, result.issues));
|
|
1990
|
+
payload.value[keyResult.value] = result.value;
|
|
1991
|
+
}));
|
|
1992
|
+
else {
|
|
1993
|
+
if (result.issues.length) payload.issues.push(...prefixIssues(key, result.issues));
|
|
1994
|
+
payload.value[keyResult.value] = result.value;
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
if (proms.length) return Promise.all(proms).then(() => payload);
|
|
1999
|
+
return payload;
|
|
2000
|
+
};
|
|
2001
|
+
});
|
|
1893
2002
|
const $ZodEnum = /*@__PURE__*/ $constructor("$ZodEnum", (inst, def) => {
|
|
1894
2003
|
$ZodType.init(inst, def);
|
|
1895
2004
|
const values = getEnumValues(def.entries);
|
|
@@ -3180,6 +3289,39 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
3180
3289
|
const isSimpleIntersection = (val) => "allOf" in val && Object.keys(val).length === 1;
|
|
3181
3290
|
json.allOf = [...isSimpleIntersection(a) ? a.allOf : [a], ...isSimpleIntersection(b) ? b.allOf : [b]];
|
|
3182
3291
|
};
|
|
3292
|
+
const recordProcessor = (schema, ctx, _json, params) => {
|
|
3293
|
+
const json = _json;
|
|
3294
|
+
const def = schema._zod.def;
|
|
3295
|
+
json.type = "object";
|
|
3296
|
+
const keyType = def.keyType;
|
|
3297
|
+
const patterns = keyType._zod.bag?.patterns;
|
|
3298
|
+
if (def.mode === "loose" && patterns && patterns.size > 0) {
|
|
3299
|
+
const valueSchema = process(def.valueType, ctx, {
|
|
3300
|
+
...params,
|
|
3301
|
+
path: [
|
|
3302
|
+
...params.path,
|
|
3303
|
+
"patternProperties",
|
|
3304
|
+
"*"
|
|
3305
|
+
]
|
|
3306
|
+
});
|
|
3307
|
+
json.patternProperties = {};
|
|
3308
|
+
for (const pattern of patterns) json.patternProperties[pattern.source] = valueSchema;
|
|
3309
|
+
} else {
|
|
3310
|
+
if (ctx.target === "draft-07" || ctx.target === "draft-2020-12") json.propertyNames = process(def.keyType, ctx, {
|
|
3311
|
+
...params,
|
|
3312
|
+
path: [...params.path, "propertyNames"]
|
|
3313
|
+
});
|
|
3314
|
+
json.additionalProperties = process(def.valueType, ctx, {
|
|
3315
|
+
...params,
|
|
3316
|
+
path: [...params.path, "additionalProperties"]
|
|
3317
|
+
});
|
|
3318
|
+
}
|
|
3319
|
+
const keyValues = keyType._zod.values;
|
|
3320
|
+
if (keyValues) {
|
|
3321
|
+
const validKeyValues = [...keyValues].filter((v) => typeof v === "string" || typeof v === "number");
|
|
3322
|
+
if (validKeyValues.length > 0) json.required = validKeyValues;
|
|
3323
|
+
}
|
|
3324
|
+
};
|
|
3183
3325
|
const nullableProcessor = (schema, ctx, json, params) => {
|
|
3184
3326
|
const def = schema._zod.def;
|
|
3185
3327
|
const inner = process(def.innerType, ctx, params);
|
|
@@ -3871,6 +4013,27 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
3871
4013
|
right
|
|
3872
4014
|
});
|
|
3873
4015
|
}
|
|
4016
|
+
const ZodRecord = /*@__PURE__*/ $constructor("ZodRecord", (inst, def) => {
|
|
4017
|
+
$ZodRecord.init(inst, def);
|
|
4018
|
+
ZodType.init(inst, def);
|
|
4019
|
+
inst._zod.processJSONSchema = (ctx, json, params) => recordProcessor(inst, ctx, json, params);
|
|
4020
|
+
inst.keyType = def.keyType;
|
|
4021
|
+
inst.valueType = def.valueType;
|
|
4022
|
+
});
|
|
4023
|
+
function record(keyType, valueType, params) {
|
|
4024
|
+
if (!valueType || !valueType._zod) return new ZodRecord({
|
|
4025
|
+
type: "record",
|
|
4026
|
+
keyType: string(),
|
|
4027
|
+
valueType: keyType,
|
|
4028
|
+
...normalizeParams(valueType)
|
|
4029
|
+
});
|
|
4030
|
+
return new ZodRecord({
|
|
4031
|
+
type: "record",
|
|
4032
|
+
keyType,
|
|
4033
|
+
valueType,
|
|
4034
|
+
...normalizeParams(params)
|
|
4035
|
+
});
|
|
4036
|
+
}
|
|
3874
4037
|
const ZodEnum = /*@__PURE__*/ $constructor("ZodEnum", (inst, def) => {
|
|
3875
4038
|
$ZodEnum.init(inst, def);
|
|
3876
4039
|
ZodType.init(inst, def);
|
|
@@ -4140,7 +4303,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4140
4303
|
"denied": array(object({
|
|
4141
4304
|
"name": string(),
|
|
4142
4305
|
"detail": string()
|
|
4143
|
-
}))
|
|
4306
|
+
})),
|
|
4307
|
+
"stars": record(string(), number())
|
|
4144
4308
|
});
|
|
4145
4309
|
const dsh_plugin_shop_shop_installStart_parameter_0$schema = object({
|
|
4146
4310
|
"name": string(),
|
|
@@ -4220,7 +4384,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4220
4384
|
},
|
|
4221
4385
|
sourceLocation: {
|
|
4222
4386
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4223
|
-
"line":
|
|
4387
|
+
"line": 180,
|
|
4224
4388
|
"column": 9
|
|
4225
4389
|
}
|
|
4226
4390
|
},
|
|
@@ -4248,7 +4412,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4248
4412
|
},
|
|
4249
4413
|
sourceLocation: {
|
|
4250
4414
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4251
|
-
"line":
|
|
4415
|
+
"line": 206,
|
|
4252
4416
|
"column": 9
|
|
4253
4417
|
}
|
|
4254
4418
|
},
|
|
@@ -4275,7 +4439,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4275
4439
|
},
|
|
4276
4440
|
sourceLocation: {
|
|
4277
4441
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4278
|
-
"line":
|
|
4442
|
+
"line": 244,
|
|
4279
4443
|
"column": 3
|
|
4280
4444
|
}
|
|
4281
4445
|
},
|
|
@@ -4293,7 +4457,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4293
4457
|
},
|
|
4294
4458
|
sourceLocation: {
|
|
4295
4459
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4296
|
-
"line":
|
|
4460
|
+
"line": 252,
|
|
4297
4461
|
"column": 9
|
|
4298
4462
|
}
|
|
4299
4463
|
},
|
|
@@ -4320,7 +4484,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4320
4484
|
},
|
|
4321
4485
|
sourceLocation: {
|
|
4322
4486
|
"file": "packages/dsh-plugin-shop/src/host/index.ts",
|
|
4323
|
-
"line":
|
|
4487
|
+
"line": 149,
|
|
4324
4488
|
"column": 3
|
|
4325
4489
|
}
|
|
4326
4490
|
}
|
|
@@ -4426,6 +4590,17 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4426
4590
|
const segments = name.toLowerCase().split(/[-_.]+/);
|
|
4427
4591
|
const hasPlugin = segments.includes("plugin");
|
|
4428
4592
|
if (segments.some((segment) => /plugin(store|market|mall|shop|marketplace)/.test(segment) || /(store|market|mall|shop|marketplace)plugin/.test(segment))) return true;
|
|
4593
|
+
if (segments.some((segment) => segment.startsWith("dsh") && SHOP_KEYWORDS.some((keyword) => segment === `dsh${keyword}`))) return true;
|
|
4594
|
+
if (segments.length >= 2 && (segments[0] === "dsh" || segments[0]?.endsWith("/dsh") === true) && SHOP_KEYWORDS.includes(segments[1]) && (segments.length === 2 || segments[2] !== void 0 && [
|
|
4595
|
+
"plus",
|
|
4596
|
+
"pro",
|
|
4597
|
+
"hub",
|
|
4598
|
+
"center",
|
|
4599
|
+
"centre",
|
|
4600
|
+
"max",
|
|
4601
|
+
"free",
|
|
4602
|
+
"lite"
|
|
4603
|
+
].includes(segments[2]))) return true;
|
|
4429
4604
|
if (segments.filter((segment) => SHOP_KEYWORDS.includes(segment)).length === 0) return false;
|
|
4430
4605
|
return hasPlugin || SHOP_KEYWORDS.includes(segments.at(-1));
|
|
4431
4606
|
}
|
|
@@ -4442,6 +4617,28 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4442
4617
|
function categoryKey(entry) {
|
|
4443
4618
|
return CATEGORY_KEYS[entry.catalog?.category ?? "other"];
|
|
4444
4619
|
}
|
|
4620
|
+
/** The six categories in display order (map insertion order). */
|
|
4621
|
+
const CATEGORY_ORDER = Object.keys(CATEGORY_KEYS);
|
|
4622
|
+
/** The locale key for one bare category value (the filter buttons). */
|
|
4623
|
+
function categoryLocaleKey(category) {
|
|
4624
|
+
return CATEGORY_KEYS[category];
|
|
4625
|
+
}
|
|
4626
|
+
/** Sort the shelf: stars descending, un-starred entries last, name ascending
|
|
4627
|
+
* (case-insensitive) on ties (spec 2026-08-26-github-stars-design.md D1).
|
|
4628
|
+
* Display-time only — the catalog's own name sort is untouched. */
|
|
4629
|
+
function sortByStars(entries, stars) {
|
|
4630
|
+
const count = (e) => stars[e.name] ?? -1;
|
|
4631
|
+
return [...entries].sort((a, b) => {
|
|
4632
|
+
const byStars = count(b) - count(a);
|
|
4633
|
+
if (byStars !== 0) return byStars;
|
|
4634
|
+
return a.name.localeCompare(b.name, void 0, { sensitivity: "base" });
|
|
4635
|
+
});
|
|
4636
|
+
}
|
|
4637
|
+
/** 999 → "999"; 1000 → "1k"; 1234 → "1.2k"; 1500 → "1.5k"; 99999 → "100k". */
|
|
4638
|
+
function formatStars(n) {
|
|
4639
|
+
if (n < 1e3) return String(n);
|
|
4640
|
+
return `${(n / 1e3).toFixed(1).replace(/\.0$/, "")}k`;
|
|
4641
|
+
}
|
|
4445
4642
|
//#endregion
|
|
4446
4643
|
//#region src/client/locales.ts
|
|
4447
4644
|
/** Copy dictionaries for the shop Settings tab. */
|
|
@@ -4452,8 +4649,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4452
4649
|
error: "暂时无法读取目录。",
|
|
4453
4650
|
retry: "重试",
|
|
4454
4651
|
search: "搜索插件",
|
|
4652
|
+
all: "全部",
|
|
4455
4653
|
catalog: "插件目录",
|
|
4456
4654
|
catalogStats: "{count} 个插件 · 构建于 {date}",
|
|
4655
|
+
stars: "{count} 星",
|
|
4457
4656
|
categoryTool: "工具",
|
|
4458
4657
|
categoryProvider: "模型服务",
|
|
4459
4658
|
categoryUi: "界面",
|
|
@@ -4503,8 +4702,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4503
4702
|
error: "The catalog is temporarily unavailable.",
|
|
4504
4703
|
retry: "Retry",
|
|
4505
4704
|
search: "Search plugins",
|
|
4705
|
+
all: "All",
|
|
4506
4706
|
catalog: "Plugin catalog",
|
|
4507
4707
|
catalogStats: "{count} packages · built {date}",
|
|
4708
|
+
stars: "{count} stars",
|
|
4508
4709
|
categoryTool: "Tool",
|
|
4509
4710
|
categoryProvider: "Provider",
|
|
4510
4711
|
categoryUi: "UI",
|
|
@@ -4600,7 +4801,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4600
4801
|
}
|
|
4601
4802
|
//#endregion
|
|
4602
4803
|
//#region \0dsh-plugin-shop-css:e3675a89
|
|
4603
|
-
const css = "/* Shop tab styles — a market shelf inside the settings surface. The visual\n * language is the dsh theme's alias tokens ONLY (the four tokens the previous\n * styles referenced that do not exist — bg-layer-3, interactive-bg-hover,\n * state-error, state-success — are corrected to bg-layer-2 and the\n * state-*-primary set). The signature is the category spine: one HUE per\n * category (six fixed hues, not the brand token — the brand resolves to\n * near-black in the light theme, so six brand opacities read as six shades\n * of gray), with the category name as the cover label in the same hue. Class names are mapped to content-derived names at bundle time\n * (tsdown.s8583e3_client.s942a79_config.s5f67aa_ts) and stubbed in tests; only the keys are relied\n * on.\n *\n * One token rule the theme forces: the background LAYERS do not carry\n * contrast. In the light theme bg-base, bg-layer-1, bg-layer-2 and\n * bg-layer-3 all resolve to the same white; only the dark theme spreads them\n * (950/875/850/800). So a layer-2 fill on a layer-1 ground is invisible for\n * half of all users, and anything whose fill is its ONLY affordance — the\n * loading skeleton, the borderless capability chips — derives its fill from\n * label-primary instead, which inverts with the theme by construction.\n * Elements that also carry a border or text may keep a layer fill.\n * `css-tokens.s8583e3_client.sfd9d8a_spec.s5f67aa_ts` pins this. */\n\n.s255e5a_panel {\n display: flex;\n flex-direction: column;\n gap: 14px;\n}\n\n.s44a3ec_stateLine {\n margin: 0;\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n/* Visually hidden but screen-reader readable (the loading copy while the\n * skeleton stands in visually). */\n.s3a0ece_srOnly {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n border: 0;\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n overflow: hidden;\n white-space: nowrap;\n}\n\n/* The loading shelf: ghost cards with the same geometry as the real grid, so\n * the swap to content is a same-shape handoff instead of a jump. */\n.se0c3b4_skeletonGrid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));\n gap: 12px;\n}\n\n.s520416_skeletonCard {\n position: relative;\n display: flex;\n flex-direction: column;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-1);\n overflow: hidden;\n}\n\n/* The shimmer sweep: one gradient band travels across each ghost card while\n * the bars breathe, so the loading state reads as \"the shelf is scanning\". */\n.s520416_skeletonCard::after {\n content: '';\n position: absolute;\n inset: 0;\n background: linear-gradient(\n 100deg,\n transparent 25%,\n color-mix(in srgb, var(--dsw-alias-label-primary) 14%, transparent) 50%,\n transparent 75%\n );\n transform: translateX(-100%);\n animation: dshShopSkeletonSweep 1.4s ease-in-out infinite;\n pointer-events: none;\n}\n\n@keyframes dshShopSkeletonSweep {\n to { transform: translateX(100%); }\n}\n\n.s30e662_skeletonCover {\n display: block;\n height: 26px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 10%, transparent);\n}\n\n.s8c7d6f_skeletonName {\n width: 55%;\n height: 14px;\n margin: 12px 12px 0;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.sf5179e_skeletonSummary {\n width: 92%;\n height: 12px;\n margin: 10px 12px 0;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.sbb0d25_skeletonSummaryShort {\n width: 68%;\n height: 12px;\n margin: 6px 12px 14px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.s520416_skeletonCard span {\n animation: dshShopSkeletonPulse 1.6s ease-in-out infinite;\n}\n\n@keyframes dshShopSkeletonPulse {\n 0%, 100% { opacity: 0.5; }\n 50% { opacity: 1; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .s520416_skeletonCard span { animation: none; opacity: 0.75; }\n .s520416_skeletonCard::after { content: none; }\n}\n\n.s73242a_actionButton {\n align-self: flex-start;\n padding: 5px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.s73242a_actionButton:hover {\n border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l2));\n color: var(--dsw-alias-brand-primary);\n}\n\n.s73242a_actionButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.s646509_toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.s232ec4_searchInput {\n width: min(280px, 100%);\n padding: 6px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n.s232ec4_searchInput:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.s1555aa_staleBlock {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.s3946f1_staleBadge {\n padding: 2px 8px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary) 40%, transparent);\n color: var(--dsw-alias-state-warn-primary);\n font-size: 12px;\n}\n\n.s668774_catalogHeading {\n margin: 2px 0 0;\n font-size: 16px;\n font-weight: 600;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-primary);\n}\n\n.sccc4d1_catalogStats {\n margin: -8px 0 0;\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s6de1ab_emptyLine {\n margin: 0;\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n/* The shelf: a responsive grid of cards. */\n.sf657f5_cards {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));\n gap: 12px;\n list-style: none;\n margin: 0;\n padding: 0;\n animation: dshShopCardsIn 220ms ease;\n}\n\n@keyframes dshShopCardsIn {\n from { opacity: 0; transform: translateY(3px); }\n to { opacity: 1; transform: none; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .sf657f5_cards { animation: none; }\n}\n\n.s65f383_card {\n position: relative;\n display: flex;\n flex-direction: column;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-1);\n overflow: hidden;\n transition: border-color 120ms ease, transform 120ms ease, box-shadow 120ms ease;\n /* Off-screen cards skip layout and paint; the placeholder keeps scrollbar\n * estimation sane before the browser has measured the real height. */\n content-visibility: auto;\n contain-intrinsic-size: auto 240px;\n}\n\n/* The sentinel that triggers the next shelf batch: zero visual footprint. */\n.sd7ee01_cardsSentry {\n height: 1px;\n margin: 0;\n padding: 0;\n}\n\n.s705cc6_showingLine {\n margin: 0;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s65f383_card:hover {\n border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l1));\n transform: translateY(-1px);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .s65f383_card { transition: none; }\n .s65f383_card:hover { transform: none; }\n}\n\n/* The signature: a spine and cover in the category's own hue. The hues are\n * fixed mid-bright colors that read on both themes; `other` deliberately\n * stays a neutral gray so the fallback category does not compete. */\n.s4ce25d_cardSpine {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 3px;\n background: var(--spine-hue, #8B8E96);\n}\n\n.s65f383_card[data-category='tool'] { --spine-hue: #4C8DFF; }\n.s65f383_card[data-category='provider'] { --spine-hue: #A78BFA; }\n.s65f383_card[data-category='ui'] { --spine-hue: #2DD4BF; }\n.s65f383_card[data-category='workflow'] { --spine-hue: #F59E0B; }\n.s65f383_card[data-category='integration'] { --spine-hue: #34D399; }\n.s65f383_card[data-category='other'] { --spine-hue: #8B8E96; }\n\n.s9e2bec_cardCover {\n display: block;\n padding: 5px 12px 5px 12px;\n border-bottom: 1px solid var(--dsw-alias-border-l1);\n background: color-mix(in srgb, var(--spine-hue, #8B8E96) 12%, transparent);\n}\n\n.s1a7c5b_coverLabel {\n display: block;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--spine-hue, #8B8E96);\n}\n\n.sa97234_entryHeader {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: 8px;\n padding: 10px 12px 6px;\n border: none;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n\n.sa97234_entryHeader:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n/* The name owns the full header width; badges sit on their own line below. */\n.sc3f149_name {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 13px;\n font-weight: 600;\n overflow-wrap: anywhere;\n color: var(--dsw-alias-label-primary);\n}\n\n.s513026_badges {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.s5e82cb_tierBadge {\n padding: 1px 7px;\n border-radius: 999px;\n font-size: 11px;\n line-height: 1.6;\n}\n\n.s5e82cb_tierBadge[data-tier='verified'] {\n color: var(--dsw-alias-state-success-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-success-primary) 40%, transparent);\n}\n\n.s5e82cb_tierBadge[data-tier='verified-stale'] {\n color: var(--dsw-alias-state-warn-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary) 40%, transparent);\n}\n\n.s5e82cb_tierBadge[data-tier='community'] {\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n border: 1px solid var(--dsw-alias-border-l2);\n}\n\n.s23e3e1_unclaimedBadge {\n padding: 1px 7px;\n border-radius: 999px;\n border: 1px dashed var(--dsw-alias-border-l2);\n font-size: 11px;\n line-height: 1.6;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s73f7fb_chevron, .s61393c_chevronOpen {\n color: var(--dsw-alias-label-secondary);\n transition: transform 120ms ease;\n}\n\n.s61393c_chevronOpen { transform: rotate(180deg); }\n\n@media (prefers-reduced-motion: reduce) {\n .s73f7fb_chevron, .s61393c_chevronOpen { transition: none; }\n}\n\n.s820ebf_body {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 0 12px 12px;\n}\n\n.sb8fbc8_summary {\n margin: 0;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.se507db_summaryZh {\n margin: -4px 0 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n display: -webkit-box;\n -webkit-line-clamp: 1;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.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.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.sc4f2ac_gate {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 10px;\n border: 1px solid var(--dsw-alias-state-warn-primary);\n border-radius: 8px;\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 8%, transparent);\n}\n\n.s559c38_gateTitle {\n margin: 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n.s3c0ace_gateBody {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-primary);\n}\n\n.s4b2373_gateActions {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n.s52b5a5_installing {\n margin: 0;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s64ab71_log {\n max-height: 160px;\n overflow: auto;\n margin: 0;\n padding: 8px;\n border-radius: 6px;\n background: var(--dsw-alias-bg-base);\n border: 1px solid var(--dsw-alias-border-l1);\n list-style: none;\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px;\n line-height: 1.5;\n}\n\n.s1531a8_logLine {\n white-space: pre-wrap;\n word-break: break-all;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sf20dda_notice {\n margin: 0;\n font-size: 12px;\n color: var(--dsw-alias-state-success-primary);\n}\n\n.seb9b08_failedHeading {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.sfd3021_failedDetail {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-primary);\n}\n\n.s50ba35_rejectedCode {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.se75489_rejectedDetail {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-primary);\n}\n\n/* Installed section. */\n.sa276cf_outdatedSection {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 6px;\n}\n\n.sceae6e_outdatedList {\n display: flex;\n flex-direction: column;\n gap: 8px;\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.s8d2072_outdatedRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n flex-wrap: wrap;\n padding: 10px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-1);\n}\n\n.s5c2545_outdatedInfo {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.s5c2545_outdatedInfo strong {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 13px;\n word-break: break-all;\n color: var(--dsw-alias-label-primary);\n}\n\n.s877c68_outdatedVersions {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sa1f556_outdatedActions {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n/* The enable/disable switch. */\n.s11c019_switch {\n position: relative;\n width: 32px;\n height: 18px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-2);\n cursor: pointer;\n transition: background 120ms ease, border-color 120ms ease;\n}\n\n.s7580e8_switchOn {\n border-color: var(--dsw-alias-state-success-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 55%, transparent);\n}\n\n.saab2b3_switchKnob {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 12px;\n height: 12px;\n border-radius: 999px;\n background: var(--dsw-alias-label-primary);\n transition: transform 120ms ease, background 120ms ease;\n}\n\n.s7580e8_switchOn .saab2b3_switchKnob {\n transform: translateX(14px);\n background: var(--dsw-alias-bg-base);\n}\n\n.s11c019_switch:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .s11c019_switch, .saab2b3_switchKnob { transition: none; }\n}\n";
|
|
4804
|
+
const css = "/* Shop tab styles — a market shelf inside the settings surface. The visual\n * language is the dsh theme's alias tokens ONLY (the four tokens the previous\n * styles referenced that do not exist — bg-layer-3, interactive-bg-hover,\n * state-error, state-success — are corrected to bg-layer-2 and the\n * state-*-primary set). The signature is the category spine: one HUE per\n * category (six fixed hues, not the brand token — the brand resolves to\n * near-black in the light theme, so six brand opacities read as six shades\n * of gray), with the category name as the cover label in the same hue. Class names are mapped to content-derived names at bundle time\n * (tsdown.s8583e3_client.s942a79_config.s5f67aa_ts) and stubbed in tests; only the keys are relied\n * on.\n *\n * One token rule the theme forces: the background LAYERS do not carry\n * contrast. In the light theme bg-base, bg-layer-1, bg-layer-2 and\n * bg-layer-3 all resolve to the same white; only the dark theme spreads them\n * (950/875/850/800). So a layer-2 fill on a layer-1 ground is invisible for\n * half of all users, and anything whose fill is its ONLY affordance — the\n * loading skeleton, the borderless capability chips — derives its fill from\n * label-primary instead, which inverts with the theme by construction.\n * Elements that also carry a border or text may keep a layer fill.\n * `css-tokens.s8583e3_client.sfd9d8a_spec.s5f67aa_ts` pins this. */\n\n.s255e5a_panel {\n display: flex;\n flex-direction: column;\n gap: 14px;\n}\n\n.s44a3ec_stateLine {\n margin: 0;\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n/* Visually hidden but screen-reader readable (the loading copy while the\n * skeleton stands in visually). */\n.s3a0ece_srOnly {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n border: 0;\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n overflow: hidden;\n white-space: nowrap;\n}\n\n/* The loading shelf: ghost cards with the same geometry as the real grid, so\n * the swap to content is a same-shape handoff instead of a jump. */\n.se0c3b4_skeletonGrid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));\n gap: 12px;\n}\n\n.s520416_skeletonCard {\n position: relative;\n display: flex;\n flex-direction: column;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-1);\n overflow: hidden;\n}\n\n/* The shimmer sweep: one gradient band travels across each ghost card while\n * the bars breathe, so the loading state reads as \"the shelf is scanning\". */\n.s520416_skeletonCard::after {\n content: '';\n position: absolute;\n inset: 0;\n background: linear-gradient(\n 100deg,\n transparent 25%,\n color-mix(in srgb, var(--dsw-alias-label-primary) 14%, transparent) 50%,\n transparent 75%\n );\n transform: translateX(-100%);\n animation: dshShopSkeletonSweep 1.4s ease-in-out infinite;\n pointer-events: none;\n}\n\n@keyframes dshShopSkeletonSweep {\n to { transform: translateX(100%); }\n}\n\n.s30e662_skeletonCover {\n display: block;\n height: 26px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 10%, transparent);\n}\n\n.s8c7d6f_skeletonName {\n width: 55%;\n height: 14px;\n margin: 12px 12px 0;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.sf5179e_skeletonSummary {\n width: 92%;\n height: 12px;\n margin: 10px 12px 0;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.sbb0d25_skeletonSummaryShort {\n width: 68%;\n height: 12px;\n margin: 6px 12px 14px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.s520416_skeletonCard span {\n animation: dshShopSkeletonPulse 1.6s ease-in-out infinite;\n}\n\n@keyframes dshShopSkeletonPulse {\n 0%, 100% { opacity: 0.5; }\n 50% { opacity: 1; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .s520416_skeletonCard span { animation: none; opacity: 0.75; }\n .s520416_skeletonCard::after { content: none; }\n}\n\n.s73242a_actionButton {\n align-self: flex-start;\n padding: 5px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.s73242a_actionButton:hover {\n border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l2));\n color: var(--dsw-alias-brand-primary);\n}\n\n.s73242a_actionButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.s646509_toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.s232ec4_searchInput {\n width: min(280px, 100%);\n padding: 6px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n.s232ec4_searchInput:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.s1555aa_staleBlock {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.s3946f1_staleBadge {\n padding: 2px 8px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary) 40%, transparent);\n color: var(--dsw-alias-state-warn-primary);\n font-size: 12px;\n}\n\n.sb2c859_categoryBar {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin-top: 10px;\n}\n\n.s26d58e_categoryButton {\n padding: 3px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n cursor: pointer;\n}\n\n.s26d58e_categoryButton:hover {\n border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l2));\n color: var(--dsw-alias-brand-primary);\n}\n\n.s5f68c4_categoryButtonOn {\n border-color: var(--dsw-alias-brand-primary);\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n color: var(--dsw-alias-brand-primary);\n font-weight: 600;\n}\n\n.s26d58e_categoryButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.s668774_catalogHeading {\n margin: 2px 0 0;\n font-size: 16px;\n font-weight: 600;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-primary);\n}\n\n.sccc4d1_catalogStats {\n margin: -8px 0 0;\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s6de1ab_emptyLine {\n margin: 0;\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n/* The shelf: a responsive grid of cards. */\n.sf657f5_cards {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));\n gap: 12px;\n list-style: none;\n margin: 0;\n padding: 0;\n animation: dshShopCardsIn 220ms ease;\n}\n\n@keyframes dshShopCardsIn {\n from { opacity: 0; transform: translateY(3px); }\n to { opacity: 1; transform: none; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .sf657f5_cards { animation: none; }\n}\n\n.s65f383_card {\n position: relative;\n display: flex;\n flex-direction: column;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-1);\n overflow: hidden;\n transition: border-color 120ms ease, transform 120ms ease, box-shadow 120ms ease;\n /* Off-screen cards skip layout and paint; the placeholder keeps scrollbar\n * estimation sane before the browser has measured the real height. */\n content-visibility: auto;\n contain-intrinsic-size: auto 240px;\n}\n\n/* The sentinel that triggers the next shelf batch: zero visual footprint. */\n.sd7ee01_cardsSentry {\n height: 1px;\n margin: 0;\n padding: 0;\n}\n\n.s705cc6_showingLine {\n margin: 0;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s65f383_card:hover {\n border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l1));\n transform: translateY(-1px);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .s65f383_card { transition: none; }\n .s65f383_card:hover { transform: none; }\n}\n\n/* The signature: a spine and cover in the category's own hue. The hues are\n * fixed mid-bright colors that read on both themes; `other` deliberately\n * stays a neutral gray so the fallback category does not compete. */\n.s4ce25d_cardSpine {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 3px;\n background: var(--spine-hue, #8B8E96);\n}\n\n.s65f383_card[data-category='tool'] { --spine-hue: #4C8DFF; }\n.s65f383_card[data-category='provider'] { --spine-hue: #A78BFA; }\n.s65f383_card[data-category='ui'] { --spine-hue: #2DD4BF; }\n.s65f383_card[data-category='workflow'] { --spine-hue: #F59E0B; }\n.s65f383_card[data-category='integration'] { --spine-hue: #34D399; }\n.s65f383_card[data-category='other'] { --spine-hue: #8B8E96; }\n\n.s9e2bec_cardCover {\n display: block;\n padding: 5px 12px 5px 12px;\n border-bottom: 1px solid var(--dsw-alias-border-l1);\n background: color-mix(in srgb, var(--spine-hue, #8B8E96) 12%, transparent);\n}\n\n.s1a7c5b_coverLabel {\n display: block;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--spine-hue, #8B8E96);\n}\n\n.sa97234_entryHeader {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n gap: 8px;\n padding: 10px 12px 6px;\n border: none;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n\n.sa97234_entryHeader:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n/* The name owns the full header width; badges sit on their own line below. */\n.sc3f149_name {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 13px;\n font-weight: 600;\n overflow-wrap: anywhere;\n color: var(--dsw-alias-label-primary);\n}\n\n.s513026_badges {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.s5e82cb_tierBadge {\n padding: 1px 7px;\n border-radius: 999px;\n font-size: 11px;\n line-height: 1.6;\n}\n\n.s5e82cb_tierBadge[data-tier='verified'] {\n color: var(--dsw-alias-state-success-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-success-primary) 40%, transparent);\n}\n\n.s5e82cb_tierBadge[data-tier='verified-stale'] {\n color: var(--dsw-alias-state-warn-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary) 40%, transparent);\n}\n\n.s5e82cb_tierBadge[data-tier='community'] {\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n border: 1px solid var(--dsw-alias-border-l2);\n}\n\n.s23e3e1_unclaimedBadge {\n padding: 1px 7px;\n border-radius: 999px;\n border: 1px dashed var(--dsw-alias-border-l2);\n font-size: 11px;\n line-height: 1.6;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sff43b4_starsBadge {\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s73f7fb_chevron, .s61393c_chevronOpen {\n color: var(--dsw-alias-label-secondary);\n transition: transform 120ms ease;\n}\n\n.s61393c_chevronOpen { transform: rotate(180deg); }\n\n@media (prefers-reduced-motion: reduce) {\n .s73f7fb_chevron, .s61393c_chevronOpen { transition: none; }\n}\n\n.s820ebf_body {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 0 12px 12px;\n}\n\n.sb8fbc8_summary {\n margin: 0;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.se507db_summaryZh {\n margin: -4px 0 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n display: -webkit-box;\n -webkit-line-clamp: 1;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n/* Expanded cards show the full summary: the clamp is lifted, never raised —\n * a higher number would still truncate the author's text, just later. */\n.s10baf0_summaryExpanded {\n -webkit-line-clamp: unset;\n display: block;\n}\n\n.sb9dba9_summaryZhExpanded {\n -webkit-line-clamp: unset;\n display: block;\n}\n\n.s9d2029_capabilitiesBlock {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.s3bdbd5_capabilitiesNote {\n margin: 0;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s441702_capabilities {\n display: flex;\n flex-wrap: wrap;\n gap: 4px;\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.s441702_capabilities li {\n padding: 1px 6px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 6%, transparent);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sde3f0c_detail {\n border-top: 1px solid var(--dsw-alias-border-l1);\n padding-top: 8px;\n}\n\n.se98179_detailRows {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin: 0;\n}\n\n.sfb4d2f_detailRow {\n display: flex;\n justify-content: space-between;\n gap: 12px;\n font-size: 12px;\n}\n\n.sfb4d2f_detailRow dt {\n color: var(--dsw-alias-label-secondary);\n}\n\n.sfb4d2f_detailRow dd {\n margin: 0;\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n overflow-wrap: anywhere;\n text-align: right;\n color: var(--dsw-alias-label-primary);\n}\n\n.sfb4d2f_detailRow a {\n color: var(--dsw-alias-brand-primary);\n text-decoration: none;\n}\n\n.sfb4d2f_detailRow a:hover {\n text-decoration: underline;\n}\n\n.sfb4d2f_detailRow a:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.sd58c6d_reviewedLine {\n margin: 8px 0 0;\n padding: 6px 8px;\n border-radius: 6px;\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 12%, transparent);\n font-size: 12px;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n/* Install flow. */\n.s4d374c_installPanel {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 2px;\n}\n\n.se7aaed_installButton, .sb43a49_confirmButton, .s6e45d1_cancelButton {\n align-self: flex-start;\n padding: 5px 14px;\n border-radius: 6px;\n font-size: 13px;\n cursor: pointer;\n}\n\n.se7aaed_installButton {\n border: 1px solid var(--dsw-alias-brand-primary);\n background: var(--dsw-alias-brand-primary);\n color: var(--dsw-alias-bg-base);\n}\n\n.se7aaed_installButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 88%, var(--dsw-alias-label-primary));\n}\n\n.sb43a49_confirmButton {\n border: 1px solid var(--dsw-alias-brand-primary);\n background: var(--dsw-alias-brand-primary);\n color: var(--dsw-alias-bg-base);\n}\n\n.sb43a49_confirmButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 88%, var(--dsw-alias-label-primary));\n}\n\n.s6e45d1_cancelButton {\n border: 1px solid var(--dsw-alias-border-l2);\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n}\n\n.s6e45d1_cancelButton:hover {\n border-color: var(--dsw-alias-label-secondary);\n}\n\n.se7aaed_installButton:focus-visible, .sb43a49_confirmButton:focus-visible, .s6e45d1_cancelButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.sc4f2ac_gate {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 10px;\n border: 1px solid var(--dsw-alias-state-warn-primary);\n border-radius: 8px;\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 8%, transparent);\n}\n\n.s559c38_gateTitle {\n margin: 0;\n font-size: 13px;\n font-weight: 600;\n color: var(--dsw-alias-state-warn-primary);\n}\n\n.s3c0ace_gateBody {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-primary);\n}\n\n.s4b2373_gateActions {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n.s52b5a5_installing {\n margin: 0;\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s64ab71_log {\n max-height: 160px;\n overflow: auto;\n margin: 0;\n padding: 8px;\n border-radius: 6px;\n background: var(--dsw-alias-bg-base);\n border: 1px solid var(--dsw-alias-border-l1);\n list-style: none;\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px;\n line-height: 1.5;\n}\n\n.s1531a8_logLine {\n white-space: pre-wrap;\n word-break: break-all;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sf20dda_notice {\n margin: 0;\n font-size: 12px;\n color: var(--dsw-alias-state-success-primary);\n}\n\n.seb9b08_failedHeading {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.sfd3021_failedDetail {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-primary);\n}\n\n.s50ba35_rejectedCode {\n margin: 0;\n font-size: 12px;\n font-weight: 600;\n color: var(--dsw-alias-state-error-primary);\n}\n\n.se75489_rejectedDetail {\n margin: 0;\n font-size: 12px;\n line-height: 1.6;\n color: var(--dsw-alias-label-primary);\n}\n\n/* Installed section. */\n.sa276cf_outdatedSection {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 6px;\n}\n\n.sceae6e_outdatedList {\n display: flex;\n flex-direction: column;\n gap: 8px;\n list-style: none;\n margin: 0;\n padding: 0;\n}\n\n.s8d2072_outdatedRow {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n flex-wrap: wrap;\n padding: 10px 12px;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 8px;\n background: var(--dsw-alias-bg-layer-1);\n}\n\n.s5c2545_outdatedInfo {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.s5c2545_outdatedInfo strong {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 13px;\n word-break: break-all;\n color: var(--dsw-alias-label-primary);\n}\n\n.s877c68_outdatedVersions {\n font-size: 12px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sa1f556_outdatedActions {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n\n/* The enable/disable switch. */\n.s11c019_switch {\n position: relative;\n width: 32px;\n height: 18px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-2);\n cursor: pointer;\n transition: background 120ms ease, border-color 120ms ease;\n}\n\n.s7580e8_switchOn {\n border-color: var(--dsw-alias-state-success-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 55%, transparent);\n}\n\n.saab2b3_switchKnob {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 12px;\n height: 12px;\n border-radius: 999px;\n background: var(--dsw-alias-label-primary);\n transition: transform 120ms ease, background 120ms ease;\n}\n\n.s7580e8_switchOn .saab2b3_switchKnob {\n transform: translateX(14px);\n background: var(--dsw-alias-bg-base);\n}\n\n.s11c019_switch:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .s11c019_switch, .saab2b3_switchKnob { transition: none; }\n}\n";
|
|
4604
4805
|
if (typeof document !== "undefined" && !document.querySelector("style[data-plugin-css=\"e3675a89\"]")) {
|
|
4605
4806
|
const tag = document.createElement("style");
|
|
4606
4807
|
tag.dataset.pluginCss = "e3675a89";
|
|
@@ -4626,6 +4827,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4626
4827
|
"searchInput": "s232ec4_searchInput",
|
|
4627
4828
|
"staleBlock": "s1555aa_staleBlock",
|
|
4628
4829
|
"staleBadge": "s3946f1_staleBadge",
|
|
4830
|
+
"categoryBar": "sb2c859_categoryBar",
|
|
4831
|
+
"categoryButton": "s26d58e_categoryButton",
|
|
4832
|
+
"categoryButtonOn": "s5f68c4_categoryButtonOn",
|
|
4629
4833
|
"catalogHeading": "s668774_catalogHeading",
|
|
4630
4834
|
"catalogStats": "sccc4d1_catalogStats",
|
|
4631
4835
|
"emptyLine": "s6de1ab_emptyLine",
|
|
@@ -4641,11 +4845,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4641
4845
|
"badges": "s513026_badges",
|
|
4642
4846
|
"tierBadge": "s5e82cb_tierBadge",
|
|
4643
4847
|
"unclaimedBadge": "s23e3e1_unclaimedBadge",
|
|
4848
|
+
"starsBadge": "sff43b4_starsBadge",
|
|
4644
4849
|
"chevron": "s73f7fb_chevron",
|
|
4645
4850
|
"chevronOpen": "s61393c_chevronOpen",
|
|
4646
4851
|
"body": "s820ebf_body",
|
|
4647
4852
|
"summary": "sb8fbc8_summary",
|
|
4648
4853
|
"summaryZh": "se507db_summaryZh",
|
|
4854
|
+
"summaryExpanded": "s10baf0_summaryExpanded",
|
|
4855
|
+
"summaryZhExpanded": "sb9dba9_summaryZhExpanded",
|
|
4649
4856
|
"capabilitiesBlock": "s9d2029_capabilitiesBlock",
|
|
4650
4857
|
"capabilitiesNote": "s3bdbd5_capabilitiesNote",
|
|
4651
4858
|
"capabilities": "s441702_capabilities",
|
|
@@ -4703,7 +4910,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4703
4910
|
* (name, tier, category, unclaimed marker), the plain-text summary in both
|
|
4704
4911
|
* languages, the self-declared capabilities, the detail section, and the
|
|
4705
4912
|
* install controls. */
|
|
4706
|
-
const EntryCard = (0, react.memo)(function EntryCard({ entry, t, install, installStatus }) {
|
|
4913
|
+
const EntryCard = (0, react.memo)(function EntryCard({ entry, stars, t, install, installStatus }) {
|
|
4707
4914
|
const [open, setOpen] = (0, react.useState)(false);
|
|
4708
4915
|
const detailId = (0, react.useId)();
|
|
4709
4916
|
const summary = entry.catalog?.summary;
|
|
@@ -4746,6 +4953,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4746
4953
|
className: _dsh_plugin_shop_css_e3675a89_default.unclaimedBadge,
|
|
4747
4954
|
children: t("unclaimed")
|
|
4748
4955
|
}),
|
|
4956
|
+
stars !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
4957
|
+
className: _dsh_plugin_shop_css_e3675a89_default.starsBadge,
|
|
4958
|
+
role: "img",
|
|
4959
|
+
"aria-label": t("stars", { count: stars }),
|
|
4960
|
+
children: ["★ ", formatStars(stars)]
|
|
4961
|
+
}),
|
|
4749
4962
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronIcon, { open })
|
|
4750
4963
|
]
|
|
4751
4964
|
})]
|
|
@@ -4754,11 +4967,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4754
4967
|
className: _dsh_plugin_shop_css_e3675a89_default.body,
|
|
4755
4968
|
children: [
|
|
4756
4969
|
summary !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4757
|
-
className: _dsh_plugin_shop_css_e3675a89_default.summary,
|
|
4970
|
+
className: open ? `${_dsh_plugin_shop_css_e3675a89_default.summary} ${_dsh_plugin_shop_css_e3675a89_default.summaryExpanded}` : _dsh_plugin_shop_css_e3675a89_default.summary,
|
|
4758
4971
|
children: summary.en
|
|
4759
4972
|
}),
|
|
4760
4973
|
summary?.zh !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4761
|
-
className: _dsh_plugin_shop_css_e3675a89_default.summaryZh,
|
|
4974
|
+
className: open ? `${_dsh_plugin_shop_css_e3675a89_default.summaryZh} ${_dsh_plugin_shop_css_e3675a89_default.summaryZhExpanded}` : _dsh_plugin_shop_css_e3675a89_default.summaryZh,
|
|
4762
4975
|
children: summary.zh
|
|
4763
4976
|
}),
|
|
4764
4977
|
entry.catalog !== void 0 && entry.catalog.capabilities.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -4783,7 +4996,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
4783
4996
|
}),
|
|
4784
4997
|
entry.repository !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4785
4998
|
className: _dsh_plugin_shop_css_e3675a89_default.detailRow,
|
|
4786
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", { children: t("repository") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dd", { children: entry.repository
|
|
4999
|
+
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", {
|
|
5000
|
+
href: entry.repository,
|
|
5001
|
+
target: "_blank",
|
|
5002
|
+
rel: "noopener noreferrer",
|
|
5003
|
+
children: entry.repository
|
|
5004
|
+
}) : entry.repository })]
|
|
4787
5005
|
}),
|
|
4788
5006
|
entry.license !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
4789
5007
|
className: _dsh_plugin_shop_css_e3675a89_default.detailRow,
|
|
@@ -5044,6 +5262,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5044
5262
|
const [outdatedState, setOutdatedState] = (0, react.useState)({ kind: "loading" });
|
|
5045
5263
|
const [request, setRequest] = (0, react.useState)({ kind: "initial" });
|
|
5046
5264
|
const [query, setQuery] = (0, react.useState)("");
|
|
5265
|
+
const [category, setCategory] = (0, react.useState)(null);
|
|
5047
5266
|
const incremental = typeof IntersectionObserver !== "undefined";
|
|
5048
5267
|
const [visibleCount, setVisibleCount] = (0, react.useState)(48);
|
|
5049
5268
|
const sentinelRef = (0, react.useRef)(null);
|
|
@@ -5090,13 +5309,20 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5090
5309
|
const q = query.trim().toLowerCase();
|
|
5091
5310
|
return catalogState.result.plugins.filter((entry) => {
|
|
5092
5311
|
if (isShopLike(entry.name)) return false;
|
|
5312
|
+
if (category !== null && categoryKey(entry) !== categoryLocaleKey(category)) return false;
|
|
5093
5313
|
if (q === "") return true;
|
|
5094
5314
|
const summaryEn = entry.catalog?.summary.en ?? "";
|
|
5095
5315
|
const summaryZh = entry.catalog?.summary.zh ?? "";
|
|
5096
5316
|
return entry.name.toLowerCase().includes(q) || summaryEn.toLowerCase().includes(q) || summaryZh.toLowerCase().includes(q);
|
|
5097
5317
|
});
|
|
5098
|
-
}, [
|
|
5318
|
+
}, [
|
|
5319
|
+
catalogState,
|
|
5320
|
+
query,
|
|
5321
|
+
category
|
|
5322
|
+
]);
|
|
5099
5323
|
filteredLenRef.current = filtered.length;
|
|
5324
|
+
const stars = catalogState.kind === "ready" ? catalogState.result.stars : {};
|
|
5325
|
+
const sorted = (0, react.useMemo)(() => sortByStars(filtered, stars), [filtered, stars]);
|
|
5100
5326
|
(0, react.useEffect)(() => {
|
|
5101
5327
|
if (!incremental) return;
|
|
5102
5328
|
const node = sentinelRef.current;
|
|
@@ -5111,7 +5337,17 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5111
5337
|
visibleCount,
|
|
5112
5338
|
filtered.length
|
|
5113
5339
|
]);
|
|
5114
|
-
const visible = incremental ?
|
|
5340
|
+
const visible = incremental ? sorted.slice(0, visibleCount) : sorted;
|
|
5341
|
+
const categoryCounts = (0, react.useMemo)(() => {
|
|
5342
|
+
const counts = /* @__PURE__ */ new Map();
|
|
5343
|
+
if (catalogState.kind === "ready") for (const entry of catalogState.result.plugins) {
|
|
5344
|
+
if (isShopLike(entry.name)) continue;
|
|
5345
|
+
const key = categoryKey(entry);
|
|
5346
|
+
const bare = CATEGORY_ORDER.find((c) => categoryLocaleKey(c) === key);
|
|
5347
|
+
if (bare !== void 0) counts.set(bare, (counts.get(bare) ?? 0) + 1);
|
|
5348
|
+
}
|
|
5349
|
+
return counts;
|
|
5350
|
+
}, [catalogState]);
|
|
5115
5351
|
const tiers = (0, react.useMemo)(() => {
|
|
5116
5352
|
const map = /* @__PURE__ */ new Map();
|
|
5117
5353
|
if (catalogState.kind === "ready") for (const entry of catalogState.result.plugins) map.set(entry.name, entry.tier);
|
|
@@ -5182,6 +5418,38 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5182
5418
|
})]
|
|
5183
5419
|
})]
|
|
5184
5420
|
}),
|
|
5421
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
5422
|
+
className: _dsh_plugin_shop_css_e3675a89_default.categoryBar,
|
|
5423
|
+
role: "group",
|
|
5424
|
+
"aria-label": t("catalog"),
|
|
5425
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5426
|
+
type: "button",
|
|
5427
|
+
className: category === null ? `${_dsh_plugin_shop_css_e3675a89_default.categoryButton} ${_dsh_plugin_shop_css_e3675a89_default.categoryButtonOn}` : _dsh_plugin_shop_css_e3675a89_default.categoryButton,
|
|
5428
|
+
"aria-pressed": category === null,
|
|
5429
|
+
onClick: () => {
|
|
5430
|
+
setCategory(null);
|
|
5431
|
+
setVisibleCount(48);
|
|
5432
|
+
},
|
|
5433
|
+
children: [
|
|
5434
|
+
t("all"),
|
|
5435
|
+
" ",
|
|
5436
|
+
[...categoryCounts.values()].reduce((a, b) => a + b, 0)
|
|
5437
|
+
]
|
|
5438
|
+
}), CATEGORY_ORDER.map((key) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
5439
|
+
type: "button",
|
|
5440
|
+
className: category === key ? `${_dsh_plugin_shop_css_e3675a89_default.categoryButton} ${_dsh_plugin_shop_css_e3675a89_default.categoryButtonOn}` : _dsh_plugin_shop_css_e3675a89_default.categoryButton,
|
|
5441
|
+
"aria-pressed": category === key,
|
|
5442
|
+
onClick: () => {
|
|
5443
|
+
setCategory(key);
|
|
5444
|
+
setVisibleCount(48);
|
|
5445
|
+
},
|
|
5446
|
+
children: [
|
|
5447
|
+
t(categoryLocaleKey(key)),
|
|
5448
|
+
" ",
|
|
5449
|
+
categoryCounts.get(key) ?? 0
|
|
5450
|
+
]
|
|
5451
|
+
}, key))]
|
|
5452
|
+
}),
|
|
5185
5453
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
5186
5454
|
className: _dsh_plugin_shop_css_e3675a89_default.catalogHeading,
|
|
5187
5455
|
children: t("catalog")
|
|
@@ -5203,6 +5471,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
|
|
|
5203
5471
|
className: _dsh_plugin_shop_css_e3675a89_default.cards,
|
|
5204
5472
|
children: [visible.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EntryCard, {
|
|
5205
5473
|
entry,
|
|
5474
|
+
stars: stars[entry.name],
|
|
5206
5475
|
t,
|
|
5207
5476
|
install,
|
|
5208
5477
|
installStatus
|
package/lib/index.js
CHANGED
|
@@ -63,7 +63,11 @@ const pointerSchema = z.object({
|
|
|
63
63
|
plugins: z.object({
|
|
64
64
|
url: z.string(),
|
|
65
65
|
sha256: z.string()
|
|
66
|
-
})
|
|
66
|
+
}),
|
|
67
|
+
stars: z.object({
|
|
68
|
+
url: z.string(),
|
|
69
|
+
sha256: z.string()
|
|
70
|
+
}).optional()
|
|
67
71
|
});
|
|
68
72
|
const nodeFs = {
|
|
69
73
|
exists: (path) => existsSync(path),
|
|
@@ -85,13 +89,28 @@ function resolveDataUrl(baseUrl, url) {
|
|
|
85
89
|
if (resolved.origin !== new URL(baseUrl).origin) throw new Error("catalog data url must be relative to the catalog base");
|
|
86
90
|
return resolved.href;
|
|
87
91
|
}
|
|
92
|
+
/** Read and verify a cached/fetched stars sidecar; ANY irregularity degrades
|
|
93
|
+
* to an empty map — stars are advisory (spec §5). */
|
|
94
|
+
function parseStarsText(text) {
|
|
95
|
+
try {
|
|
96
|
+
const parsed = JSON.parse(text);
|
|
97
|
+
if (typeof parsed.stars !== "object" || parsed.stars === null) return {};
|
|
98
|
+
const out = {};
|
|
99
|
+
for (const [key, value] of Object.entries(parsed.stars)) if (typeof value === "number") out[key] = value;
|
|
100
|
+
return out;
|
|
101
|
+
} catch {
|
|
102
|
+
return {};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
88
105
|
/**
|
|
89
106
|
* Load the catalog snapshot: fetch the pointer, verify the data file's sha256
|
|
90
107
|
* against it, cache both on disk, and serve the cached copy with `stale: true`
|
|
91
108
|
* only when the transport itself failed — the fetch threw or returned a
|
|
92
109
|
* non-2xx (§10). A schemaVersion higher than this build supports, a malformed
|
|
93
110
|
* pointer or data file, an absolute data URL, or a sha256 mismatch — fresh or
|
|
94
|
-
* cached — throws even when a cache exists; never silently degraded.
|
|
111
|
+
* cached — throws even when a cache exists; never silently degraded. The stars
|
|
112
|
+
* sidecar is the sole exception: any irregularity there, a refused url
|
|
113
|
+
* included, degrades to no stars (spec §5).
|
|
95
114
|
*/
|
|
96
115
|
async function loadCatalog(options) {
|
|
97
116
|
const { baseUrl, cacheDir, refresh = false, fetchImpl = fetch, now = () => /* @__PURE__ */ new Date(), fsImpl = nodeFs } = options;
|
|
@@ -118,11 +137,17 @@ async function loadCatalog(options) {
|
|
|
118
137
|
if (actual !== pointer.plugins.sha256) throw new Error(`cached catalog data failed integrity check: expected ${pointer.plugins.sha256}, got ${actual}`);
|
|
119
138
|
const data = dataSchema.parse(JSON.parse(dataText));
|
|
120
139
|
if (data.schemaVersion > 2) throw new Error(`catalog schemaVersion ${data.schemaVersion} is newer than this build supports (2)`);
|
|
140
|
+
let stars = {};
|
|
141
|
+
if (pointer.stars !== void 0) try {
|
|
142
|
+
const starsText = fsImpl.read(join(cacheDir, basename(pointer.stars.url)));
|
|
143
|
+
if (createHash("sha256").update(starsText).digest("hex") === pointer.stars.sha256) stars = parseStarsText(starsText);
|
|
144
|
+
} catch {}
|
|
121
145
|
return {
|
|
122
146
|
schemaVersion: pointer.schemaVersion,
|
|
123
147
|
builtAt: pointer.builtAt,
|
|
124
148
|
entries: data.plugins,
|
|
125
|
-
denied: data.denied
|
|
149
|
+
denied: data.denied,
|
|
150
|
+
stars
|
|
126
151
|
};
|
|
127
152
|
} catch {
|
|
128
153
|
return null;
|
|
@@ -171,11 +196,23 @@ async function loadCatalog(options) {
|
|
|
171
196
|
if (actual !== pointer.plugins.sha256) throw new Error(`catalog data failed integrity check: expected ${pointer.plugins.sha256}, got ${actual}`);
|
|
172
197
|
const data = dataSchema.parse(JSON.parse(dataText));
|
|
173
198
|
if (data.schemaVersion > 2) throw new Error(`catalog schemaVersion ${data.schemaVersion} is newer than this build supports (2)`);
|
|
199
|
+
let stars = {};
|
|
200
|
+
if (pointer.stars !== void 0) try {
|
|
201
|
+
const starsResponse = await fetchImpl(resolveDataUrl(baseUrl, pointer.stars.url));
|
|
202
|
+
if (starsResponse.ok) {
|
|
203
|
+
const starsText = await starsResponse.text();
|
|
204
|
+
if (createHash("sha256").update(starsText).digest("hex") === pointer.stars.sha256) {
|
|
205
|
+
stars = parseStarsText(starsText);
|
|
206
|
+
fsImpl.write(join(cacheDir, basename(pointer.stars.url)), starsText);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
} catch {}
|
|
174
210
|
const snapshot = {
|
|
175
211
|
schemaVersion: pointer.schemaVersion,
|
|
176
212
|
builtAt: pointer.builtAt,
|
|
177
213
|
entries: data.plugins,
|
|
178
|
-
denied: data.denied
|
|
214
|
+
denied: data.denied,
|
|
215
|
+
stars
|
|
179
216
|
};
|
|
180
217
|
fsImpl.write(indexPath, JSON.stringify(pointer));
|
|
181
218
|
fsImpl.write(join(cacheDir, basename(pointer.plugins.url)), dataText);
|
|
@@ -626,7 +663,8 @@ let ShopGateway = (() => {
|
|
|
626
663
|
builtAt: snapshot.builtAt,
|
|
627
664
|
stale,
|
|
628
665
|
plugins: snapshot.entries,
|
|
629
|
-
denied: snapshot.denied
|
|
666
|
+
denied: snapshot.denied,
|
|
667
|
+
stars: snapshot.stars
|
|
630
668
|
};
|
|
631
669
|
}
|
|
632
670
|
/**
|
package/lib/typert.host.js
CHANGED
|
@@ -36,6 +36,7 @@ const dsh_plugin_shop_shop_catalog_result$schema = z.object({
|
|
|
36
36
|
'name': z.string(),
|
|
37
37
|
'detail': z.string(),
|
|
38
38
|
})),
|
|
39
|
+
'stars': z.record(z.string(), z.number()),
|
|
39
40
|
})
|
|
40
41
|
const dsh_plugin_shop_shop_installStart_parameter_0$schema = z.object({
|
|
41
42
|
'name': z.string(),
|
|
@@ -104,7 +105,7 @@ export const TYPERT = {
|
|
|
104
105
|
typeSymbol: 'dsh-plugin-shop/types#ShopCatalogResult',
|
|
105
106
|
schema: dsh_plugin_shop_shop_catalog_result$schema,
|
|
106
107
|
},
|
|
107
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
108
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":180,"column":9},
|
|
108
109
|
},
|
|
109
110
|
{
|
|
110
111
|
id: 'dsh-plugin-shop#shop/installStart',
|
|
@@ -130,7 +131,7 @@ export const TYPERT = {
|
|
|
130
131
|
typeSymbol: 'dsh-plugin-shop/types#ShopInstallResult',
|
|
131
132
|
schema: dsh_plugin_shop_shop_installStart_result$schema,
|
|
132
133
|
},
|
|
133
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
134
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":206,"column":9},
|
|
134
135
|
},
|
|
135
136
|
{
|
|
136
137
|
id: 'dsh-plugin-shop#shop/installStatus',
|
|
@@ -155,7 +156,7 @@ export const TYPERT = {
|
|
|
155
156
|
typeSymbol: 'dsh-plugin-shop/types#ShopInstallStatusResult',
|
|
156
157
|
schema: dsh_plugin_shop_shop_installStatus_result$schema,
|
|
157
158
|
},
|
|
158
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
159
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":244,"column":3},
|
|
159
160
|
},
|
|
160
161
|
{
|
|
161
162
|
id: 'dsh-plugin-shop#shop/outdated',
|
|
@@ -170,7 +171,7 @@ export const TYPERT = {
|
|
|
170
171
|
typeSymbol: 'dsh-plugin-shop#shop/outdated:result',
|
|
171
172
|
schema: dsh_plugin_shop_shop_outdated_result$schema,
|
|
172
173
|
},
|
|
173
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
174
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":252,"column":9},
|
|
174
175
|
},
|
|
175
176
|
{
|
|
176
177
|
id: 'dsh-plugin-shop#shop/setEnabled',
|
|
@@ -195,7 +196,7 @@ export const TYPERT = {
|
|
|
195
196
|
typeSymbol: 'dsh-plugin-shop/types#ShopSetEnabledResult',
|
|
196
197
|
schema: dsh_plugin_shop_shop_setEnabled_result$schema,
|
|
197
198
|
},
|
|
198
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
199
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":149,"column":3},
|
|
199
200
|
},
|
|
200
201
|
],
|
|
201
202
|
model: {
|
|
@@ -285,7 +286,7 @@ export const TYPERT = {
|
|
|
285
286
|
},
|
|
286
287
|
{
|
|
287
288
|
"name": "ShopCatalogResult",
|
|
288
|
-
"declaration": "export interface ShopCatalogResult {\n schemaVersion: number;\n builtAt: string;\n stale: boolean;\n plugins: CatalogEntry[];\n denied: DeniedEntry[];\n}"
|
|
289
|
+
"declaration": "export interface ShopCatalogResult {\n schemaVersion: number;\n builtAt: string;\n stale: boolean;\n plugins: CatalogEntry[];\n denied: DeniedEntry[];\n stars: Record<string, number>;\n}"
|
|
289
290
|
},
|
|
290
291
|
{
|
|
291
292
|
"name": "ShopInstallResult",
|
|
@@ -36,6 +36,7 @@ const dsh_plugin_shop_shop_catalog_result$schema = z.object({
|
|
|
36
36
|
'name': z.string(),
|
|
37
37
|
'detail': z.string(),
|
|
38
38
|
})),
|
|
39
|
+
'stars': z.record(z.string(), z.number()),
|
|
39
40
|
})
|
|
40
41
|
const dsh_plugin_shop_shop_installStart_parameter_0$schema = z.object({
|
|
41
42
|
'name': z.string(),
|
|
@@ -101,7 +102,7 @@ export const TYPERT_REMOTE = {
|
|
|
101
102
|
typeSymbol: 'dsh-plugin-shop/types#ShopCatalogResult',
|
|
102
103
|
schema: dsh_plugin_shop_shop_catalog_result$schema,
|
|
103
104
|
},
|
|
104
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
105
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":180,"column":9},
|
|
105
106
|
},
|
|
106
107
|
{
|
|
107
108
|
id: 'dsh-plugin-shop#shop/installStart',
|
|
@@ -127,7 +128,7 @@ export const TYPERT_REMOTE = {
|
|
|
127
128
|
typeSymbol: 'dsh-plugin-shop/types#ShopInstallResult',
|
|
128
129
|
schema: dsh_plugin_shop_shop_installStart_result$schema,
|
|
129
130
|
},
|
|
130
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
131
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":206,"column":9},
|
|
131
132
|
},
|
|
132
133
|
{
|
|
133
134
|
id: 'dsh-plugin-shop#shop/installStatus',
|
|
@@ -152,7 +153,7 @@ export const TYPERT_REMOTE = {
|
|
|
152
153
|
typeSymbol: 'dsh-plugin-shop/types#ShopInstallStatusResult',
|
|
153
154
|
schema: dsh_plugin_shop_shop_installStatus_result$schema,
|
|
154
155
|
},
|
|
155
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
156
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":244,"column":3},
|
|
156
157
|
},
|
|
157
158
|
{
|
|
158
159
|
id: 'dsh-plugin-shop#shop/outdated',
|
|
@@ -167,7 +168,7 @@ export const TYPERT_REMOTE = {
|
|
|
167
168
|
typeSymbol: 'dsh-plugin-shop#shop/outdated:result',
|
|
168
169
|
schema: dsh_plugin_shop_shop_outdated_result$schema,
|
|
169
170
|
},
|
|
170
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
171
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":252,"column":9},
|
|
171
172
|
},
|
|
172
173
|
{
|
|
173
174
|
id: 'dsh-plugin-shop#shop/setEnabled',
|
|
@@ -192,7 +193,7 @@ export const TYPERT_REMOTE = {
|
|
|
192
193
|
typeSymbol: 'dsh-plugin-shop/types#ShopSetEnabledResult',
|
|
193
194
|
schema: dsh_plugin_shop_shop_setEnabled_result$schema,
|
|
194
195
|
},
|
|
195
|
-
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":
|
|
196
|
+
sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":149,"column":3},
|
|
196
197
|
},
|
|
197
198
|
],
|
|
198
199
|
}
|
|
@@ -6,8 +6,10 @@ export declare const zh: {
|
|
|
6
6
|
error: string;
|
|
7
7
|
retry: string;
|
|
8
8
|
search: string;
|
|
9
|
+
all: string;
|
|
9
10
|
catalog: string;
|
|
10
11
|
catalogStats: string;
|
|
12
|
+
stars: string;
|
|
11
13
|
categoryTool: string;
|
|
12
14
|
categoryProvider: string;
|
|
13
15
|
categoryUi: string;
|
|
@@ -59,8 +61,10 @@ export declare const en: {
|
|
|
59
61
|
error: string;
|
|
60
62
|
retry: string;
|
|
61
63
|
search: string;
|
|
64
|
+
all: string;
|
|
62
65
|
catalog: string;
|
|
63
66
|
catalogStats: string;
|
|
67
|
+
stars: string;
|
|
64
68
|
categoryTool: string;
|
|
65
69
|
categoryProvider: string;
|
|
66
70
|
categoryUi: string;
|
|
@@ -81,6 +81,28 @@ export declare function reduceInstall(state: InstallView, event: InstallEvent):
|
|
|
81
81
|
* it matches other people's package names, not ours.
|
|
82
82
|
*/
|
|
83
83
|
export declare function isShopLike(name: string): boolean;
|
|
84
|
+
declare const CATEGORY_KEYS: {
|
|
85
|
+
readonly tool: "categoryTool";
|
|
86
|
+
readonly provider: "categoryProvider";
|
|
87
|
+
readonly ui: "categoryUi";
|
|
88
|
+
readonly workflow: "categoryWorkflow";
|
|
89
|
+
readonly integration: "categoryIntegration";
|
|
90
|
+
readonly other: "categoryOther";
|
|
91
|
+
};
|
|
84
92
|
/** The locale key for one entry's category — derived entries read as `other`
|
|
85
93
|
* (§6.1: a derived listing has no declared category). */
|
|
86
94
|
export declare function categoryKey(entry: CatalogEntry): ShopLocaleKey;
|
|
95
|
+
/** The category vocabulary as a type, derived from the map above so the
|
|
96
|
+
* client never repeats the six literals. */
|
|
97
|
+
export type Category = keyof typeof CATEGORY_KEYS;
|
|
98
|
+
/** The six categories in display order (map insertion order). */
|
|
99
|
+
export declare const CATEGORY_ORDER: Category[];
|
|
100
|
+
/** The locale key for one bare category value (the filter buttons). */
|
|
101
|
+
export declare function categoryLocaleKey(category: Category): ShopLocaleKey;
|
|
102
|
+
/** Sort the shelf: stars descending, un-starred entries last, name ascending
|
|
103
|
+
* (case-insensitive) on ties (spec 2026-08-26-github-stars-design.md D1).
|
|
104
|
+
* Display-time only — the catalog's own name sort is untouched. */
|
|
105
|
+
export declare function sortByStars(entries: CatalogEntry[], stars: Record<string, number>): CatalogEntry[];
|
|
106
|
+
/** 999 → "999"; 1000 → "1k"; 1234 → "1.2k"; 1500 → "1.5k"; 99999 → "100k". */
|
|
107
|
+
export declare function formatStars(n: number): string;
|
|
108
|
+
export {};
|
|
@@ -7,6 +7,9 @@ export interface CatalogSnapshot {
|
|
|
7
7
|
builtAt: string;
|
|
8
8
|
entries: CatalogEntry[];
|
|
9
9
|
denied: DeniedEntry[];
|
|
10
|
+
/** GitHub star counts by package name; {} when the pointer names no
|
|
11
|
+
* sidecar or the sidecar could not be fetched/verified (spec §5). */
|
|
12
|
+
stars: Record<string, number>;
|
|
10
13
|
}
|
|
11
14
|
export interface CatalogResult {
|
|
12
15
|
snapshot: CatalogSnapshot;
|
|
@@ -32,6 +35,8 @@ export interface LoadCatalogOptions {
|
|
|
32
35
|
* only when the transport itself failed — the fetch threw or returned a
|
|
33
36
|
* non-2xx (§10). A schemaVersion higher than this build supports, a malformed
|
|
34
37
|
* pointer or data file, an absolute data URL, or a sha256 mismatch — fresh or
|
|
35
|
-
* cached — throws even when a cache exists; never silently degraded.
|
|
38
|
+
* cached — throws even when a cache exists; never silently degraded. The stars
|
|
39
|
+
* sidecar is the sole exception: any irregularity there, a refused url
|
|
40
|
+
* included, degrades to no stars (spec §5).
|
|
36
41
|
*/
|
|
37
42
|
export declare function loadCatalog(options: LoadCatalogOptions): Promise<CatalogResult>;
|
|
@@ -62,6 +62,9 @@ export interface ShopCatalogResult {
|
|
|
62
62
|
stale: boolean;
|
|
63
63
|
plugins: CatalogEntry[];
|
|
64
64
|
denied: DeniedEntry[];
|
|
65
|
+
/** GitHub star counts by package name; {} when the pointer names no sidecar
|
|
66
|
+
* or the sidecar could not be fetched/verified (§5). */
|
|
67
|
+
stars: Record<string, number>;
|
|
65
68
|
}
|
|
66
69
|
/** Remote-only service exposing the shop Remote methods of §7.3.
|
|
67
70
|
*
|
package/package.json
CHANGED