dsh-plugin-shop 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/client.js CHANGED
@@ -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": 177,
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": 202,
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": 240,
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": 248,
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": 146,
4487
+ "line": 149,
4324
4488
  "column": 3
4325
4489
  }
4326
4490
  }
@@ -4442,6 +4606,22 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4442
4606
  function categoryKey(entry) {
4443
4607
  return CATEGORY_KEYS[entry.catalog?.category ?? "other"];
4444
4608
  }
4609
+ /** Sort the shelf: stars descending, un-starred entries last, name ascending
4610
+ * (case-insensitive) on ties (spec 2026-08-26-github-stars-design.md D1).
4611
+ * Display-time only — the catalog's own name sort is untouched. */
4612
+ function sortByStars(entries, stars) {
4613
+ const count = (e) => stars[e.name] ?? -1;
4614
+ return [...entries].sort((a, b) => {
4615
+ const byStars = count(b) - count(a);
4616
+ if (byStars !== 0) return byStars;
4617
+ return a.name.localeCompare(b.name, void 0, { sensitivity: "base" });
4618
+ });
4619
+ }
4620
+ /** 999 → "999"; 1000 → "1k"; 1234 → "1.2k"; 1500 → "1.5k"; 99999 → "100k". */
4621
+ function formatStars(n) {
4622
+ if (n < 1e3) return String(n);
4623
+ return `${(n / 1e3).toFixed(1).replace(/\.0$/, "")}k`;
4624
+ }
4445
4625
  //#endregion
4446
4626
  //#region src/client/locales.ts
4447
4627
  /** Copy dictionaries for the shop Settings tab. */
@@ -4454,6 +4634,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4454
4634
  search: "搜索插件",
4455
4635
  catalog: "插件目录",
4456
4636
  catalogStats: "{count} 个插件 · 构建于 {date}",
4637
+ stars: "{count} 星",
4457
4638
  categoryTool: "工具",
4458
4639
  categoryProvider: "模型服务",
4459
4640
  categoryUi: "界面",
@@ -4505,6 +4686,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4505
4686
  search: "Search plugins",
4506
4687
  catalog: "Plugin catalog",
4507
4688
  catalogStats: "{count} packages · built {date}",
4689
+ stars: "{count} stars",
4508
4690
  categoryTool: "Tool",
4509
4691
  categoryProvider: "Provider",
4510
4692
  categoryUi: "UI",
@@ -4600,7 +4782,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4600
4782
  }
4601
4783
  //#endregion
4602
4784
  //#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";
4785
+ 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.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.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
4786
  if (typeof document !== "undefined" && !document.querySelector("style[data-plugin-css=\"e3675a89\"]")) {
4605
4787
  const tag = document.createElement("style");
4606
4788
  tag.dataset.pluginCss = "e3675a89";
@@ -4641,6 +4823,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4641
4823
  "badges": "s513026_badges",
4642
4824
  "tierBadge": "s5e82cb_tierBadge",
4643
4825
  "unclaimedBadge": "s23e3e1_unclaimedBadge",
4826
+ "starsBadge": "sff43b4_starsBadge",
4644
4827
  "chevron": "s73f7fb_chevron",
4645
4828
  "chevronOpen": "s61393c_chevronOpen",
4646
4829
  "body": "s820ebf_body",
@@ -4703,7 +4886,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4703
4886
  * (name, tier, category, unclaimed marker), the plain-text summary in both
4704
4887
  * languages, the self-declared capabilities, the detail section, and the
4705
4888
  * install controls. */
4706
- const EntryCard = (0, react.memo)(function EntryCard({ entry, t, install, installStatus }) {
4889
+ const EntryCard = (0, react.memo)(function EntryCard({ entry, stars, t, install, installStatus }) {
4707
4890
  const [open, setOpen] = (0, react.useState)(false);
4708
4891
  const detailId = (0, react.useId)();
4709
4892
  const summary = entry.catalog?.summary;
@@ -4746,6 +4929,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4746
4929
  className: _dsh_plugin_shop_css_e3675a89_default.unclaimedBadge,
4747
4930
  children: t("unclaimed")
4748
4931
  }),
4932
+ stars !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
4933
+ className: _dsh_plugin_shop_css_e3675a89_default.starsBadge,
4934
+ role: "img",
4935
+ "aria-label": t("stars", { count: stars }),
4936
+ children: ["★ ", formatStars(stars)]
4937
+ }),
4749
4938
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronIcon, { open })
4750
4939
  ]
4751
4940
  })]
@@ -4783,7 +4972,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4783
4972
  }),
4784
4973
  entry.repository !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4785
4974
  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 })]
4975
+ 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", {
4976
+ href: entry.repository,
4977
+ target: "_blank",
4978
+ rel: "noopener noreferrer",
4979
+ children: entry.repository
4980
+ }) : entry.repository })]
4787
4981
  }),
4788
4982
  entry.license !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4789
4983
  className: _dsh_plugin_shop_css_e3675a89_default.detailRow,
@@ -5097,6 +5291,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5097
5291
  });
5098
5292
  }, [catalogState, query]);
5099
5293
  filteredLenRef.current = filtered.length;
5294
+ const stars = catalogState.kind === "ready" ? catalogState.result.stars : {};
5295
+ const sorted = (0, react.useMemo)(() => sortByStars(filtered, stars), [filtered, stars]);
5100
5296
  (0, react.useEffect)(() => {
5101
5297
  if (!incremental) return;
5102
5298
  const node = sentinelRef.current;
@@ -5111,7 +5307,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5111
5307
  visibleCount,
5112
5308
  filtered.length
5113
5309
  ]);
5114
- const visible = incremental ? filtered.slice(0, visibleCount) : filtered;
5310
+ const visible = incremental ? sorted.slice(0, visibleCount) : sorted;
5115
5311
  const tiers = (0, react.useMemo)(() => {
5116
5312
  const map = /* @__PURE__ */ new Map();
5117
5313
  if (catalogState.kind === "ready") for (const entry of catalogState.result.plugins) map.set(entry.name, entry.tier);
@@ -5203,6 +5399,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5203
5399
  className: _dsh_plugin_shop_css_e3675a89_default.cards,
5204
5400
  children: [visible.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EntryCard, {
5205
5401
  entry,
5402
+ stars: stars[entry.name],
5206
5403
  t,
5207
5404
  install,
5208
5405
  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
  /**
@@ -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":177,"column":9},
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":202,"column":9},
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":240,"column":3},
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":248,"column":9},
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":146,"column":3},
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":177,"column":9},
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":202,"column":9},
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":240,"column":3},
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":248,"column":9},
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":146,"column":3},
196
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":149,"column":3},
196
197
  },
197
198
  ],
198
199
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-shop",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "The DeepSeek Harness plugin shop: browse, install, enable, and update dsh plugins from a git-auditable catalog.",
5
5
  "repository": {
6
6
  "type": "git",