dsh-plugin-shop 0.4.13 → 0.4.14

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/README.md CHANGED
@@ -52,9 +52,9 @@ install them once with `npm install -g @deepseek-ai/dsh pnpm` and verify with
52
52
  # holds back very recent releases, so a bare `add dsh-plugin-shop` can hand
53
53
  # you an older version for a while. Pin the current release — refresh it
54
54
  # with `npm view dsh-plugin-shop version`.
55
- dsh plugin --profile web add dsh-plugin-shop@0.4.13
55
+ dsh plugin --profile web add dsh-plugin-shop@0.4.14
56
56
  # or straight through npx, nothing installed:
57
- npx -y @deepseek-ai/dsh plugin --profile web add dsh-plugin-shop@0.4.13
57
+ npx -y @deepseek-ai/dsh plugin --profile web add dsh-plugin-shop@0.4.14
58
58
  ```
59
59
 
60
60
  Replace `web` with your profile if you use another one. Then restart `dsh` once — a
@@ -84,7 +84,7 @@ ls -1 "${DSH_HOME:-$HOME/.dsh}/profiles" | grep -v '^node_modules$'
84
84
  cooldown, and deterministic installs are the point of the agent path.
85
85
 
86
86
  ```sh
87
- dsh plugin --profile <profile> add dsh-plugin-shop@0.4.13
87
+ dsh plugin --profile <profile> add dsh-plugin-shop@0.4.14
88
88
  ```
89
89
 
90
90
  **3. Verify — do not skip this.** A zero exit from step 2 means pnpm resolved the
package/lib/client.js CHANGED
@@ -4301,7 +4301,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4301
4301
  "capabilities": array(string())
4302
4302
  })]).optional(),
4303
4303
  "source": union([literal("npm"), literal("github")]),
4304
- "repo": union([_undefined(), string()]).optional()
4304
+ "repo": union([_undefined(), string()]).optional(),
4305
+ "subdir": union([_undefined(), string()]).optional()
4305
4306
  })),
4306
4307
  "denied": array(object({
4307
4308
  "name": string(),
@@ -4313,7 +4314,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4313
4314
  "name": string(),
4314
4315
  "installed": string(),
4315
4316
  "latest": string(),
4316
- "outdated": boolean()
4317
+ "outdated": boolean(),
4318
+ "enabled": boolean()
4317
4319
  }));
4318
4320
  const dsh_plugin_shop_shop_installStart_parameter_0$schema = object({
4319
4321
  "name": string(),
@@ -4414,7 +4416,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4414
4416
  },
4415
4417
  sourceLocation: {
4416
4418
  "file": "packages/dsh-plugin-shop/src/host/index.ts",
4417
- "line": 270,
4419
+ "line": 275,
4418
4420
  "column": 9
4419
4421
  }
4420
4422
  },
@@ -4432,7 +4434,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4432
4434
  },
4433
4435
  sourceLocation: {
4434
4436
  "file": "packages/dsh-plugin-shop/src/host/index.ts",
4435
- "line": 374,
4437
+ "line": 380,
4436
4438
  "column": 9
4437
4439
  }
4438
4440
  },
@@ -4460,7 +4462,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4460
4462
  },
4461
4463
  sourceLocation: {
4462
4464
  "file": "packages/dsh-plugin-shop/src/host/index.ts",
4463
- "line": 296,
4465
+ "line": 301,
4464
4466
  "column": 9
4465
4467
  }
4466
4468
  },
@@ -4487,7 +4489,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4487
4489
  },
4488
4490
  sourceLocation: {
4489
4491
  "file": "packages/dsh-plugin-shop/src/host/index.ts",
4490
- "line": 363,
4492
+ "line": 369,
4491
4493
  "column": 3
4492
4494
  }
4493
4495
  },
@@ -4505,7 +4507,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4505
4507
  },
4506
4508
  sourceLocation: {
4507
4509
  "file": "packages/dsh-plugin-shop/src/host/index.ts",
4508
- "line": 469,
4510
+ "line": 491,
4509
4511
  "column": 9
4510
4512
  }
4511
4513
  },
@@ -4532,7 +4534,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4532
4534
  },
4533
4535
  sourceLocation: {
4534
4536
  "file": "packages/dsh-plugin-shop/src/host/index.ts",
4535
- "line": 239,
4537
+ "line": 241,
4536
4538
  "column": 3
4537
4539
  }
4538
4540
  },
@@ -4560,7 +4562,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4560
4562
  },
4561
4563
  sourceLocation: {
4562
4564
  "file": "packages/dsh-plugin-shop/src/host/index.ts",
4563
- "line": 429,
4565
+ "line": 451,
4564
4566
  "column": 9
4565
4567
  }
4566
4568
  },
@@ -4587,7 +4589,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4587
4589
  },
4588
4590
  sourceLocation: {
4589
4591
  "file": "packages/dsh-plugin-shop/src/host/index.ts",
4590
- "line": 518,
4592
+ "line": 540,
4591
4593
  "column": 9
4592
4594
  }
4593
4595
  },
@@ -4605,7 +4607,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4605
4607
  },
4606
4608
  sourceLocation: {
4607
4609
  "file": "packages/dsh-plugin-shop/src/host/index.ts",
4608
- "line": 503,
4610
+ "line": 525,
4609
4611
  "column": 9
4610
4612
  }
4611
4613
  }
@@ -5084,7 +5086,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5084
5086
  }
5085
5087
  //#endregion
5086
5088
  //#region \0dsh-plugin-shop-css:e3675a89
5087
- 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 /* The shelf never scrolls sideways: a card too wide for its track wraps\n * its badges rather than widening the panel. */\n overflow-x: hidden;\n}\n\n.s44a3ec_stateLine {\n margin: 0;\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n/* Visually hidden but screen-reader readable (the loading copy while the\n * skeleton stands in visually). */\n.s3a0ece_srOnly {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n border: 0;\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n overflow: hidden;\n white-space: nowrap;\n}\n\n/* The loading shelf: ghost cards with the same geometry as the real grid, so\n * the swap to content is a same-shape handoff instead of a jump. */\n.se0c3b4_skeletonGrid {\n display: grid;\n grid-template-columns: 1fr;\n gap: 8px;\n}\n\n.s520416_skeletonCard {\n position: relative;\n display: flex;\n flex-direction: column;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-1);\n overflow: hidden;\n}\n\n/* The shimmer sweep: one gradient band travels across each ghost card while\n * the bars breathe, so the loading state reads as \"the shelf is scanning\". */\n.s520416_skeletonCard::after {\n content: '';\n position: absolute;\n inset: 0;\n background: linear-gradient(\n 100deg,\n transparent 25%,\n color-mix(in srgb, var(--dsw-alias-label-primary) 14%, transparent) 50%,\n transparent 75%\n );\n transform: translateX(-100%);\n animation: dshShopSkeletonSweep 1.4s ease-in-out infinite;\n pointer-events: none;\n}\n\n@keyframes dshShopSkeletonSweep {\n to { transform: translateX(100%); }\n}\n\n.s8c7d6f_skeletonName {\n width: 55%;\n height: 14px;\n margin: 10px 12px 0;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.sf5179e_skeletonSummary {\n width: 92%;\n height: 12px;\n margin: 10px 12px 0;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.sbb0d25_skeletonSummaryShort {\n width: 68%;\n height: 12px;\n margin: 6px 12px 10px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n/* The ghost of the action line, right where the real buttons land. */\n.s96e910_skeletonActions {\n display: block;\n width: 84px;\n height: 24px;\n margin: 0 12px 10px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.s520416_skeletonCard span {\n animation: dshShopSkeletonPulse 1.6s ease-in-out infinite;\n}\n\n@keyframes dshShopSkeletonPulse {\n 0%, 100% { opacity: 0.5; }\n 50% { opacity: 1; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .s520416_skeletonCard span { animation: none; opacity: 0.75; }\n .s520416_skeletonCard::after { content: none; }\n}\n\n.s73242a_actionButton {\n align-self: flex-start;\n padding: 5px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.s73242a_actionButton:hover {\n border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l2));\n color: var(--dsw-alias-brand-primary);\n}\n\n.s73242a_actionButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.s646509_toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.s745e3c_toolbarRight {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n/* The shop's own version, right of the search box: `v0.4.4`, plus the\n * update button when the version check found a newer release. */\n.s4fe61f_versionBlock {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.sb9b924_versionText {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sa04455_updateSelfButton {\n padding: 2px 10px;\n border: 1px solid var(--dsw-alias-brand-primary);\n border-radius: 999px;\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n color: var(--dsw-alias-brand-primary);\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n}\n\n.sa04455_updateSelfButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 20%, transparent);\n}\n\n.sa04455_updateSelfButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The on-demand re-check next to the version number: a quiet pill; the\n * border and text carry it in the light theme where bg layers are white. */\n.s614a38_checkUpdateButton {\n padding: 2px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n cursor: pointer;\n}\n\n.s614a38_checkUpdateButton:hover {\n border-color: var(--dsw-alias-label-secondary);\n color: var(--dsw-alias-label-primary);\n}\n\n.s614a38_checkUpdateButton:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n.s614a38_checkUpdateButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The project's GitHub mark, right of the version row: a quiet icon link.\n * The octocat carries itself — color shift on hover, no text, no border. */\n.sa987f7_githubLink {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 3px;\n border-radius: 6px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sa987f7_githubLink:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sa987f7_githubLink:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The self-update's expanded states (running log / done + restart /\n * failure detail) sit under the toolbar, above the category bar. */\n.sb81ba5_selfUpdatePanel {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 8px;\n}\n\n.s232ec4_searchInput {\n width: min(280px, 100%);\n padding: 6px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n.s232ec4_searchInput:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.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: 6px;\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: 6px 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/* Single-line rows: one full-width column, one plugin per line. */\n.sf657f5_cards {\n display: grid;\n grid-template-columns: 1fr;\n max-width: 100%;\n gap: 8px;\n list-style: none;\n margin: 0;\n padding: 0;\n animation: dshShopCardsIn 220ms ease;\n}\n\n@keyframes dshShopCardsIn {\n from { opacity: 0; transform: translateY(3px); }\n to { opacity: 1; transform: none; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .sf657f5_cards { animation: none; }\n}\n\n.s65f383_card {\n position: relative;\n display: flex;\n flex-direction: column;\n /* A grid item's min-width is auto by default: its content would widen the\n * 1fr track past the panel and overflow horizontally. */\n min-width: 0;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-1);\n overflow: hidden;\n transition: border-color 120ms ease, transform 120ms ease, box-shadow 120ms ease;\n /* Off-screen cards skip layout and paint; the placeholder keeps scrollbar\n * estimation sane before the browser has measured the real height. */\n content-visibility: auto;\n contain-intrinsic-size: auto 140px;\n}\n\n/* The sentinel that triggers the next shelf batch: zero visual footprint. */\n.sd7ee01_cardsSentry {\n height: 1px;\n margin: 0;\n padding: 0;\n}\n\n.s705cc6_showingLine {\n margin: 0;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s65f383_card:hover {\n border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l1));\n transform: translateY(-1px);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .s65f383_card { transition: none; }\n .s65f383_card:hover { transform: none; }\n}\n\n/* The signature: a spine and cover in the category's own hue. The hues are\n * fixed mid-bright colors that read on both themes; `other` deliberately\n * stays a neutral gray so the fallback category does not compete. */\n.s4ce25d_cardSpine {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 3px;\n background: var(--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/* The category sits in the badge row as a quiet chip in the category's own\n * hue — the border carries it where the light theme's white layers cannot\n * (the same lesson as the check button). */\n.s9c8eaa_categoryBadge {\n padding: 1px 7px;\n border: 1px solid color-mix(in srgb, var(--spine-hue, #8B8E96) 45%, transparent);\n border-radius: 999px;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--spine-hue, #8B8E96);\n white-space: nowrap;\n}\n\n.sa97234_entryHeader {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 12px;\n min-width: 0;\n padding: 8px 12px 2px;\n border: none;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n\n.sa97234_entryHeader:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n/* The name fills the row and truncates rather than wraps; the badges stay\n * intact beside it. */\n.sc3f149_name {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 13px;\n font-weight: 600;\n flex: 1 1 auto;\n min-width: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--dsw-alias-label-primary);\n}\n\n.s513026_badges {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n/* The action line: buttons side by side; an active install/uninstall flow\n * (gate, log, notices) takes the full width below them. */\n.s9b5048_cardActions {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n padding: 8px 12px 10px;\n}\n\n.s9b5048_cardActions > .sc4f2ac_gate,\n.s9b5048_cardActions > .s4d374c_installPanel,\n.s9b5048_cardActions > .sf53ca0_installedActions {\n flex-basis: 100%;\n}\n\n/* The entry's version, always visible on the card (the expanded detail\n * repeats it in the version row) — the same quiet mono as the toolbar. */\n.sc6ec6e_cardVersion {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* The octocat marking a github-sourced entry, beside its short commit. */\n.s71f10f_sourceBadge {\n display: inline-flex;\n align-items: center;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s5e82cb_tierBadge {\n padding: 1px 7px;\n border-radius: 999px;\n font-size: 11px;\n line-height: 1.6;\n}\n\n.s5e82cb_tierBadge[data-tier='verified'] {\n color: var(--dsw-alias-state-success-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-success-primary) 40%, transparent);\n}\n\n.s5e82cb_tierBadge[data-tier='verified-stale'] {\n color: var(--dsw-alias-state-warn-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary) 40%, transparent);\n}\n\n.s5e82cb_tierBadge[data-tier='community'] {\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n border: 1px solid var(--dsw-alias-border-l2);\n}\n\n.sff43b4_starsBadge {\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s73f7fb_chevron, .s61393c_chevronOpen {\n color: var(--dsw-alias-label-secondary);\n transition: transform 120ms ease;\n}\n\n.s61393c_chevronOpen { transform: rotate(180deg); }\n\n@media (prefers-reduced-motion: reduce) {\n .s73f7fb_chevron, .s61393c_chevronOpen { transition: none; }\n}\n\n.s820ebf_body {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 0 12px;\n}\n\n.sb8fbc8_summary {\n margin: 0;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.se507db_summaryZh {\n margin: -4px 0 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n display: -webkit-box;\n -webkit-line-clamp: 1;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n/* Expanded cards show the full summary: the clamp is lifted, never raised —\n * a higher number would still truncate the author's text, just later. */\n.s10baf0_summaryExpanded {\n -webkit-line-clamp: unset;\n display: block;\n}\n\n.sb9dba9_summaryZhExpanded {\n -webkit-line-clamp: unset;\n display: block;\n}\n\n.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/* The shelf card of a plugin that is installed and current carries this\n * non-interactive label instead of an install button. Border + text do the\n * work in the light theme, where every bg-layer token resolves to white. */\n.sb6fc74_installedLabel {\n align-self: flex-start;\n margin: 0;\n padding: 5px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n font-size: 13px;\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n}\n\n/* An installed card's controls: the installed label or the update button,\n * plus the uninstall button. The install panel's running/failed states are\n * column layouts and sit as one item of this row. */\n.sf53ca0_installedActions {\n display: flex;\n align-items: flex-start;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n/* Uninstall revokes privilege; the quiet outline keeps it from competing\n * with the primary action, and the error tint says \"removes\". */\n.s96967f_uninstallButton {\n align-self: flex-start;\n padding: 5px 14px;\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 55%, transparent);\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-state-error-primary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.s96967f_uninstallButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);\n}\n\n.s96967f_uninstallButton:focus-visible {\n outline: 2px solid var(--dsw-alias-state-error-primary);\n outline-offset: 1px;\n}\n\n/* The restart dsh button on a done install: quiet outline, same rank as\n * the other secondary actions. */\n.s6ac237_restartButton {\n align-self: flex-start;\n padding: 5px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.s6ac237_restartButton:hover {\n border-color: var(--dsw-alias-label-secondary);\n}\n\n.s6ac237_restartButton: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";
5089
+ 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 /* The shelf never scrolls sideways: a card too wide for its track wraps\n * its badges rather than widening the panel. */\n overflow-x: hidden;\n}\n\n.s44a3ec_stateLine {\n margin: 0;\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n/* Visually hidden but screen-reader readable (the loading copy while the\n * skeleton stands in visually). */\n.s3a0ece_srOnly {\n position: absolute;\n width: 1px;\n height: 1px;\n margin: -1px;\n padding: 0;\n border: 0;\n clip: rect(0 0 0 0);\n clip-path: inset(50%);\n overflow: hidden;\n white-space: nowrap;\n}\n\n/* The loading shelf: ghost cards with the same geometry as the real grid, so\n * the swap to content is a same-shape handoff instead of a jump. */\n.se0c3b4_skeletonGrid {\n display: grid;\n grid-template-columns: 1fr;\n gap: 8px;\n}\n\n.s520416_skeletonCard {\n position: relative;\n display: flex;\n flex-direction: column;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-1);\n overflow: hidden;\n}\n\n/* The shimmer sweep: one gradient band travels across each ghost card while\n * the bars breathe, so the loading state reads as \"the shelf is scanning\". */\n.s520416_skeletonCard::after {\n content: '';\n position: absolute;\n inset: 0;\n background: linear-gradient(\n 100deg,\n transparent 25%,\n color-mix(in srgb, var(--dsw-alias-label-primary) 14%, transparent) 50%,\n transparent 75%\n );\n transform: translateX(-100%);\n animation: dshShopSkeletonSweep 1.4s ease-in-out infinite;\n pointer-events: none;\n}\n\n@keyframes dshShopSkeletonSweep {\n to { transform: translateX(100%); }\n}\n\n.s8c7d6f_skeletonName {\n width: 55%;\n height: 14px;\n margin: 10px 12px 0;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.sf5179e_skeletonSummary {\n width: 92%;\n height: 12px;\n margin: 10px 12px 0;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.sbb0d25_skeletonSummaryShort {\n width: 68%;\n height: 12px;\n margin: 6px 12px 10px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n/* The ghost of the action line, right where the real buttons land. */\n.s96e910_skeletonActions {\n display: block;\n width: 84px;\n height: 24px;\n margin: 0 12px 10px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent);\n}\n\n.s520416_skeletonCard span {\n animation: dshShopSkeletonPulse 1.6s ease-in-out infinite;\n}\n\n@keyframes dshShopSkeletonPulse {\n 0%, 100% { opacity: 0.5; }\n 50% { opacity: 1; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .s520416_skeletonCard span { animation: none; opacity: 0.75; }\n .s520416_skeletonCard::after { content: none; }\n}\n\n.s73242a_actionButton {\n align-self: flex-start;\n padding: 5px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.s73242a_actionButton:hover {\n border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l2));\n color: var(--dsw-alias-brand-primary);\n}\n\n.s73242a_actionButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.s646509_toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.s745e3c_toolbarRight {\n display: flex;\n align-items: center;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n/* The shop's own version, right of the search box: `v0.4.4`, plus the\n * update button when the version check found a newer release. */\n.s4fe61f_versionBlock {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.sb9b924_versionText {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sa04455_updateSelfButton {\n padding: 2px 10px;\n border: 1px solid var(--dsw-alias-brand-primary);\n border-radius: 999px;\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n color: var(--dsw-alias-brand-primary);\n font-size: 12px;\n font-weight: 600;\n cursor: pointer;\n}\n\n.sa04455_updateSelfButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-brand-primary) 20%, transparent);\n}\n\n.sa04455_updateSelfButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The on-demand re-check next to the version number: a quiet pill; the\n * border and text carry it in the light theme where bg layers are white. */\n.s614a38_checkUpdateButton {\n padding: 2px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 999px;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-secondary);\n font-size: 12px;\n cursor: pointer;\n}\n\n.s614a38_checkUpdateButton:hover {\n border-color: var(--dsw-alias-label-secondary);\n color: var(--dsw-alias-label-primary);\n}\n\n.s614a38_checkUpdateButton:disabled {\n opacity: 0.6;\n cursor: default;\n}\n\n.s614a38_checkUpdateButton:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The project's GitHub mark, right of the version row: a quiet icon link.\n * The octocat carries itself — color shift on hover, no text, no border. */\n.sa987f7_githubLink {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 3px;\n border-radius: 6px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.sa987f7_githubLink:hover {\n color: var(--dsw-alias-label-primary);\n background: var(--dsw-alias-bg-layer-2);\n}\n\n.sa987f7_githubLink:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n/* The self-update's expanded states (running log / done + restart /\n * failure detail) sit under the toolbar, above the category bar. */\n.sb81ba5_selfUpdatePanel {\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin-top: 8px;\n}\n\n.s232ec4_searchInput {\n width: min(280px, 100%);\n padding: 6px 10px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-1);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n}\n\n.s232ec4_searchInput:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: 1px;\n}\n\n.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: 6px;\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: 6px 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/* Single-line rows: one full-width column, one plugin per line. */\n.sf657f5_cards {\n display: grid;\n grid-template-columns: 1fr;\n max-width: 100%;\n gap: 8px;\n list-style: none;\n margin: 0;\n padding: 0;\n animation: dshShopCardsIn 220ms ease;\n}\n\n@keyframes dshShopCardsIn {\n from { opacity: 0; transform: translateY(3px); }\n to { opacity: 1; transform: none; }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .sf657f5_cards { animation: none; }\n}\n\n.s65f383_card {\n position: relative;\n display: flex;\n flex-direction: column;\n /* A grid item's min-width is auto by default: its content would widen the\n * 1fr track past the panel and overflow horizontally. */\n min-width: 0;\n border: 1px solid var(--dsw-alias-border-l1);\n border-radius: 10px;\n background: var(--dsw-alias-bg-layer-1);\n overflow: hidden;\n transition: border-color 120ms ease, transform 120ms ease, box-shadow 120ms ease;\n /* Off-screen cards skip layout and paint; the placeholder keeps scrollbar\n * estimation sane before the browser has measured the real height. */\n content-visibility: auto;\n contain-intrinsic-size: auto 140px;\n}\n\n/* The sentinel that triggers the next shelf batch: zero visual footprint. */\n.sd7ee01_cardsSentry {\n height: 1px;\n margin: 0;\n padding: 0;\n}\n\n.s705cc6_showingLine {\n margin: 0;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s65f383_card:hover {\n border-color: color-mix(in srgb, var(--dsw-alias-brand-primary) 45%, var(--dsw-alias-border-l1));\n transform: translateY(-1px);\n box-shadow: 0 2px 8px color-mix(in srgb, var(--dsw-alias-brand-primary) 12%, transparent);\n}\n\n@media (prefers-reduced-motion: reduce) {\n .s65f383_card { transition: none; }\n .s65f383_card:hover { transform: none; }\n}\n\n/* The signature: a spine and cover in the category's own hue. The hues are\n * fixed mid-bright colors that read on both themes; `other` deliberately\n * stays a neutral gray so the fallback category does not compete. */\n.s4ce25d_cardSpine {\n position: absolute;\n top: 0;\n bottom: 0;\n left: 0;\n width: 3px;\n background: var(--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/* The category sits in the badge row as a quiet chip in the category's own\n * hue — the border carries it where the light theme's white layers cannot\n * (the same lesson as the check button). */\n.s9c8eaa_categoryBadge {\n padding: 1px 7px;\n border: 1px solid color-mix(in srgb, var(--spine-hue, #8B8E96) 45%, transparent);\n border-radius: 999px;\n font-size: 11px;\n font-weight: 600;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n color: var(--spine-hue, #8B8E96);\n white-space: nowrap;\n}\n\n.sa97234_entryHeader {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 12px;\n min-width: 0;\n padding: 8px 12px 2px;\n border: none;\n background: transparent;\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n}\n\n.sa97234_entryHeader:focus-visible {\n outline: 2px solid var(--dsw-alias-brand-primary);\n outline-offset: -2px;\n}\n\n/* The name fills the row and truncates rather than wraps; the badges stay\n * intact beside it. */\n.sc3f149_name {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 13px;\n font-weight: 600;\n flex: 1 1 auto;\n min-width: 0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--dsw-alias-label-primary);\n}\n\n.s513026_badges {\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n/* The action line: buttons side by side; an active install/uninstall flow\n * (gate, log, notices) takes the full width below them. */\n.s9b5048_cardActions {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n align-items: center;\n gap: 8px;\n padding: 8px 12px 10px;\n}\n\n.s9b5048_cardActions > .sc4f2ac_gate,\n.s9b5048_cardActions > .s4d374c_installPanel,\n.s9b5048_cardActions > .sf53ca0_installedActions {\n flex-basis: 100%;\n}\n\n/* The entry's version, always visible on the card (the expanded detail\n * repeats it in the version row) — the same quiet mono as the toolbar. */\n.sc6ec6e_cardVersion {\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n letter-spacing: 0.02em;\n color: var(--dsw-alias-label-secondary);\n}\n\n/* The octocat marking a github-sourced entry, beside its short commit. */\n.s71f10f_sourceBadge {\n display: inline-flex;\n align-items: center;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s5e82cb_tierBadge {\n padding: 1px 7px;\n border-radius: 999px;\n font-size: 11px;\n line-height: 1.6;\n}\n\n.s5e82cb_tierBadge[data-tier='verified'] {\n color: var(--dsw-alias-state-success-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-success-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-success-primary) 40%, transparent);\n}\n\n.s5e82cb_tierBadge[data-tier='verified-stale'] {\n color: var(--dsw-alias-state-warn-primary);\n background: color-mix(in srgb, var(--dsw-alias-state-warn-primary) 14%, transparent);\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-warn-primary) 40%, transparent);\n}\n\n.s5e82cb_tierBadge[data-tier='community'] {\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n border: 1px solid var(--dsw-alias-border-l2);\n}\n\n.sff43b4_starsBadge {\n font-size: 11px;\n color: var(--dsw-alias-label-secondary);\n}\n\n.s73f7fb_chevron, .s61393c_chevronOpen {\n color: var(--dsw-alias-label-secondary);\n transition: transform 120ms ease;\n}\n\n.s61393c_chevronOpen { transform: rotate(180deg); }\n\n@media (prefers-reduced-motion: reduce) {\n .s73f7fb_chevron, .s61393c_chevronOpen { transition: none; }\n}\n\n.s820ebf_body {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 0 12px;\n}\n\n.sb8fbc8_summary {\n margin: 0;\n font-size: 13px;\n line-height: 1.5;\n color: var(--dsw-alias-label-primary);\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n.se507db_summaryZh {\n margin: -4px 0 0;\n font-size: 12px;\n line-height: 1.5;\n color: var(--dsw-alias-label-secondary);\n display: -webkit-box;\n -webkit-line-clamp: 1;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n\n/* Expanded cards show the full summary: the clamp is lifted, never raised —\n * a higher number would still truncate the author's text, just later. */\n.s10baf0_summaryExpanded {\n -webkit-line-clamp: unset;\n display: block;\n}\n\n.sb9dba9_summaryZhExpanded {\n -webkit-line-clamp: unset;\n display: block;\n}\n\n.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/* The shelf card of a plugin that is installed and current carries this\n * non-interactive label instead of an install button. Border + text do the\n * work in the light theme, where every bg-layer token resolves to white. */\n.sb6fc74_installedLabel {\n align-self: flex-start;\n margin: 0;\n padding: 5px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n font-size: 13px;\n color: var(--dsw-alias-label-secondary);\n background: var(--dsw-alias-bg-layer-2);\n}\n\n/* An installed card's controls: the installed label or the update button,\n * plus the uninstall button. The install panel's running/failed states are\n * column layouts and sit as one item of this row. */\n.sf53ca0_installedActions {\n display: flex;\n align-items: flex-start;\n flex-wrap: wrap;\n gap: 8px;\n}\n\n/* Uninstall revokes privilege; the quiet outline keeps it from competing\n * with the primary action, and the error tint says \"removes\". */\n.s96967f_uninstallButton {\n align-self: flex-start;\n padding: 5px 14px;\n border: 1px solid color-mix(in srgb, var(--dsw-alias-state-error-primary) 55%, transparent);\n border-radius: 6px;\n background: transparent;\n color: var(--dsw-alias-state-error-primary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.s96967f_uninstallButton:hover {\n background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 10%, transparent);\n}\n\n.s96967f_uninstallButton:focus-visible {\n outline: 2px solid var(--dsw-alias-state-error-primary);\n outline-offset: 1px;\n}\n\n/* The restart dsh button on a done install: quiet outline, same rank as\n * the other secondary actions. */\n.s6ac237_restartButton {\n align-self: flex-start;\n padding: 5px 14px;\n border: 1px solid var(--dsw-alias-border-l2);\n border-radius: 6px;\n background: var(--dsw-alias-bg-layer-2);\n color: var(--dsw-alias-label-primary);\n font-size: 13px;\n cursor: pointer;\n}\n\n.s6ac237_restartButton:hover {\n border-color: var(--dsw-alias-label-secondary);\n}\n\n.s6ac237_restartButton: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\n/* The hot enable/disable switch on installed rows: the knob button plus the\n * §8 hot-apply / failure notices rendered beside it. */\n.sd9f9e4_switchWrap {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n}\n";
5088
5090
  if (typeof document !== "undefined" && !document.querySelector("style[data-plugin-css=\"e3675a89\"]")) {
5089
5091
  const tag = document.createElement("style");
5090
5092
  tag.dataset.pluginCss = "e3675a89";
@@ -5179,7 +5181,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5179
5181
  "outdatedActions": "sa1f556_outdatedActions",
5180
5182
  "switch": "s11c019_switch",
5181
5183
  "switchOn": "s7580e8_switchOn",
5182
- "switchKnob": "saab2b3_switchKnob"
5184
+ "switchKnob": "saab2b3_switchKnob",
5185
+ "switchWrap": "sd9f9e4_switchWrap"
5183
5186
  };
5184
5187
  //#endregion
5185
5188
  //#region src/client/ShopTab.tsx
@@ -5209,7 +5212,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5209
5212
  * install controls. An installed plugin's card carries its installed row:
5210
5213
  * current → the non-interactive installed label, behind → the update button;
5211
5214
  * uninstalled → the install button. */
5212
- const EntryCard = (0, react.memo)(function EntryCard({ entry, stars, installed, t, install, installStatus, uninstall, restart }) {
5215
+ const EntryCard = (0, react.memo)(function EntryCard({ entry, stars, installed, t, install, installStatus, uninstall, restart, setEnabled }) {
5213
5216
  const [open, setOpen] = (0, react.useState)(false);
5214
5217
  const detailId = (0, react.useId)();
5215
5218
  const summary = entry.catalog?.summary;
@@ -5330,26 +5333,34 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5330
5333
  install,
5331
5334
  installStatus,
5332
5335
  restart
5333
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [installed.outdated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstallPanel, {
5334
- name: entry.name,
5335
- version: entry.version,
5336
- tier: entry.tier,
5337
- variant: "update",
5338
- t,
5339
- install,
5340
- installStatus,
5341
- restart
5342
- }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5343
- className: _dsh_plugin_shop_css_e3675a89_default.installedLabel,
5344
- "data-shop-installed": true,
5345
- children: t("installed")
5346
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UninstallPanel, {
5347
- name: entry.name,
5348
- t,
5349
- uninstall,
5350
- installStatus,
5351
- restart
5352
- })] })
5336
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
5337
+ installed.outdated ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstallPanel, {
5338
+ name: entry.name,
5339
+ version: entry.version,
5340
+ tier: entry.tier,
5341
+ variant: "update",
5342
+ t,
5343
+ install,
5344
+ installStatus,
5345
+ restart
5346
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5347
+ className: _dsh_plugin_shop_css_e3675a89_default.installedLabel,
5348
+ "data-shop-installed": true,
5349
+ children: t("installed")
5350
+ }),
5351
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(EnabledSwitch, {
5352
+ row: installed,
5353
+ t,
5354
+ setEnabled
5355
+ }),
5356
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(UninstallPanel, {
5357
+ name: entry.name,
5358
+ t,
5359
+ uninstall,
5360
+ installStatus,
5361
+ restart
5362
+ })
5363
+ ] })
5353
5364
  })
5354
5365
  ]
5355
5366
  });
@@ -5624,14 +5635,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5624
5635
  children: t("restart")
5625
5636
  });
5626
5637
  }
5627
- /** One outdated install row (§7.3): the name, the installed and latest
5628
- * versions, an optimistic enable/disable switch, and the update button (the
5629
- * install flow for `name@latest`, reusing `InstallPanel`). The switch has no
5630
- * installed-state RPC behind it the tab assumes an installed plugin is on,
5631
- * so the first toggle sends the inverted value. After a successful `setEnabled`
5632
- * the §8 hot note renders. */
5633
- function OutdatedRow({ row, tier, source, t, setEnabled, install, installStatus, restart }) {
5634
- const [enabled, setEnabledState] = (0, react.useState)(true);
5638
+ /**
5639
+ * The hot enable/disable switch (§8). The initial state is the Host's
5640
+ * inventory verdict carried on the installed row; the click is optimistic
5641
+ * (the value flips on success) and a success renders the §8 hot note. A
5642
+ * transport throw renders the localized failure line its private detail
5643
+ * (which can name hosts and ports) never reaches the UI.
5644
+ */
5645
+ function EnabledSwitch({ row, t, setEnabled }) {
5646
+ const [enabled, setEnabledState] = (0, react.useState)(row.enabled);
5635
5647
  const [toggle, setToggle] = (0, react.useState)({ kind: "idle" });
5636
5648
  const onToggle = async () => {
5637
5649
  if (toggle.kind === "saving") return;
@@ -5657,47 +5669,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5657
5669
  }
5658
5670
  };
5659
5671
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5660
- className: _dsh_plugin_shop_css_e3675a89_default.outdatedRow,
5661
- "data-shop-outdated-entry": row.name,
5672
+ className: _dsh_plugin_shop_css_e3675a89_default.switchWrap,
5673
+ "data-shop-enabled-switch": row.name,
5662
5674
  children: [
5663
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5664
- className: _dsh_plugin_shop_css_e3675a89_default.outdatedInfo,
5665
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5666
- className: _dsh_plugin_shop_css_e3675a89_default.name,
5667
- children: row.name
5668
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5669
- className: _dsh_plugin_shop_css_e3675a89_default.outdatedVersions,
5670
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5671
- className: _dsh_plugin_shop_css_e3675a89_default.outdatedVersion,
5672
- children: t("installedVersion", { version: source === "github" ? row.installed.slice(0, 7) : row.installed })
5673
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5674
- className: _dsh_plugin_shop_css_e3675a89_default.outdatedVersion,
5675
- children: t("latestVersion", { version: row.latest })
5676
- })]
5677
- })]
5678
- }),
5679
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5680
- className: _dsh_plugin_shop_css_e3675a89_default.outdatedActions,
5681
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5682
- type: "button",
5683
- role: "switch",
5684
- "aria-checked": enabled,
5685
- "aria-label": t("enabledSwitch"),
5686
- "data-shop-toggle": true,
5687
- className: `${_dsh_plugin_shop_css_e3675a89_default.switch} ${enabled ? _dsh_plugin_shop_css_e3675a89_default.switchOn : ""}`,
5688
- onClick: () => void onToggle(),
5689
- disabled: toggle.kind === "saving",
5690
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_e3675a89_default.switchKnob })
5691
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstallPanel, {
5692
- name: row.name,
5693
- version: row.latest,
5694
- tier,
5695
- variant: "update",
5696
- t,
5697
- install,
5698
- installStatus,
5699
- restart
5700
- })]
5675
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
5676
+ type: "button",
5677
+ role: "switch",
5678
+ "aria-checked": enabled,
5679
+ "aria-label": t("enabledSwitch"),
5680
+ "data-shop-toggle": true,
5681
+ className: `${_dsh_plugin_shop_css_e3675a89_default.switch} ${enabled ? _dsh_plugin_shop_css_e3675a89_default.switchOn : ""}`,
5682
+ onClick: () => void onToggle(),
5683
+ disabled: toggle.kind === "saving",
5684
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: _dsh_plugin_shop_css_e3675a89_default.switchKnob })
5701
5685
  }),
5702
5686
  toggle.kind === "saved" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
5703
5687
  className: _dsh_plugin_shop_css_e3675a89_default.notice,
@@ -5712,6 +5696,47 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5712
5696
  ]
5713
5697
  });
5714
5698
  }
5699
+ /** One outdated install row (§7.3): the name, the installed and latest
5700
+ * versions, the hot enable/disable switch, and the update button (the
5701
+ * install flow for `name@latest`, reusing `InstallPanel`). */
5702
+ function OutdatedRow({ row, tier, source, t, setEnabled, install, installStatus, restart }) {
5703
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5704
+ className: _dsh_plugin_shop_css_e3675a89_default.outdatedRow,
5705
+ "data-shop-outdated-entry": row.name,
5706
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5707
+ className: _dsh_plugin_shop_css_e3675a89_default.outdatedInfo,
5708
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5709
+ className: _dsh_plugin_shop_css_e3675a89_default.name,
5710
+ children: row.name
5711
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
5712
+ className: _dsh_plugin_shop_css_e3675a89_default.outdatedVersions,
5713
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5714
+ className: _dsh_plugin_shop_css_e3675a89_default.outdatedVersion,
5715
+ children: t("installedVersion", { version: source === "github" ? row.installed.slice(0, 7) : row.installed })
5716
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
5717
+ className: _dsh_plugin_shop_css_e3675a89_default.outdatedVersion,
5718
+ children: t("latestVersion", { version: row.latest })
5719
+ })]
5720
+ })]
5721
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
5722
+ className: _dsh_plugin_shop_css_e3675a89_default.outdatedActions,
5723
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(EnabledSwitch, {
5724
+ row,
5725
+ t,
5726
+ setEnabled
5727
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstallPanel, {
5728
+ name: row.name,
5729
+ version: row.latest,
5730
+ tier,
5731
+ variant: "update",
5732
+ t,
5733
+ install,
5734
+ installStatus,
5735
+ restart
5736
+ })]
5737
+ })]
5738
+ });
5739
+ }
5715
5740
  /** The §7.3 installed list, rendered as the "installed" section: each row
5716
5741
  * shows both versions, a switch, and an update button. The rows are the
5717
5742
  * installed entries filtered to `outdated` — a current install is already
@@ -6133,7 +6158,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
6133
6158
  install,
6134
6159
  installStatus,
6135
6160
  uninstall,
6136
- restart
6161
+ restart,
6162
+ setEnabled
6137
6163
  }) }, entry.name)), incremental && visibleCount < filtered.length && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("li", {
6138
6164
  ref: sentinelRef,
6139
6165
  className: _dsh_plugin_shop_css_e3675a89_default.cardsSentry,
package/lib/index.js CHANGED
@@ -59,7 +59,8 @@ const entrySchema = z.object({
59
59
  capabilities: z.array(z.string())
60
60
  }).optional(),
61
61
  source: z.enum(["npm", "github"]).default("npm"),
62
- repo: z.string().optional()
62
+ repo: z.string().optional(),
63
+ subdir: z.string().regex(/^(?!.*(^|\/)\.\.?(\/|$))[A-Za-z0-9._-]+(\/[A-Za-z0-9._-]+)*$/).optional()
63
64
  });
64
65
  const dataSchema = z.object({
65
66
  schemaVersion: z.number(),
@@ -143,13 +144,13 @@ async function loadCatalog(options) {
143
144
  const readCached = () => {
144
145
  try {
145
146
  const pointer = pointerSchema.parse(JSON.parse(fsImpl.read(indexPath)));
146
- if (pointer.schemaVersion > 3) throw new Error(`catalog schemaVersion ${pointer.schemaVersion} is newer than this build supports (3)`);
147
+ if (pointer.schemaVersion > 4) throw new Error(`catalog schemaVersion ${pointer.schemaVersion} is newer than this build supports (4)`);
147
148
  const dataPath = join(cacheDir, basename(pointer.plugins.url));
148
149
  const dataText = fsImpl.read(dataPath);
149
150
  const actual = createHash("sha256").update(dataText).digest("hex");
150
151
  if (actual !== pointer.plugins.sha256) throw new Error(`cached catalog data failed integrity check: expected ${pointer.plugins.sha256}, got ${actual}`);
151
152
  const data = dataSchema.parse(JSON.parse(dataText));
152
- if (data.schemaVersion > 3) throw new Error(`catalog schemaVersion ${data.schemaVersion} is newer than this build supports (3)`);
153
+ if (data.schemaVersion > 4) throw new Error(`catalog schemaVersion ${data.schemaVersion} is newer than this build supports (4)`);
153
154
  let stars = {};
154
155
  if (pointer.stars !== void 0) try {
155
156
  const starsText = fsImpl.read(join(cacheDir, basename(pointer.stars.url)));
@@ -190,7 +191,7 @@ async function loadCatalog(options) {
190
191
  throw error;
191
192
  }
192
193
  const pointer = pointerSchema.parse(JSON.parse(pointerText));
193
- if (pointer.schemaVersion > 3) throw new Error(`catalog schemaVersion ${pointer.schemaVersion} is newer than this build supports (3)`);
194
+ if (pointer.schemaVersion > 4) throw new Error(`catalog schemaVersion ${pointer.schemaVersion} is newer than this build supports (4)`);
194
195
  const dataUrl = resolveDataUrl(baseUrl, pointer.plugins.url);
195
196
  let dataText;
196
197
  try {
@@ -208,7 +209,7 @@ async function loadCatalog(options) {
208
209
  const actual = createHash("sha256").update(dataText).digest("hex");
209
210
  if (actual !== pointer.plugins.sha256) throw new Error(`catalog data failed integrity check: expected ${pointer.plugins.sha256}, got ${actual}`);
210
211
  const data = dataSchema.parse(JSON.parse(dataText));
211
- if (data.schemaVersion > 3) throw new Error(`catalog schemaVersion ${data.schemaVersion} is newer than this build supports (3)`);
212
+ if (data.schemaVersion > 4) throw new Error(`catalog schemaVersion ${data.schemaVersion} is newer than this build supports (4)`);
212
213
  let stars = {};
213
214
  if (pointer.stars !== void 0) try {
214
215
  const starsResponse = await fetchImpl(resolveDataUrl(baseUrl, pointer.stars.url));
@@ -851,8 +852,14 @@ let ShopGateway = (() => {
851
852
  }
852
853
  /** Enable or disable one installed plugin, hot (§8): a disable writes the
853
854
  * row to the user layer, an enable drops it again so the bundle default
854
- * rules — the CLI's watchUserPatches applies either through HMR. */
855
+ * rules — the CLI's watchUserPatches applies either through HMR. The shop's
856
+ * own row and the framework's bundles are never toggleable: disabling the
857
+ * host chain would break HMR itself. */
855
858
  setEnabled(args) {
859
+ if (args.name === "dsh-plugin-shop" || args.name.startsWith("@deepseek-ai/")) return {
860
+ ok: false,
861
+ detail: `dsh-plugin-shop: ${args.name} is part of the harness chain and cannot be toggled from the shop`
862
+ };
856
863
  const entry = this.listInventory().find((entry) => entry.moduleName === args.name);
857
864
  if (entry === void 0) return {
858
865
  ok: false,
@@ -937,7 +944,7 @@ let ShopGateway = (() => {
937
944
  code: "git-missing",
938
945
  detail: "dsh-plugin-shop: git is not on PATH, which github installs require; install git and retry"
939
946
  };
940
- spec = `github:${entry.repo}#${args.version}`;
947
+ spec = `github:${entry.repo}#${args.version}${entry.subdir !== void 0 ? `&path:${entry.subdir}` : ""}`;
941
948
  } else spec = `${args.name}@${args.version}`;
942
949
  const running = startInstall({
943
950
  profile: this.profile,
@@ -1001,6 +1008,10 @@ let ShopGateway = (() => {
1001
1008
  }
1002
1009
  const dependencies = readProfileManifest("dsh-plugin-shop", this.profileDirResolved()).dependencies ?? {};
1003
1010
  const pins = readRepoPins(this.pinFs, this.pinsPath());
1011
+ let byName = /* @__PURE__ */ new Map();
1012
+ try {
1013
+ byName = new Map(this.listInventory().map((entry) => [entry.moduleName, entry.enabled]));
1014
+ } catch {}
1004
1015
  const installed = [];
1005
1016
  for (const entry of this.lastSnapshot.entries) {
1006
1017
  const spec = dependencies[entry.name];
@@ -1011,13 +1022,15 @@ let ShopGateway = (() => {
1011
1022
  name: entry.name,
1012
1023
  installed: pin ?? spec,
1013
1024
  latest: entry.version,
1014
- outdated: pin !== void 0 && pin !== entry.version
1025
+ outdated: pin !== void 0 && pin !== entry.version,
1026
+ enabled: byName.get(entry.name) ?? true
1015
1027
  });
1016
1028
  } else installed.push({
1017
1029
  name: entry.name,
1018
1030
  installed: spec,
1019
1031
  latest: entry.version,
1020
- outdated: this.isBehind(spec, entry.version)
1032
+ outdated: this.isBehind(spec, entry.version),
1033
+ enabled: byName.get(entry.name) ?? true
1021
1034
  });
1022
1035
  }
1023
1036
  return installed;
@@ -34,6 +34,7 @@ const dsh_plugin_shop_shop_catalog_result$schema = z.object({
34
34
  })]).optional(),
35
35
  'source': z.union([z.literal("npm"), z.literal("github")]),
36
36
  'repo': z.union([z.undefined(), z.string()]).optional(),
37
+ 'subdir': z.union([z.undefined(), z.string()]).optional(),
37
38
  })),
38
39
  'denied': z.array(z.object({
39
40
  'name': z.string(),
@@ -46,6 +47,7 @@ const dsh_plugin_shop_shop_installed_result$schema = z.array(z.object({
46
47
  'installed': z.string(),
47
48
  'latest': z.string(),
48
49
  'outdated': z.boolean(),
50
+ 'enabled': z.boolean(),
49
51
  }))
50
52
  const dsh_plugin_shop_shop_installStart_parameter_0$schema = z.object({
51
53
  'name': z.string(),
@@ -140,7 +142,7 @@ export const TYPERT = {
140
142
  typeSymbol: 'dsh-plugin-shop/types#ShopCatalogResult',
141
143
  schema: dsh_plugin_shop_shop_catalog_result$schema,
142
144
  },
143
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":270,"column":9},
145
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":275,"column":9},
144
146
  },
145
147
  {
146
148
  id: 'dsh-plugin-shop#shop/installed',
@@ -155,7 +157,7 @@ export const TYPERT = {
155
157
  typeSymbol: 'dsh-plugin-shop#shop/installed:result',
156
158
  schema: dsh_plugin_shop_shop_installed_result$schema,
157
159
  },
158
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":374,"column":9},
160
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":380,"column":9},
159
161
  },
160
162
  {
161
163
  id: 'dsh-plugin-shop#shop/installStart',
@@ -181,7 +183,7 @@ export const TYPERT = {
181
183
  typeSymbol: 'dsh-plugin-shop/types#ShopInstallResult',
182
184
  schema: dsh_plugin_shop_shop_installStart_result$schema,
183
185
  },
184
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":296,"column":9},
186
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":301,"column":9},
185
187
  },
186
188
  {
187
189
  id: 'dsh-plugin-shop#shop/installStatus',
@@ -206,7 +208,7 @@ export const TYPERT = {
206
208
  typeSymbol: 'dsh-plugin-shop/types#ShopInstallStatusResult',
207
209
  schema: dsh_plugin_shop_shop_installStatus_result$schema,
208
210
  },
209
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":363,"column":3},
211
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":369,"column":3},
210
212
  },
211
213
  {
212
214
  id: 'dsh-plugin-shop#shop/restart',
@@ -221,7 +223,7 @@ export const TYPERT = {
221
223
  typeSymbol: 'dsh-plugin-shop/types#ShopRestartResult',
222
224
  schema: dsh_plugin_shop_shop_restart_result$schema,
223
225
  },
224
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":469,"column":9},
226
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":491,"column":9},
225
227
  },
226
228
  {
227
229
  id: 'dsh-plugin-shop#shop/setEnabled',
@@ -246,7 +248,7 @@ export const TYPERT = {
246
248
  typeSymbol: 'dsh-plugin-shop/types#ShopSetEnabledResult',
247
249
  schema: dsh_plugin_shop_shop_setEnabled_result$schema,
248
250
  },
249
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":239,"column":3},
251
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":241,"column":3},
250
252
  },
251
253
  {
252
254
  id: 'dsh-plugin-shop#shop/uninstallStart',
@@ -272,7 +274,7 @@ export const TYPERT = {
272
274
  typeSymbol: 'dsh-plugin-shop/types#ShopUninstallResult',
273
275
  schema: dsh_plugin_shop_shop_uninstallStart_result$schema,
274
276
  },
275
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":429,"column":9},
277
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":451,"column":9},
276
278
  },
277
279
  {
278
280
  id: 'dsh-plugin-shop#shop/updateStart',
@@ -297,7 +299,7 @@ export const TYPERT = {
297
299
  typeSymbol: 'dsh-plugin-shop/types#ShopUpdateResult',
298
300
  schema: dsh_plugin_shop_shop_updateStart_result$schema,
299
301
  },
300
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":518,"column":9},
302
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":540,"column":9},
301
303
  },
302
304
  {
303
305
  id: 'dsh-plugin-shop#shop/version',
@@ -312,7 +314,7 @@ export const TYPERT = {
312
314
  typeSymbol: 'dsh-plugin-shop/types#ShopVersionResult',
313
315
  schema: dsh_plugin_shop_shop_version_result$schema,
314
316
  },
315
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":503,"column":9},
317
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":525,"column":9},
316
318
  },
317
319
  ],
318
320
  model: {
@@ -336,7 +338,7 @@ export const TYPERT = {
336
338
  "name": "setEnabled",
337
339
  "signature": "@Remote('setEnabled') setEnabled(args: { name: string; enabled: boolean }): ShopSetEnabledResult",
338
340
  "summary": "Enable or disable one installed plugin, hot (§8): a disable writes the row to the user layer, an enable drops it again so the bundle default rules — the CLI's watchUserPatches applies either through HMR.",
339
- "jsDoc": "/** Enable or disable one installed plugin, hot (§8): a disable writes the\n * row to the user layer, an enable drops it again so the bundle default\n * rules — the CLI's watchUserPatches applies either through HMR. */"
341
+ "jsDoc": "/** Enable or disable one installed plugin, hot (§8): a disable writes the\n * row to the user layer, an enable drops it again so the bundle default\n * rules — the CLI's watchUserPatches applies either through HMR. The shop's\n * own row and the framework's bundles are never toggleable: disabling the\n * host chain would break HMR itself. */"
340
342
  },
341
343
  {
342
344
  "kind": "method",
@@ -398,7 +400,7 @@ export const TYPERT = {
398
400
  "types": [
399
401
  {
400
402
  "name": "CatalogEntry",
401
- "declaration": "export interface CatalogEntry {\n name: string;\n version: string;\n integrity: string | null;\n publishedAt: string | null;\n repository: string | null;\n license: string | null;\n tier: 'verified' | 'verified-stale' | 'community';\n metadata: 'declared' | 'derived';\n review?: { reviewedVersion?: string; reviewedCommit?: string; reviewer: string; reviewCommit: string; notes: string; };\n catalog?: CatalogSection;\n source: 'npm' | 'github';\n repo?: string;\n}"
403
+ "declaration": "export interface CatalogEntry {\n name: string;\n version: string;\n integrity: string | null;\n publishedAt: string | null;\n repository: string | null;\n license: string | null;\n tier: 'verified' | 'verified-stale' | 'community';\n metadata: 'declared' | 'derived';\n review?: { reviewedVersion?: string; reviewedCommit?: string; reviewer: string; reviewCommit: string; notes: string; };\n catalog?: CatalogSection;\n source: 'npm' | 'github';\n repo?: string;\n subdir?: string;\n}"
402
404
  },
403
405
  {
404
406
  "name": "CatalogSection",
@@ -438,7 +440,7 @@ export const TYPERT = {
438
440
  },
439
441
  {
440
442
  "name": "ShopInstalledEntry",
441
- "declaration": "export interface ShopInstalledEntry {\n name: string;\n installed: string;\n latest: string;\n outdated: boolean;\n}"
443
+ "declaration": "export interface ShopInstalledEntry {\n name: string;\n installed: string;\n latest: string;\n outdated: boolean;\n enabled: boolean;\n}"
442
444
  },
443
445
  {
444
446
  "name": "ShopInstallResult",
@@ -34,6 +34,7 @@ const dsh_plugin_shop_shop_catalog_result$schema = z.object({
34
34
  })]).optional(),
35
35
  'source': z.union([z.literal("npm"), z.literal("github")]),
36
36
  'repo': z.union([z.undefined(), z.string()]).optional(),
37
+ 'subdir': z.union([z.undefined(), z.string()]).optional(),
37
38
  })),
38
39
  'denied': z.array(z.object({
39
40
  'name': z.string(),
@@ -46,6 +47,7 @@ const dsh_plugin_shop_shop_installed_result$schema = z.array(z.object({
46
47
  'installed': z.string(),
47
48
  'latest': z.string(),
48
49
  'outdated': z.boolean(),
50
+ 'enabled': z.boolean(),
49
51
  }))
50
52
  const dsh_plugin_shop_shop_installStart_parameter_0$schema = z.object({
51
53
  'name': z.string(),
@@ -137,7 +139,7 @@ export const TYPERT_REMOTE = {
137
139
  typeSymbol: 'dsh-plugin-shop/types#ShopCatalogResult',
138
140
  schema: dsh_plugin_shop_shop_catalog_result$schema,
139
141
  },
140
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":270,"column":9},
142
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":275,"column":9},
141
143
  },
142
144
  {
143
145
  id: 'dsh-plugin-shop#shop/installed',
@@ -152,7 +154,7 @@ export const TYPERT_REMOTE = {
152
154
  typeSymbol: 'dsh-plugin-shop#shop/installed:result',
153
155
  schema: dsh_plugin_shop_shop_installed_result$schema,
154
156
  },
155
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":374,"column":9},
157
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":380,"column":9},
156
158
  },
157
159
  {
158
160
  id: 'dsh-plugin-shop#shop/installStart',
@@ -178,7 +180,7 @@ export const TYPERT_REMOTE = {
178
180
  typeSymbol: 'dsh-plugin-shop/types#ShopInstallResult',
179
181
  schema: dsh_plugin_shop_shop_installStart_result$schema,
180
182
  },
181
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":296,"column":9},
183
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":301,"column":9},
182
184
  },
183
185
  {
184
186
  id: 'dsh-plugin-shop#shop/installStatus',
@@ -203,7 +205,7 @@ export const TYPERT_REMOTE = {
203
205
  typeSymbol: 'dsh-plugin-shop/types#ShopInstallStatusResult',
204
206
  schema: dsh_plugin_shop_shop_installStatus_result$schema,
205
207
  },
206
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":363,"column":3},
208
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":369,"column":3},
207
209
  },
208
210
  {
209
211
  id: 'dsh-plugin-shop#shop/restart',
@@ -218,7 +220,7 @@ export const TYPERT_REMOTE = {
218
220
  typeSymbol: 'dsh-plugin-shop/types#ShopRestartResult',
219
221
  schema: dsh_plugin_shop_shop_restart_result$schema,
220
222
  },
221
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":469,"column":9},
223
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":491,"column":9},
222
224
  },
223
225
  {
224
226
  id: 'dsh-plugin-shop#shop/setEnabled',
@@ -243,7 +245,7 @@ export const TYPERT_REMOTE = {
243
245
  typeSymbol: 'dsh-plugin-shop/types#ShopSetEnabledResult',
244
246
  schema: dsh_plugin_shop_shop_setEnabled_result$schema,
245
247
  },
246
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":239,"column":3},
248
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":241,"column":3},
247
249
  },
248
250
  {
249
251
  id: 'dsh-plugin-shop#shop/uninstallStart',
@@ -269,7 +271,7 @@ export const TYPERT_REMOTE = {
269
271
  typeSymbol: 'dsh-plugin-shop/types#ShopUninstallResult',
270
272
  schema: dsh_plugin_shop_shop_uninstallStart_result$schema,
271
273
  },
272
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":429,"column":9},
274
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":451,"column":9},
273
275
  },
274
276
  {
275
277
  id: 'dsh-plugin-shop#shop/updateStart',
@@ -294,7 +296,7 @@ export const TYPERT_REMOTE = {
294
296
  typeSymbol: 'dsh-plugin-shop/types#ShopUpdateResult',
295
297
  schema: dsh_plugin_shop_shop_updateStart_result$schema,
296
298
  },
297
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":518,"column":9},
299
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":540,"column":9},
298
300
  },
299
301
  {
300
302
  id: 'dsh-plugin-shop#shop/version',
@@ -309,7 +311,7 @@ export const TYPERT_REMOTE = {
309
311
  typeSymbol: 'dsh-plugin-shop/types#ShopVersionResult',
310
312
  schema: dsh_plugin_shop_shop_version_result$schema,
311
313
  },
312
- sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":503,"column":9},
314
+ sourceLocation: {"file":"packages/dsh-plugin-shop/src/host/index.ts","line":525,"column":9},
313
315
  },
314
316
  ],
315
317
  }
@@ -1,8 +1,9 @@
1
1
  /** Catalog fetch, verification, and disk cache — the Host's only network path. */
2
2
  import type { CatalogEntry, DeniedEntry } from './types.ts';
3
3
  /** Highest schemaVersion this build understands; a higher one is refused (§10).
4
- * 3 adds `source` and repo entries (github install channel). */
5
- export declare const SUPPORTED_SCHEMA_VERSION = 3;
4
+ * 3 adds `source` and repo entries (github install channel); 4 adds `subdir`
5
+ * for monorepo-subpackage entries (2026-08-31 hub-borrowings A). */
6
+ export declare const SUPPORTED_SCHEMA_VERSION = 4;
6
7
  export interface CatalogSnapshot {
7
8
  schemaVersion: number;
8
9
  builtAt: string;
@@ -114,6 +114,7 @@ export interface ShopInstalledEntry {
114
114
  installed: string;
115
115
  latest: string;
116
116
  outdated: boolean;
117
+ enabled: boolean;
117
118
  }
118
119
  /** `shop/catalog` result (§7.3), plus the denied list for the install gate's UI. */
119
120
  export interface ShopCatalogResult {
@@ -179,7 +180,9 @@ export declare class ShopGateway extends TypertRemoteService {
179
180
  private listInventory;
180
181
  /** Enable or disable one installed plugin, hot (§8): a disable writes the
181
182
  * row to the user layer, an enable drops it again so the bundle default
182
- * rules — the CLI's watchUserPatches applies either through HMR. */
183
+ * rules — the CLI's watchUserPatches applies either through HMR. The shop's
184
+ * own row and the framework's bundles are never toggleable: disabling the
185
+ * host chain would break HMR itself. */
183
186
  setEnabled(args: {
184
187
  name: string;
185
188
  enabled: boolean;
@@ -29,6 +29,9 @@ export interface CatalogEntry {
29
29
  source: 'npm' | 'github';
30
30
  /** `owner/slug`; present exactly when `source` is github. */
31
31
  repo?: string;
32
+ /** Subpackage directory inside the repo; present exactly when the entry is
33
+ * a monorepo subpackage rather than the repo root. */
34
+ subdir?: string;
32
35
  }
33
36
  export interface DeniedEntry {
34
37
  name: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-shop",
3
- "version": "0.4.13",
3
+ "version": "0.4.14",
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",