@linxin666/dsh-client-ui-skin-center 0.1.3 → 0.1.6

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.
Files changed (3) hide show
  1. package/lib/client.js +38 -38
  2. package/lib/index.js +142 -41
  3. package/package.json +1 -1
package/lib/client.js CHANGED
@@ -464,8 +464,8 @@ window.__ModuleLoader__.load({
464
464
  }
465
465
  };
466
466
  //#endregion
467
- //#region \0dsh-css:/Users/zcl/code/dsh-web-ui/packages/skins/skin-center/src/client/skin-center.module.css.mjs
468
- const css = "body[data-dsh-skin-center] .K3rRTW_pluginCard{border:1px solid var(--dsw-alias-border-l1,#e2e8f0);background:var(--dsw-alias-bg-layer-2,#fff);border-radius:8px;list-style:none;overflow:hidden}body[data-dsh-skin-center] .K3rRTW_cardHeader{width:100%;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;align-items:center;padding:11px 14px;transition:background .12s;display:flex}body[data-dsh-skin-center] .K3rRTW_cardHeader:hover{background:var(--dsw-alias-bg-layer-1,#f1f5f9)}body[data-dsh-skin-center] .K3rRTW_cardHeader:active{background:var(--dsw-alias-bg-layer-3,#e6ecf4)}body[data-dsh-skin-center] .K3rRTW_cardHeader:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#2b7cd9);outline-offset:2px}body[data-dsh-skin-center] .K3rRTW_headText{flex-direction:column;flex:1;gap:3px;min-width:0;display:flex}body[data-dsh-skin-center] .K3rRTW_pluginName{color:var(--dsw-alias-label-primary,#172a45);align-items:baseline;gap:8px;font-size:13.5px;font-weight:600;display:flex}body[data-dsh-skin-center] .K3rRTW_cardDescription{color:var(--dsw-alias-label-secondary,#6b7280);font-size:12px;line-height:1.4}body[data-dsh-skin-center] .K3rRTW_chevron,body[data-dsh-skin-center] .K3rRTW_chevronOpen{color:var(--dsw-alias-label-secondary,#6b7280);flex:none;margin-left:10px;font-size:12px;transition:transform .12s}body[data-dsh-skin-center] .K3rRTW_chevronOpen{transform:rotate(180deg)}body[data-dsh-skin-center] .K3rRTW_cardBody{border-top:1px solid var(--dsw-alias-border-l1,#e2e8f0);flex-direction:column;gap:12px;padding:12px 14px 14px;display:flex}body[data-dsh-skin-center] .K3rRTW_head{flex-direction:column;gap:6px;display:flex}body[data-dsh-skin-center] .K3rRTW_titleBadge{color:var(--dsw-alias-label-secondary,#6b7280);font-size:11px;font-weight:500}body[data-dsh-skin-center] .K3rRTW_intro{color:var(--dsw-alias-label-secondary,#6b7280);font-size:12.5px;line-height:1.55}body[data-dsh-skin-center] .K3rRTW_themeRow{align-items:center;gap:8px;margin-top:2px;display:flex}body[data-dsh-skin-center] .K3rRTW_themeLabel{color:var(--dsw-alias-label-secondary,#6b7280);margin-right:2px;font-size:12px}body[data-dsh-skin-center] .K3rRTW_themeButton{border:1px solid var(--dsw-alias-border-l3,#cbd5e1);background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#172a45);cursor:pointer;border-radius:6px;padding:5px 10px;font-size:12px;line-height:1;transition:background .12s,border-color .12s,color .12s}body[data-dsh-skin-center] .K3rRTW_themeButton:hover{border-color:var(--dsw-alias-border-l4,#94a3b8)}body[data-dsh-skin-center] .K3rRTW_themeButton:active{border-color:var(--dsw-alias-brand-primary,#2b7cd9);background:var(--dsw-alias-button-primary-dimmed,#e8f1fc);color:var(--dsw-alias-brand-primary,#1e63b8)}body[data-dsh-skin-center] .K3rRTW_themeButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#2b7cd9);outline-offset:2px}body[data-dsh-skin-center] .K3rRTW_themeButtonActive{border-color:var(--dsw-alias-brand-primary,#2b7cd9);background:var(--dsw-alias-button-primary-dimmed,#e8f1fc);color:var(--dsw-alias-brand-primary,#1e63b8)}body[data-dsh-skin-center] .K3rRTW_list{flex-direction:column;gap:10px;display:flex}body[data-dsh-skin-center] .K3rRTW_card{border:1px solid var(--dsw-alias-border-l1,#e2e8f0);background:var(--dsw-alias-bg-layer-2,#fff);border-radius:10px;flex-direction:column;gap:8px;padding:12px 14px;display:flex}body[data-dsh-skin-center] .K3rRTW_cardHead{align-items:center;gap:10px;min-width:0;display:flex}body[data-dsh-skin-center] .K3rRTW_swatch{width:14px;height:14px;box-shadow:inset 0 0 0 1px var(--dsw-alias-border-l4,#0f172a1f);border-radius:50%;flex:none}body[data-dsh-skin-center] .K3rRTW_cardName{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:13.5px;font-weight:600;overflow:hidden}body[data-dsh-skin-center] .K3rRTW_cardTagline{color:var(--dsw-alias-label-secondary,#6b7280);font-size:12px;line-height:1.45}body[data-dsh-skin-center] .K3rRTW_badge{letter-spacing:.02em;border-radius:999px;flex:none;min-width:0;margin-left:auto;padding:2px 8px;font-size:11px;font-weight:600}body[data-dsh-skin-center] .K3rRTW_badgeActive{color:var(--dsw-alias-state-success-primary,#0f6b3a);background:var(--dsw-alias-state-success-tertiary,#dcf3e5)}body[data-dsh-skin-center] .K3rRTW_badgeTrying{color:var(--dsw-alias-brand-primary,#1e63b8);background:var(--dsw-alias-button-primary-dimmed,#e2edfc)}body[data-dsh-skin-center] .K3rRTW_actions{flex-wrap:wrap;align-items:center;gap:8px;display:flex}body[data-dsh-skin-center] .K3rRTW_button{border:1px solid var(--dsw-alias-border-l3,#cbd5e1);background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#172a45);cursor:pointer;border-radius:7px;padding:6px 12px;font-size:12px;line-height:1;transition:background .12s,border-color .12s,color .12s}body[data-dsh-skin-center] .K3rRTW_button:hover:not(:disabled){border-color:var(--dsw-alias-brand-primary,#2b7cd9);color:var(--dsw-alias-brand-primary,#1e63b8)}body[data-dsh-skin-center] .K3rRTW_button:active:not(:disabled){border-color:var(--dsw-alias-button-primary-hover,#1e63b8);background:var(--dsw-alias-button-primary-dimmed,#e8f1fc);color:var(--dsw-alias-brand-primary,#1e63b8)}body[data-dsh-skin-center] .K3rRTW_button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#2b7cd9);outline-offset:2px}body[data-dsh-skin-center] .K3rRTW_buttonPrimary{border-color:var(--dsw-alias-brand-primary,#2b7cd9);background:var(--dsw-alias-button-primary-fill,#2b7cd9);color:var(--dsw-alias-label-primary-foreground,#fff)}body[data-dsh-skin-center] .K3rRTW_buttonPrimary:hover:not(:disabled){border-color:var(--dsw-alias-button-primary-hover,#1e63b8);background:var(--dsw-alias-button-primary-hover,#1e63b8);color:var(--dsw-alias-label-primary-foreground,#fff)}body[data-dsh-skin-center] .K3rRTW_buttonPrimary:active:not(:disabled),body[data-dsh-skin-center] .K3rRTW_buttonPrimary:focus-visible:not(:disabled){border-color:var(--dsw-alias-button-primary-hover,#1e63b8);background:var(--dsw-alias-button-primary-hover,#1e63b8)}body[data-dsh-skin-center] .K3rRTW_buttonGhost{background:0 0;border-color:#0000}body[data-dsh-skin-center] .K3rRTW_button:disabled{opacity:.55;cursor:default}body[data-dsh-skin-center] .K3rRTW_error{color:var(--dsw-alias-state-error-primary,#b42318);font-size:12px}body[data-dsh-skin-center] .K3rRTW_backgroundRow{flex-direction:column;gap:6px;padding:8px 0;display:flex}body[data-dsh-skin-center] .K3rRTW_backgroundHead{align-items:center;gap:8px;display:flex}body[data-dsh-skin-center] .K3rRTW_backgroundLabel{color:var(--dsw-alias-label-primary,#172a45);font-size:12.5px;font-weight:600}body[data-dsh-skin-center] .K3rRTW_backgroundValue{font-variant-numeric:tabular-nums;color:var(--dsw-alias-brand-primary,#2b7cd9);flex:none;margin-left:auto;font-size:12px}body[data-dsh-skin-center] .K3rRTW_backgroundRange{background:var(--dsw-alias-bg-layer-3,#e2e8f0);-webkit-appearance:none;appearance:none;cursor:pointer;border-radius:999px;width:100%;height:4px;margin:0}body[data-dsh-skin-center] .K3rRTW_backgroundRange::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;border:2px solid var(--dsw-alias-label-primary-foreground,#fff);background:var(--dsw-alias-brand-primary,#2b7cd9);width:14px;height:14px;box-shadow:0 0 0 1px var(--dsw-alias-border-l4,#0f172a1f);cursor:pointer;border-radius:50%}body[data-dsh-skin-center] .K3rRTW_backgroundRange::-moz-range-thumb{border:2px solid var(--dsw-alias-label-primary-foreground,#fff);background:var(--dsw-alias-brand-primary,#2b7cd9);width:12px;height:12px;box-shadow:0 0 0 1px var(--dsw-alias-border-l4,#0f172a1f);cursor:pointer;border-radius:50%}body[data-dsh-skin-center] .K3rRTW_backgroundRange:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#2b7cd9);outline-offset:2px}body[data-dsh-skin-center] .K3rRTW_backgroundHint{color:var(--dsw-alias-label-secondary,#6b7280);font-size:12px;line-height:1.5}body[data-dsh-skin-center] .K3rRTW_backgroundHintMuted{color:var(--dsw-alias-label-tertiary,#9aa4b5);font-size:12px;line-height:1.5}@media (prefers-reduced-motion:reduce){body[data-dsh-skin-center] .K3rRTW_cardHeader,body[data-dsh-skin-center] .K3rRTW_themeButton,body[data-dsh-skin-center] .K3rRTW_button,body[data-dsh-skin-center] .K3rRTW_chevron,body[data-dsh-skin-center] .K3rRTW_chevronOpen{transition:none}}";
467
+ //#region \0dsh-css:/home/runner/work/dsh-web-ui/dsh-web-ui/packages/skins/skin-center/src/client/skin-center.module.css.mjs
468
+ const css = "body[data-dsh-skin-center] .aIQHxa_pluginCard{border:1px solid var(--dsw-alias-border-l1,#e2e8f0);background:var(--dsw-alias-bg-layer-2,#fff);border-radius:8px;list-style:none;overflow:hidden}body[data-dsh-skin-center] .aIQHxa_cardHeader{width:100%;color:inherit;font:inherit;text-align:left;cursor:pointer;background:0 0;border:0;align-items:center;padding:11px 14px;transition:background .12s;display:flex}body[data-dsh-skin-center] .aIQHxa_cardHeader:hover{background:var(--dsw-alias-bg-layer-1,#f1f5f9)}body[data-dsh-skin-center] .aIQHxa_cardHeader:active{background:var(--dsw-alias-bg-layer-3,#e6ecf4)}body[data-dsh-skin-center] .aIQHxa_cardHeader:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#2b7cd9);outline-offset:2px}body[data-dsh-skin-center] .aIQHxa_headText{flex-direction:column;flex:1;gap:3px;min-width:0;display:flex}body[data-dsh-skin-center] .aIQHxa_pluginName{color:var(--dsw-alias-label-primary,#172a45);align-items:baseline;gap:8px;font-size:13.5px;font-weight:600;display:flex}body[data-dsh-skin-center] .aIQHxa_cardDescription{color:var(--dsw-alias-label-secondary,#6b7280);font-size:12px;line-height:1.4}body[data-dsh-skin-center] .aIQHxa_chevron,body[data-dsh-skin-center] .aIQHxa_chevronOpen{color:var(--dsw-alias-label-secondary,#6b7280);flex:none;margin-left:10px;font-size:12px;transition:transform .12s}body[data-dsh-skin-center] .aIQHxa_chevronOpen{transform:rotate(180deg)}body[data-dsh-skin-center] .aIQHxa_cardBody{border-top:1px solid var(--dsw-alias-border-l1,#e2e8f0);flex-direction:column;gap:12px;padding:12px 14px 14px;display:flex}body[data-dsh-skin-center] .aIQHxa_head{flex-direction:column;gap:6px;display:flex}body[data-dsh-skin-center] .aIQHxa_titleBadge{color:var(--dsw-alias-label-secondary,#6b7280);font-size:11px;font-weight:500}body[data-dsh-skin-center] .aIQHxa_intro{color:var(--dsw-alias-label-secondary,#6b7280);font-size:12.5px;line-height:1.55}body[data-dsh-skin-center] .aIQHxa_themeRow{align-items:center;gap:8px;margin-top:2px;display:flex}body[data-dsh-skin-center] .aIQHxa_themeLabel{color:var(--dsw-alias-label-secondary,#6b7280);margin-right:2px;font-size:12px}body[data-dsh-skin-center] .aIQHxa_themeButton{border:1px solid var(--dsw-alias-border-l3,#cbd5e1);background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#172a45);cursor:pointer;border-radius:6px;padding:5px 10px;font-size:12px;line-height:1;transition:background .12s,border-color .12s,color .12s}body[data-dsh-skin-center] .aIQHxa_themeButton:hover{border-color:var(--dsw-alias-border-l4,#94a3b8)}body[data-dsh-skin-center] .aIQHxa_themeButton:active{border-color:var(--dsw-alias-brand-primary,#2b7cd9);background:var(--dsw-alias-button-primary-dimmed,#e8f1fc);color:var(--dsw-alias-brand-primary,#1e63b8)}body[data-dsh-skin-center] .aIQHxa_themeButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#2b7cd9);outline-offset:2px}body[data-dsh-skin-center] .aIQHxa_themeButtonActive{border-color:var(--dsw-alias-brand-primary,#2b7cd9);background:var(--dsw-alias-button-primary-dimmed,#e8f1fc);color:var(--dsw-alias-brand-primary,#1e63b8)}body[data-dsh-skin-center] .aIQHxa_list{flex-direction:column;gap:10px;display:flex}body[data-dsh-skin-center] .aIQHxa_card{border:1px solid var(--dsw-alias-border-l1,#e2e8f0);background:var(--dsw-alias-bg-layer-2,#fff);border-radius:10px;flex-direction:column;gap:8px;padding:12px 14px;display:flex}body[data-dsh-skin-center] .aIQHxa_cardHead{align-items:center;gap:10px;min-width:0;display:flex}body[data-dsh-skin-center] .aIQHxa_swatch{width:14px;height:14px;box-shadow:inset 0 0 0 1px var(--dsw-alias-border-l4,#0f172a1f);border-radius:50%;flex:none}body[data-dsh-skin-center] .aIQHxa_cardName{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:13.5px;font-weight:600;overflow:hidden}body[data-dsh-skin-center] .aIQHxa_cardTagline{color:var(--dsw-alias-label-secondary,#6b7280);font-size:12px;line-height:1.45}body[data-dsh-skin-center] .aIQHxa_badge{letter-spacing:.02em;border-radius:999px;flex:none;min-width:0;margin-left:auto;padding:2px 8px;font-size:11px;font-weight:600}body[data-dsh-skin-center] .aIQHxa_badgeActive{color:var(--dsw-alias-state-success-primary,#0f6b3a);background:var(--dsw-alias-state-success-tertiary,#dcf3e5)}body[data-dsh-skin-center] .aIQHxa_badgeTrying{color:var(--dsw-alias-brand-primary,#1e63b8);background:var(--dsw-alias-button-primary-dimmed,#e2edfc)}body[data-dsh-skin-center] .aIQHxa_actions{flex-wrap:wrap;align-items:center;gap:8px;display:flex}body[data-dsh-skin-center] .aIQHxa_button{border:1px solid var(--dsw-alias-border-l3,#cbd5e1);background:var(--dsw-alias-bg-layer-2,#fff);color:var(--dsw-alias-label-primary,#172a45);cursor:pointer;border-radius:7px;padding:6px 12px;font-size:12px;line-height:1;transition:background .12s,border-color .12s,color .12s}body[data-dsh-skin-center] .aIQHxa_button:hover:not(:disabled){border-color:var(--dsw-alias-brand-primary,#2b7cd9);color:var(--dsw-alias-brand-primary,#1e63b8)}body[data-dsh-skin-center] .aIQHxa_button:active:not(:disabled){border-color:var(--dsw-alias-button-primary-hover,#1e63b8);background:var(--dsw-alias-button-primary-dimmed,#e8f1fc);color:var(--dsw-alias-brand-primary,#1e63b8)}body[data-dsh-skin-center] .aIQHxa_button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#2b7cd9);outline-offset:2px}body[data-dsh-skin-center] .aIQHxa_buttonPrimary{border-color:var(--dsw-alias-brand-primary,#2b7cd9);background:var(--dsw-alias-button-primary-fill,#2b7cd9);color:var(--dsw-alias-label-primary-foreground,#fff)}body[data-dsh-skin-center] .aIQHxa_buttonPrimary:hover:not(:disabled){border-color:var(--dsw-alias-button-primary-hover,#1e63b8);background:var(--dsw-alias-button-primary-hover,#1e63b8);color:var(--dsw-alias-label-primary-foreground,#fff)}body[data-dsh-skin-center] .aIQHxa_buttonPrimary:active:not(:disabled),body[data-dsh-skin-center] .aIQHxa_buttonPrimary:focus-visible:not(:disabled){border-color:var(--dsw-alias-button-primary-hover,#1e63b8);background:var(--dsw-alias-button-primary-hover,#1e63b8)}body[data-dsh-skin-center] .aIQHxa_buttonGhost{background:0 0;border-color:#0000}body[data-dsh-skin-center] .aIQHxa_button:disabled{opacity:.55;cursor:default}body[data-dsh-skin-center] .aIQHxa_error{color:var(--dsw-alias-state-error-primary,#b42318);font-size:12px}body[data-dsh-skin-center] .aIQHxa_backgroundRow{flex-direction:column;gap:6px;padding:8px 0;display:flex}body[data-dsh-skin-center] .aIQHxa_backgroundHead{align-items:center;gap:8px;display:flex}body[data-dsh-skin-center] .aIQHxa_backgroundLabel{color:var(--dsw-alias-label-primary,#172a45);font-size:12.5px;font-weight:600}body[data-dsh-skin-center] .aIQHxa_backgroundValue{font-variant-numeric:tabular-nums;color:var(--dsw-alias-brand-primary,#2b7cd9);flex:none;margin-left:auto;font-size:12px}body[data-dsh-skin-center] .aIQHxa_backgroundRange{background:var(--dsw-alias-bg-layer-3,#e2e8f0);-webkit-appearance:none;appearance:none;cursor:pointer;border-radius:999px;width:100%;height:4px;margin:0}body[data-dsh-skin-center] .aIQHxa_backgroundRange::-webkit-slider-thumb{-webkit-appearance:none;appearance:none;border:2px solid var(--dsw-alias-label-primary-foreground,#fff);background:var(--dsw-alias-brand-primary,#2b7cd9);width:14px;height:14px;box-shadow:0 0 0 1px var(--dsw-alias-border-l4,#0f172a1f);cursor:pointer;border-radius:50%}body[data-dsh-skin-center] .aIQHxa_backgroundRange::-moz-range-thumb{border:2px solid var(--dsw-alias-label-primary-foreground,#fff);background:var(--dsw-alias-brand-primary,#2b7cd9);width:12px;height:12px;box-shadow:0 0 0 1px var(--dsw-alias-border-l4,#0f172a1f);cursor:pointer;border-radius:50%}body[data-dsh-skin-center] .aIQHxa_backgroundRange:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#2b7cd9);outline-offset:2px}body[data-dsh-skin-center] .aIQHxa_backgroundHint{color:var(--dsw-alias-label-secondary,#6b7280);font-size:12px;line-height:1.5}body[data-dsh-skin-center] .aIQHxa_backgroundHintMuted{color:var(--dsw-alias-label-tertiary,#9aa4b5);font-size:12px;line-height:1.5}@media (prefers-reduced-motion:reduce){body[data-dsh-skin-center] .aIQHxa_cardHeader,body[data-dsh-skin-center] .aIQHxa_themeButton,body[data-dsh-skin-center] .aIQHxa_button,body[data-dsh-skin-center] .aIQHxa_chevron,body[data-dsh-skin-center] .aIQHxa_chevronOpen{transition:none}}";
469
469
  const tagId = "@linxin666/dsh-client-ui-skin-center/skin-center.module.css";
470
470
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
471
471
  const tag = document.createElement("style");
@@ -475,42 +475,42 @@ window.__ModuleLoader__.load({
475
475
  document.head.appendChild(tag);
476
476
  }
477
477
  var skin_center_module_css_default = {
478
- "actions": "K3rRTW_actions",
479
- "backgroundHead": "K3rRTW_backgroundHead",
480
- "backgroundHint": "K3rRTW_backgroundHint",
481
- "backgroundHintMuted": "K3rRTW_backgroundHintMuted",
482
- "backgroundLabel": "K3rRTW_backgroundLabel",
483
- "backgroundRange": "K3rRTW_backgroundRange",
484
- "backgroundRow": "K3rRTW_backgroundRow",
485
- "backgroundValue": "K3rRTW_backgroundValue",
486
- "badge": "K3rRTW_badge",
487
- "badgeActive": "K3rRTW_badgeActive",
488
- "badgeTrying": "K3rRTW_badgeTrying",
489
- "button": "K3rRTW_button",
490
- "buttonGhost": "K3rRTW_buttonGhost",
491
- "buttonPrimary": "K3rRTW_buttonPrimary",
492
- "card": "K3rRTW_card",
493
- "cardBody": "K3rRTW_cardBody",
494
- "cardDescription": "K3rRTW_cardDescription",
495
- "cardHead": "K3rRTW_cardHead",
496
- "cardHeader": "K3rRTW_cardHeader",
497
- "cardName": "K3rRTW_cardName",
498
- "cardTagline": "K3rRTW_cardTagline",
499
- "chevron": "K3rRTW_chevron",
500
- "chevronOpen": "K3rRTW_chevronOpen",
501
- "error": "K3rRTW_error",
502
- "head": "K3rRTW_head",
503
- "headText": "K3rRTW_headText",
504
- "intro": "K3rRTW_intro",
505
- "list": "K3rRTW_list",
506
- "pluginCard": "K3rRTW_pluginCard",
507
- "pluginName": "K3rRTW_pluginName",
508
- "swatch": "K3rRTW_swatch",
509
- "themeButton": "K3rRTW_themeButton",
510
- "themeButtonActive": "K3rRTW_themeButtonActive",
511
- "themeLabel": "K3rRTW_themeLabel",
512
- "themeRow": "K3rRTW_themeRow",
513
- "titleBadge": "K3rRTW_titleBadge"
478
+ "actions": "aIQHxa_actions",
479
+ "backgroundHead": "aIQHxa_backgroundHead",
480
+ "backgroundHint": "aIQHxa_backgroundHint",
481
+ "backgroundHintMuted": "aIQHxa_backgroundHintMuted",
482
+ "backgroundLabel": "aIQHxa_backgroundLabel",
483
+ "backgroundRange": "aIQHxa_backgroundRange",
484
+ "backgroundRow": "aIQHxa_backgroundRow",
485
+ "backgroundValue": "aIQHxa_backgroundValue",
486
+ "badge": "aIQHxa_badge",
487
+ "badgeActive": "aIQHxa_badgeActive",
488
+ "badgeTrying": "aIQHxa_badgeTrying",
489
+ "button": "aIQHxa_button",
490
+ "buttonGhost": "aIQHxa_buttonGhost",
491
+ "buttonPrimary": "aIQHxa_buttonPrimary",
492
+ "card": "aIQHxa_card",
493
+ "cardBody": "aIQHxa_cardBody",
494
+ "cardDescription": "aIQHxa_cardDescription",
495
+ "cardHead": "aIQHxa_cardHead",
496
+ "cardHeader": "aIQHxa_cardHeader",
497
+ "cardName": "aIQHxa_cardName",
498
+ "cardTagline": "aIQHxa_cardTagline",
499
+ "chevron": "aIQHxa_chevron",
500
+ "chevronOpen": "aIQHxa_chevronOpen",
501
+ "error": "aIQHxa_error",
502
+ "head": "aIQHxa_head",
503
+ "headText": "aIQHxa_headText",
504
+ "intro": "aIQHxa_intro",
505
+ "list": "aIQHxa_list",
506
+ "pluginCard": "aIQHxa_pluginCard",
507
+ "pluginName": "aIQHxa_pluginName",
508
+ "swatch": "aIQHxa_swatch",
509
+ "themeButton": "aIQHxa_themeButton",
510
+ "themeButtonActive": "aIQHxa_themeButtonActive",
511
+ "themeLabel": "aIQHxa_themeLabel",
512
+ "themeRow": "aIQHxa_themeRow",
513
+ "titleBadge": "aIQHxa_titleBadge"
514
514
  };
515
515
  //#endregion
516
516
  //#region src/client/SkinCenter.tsx
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
2
2
  import z from "schemastery";
3
- import { lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, renameSync, statSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
3
+ import { lstatSync, mkdirSync, readFileSync, readdirSync, readlinkSync, renameSync, rmdirSync, statSync, symlinkSync, unlinkSync, writeFileSync } from "node:fs";
4
4
  import { dirname, join } from "node:path";
5
5
  import { homedir } from "node:os";
6
6
  import { fileURLToPath } from "node:url";
@@ -23,22 +23,55 @@ import { fileURLToPath } from "node:url";
23
23
  * @module @linxin666/dsh-client-ui-skin-center/skin-switch
24
24
  */
25
25
  /**
26
+ * Walk up from a file location to the nearest @linxin666/ scoped dir
27
+ * whose entries actually hold skin packages (dsh-skins carrier or
28
+ * dsh-client-ui-skin-* packages). pnpm's virtual store realpaths packages
29
+ * into node_modules/.pnpm/<pkg>@<ver>/node_modules/<name>, so a plain
30
+ * '../../' from the skin-center package can never see its siblings there —
31
+ * this anchor finds the scoped dir that owns them.
32
+ * @param fromDir - the realpathed package dir to walk up from.
33
+ * @returns the scoped skin dir (the skins root), or null when none is found.
34
+ */
35
+ function findScopedAnchor(fromDir) {
36
+ let current = fromDir;
37
+ for (;;) {
38
+ const scoped = join(current, "@linxin666");
39
+ try {
40
+ for (const entry of readdirSync(scoped)) {
41
+ if (entry === "dsh-skins") return scoped;
42
+ if (entry.startsWith("dsh-client-ui-skin-") && entry !== "dsh-client-ui-skin-center") return scoped;
43
+ }
44
+ } catch {}
45
+ const parent = dirname(current);
46
+ if (parent === current) return null;
47
+ current = parent;
48
+ }
49
+ }
50
+ /**
26
51
  * Resolve the directory that holds the skin packages (each a dir carrying a
27
- * skin.json). Both install layouts resolve with new URL('../../', ...):
28
- * - monorepo: packages/skins/skin-center/lib/../../ -> packages/skins/
29
- * - npm install: node_modules/@linxin666/dsh-client-ui-skin-center/lib/../..
30
- * -> node_modules/@linxin666/ (the scoped dir holding dsh-client-ui-skin-*)
31
- * The legacy '../../../skins/' spelling (which pointed at node_modules/skins/
32
- * under npm — the ENOENT of zhu1090093659/dsh-web-ui#21/#33/#34) is kept as a
33
- * fallback candidate, and DSH_SKINS_DIR overrides everything (tests use it).
52
+ * skin.json). Candidates, in order:
53
+ * - monorepo / flat npm layout: new URL('../../', import.meta.url)
54
+ * (packages/skins/ or node_modules/@linxin666/);
55
+ * - pnpm virtual-store layout: the nearest @linxin666/ scoped dir found by
56
+ * walking up from this package's realpathed location;
57
+ * - the legacy '../../../skins/' spelling (which pointed at
58
+ * node_modules/skins/ under npm — the ENOENT of
59
+ * zhu1090093659/dsh-web-ui#21/#33/#34), kept as a fallback.
60
+ * DSH_SKINS_DIR overrides everything (tests use it).
61
+ * @param fromUrl - the module URL to resolve from (defaults to this module's
62
+ * own import.meta.url); injectable so tests can place the module inside a
63
+ * simulated install layout and exercise the real candidate chain.
34
64
  */
35
- function resolveSkinsDir() {
65
+ function resolveSkinsDir(fromUrl = import.meta.url) {
36
66
  const fromEnv = process.env.DSH_SKINS_DIR;
37
67
  if (fromEnv !== void 0 && fromEnv !== "") return fromEnv;
38
- const candidates = [fileURLToPath(new URL("../../", import.meta.url)), fileURLToPath(new URL("../../../skins/", import.meta.url))];
39
- for (const candidate of candidates) try {
40
- for (const dir of readdirSync(candidate)) if (statSync(join(candidate, dir, "skin.json"), { throwIfNoEntry: false })) return candidate;
41
- } catch {}
68
+ const here = fileURLToPath(fromUrl);
69
+ const candidates = [
70
+ fileURLToPath(new URL("../../", fromUrl)),
71
+ findScopedAnchor(dirname(here)),
72
+ fileURLToPath(new URL("../../../skins/", fromUrl))
73
+ ].filter((candidate) => candidate !== null);
74
+ for (const candidate of candidates) if (listSkinDirCandidates(candidate).length > 0) return candidate;
42
75
  return candidates[0];
43
76
  }
44
77
  /** The skin-package root for this install (see resolveSkinsDir). */
@@ -52,12 +85,11 @@ const DEFAULT_PROFILE = process.env.DSH_SKIN_PROFILE ?? "web";
52
85
  * Parse the switch-relevant fields of one skin.json. Returns null for
53
86
  * anything that is not a valid skin so it is simply skipped — never walking
54
87
  * outside the skins tree (the id is validated before any path use).
55
- * @param dir - directory name under the skins root.
56
- * @param skinsDir - the skins root (defaults to the resolved install layout).
88
+ * @param absDir - absolute path of the candidate skin directory.
57
89
  */
58
- function readSkinMeta(dir, skinsDir = SKINS_DIR) {
90
+ function readSkinMeta(absDir) {
59
91
  try {
60
- const meta = JSON.parse(readFileSync(join(skinsDir, dir, "skin.json"), "utf8"));
92
+ const meta = JSON.parse(readFileSync(join(absDir, "skin.json"), "utf8"));
61
93
  if (typeof meta !== "object" || meta === null) return null;
62
94
  const record = meta;
63
95
  if (typeof record.id !== "string" || !/^[a-z0-9-]+$/.test(record.id)) return null;
@@ -78,31 +110,66 @@ function readSkinMeta(dir, skinsDir = SKINS_DIR) {
78
110
  }
79
111
  }
80
112
  /**
81
- * Derive the skin registry from each skin dir's skin.json — the single
82
- * source of truth (skin.json already carries package/wiring.id/bundleWired).
83
- * Replaces the CLI's hand-maintained SKINS dictionary, so adding a skin
84
- * needs no code change here. Directories without a readable skin.json
85
- * (the skin-center manager itself, non-skin packages in the npm scoped dir)
86
- * are skipped. The root is injectable so tests can point at either install
87
- * layout.
88
- * @param skinsDir - the skins root (defaults to the resolved install layout).
89
- * @returns skin id -> switch metadata.
113
+ * Enumerate every candidate skin directory under a skins root. Two shapes:
114
+ * - direct subdirectories carrying a skin.json (monorepo packages/skins/<id>,
115
+ * and per-skin npm packages @linxin666/dsh-client-ui-skin-<id>);
116
+ * - the bundled-skins carrier: @linxin666/dsh-skins/skins/<id> (skin assets
117
+ * shipped inside the dsh-skins aggregate so npm needs no per-skin
118
+ * package names). Directories without a skin.json are skipped.
119
+ * @param skinsDir - the skins root.
120
+ * @returns absolute candidate dirs (possibly empty).
90
121
  */
91
- function loadRegistry(skinsDir = SKINS_DIR) {
92
- const out = {};
122
+ function listSkinDirCandidates(skinsDir) {
123
+ const out = [];
93
124
  let entries;
94
125
  try {
95
126
  entries = readdirSync(skinsDir);
96
127
  } catch {
97
128
  return out;
98
129
  }
130
+ const isDir = (p) => statSync(p, { throwIfNoEntry: false })?.isDirectory() === true;
99
131
  for (const dir of entries) {
100
- const meta = readSkinMeta(dir, skinsDir);
132
+ const candidate = join(skinsDir, dir);
133
+ if (!isDir(candidate)) continue;
134
+ if (statSync(join(candidate, "skin.json"), { throwIfNoEntry: false })) out.push(candidate);
135
+ }
136
+ const bundled = join(skinsDir, "dsh-skins", "skins");
137
+ let subdirs;
138
+ try {
139
+ subdirs = readdirSync(bundled);
140
+ } catch {
141
+ return out;
142
+ }
143
+ for (const sub of subdirs) {
144
+ const subDir = join(bundled, sub);
145
+ if (!isDir(subDir)) continue;
146
+ if (statSync(join(subDir, "skin.json"), { throwIfNoEntry: false })) out.push(subDir);
147
+ }
148
+ return out;
149
+ }
150
+ /**
151
+ * Derive the skin registry from each skin dir's skin.json — the single
152
+ * source of truth (skin.json already carries package/wiring.id/bundleWired).
153
+ * Replaces the CLI's hand-maintained SKINS dictionary, so adding a skin
154
+ * needs no code change here. Candidate dirs come from
155
+ * listSkinDirCandidates (direct skin dirs + the dsh-skins bundled carrier).
156
+ * The root is injectable so tests can point at either install layout.
157
+ * @param skinsDir - the skins root (defaults to the resolved install layout).
158
+ * @returns skin id -> switch metadata.
159
+ */
160
+ function loadRegistry(skinsDir = SKINS_DIR) {
161
+ const out = {};
162
+ for (const dir of listSkinDirCandidates(skinsDir)) {
163
+ const meta = readSkinMeta(dir);
101
164
  if (meta === null || meta.wiring === void 0 || meta.package === void 0) continue;
165
+ if (out[meta.id] !== void 0) {
166
+ console.warn("[skin-center] duplicate skin id \"" + meta.id + "\": keeping " + out[meta.id].dir + ", ignoring " + dir);
167
+ continue;
168
+ }
102
169
  out[meta.id] = {
103
170
  pkg: meta.package,
104
171
  id: meta.wiring.id,
105
- dir: join(skinsDir, dir),
172
+ dir,
106
173
  bundleWired: meta.wiring.bundleWired === true
107
174
  };
108
175
  }
@@ -237,9 +304,12 @@ function ensureSymlink(entry, profileModulesDir) {
237
304
  } catch {}
238
305
  if (stat) if (stat.isSymbolicLink()) {
239
306
  if (readlinkSync(target) === entry.dir) return false;
240
- unlinkSync(target);
241
- } else if (stat.isDirectory()) return false;
242
- else throw new Error(`${target} exists and is not a symlink or directory — refusing to touch it`);
307
+ if (process.platform === "win32" && stat.isDirectory()) rmdirSync(target);
308
+ else unlinkSync(target);
309
+ } else if (stat.isDirectory()) {
310
+ if (isSkinPackageDir(target, entry)) return false;
311
+ throw new Error(target + " exists as a directory but does not look like " + entry.pkg + " — refusing to treat it as installed");
312
+ } else throw new Error(target + " exists and is not a symlink or directory — refusing to touch it");
243
313
  mkdirSync(dirname(target), { recursive: true });
244
314
  try {
245
315
  symlinkSync(entry.dir, target);
@@ -250,6 +320,24 @@ function ensureSymlink(entry, profileModulesDir) {
250
320
  }
251
321
  return true;
252
322
  }
323
+ /**
324
+ * Whether an existing directory at a profile link path really is the target
325
+ * skin's installed package (skin.json id + package match). Keeps the
326
+ * npm-install-layout pass-through from silently accepting an unrelated
327
+ * directory left over at the link path.
328
+ * @param dir - the directory to inspect.
329
+ * @param entry - the expected skin.
330
+ */
331
+ function isSkinPackageDir(dir, entry) {
332
+ try {
333
+ const meta = JSON.parse(readFileSync(join(dir, "skin.json"), "utf8"));
334
+ if (typeof meta !== "object" || meta === null) return false;
335
+ const record = meta;
336
+ return record.id === entry.id.replace(/^ui-skin-/, "") && record.package === entry.pkg;
337
+ } catch {
338
+ return false;
339
+ }
340
+ }
253
341
  /** Windows/privilege code points where symlinkSync fails. */
254
342
  const SYMLINK_PRIVILEGE_CODES = [
255
343
  "EPERM",
@@ -281,8 +369,9 @@ function symlinkFriendly(caller, fn) {
281
369
  function checkInstalled(entry, profileModulesDir) {
282
370
  let ok = false;
283
371
  try {
284
- const stat = lstatSync(join(profileModulesDir, entry.pkg));
285
- ok = stat.isSymbolicLink() || stat.isDirectory();
372
+ const target = join(profileModulesDir, entry.pkg);
373
+ const stat = lstatSync(target);
374
+ ok = stat.isSymbolicLink() || stat.isDirectory() && isSkinPackageDir(target, entry);
286
375
  } catch {}
287
376
  return ok ? null : `${entry.pkg} 未安装到 profile;先用 dsh-skin install ${entry.id.replace(/^ui-skin-/, "")}(或 dsh plugin --profile ${DEFAULT_PROFILE} add ${entry.dir})安装,否则加载会失败。`;
288
377
  }
@@ -486,22 +575,34 @@ function postRoute(path, run) {
486
575
  * skin.json. The id is validated against this map (never used as a raw
487
576
  * path) so the bundle route cannot be walked off the skins tree. The root
488
577
  * resolves per install layout (monorepo packages/skins/, npm
489
- * node_modules/@linxin666/) — see skin-switch resolveSkinsDir.
578
+ * node_modules/@linxin666/) and candidates include the bundled dsh-skins
579
+ * carrier (npm layout) — see skin-switch resolveSkinsDir /
580
+ * listSkinDirCandidates.
490
581
  * @returns skin id -> directory name.
491
582
  */
583
+ /** Memoized id -> dir map; invalidated when the skins root (or the bundled
584
+ * carrier dir) changes on disk, so a skin added mid-session still appears
585
+ * without restarting. */
586
+ let directoriesCache = null;
492
587
  function skinDirectories() {
588
+ const rootStat = statSync(SKINS_DIR, { throwIfNoEntry: false });
589
+ const carrierStat = statSync(join(SKINS_DIR, "dsh-skins", "skins"), { throwIfNoEntry: false });
590
+ const key = `${rootStat?.mtimeMs ?? -1}|${carrierStat?.mtimeMs ?? -1}`;
591
+ if (directoriesCache !== null && directoriesCache.key === key) return directoriesCache.map;
493
592
  const out = /* @__PURE__ */ new Map();
494
- for (const dir of readdirSync(SKINS_DIR)) {
495
- const metaFile = join(SKINS_DIR, dir, "skin.json");
496
- if (!statSync(metaFile, { throwIfNoEntry: false })) continue;
593
+ for (const dir of listSkinDirCandidates(SKINS_DIR)) {
497
594
  let meta;
498
595
  try {
499
- meta = JSON.parse(readFileSync(metaFile, "utf8"));
596
+ meta = JSON.parse(readFileSync(join(dir, "skin.json"), "utf8"));
500
597
  } catch {
501
598
  continue;
502
599
  }
503
600
  if (typeof meta.id === "string" && /^[a-z0-9-]+$/.test(meta.id)) out.set(meta.id, dir);
504
601
  }
602
+ directoriesCache = {
603
+ key,
604
+ map: out
605
+ };
505
606
  return out;
506
607
  }
507
608
  /**
@@ -545,7 +646,7 @@ function bundleRoute() {
545
646
  });
546
647
  return;
547
648
  }
548
- const bundle = join(SKINS_DIR, dir, "lib", "client.js");
649
+ const bundle = join(dir, "lib", "client.js");
549
650
  if (!statSync(bundle, { throwIfNoEntry: false })) {
550
651
  json(res, 404, {
551
652
  ok: false,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@linxin666/dsh-client-ui-skin-center",
3
3
  "description": "In-GUI skin center for the dsh web GUI: lists the official default plus every installed skin (qq98 / ths / xp / blue-fantasy / dragon-heir / minecraft), tries it on live inside the real GUI (executes the actual client bundle, light/dark preview), exits with a full restore, and applies in one click through the host /api/skin-center API (dsh-skin use, hot-reloaded, no restart)",
4
- "version": "0.1.3",
4
+ "version": "0.1.6",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {