@linxin666/dsh-client-ui-skin-center 0.1.4 → 0.1.7

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 +129 -46
  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, realpathSync, 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,14 +23,14 @@ 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 directory whose @linxin666/
27
- * scoped subdir actually holds skin packages (dsh-skins carrier or
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
28
  * dsh-client-ui-skin-* packages). pnpm's virtual store realpaths packages
29
29
  * into node_modules/.pnpm/<pkg>@<ver>/node_modules/<name>, so a plain
30
30
  * '../../' from the skin-center package can never see its siblings there —
31
- * this anchor finds the node_modules root that owns the scoped dir.
31
+ * this anchor finds the scoped dir that owns them.
32
32
  * @param fromDir - the realpathed package dir to walk up from.
33
- * @returns the anchor dir, or null when no scoped skin dir is found.
33
+ * @returns the scoped skin dir (the skins root), or null when none is found.
34
34
  */
35
35
  function findScopedAnchor(fromDir) {
36
36
  let current = fromDir;
@@ -38,8 +38,8 @@ function findScopedAnchor(fromDir) {
38
38
  const scoped = join(current, "@linxin666");
39
39
  try {
40
40
  for (const entry of readdirSync(scoped)) {
41
- if (entry === "dsh-skins") return current;
42
- if (entry.startsWith("dsh-client-ui-skin-") && entry !== "dsh-client-ui-skin-center") return current;
41
+ if (entry === "dsh-skins") return scoped;
42
+ if (entry.startsWith("dsh-client-ui-skin-") && entry !== "dsh-client-ui-skin-center") return scoped;
43
43
  }
44
44
  } catch {}
45
45
  const parent = dirname(current);
@@ -58,15 +58,18 @@ function findScopedAnchor(fromDir) {
58
58
  * node_modules/skins/ under npm — the ENOENT of
59
59
  * zhu1090093659/dsh-web-ui#21/#33/#34), kept as a fallback.
60
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.
61
64
  */
62
- function resolveSkinsDir() {
65
+ function resolveSkinsDir(fromUrl = import.meta.url) {
63
66
  const fromEnv = process.env.DSH_SKINS_DIR;
64
67
  if (fromEnv !== void 0 && fromEnv !== "") return fromEnv;
65
- const here = fileURLToPath(import.meta.url);
68
+ const here = fileURLToPath(fromUrl);
66
69
  const candidates = [
67
- fileURLToPath(new URL("../../", import.meta.url)),
70
+ fileURLToPath(new URL("../../", fromUrl)),
68
71
  findScopedAnchor(dirname(here)),
69
- fileURLToPath(new URL("../../../skins/", import.meta.url))
72
+ fileURLToPath(new URL("../../../skins/", fromUrl))
70
73
  ].filter((candidate) => candidate !== null);
71
74
  for (const candidate of candidates) if (listSkinDirCandidates(candidate).length > 0) return candidate;
72
75
  return candidates[0];
@@ -124,25 +127,24 @@ function listSkinDirCandidates(skinsDir) {
124
127
  } catch {
125
128
  return out;
126
129
  }
130
+ const isDir = (p) => statSync(p, { throwIfNoEntry: false })?.isDirectory() === true;
127
131
  for (const dir of entries) {
128
132
  const candidate = join(skinsDir, dir);
129
- if (statSync(join(candidate, "skin.json"), { throwIfNoEntry: false })) {
130
- out.push(candidate);
131
- continue;
132
- }
133
- if (dir === "dsh-skins") {
134
- const bundled = join(candidate, "skins");
135
- let subdirs;
136
- try {
137
- subdirs = readdirSync(bundled);
138
- } catch {
139
- continue;
140
- }
141
- for (const sub of subdirs) {
142
- const subDir = join(bundled, sub);
143
- if (statSync(join(subDir, "skin.json"), { throwIfNoEntry: false })) out.push(subDir);
144
- }
145
- }
133
+ if (lstatSync(candidate, { throwIfNoEntry: false })?.isSymbolicLink() === true) continue;
134
+ if (!isDir(candidate)) continue;
135
+ if (statSync(join(candidate, "skin.json"), { throwIfNoEntry: false })) out.push(candidate);
136
+ }
137
+ const bundled = join(skinsDir, "dsh-skins", "skins");
138
+ let subdirs;
139
+ try {
140
+ subdirs = readdirSync(bundled);
141
+ } catch {
142
+ return out;
143
+ }
144
+ for (const sub of subdirs) {
145
+ const subDir = join(bundled, sub);
146
+ if (!isDir(subDir)) continue;
147
+ if (statSync(join(subDir, "skin.json"), { throwIfNoEntry: false })) out.push(subDir);
146
148
  }
147
149
  return out;
148
150
  }
@@ -158,9 +160,25 @@ function listSkinDirCandidates(skinsDir) {
158
160
  */
159
161
  function loadRegistry(skinsDir = SKINS_DIR) {
160
162
  const out = {};
163
+ const seenReal = /* @__PURE__ */ new Set();
161
164
  for (const dir of listSkinDirCandidates(skinsDir)) {
165
+ let real;
166
+ try {
167
+ real = realpathSync(dir);
168
+ } catch {
169
+ real = dir;
170
+ }
171
+ if (seenReal.has(real)) {
172
+ console.warn("[skin-center] duplicate skin dir (realpath) \"" + real + "\": keeping the real directory, ignoring " + dir);
173
+ continue;
174
+ }
175
+ seenReal.add(real);
162
176
  const meta = readSkinMeta(dir);
163
177
  if (meta === null || meta.wiring === void 0 || meta.package === void 0) continue;
178
+ if (out[meta.id] !== void 0) {
179
+ console.warn("[skin-center] duplicate skin id \"" + meta.id + "\": keeping " + out[meta.id].dir + ", ignoring " + dir);
180
+ continue;
181
+ }
164
182
  out[meta.id] = {
165
183
  pkg: meta.package,
166
184
  id: meta.wiring.id,
@@ -291,17 +309,36 @@ function writePatchAtomic(filePath, next) {
291
309
  * @param entry - the skin switch entry.
292
310
  * @param profileModulesDir - the profile's node_modules dir.
293
311
  */
312
+ /** Canonical path a symlink resolves to, tolerant of a degraded link (a
313
+ * self-referential link whose realpath would throw ELOOP); '' when absent. */
314
+ function resolveLinkReal(linkPath) {
315
+ try {
316
+ return realpathSync(linkPath);
317
+ } catch {
318
+ return "";
319
+ }
320
+ }
294
321
  function ensureSymlink(entry, profileModulesDir) {
295
322
  const target = join(profileModulesDir, entry.pkg);
323
+ let entryReal;
324
+ try {
325
+ entryReal = realpathSync(entry.dir);
326
+ } catch {
327
+ entryReal = entry.dir;
328
+ }
329
+ if (entry.dir === target || entryReal === target) return false;
296
330
  let stat = null;
297
331
  try {
298
332
  stat = lstatSync(target);
299
333
  } catch {}
300
334
  if (stat) if (stat.isSymbolicLink()) {
301
- if (readlinkSync(target) === entry.dir) return false;
302
- unlinkSync(target);
303
- } else if (stat.isDirectory()) return false;
304
- else throw new Error(`${target} exists and is not a symlink or directory — refusing to touch it`);
335
+ if (resolveLinkReal(target) === entryReal) return false;
336
+ if (process.platform === "win32" && stat.isDirectory()) rmdirSync(target);
337
+ else unlinkSync(target);
338
+ } else if (stat.isDirectory()) {
339
+ if (isSkinPackageDir(target, entry)) return false;
340
+ throw new Error(target + " exists as a directory but does not look like " + entry.pkg + " — refusing to treat it as installed");
341
+ } else throw new Error(target + " exists and is not a symlink or directory — refusing to touch it");
305
342
  mkdirSync(dirname(target), { recursive: true });
306
343
  try {
307
344
  symlinkSync(entry.dir, target);
@@ -312,6 +349,24 @@ function ensureSymlink(entry, profileModulesDir) {
312
349
  }
313
350
  return true;
314
351
  }
352
+ /**
353
+ * Whether an existing directory at a profile link path really is the target
354
+ * skin's installed package (skin.json id + package match). Keeps the
355
+ * npm-install-layout pass-through from silently accepting an unrelated
356
+ * directory left over at the link path.
357
+ * @param dir - the directory to inspect.
358
+ * @param entry - the expected skin.
359
+ */
360
+ function isSkinPackageDir(dir, entry) {
361
+ try {
362
+ const meta = JSON.parse(readFileSync(join(dir, "skin.json"), "utf8"));
363
+ if (typeof meta !== "object" || meta === null) return false;
364
+ const record = meta;
365
+ return record.id === entry.id.replace(/^ui-skin-/, "") && record.package === entry.pkg;
366
+ } catch {
367
+ return false;
368
+ }
369
+ }
315
370
  /** Windows/privilege code points where symlinkSync fails. */
316
371
  const SYMLINK_PRIVILEGE_CODES = [
317
372
  "EPERM",
@@ -334,19 +389,37 @@ function symlinkFriendly(caller, fn) {
334
389
  }
335
390
  }
336
391
  /**
337
- * Optional soft warning when a skin is not resolvable from the web profile
338
- * (the CLI prints it; surfaced as a soft hint, not a failure — the patch
339
- * stays authoritative).
392
+ * Whether the skin package is actually resolvable as a plugin from the web
393
+ * profile - the same directory contract the boot graph relies on when it
394
+ * loads the `useSkin` insert row. Unlike the old soft warning, this is a
395
+ * hard gate: the skin-center /apply endpoint must not report ok:true for a
396
+ * skin the host cannot load. The npm aggregate layout shipped skin dirs
397
+ * without a package.json + host entry, so /apply wrote the patch, reported
398
+ * success, and the boot then died on MODULE_NOT_FOUND .../package.json.
399
+ *
400
+ * The check is structural and deterministic (pure fs): resolves what node
401
+ * would - the profile-target package dir must carry a package.json whose
402
+ * name is this skin's package, and a host entry (main, else index.js) that
403
+ * actually exists. That is exactly the resolution that failed before.
340
404
  * @param entry - the skin switch entry.
341
405
  * @param profileModulesDir - the profile's node_modules dir.
406
+ * @returns an error message when the skin is not resolvable, else null.
342
407
  */
343
- function checkInstalled(entry, profileModulesDir) {
344
- let ok = false;
408
+ function checkResolvable(entry, profileModulesDir) {
409
+ const target = join(profileModulesDir, entry.pkg);
410
+ if (!statSync(target, { throwIfNoEntry: false })?.isDirectory()) return `${entry.pkg} 未安装到 profile(profile 中无 ${target})。请先用 dsh-skin install ${entry.id.replace(/^ui-skin-/, "")} 安装,否则宿主无法加载。`;
411
+ const pkgPath = join(target, "package.json");
412
+ if (!statSync(pkgPath, { throwIfNoEntry: false })) return `${entry.pkg} 在 profile 中缺少 package.json(${pkgPath})——聚合包皮肤目录未带可解析包元数据。`;
413
+ let parsed;
345
414
  try {
346
- const stat = lstatSync(join(profileModulesDir, entry.pkg));
347
- ok = stat.isSymbolicLink() || stat.isDirectory();
348
- } catch {}
349
- return ok ? null : `${entry.pkg} 未安装到 profile;先用 dsh-skin install ${entry.id.replace(/^ui-skin-/, "")}(或 dsh plugin --profile ${DEFAULT_PROFILE} add ${entry.dir})安装,否则加载会失败。`;
415
+ parsed = JSON.parse(readFileSync(pkgPath, "utf8"));
416
+ } catch {
417
+ parsed = {};
418
+ }
419
+ if (parsed.name !== entry.pkg) return `${entry.pkg} 解析到的 package.json 名为 ${String(parsed.name)},不是本皮肤(${pkgPath})。`;
420
+ const mainPath = join(target, typeof parsed.main === "string" ? parsed.main : "index.js");
421
+ if (!statSync(mainPath, { throwIfNoEntry: false })) return `${entry.pkg} 缺少 host 入口 ${mainPath}(package.json main 未指到可加载文件)。`;
422
+ return null;
350
423
  }
351
424
  /**
352
425
  * Switch the active skin. Equivalent to `dsh-skin use <name>`:
@@ -362,19 +435,17 @@ function useSkin(name, opts = {}) {
362
435
  const registry = opts.registry ?? loadRegistry();
363
436
  if (!official && registry[name] === void 0) throw new Error(`unknown skin "${name}". Known: ${Object.keys(registry).join(", ")} (or "official" for the stock look)`);
364
437
  const paths = resolvePaths(opts.home, opts.profile);
365
- const notices = [];
366
438
  if (!official) {
367
439
  const entry = registry[name];
368
440
  symlinkFriendly(`switching to "${name}"`, () => {
369
441
  ensureSymlink(entry, paths.profileModulesDir);
370
442
  });
371
- const warn = checkInstalled(entry, paths.profileModulesDir);
372
- if (warn !== null) notices.push(warn);
443
+ const problem = checkResolvable(entry, paths.profileModulesDir);
444
+ if (problem !== null) throw new Error(problem);
373
445
  }
374
446
  const next = `${stripLegacySkinRows(stripManaged(readPatch(paths.patchPath))).replace(/\s+$/, "")}\n\n${renderManaged(official ? null : name, registry)}\n`;
375
447
  writePatchAtomic(paths.patchPath, next);
376
- const core = official ? "restored the official stock look — the config watcher applies it within seconds; refresh the page to see it." : `skin switched to "${name}" — the config watcher applies it within seconds; refresh the page (or the manifest re-fetches) to see it.`;
377
- return notices.length ? `${core}\n${notices.join("\n")}` : core;
448
+ return official ? "restored the official stock look — the config watcher applies it within seconds; refresh the page to see it." : `skin switched to "${name}" — the config watcher applies it within seconds; refresh the page (or the manifest re-fetches) to see it.`;
378
449
  }
379
450
  /**
380
451
  * Read the active skin, mirroring `dsh-skin current` (prints the name or
@@ -553,7 +624,15 @@ function postRoute(path, run) {
553
624
  * listSkinDirCandidates.
554
625
  * @returns skin id -> directory name.
555
626
  */
627
+ /** Memoized id -> dir map; invalidated when the skins root (or the bundled
628
+ * carrier dir) changes on disk, so a skin added mid-session still appears
629
+ * without restarting. */
630
+ let directoriesCache = null;
556
631
  function skinDirectories() {
632
+ const rootStat = statSync(SKINS_DIR, { throwIfNoEntry: false });
633
+ const carrierStat = statSync(join(SKINS_DIR, "dsh-skins", "skins"), { throwIfNoEntry: false });
634
+ const key = `${rootStat?.mtimeMs ?? -1}|${carrierStat?.mtimeMs ?? -1}`;
635
+ if (directoriesCache !== null && directoriesCache.key === key) return directoriesCache.map;
557
636
  const out = /* @__PURE__ */ new Map();
558
637
  for (const dir of listSkinDirCandidates(SKINS_DIR)) {
559
638
  let meta;
@@ -564,6 +643,10 @@ function skinDirectories() {
564
643
  }
565
644
  if (typeof meta.id === "string" && /^[a-z0-9-]+$/.test(meta.id)) out.set(meta.id, dir);
566
645
  }
646
+ directoriesCache = {
647
+ key,
648
+ map: out
649
+ };
567
650
  return out;
568
651
  }
569
652
  /**
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.4",
4
+ "version": "0.1.7",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {