@linxin666/dsh-client-ui-skin-center 0.4.2 → 0.4.4
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.i18n.yaml +2 -2
- package/README.md +18 -2
- package/README.zh.md +18 -2
- package/contracts/semantic-attrs-v1.md +29 -12
- package/lib/client.js +1335 -632
- package/lib/client.js.map +1 -1
- package/lib/index.js +151 -431
- package/package.json +1 -1
- package/skins/blue-fantasy/patches.css +42 -1
package/lib/client.js
CHANGED
|
@@ -7,7 +7,7 @@ window.__ModuleLoader__.load({
|
|
|
7
7
|
let react = require("react");
|
|
8
8
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
9
9
|
//#region \0dsh-css:src/client/skin-center.module.css.mjs
|
|
10
|
-
const css = "body[data-dsh-skin-center] .TJMolG_sectionList{margin:0;padding:0;list-style:none}body[data-dsh-skin-center] .TJMolG_pluginCard{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}body[data-dsh-skin-center] .TJMolG_pluginCard:hover{border-color:var(--dsw-alias-label-dimmed)}body[data-dsh-skin-center] .TJMolG_cardHeaderStatic{align-items:center;gap:12px;width:100%;padding:14px 16px;display:flex}body[data-dsh-skin-center] .TJMolG_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}body[data-dsh-skin-center] .TJMolG_pluginName{color:var(--dsw-alias-label-primary);align-items:baseline;gap:8px;font-size:15px;font-weight:600;line-height:1.4;display:flex}body[data-dsh-skin-center] .TJMolG_cardDescription{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}body[data-dsh-skin-center] .TJMolG_cardBody{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:12px;margin:0 16px;padding:12px 0 8px;display:flex}body[data-dsh-skin-center] .TJMolG_head{flex-direction:column;gap:6px;display:flex}body[data-dsh-skin-center] .TJMolG_titleBadge{color:var(--dsw-alias-label-secondary,#6b7280);font-size:11px;font-weight:500}body[data-dsh-skin-center] .TJMolG_intro{color:var(--dsw-alias-label-secondary);font-size:13px;line-height:1.5}body[data-dsh-skin-center] .TJMolG_themeRow{align-items:center;gap:8px;margin-top:2px;display:flex}body[data-dsh-skin-center] .TJMolG_themeLabel{color:var(--dsw-alias-label-secondary,#6b7280);margin-right:2px;font-size:12px}body[data-dsh-skin-center] .TJMolG_themeButton{font:inherit;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:6px;padding:4px 12px;font-size:12px;font-weight:600;line-height:1.5;transition:background .12s,border-color .12s,color .12s}body[data-dsh-skin-center] .TJMolG_themeButton:hover{border-color:var(--dsw-alias-label-dimmed)}body[data-dsh-skin-center] .TJMolG_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] .TJMolG_themeButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#2b7cd9);outline-offset:2px}body[data-dsh-skin-center] .TJMolG_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] .TJMolG_list{flex-direction:column;gap:10px;display:flex}body[data-dsh-skin-center] .TJMolG_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:8px;flex-direction:column;gap:6px;padding:12px;transition:border-color .16s;display:flex}body[data-dsh-skin-center] .TJMolG_card:hover{border-color:var(--dsw-alias-label-dimmed)}body[data-dsh-skin-center] .TJMolG_cardHead{align-items:center;gap:10px;min-width:0;display:flex}body[data-dsh-skin-center] .TJMolG_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] .TJMolG_cardName{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:13px;font-weight:600;overflow:hidden}body[data-dsh-skin-center] .TJMolG_cardTagline{color:var(--dsw-alias-label-secondary);margin:0;font-size:13px;line-height:1.45}body[data-dsh-skin-center] .TJMolG_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] .TJMolG_badgeActive{color:var(--dsw-alias-state-success-primary,#0f6b3a);background:var(--dsw-alias-state-success-tertiary,#dcf3e5)}body[data-dsh-skin-center] .TJMolG_badgeTrying{color:var(--dsw-alias-brand-primary,#1e63b8);background:var(--dsw-alias-button-primary-dimmed,#e2edfc)}body[data-dsh-skin-center] .TJMolG_actions{flex-wrap:wrap;align-items:center;gap:8px;display:flex}body[data-dsh-skin-center] .TJMolG_button{font:inherit;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:6px;padding:4px 12px;font-size:12px;font-weight:600;line-height:1.5;transition:background .12s,border-color .12s,color .12s}body[data-dsh-skin-center] .TJMolG_button:hover:not(:disabled){border-color:var(--dsw-alias-label-dimmed)}body[data-dsh-skin-center] .TJMolG_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] .TJMolG_button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#2b7cd9);outline-offset:2px}body[data-dsh-skin-center] .TJMolG_buttonPrimary{border-color:var(--dsw-alias-button-info-fill);background:var(--dsw-alias-button-info-fill);color:var(--dsw-alias-bg-layer-3)}body[data-dsh-skin-center] .TJMolG_buttonPrimary:hover:not(:disabled),body[data-dsh-skin-center] .TJMolG_buttonPrimary:active:not(:disabled){opacity:.88}body[data-dsh-skin-center] .TJMolG_buttonDanger{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-state-danger-primary,#d93025);background:0 0}body[data-dsh-skin-center] .TJMolG_buttonDanger:hover:not(:disabled){border-color:var(--dsw-alias-state-danger-primary,#d93025);background:var(--dsw-alias-state-danger-tertiary,#d9302514)}body[data-dsh-skin-center] .TJMolG_buttonDangerConfirm{border-color:var(--dsw-alias-state-danger-primary,#d93025);background:var(--dsw-alias-state-danger-primary,#d93025);color:#fff}body[data-dsh-skin-center] .TJMolG_toolbar{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px;margin:4px 0 2px;display:flex}body[data-dsh-skin-center] .TJMolG_verifySummary{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;padding:6px 12px;font-size:12px;line-height:1.4}body[data-dsh-skin-center] .TJMolG_verifySummarySuccess{color:var(--dsw-alias-state-success-primary,#0f6b3a);border-color:var(--dsw-alias-state-success-tertiary,#dcf3e5)}body[data-dsh-skin-center] .TJMolG_verifySummaryWarning{color:var(--dsw-alias-state-warning-primary,#b06000);border-color:var(--dsw-alias-state-warning-tertiary,#feebd3)}body[data-dsh-skin-center] .TJMolG_badgeSuccess{color:var(--dsw-alias-state-success-primary,#0f6b3a);background:var(--dsw-alias-state-success-tertiary,#dcf3e5)}body[data-dsh-skin-center] .TJMolG_badgeWarning{color:var(--dsw-alias-state-warning-primary,#b06000);background:var(--dsw-alias-state-warning-tertiary,#feebd3)}body[data-dsh-skin-center] .TJMolG_badgeDanger{color:var(--dsw-alias-state-danger-primary,#d93025);background:var(--dsw-alias-state-danger-tertiary,#d930251f)}body[data-dsh-skin-center] .TJMolG_integrityNote{color:var(--dsw-alias-state-warning-primary,#b06000);font-size:11px;line-height:1.4}body[data-dsh-skin-center] .TJMolG_buttonGhost{opacity:.5;background:0 0;border-color:#0000}body[data-dsh-skin-center] .TJMolG_button:disabled{opacity:.5;cursor:default}body[data-dsh-skin-center] .TJMolG_error{color:var(--dsw-alias-state-error-primary,#b42318);font-size:12px}body[data-dsh-skin-center] .TJMolG_enableRow{flex-wrap:wrap;align-items:center;gap:8px;padding:8px 0;display:flex}body[data-dsh-skin-center] .TJMolG_enableLabel{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500}body[data-dsh-skin-center] .TJMolG_enableHint{min-width:100%;color:var(--dsw-alias-label-secondary,#6b7280);flex:1;margin:0;font-size:12px;line-height:1.5}body[data-dsh-skin-center] .TJMolG_switch{border:1px solid var(--dsw-alias-border-l3,#cbd5e1);background:var(--dsw-alias-bg-layer-3,#e2e8f0);cursor:pointer;border-radius:999px;flex:none;align-items:center;width:40px;height:22px;padding:2px;transition:background .12s,border-color .12s;display:inline-flex;position:relative}body[data-dsh-skin-center] .TJMolG_switchOn{border-color:var(--dsw-alias-brand-primary,#2b7cd9);background:var(--dsw-alias-brand-primary,#2b7cd9)}body[data-dsh-skin-center] .TJMolG_switch:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#2b7cd9);outline-offset:2px}body[data-dsh-skin-center] .TJMolG_switchThumb{background:var(--dsw-alias-label-primary-foreground,#fff);width:18px;height:18px;box-shadow:0 0 0 1px var(--dsw-alias-border-l4,#0f172a1f);border-radius:50%;transition:transform .12s;display:block;transform:translate(0)}body[data-dsh-skin-center] .TJMolG_switchOn .TJMolG_switchThumb{transform:translate(18px)}body[data-dsh-skin-center] .TJMolG_offNote{color:var(--dsw-alias-label-secondary,#6b7280);margin:0;font-size:12.5px;line-height:1.5}body[data-dsh-skin-center] .TJMolG_backgroundRow{flex-direction:column;gap:6px;padding:8px 0;display:flex}body[data-dsh-skin-center] .TJMolG_backgroundHead{align-items:center;gap:8px;display:flex}body[data-dsh-skin-center] .TJMolG_backgroundLabel{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500}body[data-dsh-skin-center] .TJMolG_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] .TJMolG_backgroundRange{background:var(--dsw-alias-label-tertiary,#9aa4b5);background:color-mix(in srgb, var(--dsw-alias-label-tertiary,#9aa4b5) 45%, transparent);width:100%;height:4px;box-shadow:0 0 0 1px var(--dsw-alias-border-l3,#cbd5e1);-webkit-appearance:none;appearance:none;cursor:pointer;border-radius:999px;margin:0}body[data-dsh-skin-center] .TJMolG_backgroundRange::-webkit-slider-runnable-track{background:var(--dsw-alias-bg-layer-3,#e2e8f0);border-radius:999px;height:4px}body[data-dsh-skin-center] .TJMolG_backgroundRange::-moz-range-track{background:var(--dsw-alias-bg-layer-3,#e2e8f0);border-radius:999px;height:4px}body[data-dsh-skin-center] .TJMolG_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%;margin-top:-5px}body[data-dsh-skin-center] .TJMolG_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] .TJMolG_backgroundRange:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#2b7cd9);outline-offset:2px}body[data-dsh-skin-center] .TJMolG_backgroundHint{color:var(--dsw-alias-label-secondary,#6b7280);font-size:12px;line-height:1.5}body[data-dsh-skin-center] .TJMolG_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] .TJMolG_pluginCard,body[data-dsh-skin-center] .TJMolG_card,body[data-dsh-skin-center] .TJMolG_themeButton,body[data-dsh-skin-center] .TJMolG_button,body[data-dsh-skin-center] .TJMolG_switch,body[data-dsh-skin-center] .TJMolG_switchThumb{transition:none}}body[data-dsh-skin-center] .TJMolG_wallpaperSection{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:10px;padding-top:10px;display:flex}body[data-dsh-skin-center] .TJMolG_wallpaperStatus{color:var(--dsw-alias-label-secondary,#6b7280);align-items:center;gap:8px;font-size:12px;display:flex}body[data-dsh-skin-center] .TJMolG_wallpaperStatusError{color:var(--dsw-alias-state-danger,#c53030)}body[data-dsh-skin-center] .TJMolG_wallpaperControls{flex-direction:column;gap:10px;display:flex}body[data-dsh-skin-center] .TJMolG_wallpaperGrid{grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:10px;display:grid}body[data-dsh-skin-center] .TJMolG_wallpaperCard{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:8px;flex-direction:column;gap:6px;padding:8px;transition:border-color .16s;display:flex}body[data-dsh-skin-center] .TJMolG_wallpaperCard:hover{border-color:var(--dsw-alias-label-dimmed)}body[data-dsh-skin-center] .TJMolG_wallpaperThumbWrap{aspect-ratio:16/9;background:var(--dsw-alias-bg-layer-1,#f1f5f9);border-radius:6px;position:relative;overflow:hidden}body[data-dsh-skin-center] .TJMolG_wallpaperThumb{object-fit:cover;width:100%;height:100%;display:block}body[data-dsh-skin-center] .TJMolG_wallpaperThumbEmpty{width:100%;height:100%}body[data-dsh-skin-center] .TJMolG_wallpaperType{color:var(--dsw-alias-label-primary,#172a45);background:var(--dsw-alias-bg-layer-2,#ffffffd9);border-radius:4px;padding:3px 6px;font-size:10.5px;line-height:1;position:absolute;top:6px;left:6px}body[data-dsh-skin-center] .TJMolG_wallpaperThumbWrap .TJMolG_badge{position:absolute;top:6px;right:6px}body[data-dsh-skin-center] .TJMolG_wallpaperName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:1.35;overflow:hidden}body[data-dsh-skin-center] .TJMolG_wallpaperActions{flex-wrap:wrap;gap:6px;display:flex}body[data-dsh-skin-center] .TJMolG_customThemeCard{overflow:hidden}body[data-dsh-skin-center] .TJMolG_customThemeEditor{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:9px;flex-direction:column;gap:14px;margin:2px -2px -2px;padding:14px;display:flex}body[data-dsh-skin-center] .TJMolG_customThemeScheme{flex-wrap:wrap;align-items:center;gap:8px;display:flex}body[data-dsh-skin-center] .TJMolG_customThemeFields{grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;display:grid}body[data-dsh-skin-center] .TJMolG_customThemeField,body[data-dsh-skin-center] .TJMolG_customThemeContrast{flex-direction:column;gap:7px;min-width:0;display:flex}body[data-dsh-skin-center] .TJMolG_customThemeFieldLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500}body[data-dsh-skin-center] .TJMolG_customThemeInputRow{align-items:center;gap:7px;min-width:0;display:flex}body[data-dsh-skin-center] .TJMolG_customThemeColor{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);cursor:pointer;border-radius:7px;flex:none;width:34px;height:30px;padding:2px}body[data-dsh-skin-center] .TJMolG_customThemeHex{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-specific-input-major);width:100%;min-width:0;height:30px;color:var(--dsw-alias-label-primary);font:inherit;border-radius:7px;outline:none;padding:0 9px;font-size:12px}body[data-dsh-skin-center] .TJMolG_customThemeHex:focus,body[data-dsh-skin-center] .TJMolG_customThemeColor:focus-visible{border-color:var(--dsw-alias-brand-primary);box-shadow:0 0 0 2px var(--dsw-alias-button-primary-dimmed)}body[data-dsh-skin-center] .TJMolG_customThemeFooter{justify-content:space-between;align-items:center;gap:12px;padding-top:2px;display:flex}@media (width<=680px){body[data-dsh-skin-center] .TJMolG_customThemeFields{grid-template-columns:1fr}body[data-dsh-skin-center] .TJMolG_customThemeFooter{flex-direction:column;align-items:flex-start}}body[data-dsh-skin-center] .TJMolG_wallpaperToolbar{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:8px;margin:8px 0;display:flex}body[data-dsh-skin-center] .TJMolG_ratingFilterGroup{align-items:center;gap:4px;display:flex}body[data-dsh-skin-center] .TJMolG_ratingFilterButton{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);cursor:pointer;border-radius:6px;padding:4px 10px;font-size:12px;transition:all .15s}body[data-dsh-skin-center] .TJMolG_ratingFilterButton:hover{background:var(--dsw-alias-interactive-bg-hover,#ffffff14);color:var(--dsw-alias-label-primary)}body[data-dsh-skin-center] .TJMolG_ratingFilterActive{background:var(--dsw-alias-brand-primary,#2563eb);color:#fff;border-color:#0000}body[data-dsh-skin-center] .TJMolG_ratingFilterActive:hover{background:var(--dsw-alias-brand-primary,#2563eb);color:#fff}body[data-dsh-skin-center] .TJMolG_wallpaperRating{text-transform:uppercase;border-radius:4px;padding:3px 6px;font-size:10px;font-weight:600;line-height:1;position:absolute;bottom:6px;right:6px}body[data-dsh-skin-center] .TJMolG_ratingG{color:var(--dsw-alias-label-secondary,#94a3b8);background:var(--dsw-alias-bg-layer-2,#0009)}body[data-dsh-skin-center] .TJMolG_ratingPg13{color:#fbbf24;background:#000000b3;border:1px solid #fbbf2466}body[data-dsh-skin-center] .TJMolG_ratingR18{color:#fff;background:#dc2626;font-weight:700;box-shadow:0 1px 4px #dc262666}body[data-dsh-skin-center] .TJMolG_wallpaperPagination{flex-wrap:wrap;justify-content:center;align-items:center;gap:6px;margin:16px 0 8px;display:flex}body[data-dsh-skin-center] .TJMolG_pageButton{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);cursor:pointer;text-align:center;border-radius:6px;min-width:30px;padding:4px 10px;font-size:12px;transition:all .15s}body[data-dsh-skin-center] .TJMolG_pageButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#ffffff14);color:var(--dsw-alias-label-primary)}body[data-dsh-skin-center] .TJMolG_pageButton:disabled{opacity:.4;cursor:not-allowed}body[data-dsh-skin-center] .TJMolG_pageButtonActive{background:var(--dsw-alias-brand-primary,#2563eb);color:#fff;border-color:#0000}body[data-dsh-skin-center] .TJMolG_pageEllipsis{color:var(--dsw-alias-label-dimmed,#888);user-select:none;padding:0 4px;font-size:12px}body[data-dsh-skin-center] .TJMolG_pageJumpForm{align-items:center;gap:4px;margin-left:8px;display:inline-flex}body[data-dsh-skin-center] .TJMolG_pageJumpInput{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-specific-input-major,#0003);width:44px;height:26px;color:var(--dsw-alias-label-primary);text-align:center;border-radius:6px;outline:none;padding:0 4px;font-size:12px}body[data-dsh-skin-center] .TJMolG_pageJumpInput:focus{border-color:var(--dsw-alias-brand-primary)}body[data-dsh-skin-center] .TJMolG_pageTotalInfo{color:var(--dsw-alias-label-dimmed,#888);margin-left:6px;font-size:12px}";
|
|
10
|
+
const css = "body[data-dsh-skin-center] .TJMolG_sectionList{margin:0;padding:0;list-style:none}body[data-dsh-skin-center] .TJMolG_pluginCard{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}body[data-dsh-skin-center] .TJMolG_pluginCard:hover{border-color:var(--dsw-alias-label-dimmed)}body[data-dsh-skin-center] .TJMolG_cardHeaderStatic{align-items:center;gap:12px;width:100%;padding:14px 16px;display:flex}body[data-dsh-skin-center] .TJMolG_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}body[data-dsh-skin-center] .TJMolG_pluginName{color:var(--dsw-alias-label-primary);align-items:baseline;gap:8px;font-size:15px;font-weight:600;line-height:1.4;display:flex}body[data-dsh-skin-center] .TJMolG_cardDescription{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}body[data-dsh-skin-center] .TJMolG_cardBody{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:12px;margin:0 16px;padding:12px 0 8px;display:flex}body[data-dsh-skin-center] .TJMolG_head{flex-direction:column;gap:6px;display:flex}body[data-dsh-skin-center] .TJMolG_titleBadge{color:var(--dsw-alias-label-secondary,#6b7280);font-size:11px;font-weight:500}body[data-dsh-skin-center] .TJMolG_intro{color:var(--dsw-alias-label-secondary);font-size:13px;line-height:1.5}body[data-dsh-skin-center] .TJMolG_themeRow{align-items:center;gap:8px;margin-top:2px;display:flex}body[data-dsh-skin-center] .TJMolG_themeLabel{color:var(--dsw-alias-label-secondary,#6b7280);margin-right:2px;font-size:12px}body[data-dsh-skin-center] .TJMolG_themeButton{font:inherit;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:6px;padding:4px 12px;font-size:12px;font-weight:600;line-height:1.5;transition:background .12s,border-color .12s,color .12s}body[data-dsh-skin-center] .TJMolG_themeButton:hover{border-color:var(--dsw-alias-label-dimmed)}body[data-dsh-skin-center] .TJMolG_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] .TJMolG_themeButton:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#2b7cd9);outline-offset:2px}body[data-dsh-skin-center] .TJMolG_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] .TJMolG_list{flex-direction:column;gap:10px;display:flex}body[data-dsh-skin-center] .TJMolG_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:8px;flex-direction:column;gap:6px;padding:12px;transition:border-color .16s;display:flex}body[data-dsh-skin-center] .TJMolG_card:hover{border-color:var(--dsw-alias-label-dimmed)}body[data-dsh-skin-center] .TJMolG_cardHead{align-items:center;gap:10px;min-width:0;display:flex}body[data-dsh-skin-center] .TJMolG_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] .TJMolG_cardName{text-overflow:ellipsis;white-space:nowrap;min-width:0;font-size:13px;font-weight:600;overflow:hidden}body[data-dsh-skin-center] .TJMolG_cardTagline{color:var(--dsw-alias-label-secondary);margin:0;font-size:13px;line-height:1.45}body[data-dsh-skin-center] .TJMolG_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] .TJMolG_badgeActive{color:var(--dsw-alias-state-success-primary,#0f6b3a);background:var(--dsw-alias-state-success-tertiary,#dcf3e5)}body[data-dsh-skin-center] .TJMolG_badgeTrying{color:var(--dsw-alias-brand-primary,#1e63b8);background:var(--dsw-alias-button-primary-dimmed,#e2edfc)}body[data-dsh-skin-center] .TJMolG_actions{flex-wrap:wrap;align-items:center;gap:8px;display:flex}body[data-dsh-skin-center] .TJMolG_button{font:inherit;border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border-radius:6px;padding:4px 12px;font-size:12px;font-weight:600;line-height:1.5;transition:background .12s,border-color .12s,color .12s}body[data-dsh-skin-center] .TJMolG_button:hover:not(:disabled){border-color:var(--dsw-alias-label-dimmed)}body[data-dsh-skin-center] .TJMolG_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] .TJMolG_button:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#2b7cd9);outline-offset:2px}body[data-dsh-skin-center] .TJMolG_buttonPrimary{border-color:var(--dsw-alias-button-info-fill);background:var(--dsw-alias-button-info-fill);color:var(--dsw-alias-bg-layer-3)}body[data-dsh-skin-center] .TJMolG_buttonPrimary:hover:not(:disabled),body[data-dsh-skin-center] .TJMolG_buttonPrimary:active:not(:disabled){opacity:.88}body[data-dsh-skin-center] .TJMolG_buttonDanger{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-state-danger-primary,#d93025);background:0 0}body[data-dsh-skin-center] .TJMolG_buttonDanger:hover:not(:disabled){border-color:var(--dsw-alias-state-danger-primary,#d93025);background:var(--dsw-alias-state-danger-tertiary,#d9302514)}body[data-dsh-skin-center] .TJMolG_buttonDangerConfirm{border-color:var(--dsw-alias-state-danger-primary,#d93025);background:var(--dsw-alias-state-danger-primary,#d93025);color:#fff}body[data-dsh-skin-center] .TJMolG_toolbar{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:10px;margin:4px 0 2px;display:flex}body[data-dsh-skin-center] .TJMolG_verifySummary{background:var(--dsw-alias-bg-layer-2);border:1px solid var(--dsw-alias-border-l2);border-radius:6px;padding:6px 12px;font-size:12px;line-height:1.4}body[data-dsh-skin-center] .TJMolG_verifySummarySuccess{color:var(--dsw-alias-state-success-primary,#0f6b3a);border-color:var(--dsw-alias-state-success-tertiary,#dcf3e5)}body[data-dsh-skin-center] .TJMolG_verifySummaryWarning{color:var(--dsw-alias-state-warning-primary,#b06000);border-color:var(--dsw-alias-state-warning-tertiary,#feebd3)}body[data-dsh-skin-center] .TJMolG_badgeSuccess{color:var(--dsw-alias-state-success-primary,#0f6b3a);background:var(--dsw-alias-state-success-tertiary,#dcf3e5)}body[data-dsh-skin-center] .TJMolG_badgeWarning{color:var(--dsw-alias-state-warning-primary,#b06000);background:var(--dsw-alias-state-warning-tertiary,#feebd3)}body[data-dsh-skin-center] .TJMolG_badgeDanger{color:var(--dsw-alias-state-danger-primary,#d93025);background:var(--dsw-alias-state-danger-tertiary,#d930251f)}body[data-dsh-skin-center] .TJMolG_integrityNote{color:var(--dsw-alias-state-warning-primary,#b06000);font-size:11px;line-height:1.4}body[data-dsh-skin-center] .TJMolG_buttonGhost{opacity:.5;background:0 0;border-color:#0000}body[data-dsh-skin-center] .TJMolG_button:disabled{opacity:.5;cursor:default}body[data-dsh-skin-center] .TJMolG_error{color:var(--dsw-alias-state-error-primary,#b42318);font-size:12px}body[data-dsh-skin-center] .TJMolG_enableRow{flex-wrap:wrap;align-items:center;gap:8px;padding:8px 0;display:flex}body[data-dsh-skin-center] .TJMolG_enableLabel{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500}body[data-dsh-skin-center] .TJMolG_enableHint{min-width:100%;color:var(--dsw-alias-label-secondary,#6b7280);flex:1;margin:0;font-size:12px;line-height:1.5}body[data-dsh-skin-center] .TJMolG_switch{border:1px solid var(--dsw-alias-border-l3,#cbd5e1);background:var(--dsw-alias-bg-layer-3,#e2e8f0);cursor:pointer;border-radius:999px;flex:none;align-items:center;width:40px;height:22px;padding:2px;transition:background .12s,border-color .12s;display:inline-flex;position:relative}body[data-dsh-skin-center] .TJMolG_switchOn{border-color:var(--dsw-alias-brand-primary,#2b7cd9);background:var(--dsw-alias-brand-primary,#2b7cd9)}body[data-dsh-skin-center] .TJMolG_switch:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#2b7cd9);outline-offset:2px}body[data-dsh-skin-center] .TJMolG_switchThumb{background:var(--dsw-alias-label-primary-foreground,#fff);width:18px;height:18px;box-shadow:0 0 0 1px var(--dsw-alias-border-l4,#0f172a1f);border-radius:50%;transition:transform .12s;display:block;transform:translate(0)}body[data-dsh-skin-center] .TJMolG_switchOn .TJMolG_switchThumb{transform:translate(18px)}body[data-dsh-skin-center] .TJMolG_offNote{color:var(--dsw-alias-label-secondary,#6b7280);margin:0;font-size:12.5px;line-height:1.5}body[data-dsh-skin-center] .TJMolG_backgroundRow{flex-direction:column;gap:6px;padding:8px 0;display:flex}body[data-dsh-skin-center] .TJMolG_backgroundHead{align-items:center;gap:8px;display:flex}body[data-dsh-skin-center] .TJMolG_backgroundLabel{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500}body[data-dsh-skin-center] .TJMolG_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] .TJMolG_backgroundRange{background:var(--dsw-alias-label-tertiary,#9aa4b5);background:color-mix(in srgb, var(--dsw-alias-label-tertiary,#9aa4b5) 45%, transparent);width:100%;height:4px;box-shadow:0 0 0 1px var(--dsw-alias-border-l3,#cbd5e1);-webkit-appearance:none;appearance:none;cursor:pointer;border-radius:999px;margin:0}body[data-dsh-skin-center] .TJMolG_backgroundRange::-webkit-slider-runnable-track{background:var(--dsw-alias-bg-layer-3,#e2e8f0);border-radius:999px;height:4px}body[data-dsh-skin-center] .TJMolG_backgroundRange::-moz-range-track{background:var(--dsw-alias-bg-layer-3,#e2e8f0);border-radius:999px;height:4px}body[data-dsh-skin-center] .TJMolG_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%;margin-top:-5px}body[data-dsh-skin-center] .TJMolG_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] .TJMolG_backgroundRange:focus-visible{outline:2px solid var(--dsw-alias-brand-primary,#2b7cd9);outline-offset:2px}body[data-dsh-skin-center] .TJMolG_backgroundHint{color:var(--dsw-alias-label-secondary,#6b7280);font-size:12px;line-height:1.5}body[data-dsh-skin-center] .TJMolG_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] .TJMolG_pluginCard,body[data-dsh-skin-center] .TJMolG_card,body[data-dsh-skin-center] .TJMolG_themeButton,body[data-dsh-skin-center] .TJMolG_button,body[data-dsh-skin-center] .TJMolG_switch,body[data-dsh-skin-center] .TJMolG_switchThumb{transition:none}}body[data-dsh-skin-center] .TJMolG_wallpaperSection{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:10px;padding-top:10px;display:flex}body[data-dsh-skin-center] .TJMolG_wallpaperStatus{color:var(--dsw-alias-label-secondary,#6b7280);align-items:center;gap:8px;font-size:12px;display:flex}body[data-dsh-skin-center] .TJMolG_wallpaperStatusError{color:var(--dsw-alias-state-danger,#c53030)}body[data-dsh-skin-center] .TJMolG_wallpaperControls{flex-direction:column;gap:10px;display:flex}body[data-dsh-skin-center] .TJMolG_wallpaperGrid{grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:10px;display:grid}body[data-dsh-skin-center] .TJMolG_wallpaperCard{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:8px;flex-direction:column;gap:6px;padding:8px;transition:border-color .16s;display:flex}body[data-dsh-skin-center] .TJMolG_wallpaperCard:hover{border-color:var(--dsw-alias-label-dimmed)}body[data-dsh-skin-center] .TJMolG_wallpaperThumbWrap{aspect-ratio:16/9;background:var(--dsw-alias-bg-layer-1,#f1f5f9);border-radius:6px;position:relative;overflow:hidden}body[data-dsh-skin-center] .TJMolG_wallpaperThumb{object-fit:cover;width:100%;height:100%;display:block}body[data-dsh-skin-center] .TJMolG_wallpaperThumbEmpty{width:100%;height:100%}body[data-dsh-skin-center] .TJMolG_wallpaperType{color:var(--dsw-alias-label-primary,#172a45);background:var(--dsw-alias-bg-layer-2,#ffffffd9);border-radius:4px;padding:3px 6px;font-size:10.5px;line-height:1;position:absolute;top:6px;left:6px}body[data-dsh-skin-center] .TJMolG_wallpaperThumbWrap .TJMolG_badge{position:absolute;top:6px;right:6px}body[data-dsh-skin-center] .TJMolG_wallpaperName{color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;font-size:12px;line-height:1.35;overflow:hidden}body[data-dsh-skin-center] .TJMolG_wallpaperActions{flex-wrap:wrap;gap:6px;display:flex}body[data-dsh-skin-center] .TJMolG_customThemeCard{overflow:hidden}body[data-dsh-skin-center] .TJMolG_customThemeEditor{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1);border-radius:9px;flex-direction:column;gap:14px;margin:2px -2px -2px;padding:14px;display:flex}body[data-dsh-skin-center] .TJMolG_customThemeScheme{flex-wrap:wrap;align-items:center;gap:8px;display:flex}body[data-dsh-skin-center] .TJMolG_customThemeFields{grid-template-columns:repeat(3,minmax(0,1fr));gap:10px;display:grid}body[data-dsh-skin-center] .TJMolG_customThemeField,body[data-dsh-skin-center] .TJMolG_customThemeContrast{flex-direction:column;gap:7px;min-width:0;display:flex}body[data-dsh-skin-center] .TJMolG_customThemeFieldLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500}body[data-dsh-skin-center] .TJMolG_customThemeInputRow{align-items:center;gap:7px;min-width:0;display:flex}body[data-dsh-skin-center] .TJMolG_customThemeColor{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);cursor:pointer;border-radius:7px;flex:none;width:34px;height:30px;padding:2px}body[data-dsh-skin-center] .TJMolG_customThemeHex{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-specific-input-major);width:100%;min-width:0;height:30px;color:var(--dsw-alias-label-primary);font:inherit;border-radius:7px;outline:none;padding:0 9px;font-size:12px}body[data-dsh-skin-center] .TJMolG_customThemeHex:focus,body[data-dsh-skin-center] .TJMolG_customThemeColor:focus-visible{border-color:var(--dsw-alias-brand-primary);box-shadow:0 0 0 2px var(--dsw-alias-button-primary-dimmed)}body[data-dsh-skin-center] .TJMolG_customThemeFooter{justify-content:space-between;align-items:center;gap:12px;padding-top:2px;display:flex}@media (width<=680px){body[data-dsh-skin-center] .TJMolG_customThemeFields{grid-template-columns:1fr}body[data-dsh-skin-center] .TJMolG_customThemeFooter{flex-direction:column;align-items:flex-start}}body[data-dsh-skin-center] .TJMolG_wallpaperToolbar{flex-wrap:wrap;justify-content:space-between;align-items:center;gap:8px;margin:8px 0;display:flex}body[data-dsh-skin-center] .TJMolG_ratingFilterGroup{align-items:center;gap:4px;display:flex}body[data-dsh-skin-center] .TJMolG_ratingFilterButton{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);cursor:pointer;border-radius:6px;padding:4px 10px;font-size:12px;transition:all .15s}body[data-dsh-skin-center] .TJMolG_ratingFilterButton:hover{background:var(--dsw-alias-interactive-bg-hover,#ffffff14);color:var(--dsw-alias-label-primary)}body[data-dsh-skin-center] .TJMolG_ratingFilterActive{background:var(--dsw-alias-brand-primary,#2563eb);color:#fff;border-color:#0000}body[data-dsh-skin-center] .TJMolG_ratingFilterActive:hover{background:var(--dsw-alias-brand-primary,#2563eb);color:#fff}body[data-dsh-skin-center] .TJMolG_wallpaperRating{text-transform:uppercase;border-radius:4px;padding:3px 6px;font-size:10px;font-weight:600;line-height:1;position:absolute;bottom:6px;right:6px}body[data-dsh-skin-center] .TJMolG_ratingG{color:var(--dsw-alias-label-secondary,#94a3b8);background:var(--dsw-alias-bg-layer-2,#0009)}body[data-dsh-skin-center] .TJMolG_ratingPg13{color:#fbbf24;background:#000000b3;border:1px solid #fbbf2466}body[data-dsh-skin-center] .TJMolG_ratingR18{color:#fff;background:#dc2626;font-weight:700;box-shadow:0 1px 4px #dc262666}body[data-dsh-skin-center] .TJMolG_wallpaperPagination{flex-wrap:wrap;justify-content:center;align-items:center;gap:6px;margin:16px 0 8px;display:flex}body[data-dsh-skin-center] .TJMolG_pageButton{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);cursor:pointer;text-align:center;border-radius:6px;min-width:30px;padding:4px 10px;font-size:12px;transition:all .15s}body[data-dsh-skin-center] .TJMolG_pageButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#ffffff14);color:var(--dsw-alias-label-primary)}body[data-dsh-skin-center] .TJMolG_pageButton:disabled{opacity:.4;cursor:not-allowed}body[data-dsh-skin-center] .TJMolG_pageButtonActive{background:var(--dsw-alias-brand-primary,#2563eb);color:#fff;border-color:#0000}body[data-dsh-skin-center] .TJMolG_pageEllipsis{color:var(--dsw-alias-label-dimmed,#888);user-select:none;padding:0 4px;font-size:12px}body[data-dsh-skin-center] .TJMolG_pageJumpForm{align-items:center;gap:4px;margin-left:8px;display:inline-flex}body[data-dsh-skin-center] .TJMolG_pageJumpInput{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-specific-input-major,#0003);width:44px;height:26px;color:var(--dsw-alias-label-primary);text-align:center;border-radius:6px;outline:none;padding:0 4px;font-size:12px}body[data-dsh-skin-center] .TJMolG_pageJumpInput:focus{border-color:var(--dsw-alias-brand-primary)}body[data-dsh-skin-center] .TJMolG_pageTotalInfo{color:var(--dsw-alias-label-dimmed,#888);margin-left:6px;font-size:12px}body[data-dsh-skin-center] .TJMolG_wallpaperDirs{flex-direction:column;gap:6px;display:flex}body[data-dsh-skin-center] .TJMolG_wallpaperDir{background:var(--dsw-alias-bg-layer-2,#7f7f7f14);border-radius:6px;align-items:center;gap:6px;padding:4px 6px;display:flex}body[data-dsh-skin-center] .TJMolG_wallpaperDirPath{min-width:0;color:var(--dsw-alias-label-primary);text-overflow:ellipsis;white-space:nowrap;flex:1;font-size:12px;overflow:hidden}body[data-dsh-skin-center] .TJMolG_wallpaperDirRemove{width:20px;height:20px;color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;background:0 0;border:0;border-radius:4px;flex:none;font-size:13px;line-height:1}body[data-dsh-skin-center] .TJMolG_wallpaperDirRemove:hover{color:var(--dsw-alias-state-danger,#c53030)}body[data-dsh-skin-center] .TJMolG_wallpaperDirAdd{align-items:center;gap:6px;display:flex}body[data-dsh-skin-center] .TJMolG_wallpaperDirInput{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-specific-input-major,#0003);min-width:0;height:28px;color:var(--dsw-alias-label-primary);border-radius:6px;outline:none;flex:1;padding:0 8px;font-size:12px}body[data-dsh-skin-center] .TJMolG_wallpaperDirInput:focus{border-color:var(--dsw-alias-brand-primary)}body[data-dsh-skin-center] .TJMolG_dirBrowserOverlay{z-index:60;background:#0000006b;justify-content:center;align-items:center;padding:16px;display:flex;position:fixed;inset:0}body[data-dsh-skin-center] .TJMolG_dirBrowser{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-1,#1c1c1e);width:min(520px,100%);max-height:min(72vh,560px);color:var(--dsw-alias-label-primary);border-radius:12px;outline:none;flex-direction:column;gap:10px;padding:14px;display:flex;box-shadow:0 18px 48px #00000047}body[data-dsh-skin-center] .TJMolG_dirBrowserTitle{font-size:13px;font-weight:600}body[data-dsh-skin-center] .TJMolG_dirBrowserCrumbs{color:var(--dsw-alias-label-secondary,#6b7280);flex-wrap:wrap;align-items:center;gap:2px;font-size:12px;display:flex}body[data-dsh-skin-center] .TJMolG_dirBrowserCrumb{align-items:center;gap:2px;max-width:180px;display:inline-flex}body[data-dsh-skin-center] .TJMolG_dirBrowserCrumbSep{color:var(--dsw-alias-label-dimmed,#888)}body[data-dsh-skin-center] .TJMolG_dirBrowserCrumbButton{max-width:100%;color:inherit;cursor:pointer;text-overflow:ellipsis;white-space:nowrap;background:0 0;border:0;border-radius:4px;padding:2px 4px;font-size:12px;overflow:hidden}body[data-dsh-skin-center] .TJMolG_dirBrowserCrumbButton:hover{background:var(--dsw-alias-bg-layer-2,#7f7f7f1f);color:var(--dsw-alias-label-primary)}body[data-dsh-skin-center] .TJMolG_dirBrowserList{border:1px solid var(--dsw-alias-border-l2);border-radius:8px;flex-direction:column;flex:1;gap:2px;min-height:140px;padding:4px;display:flex;overflow:auto}body[data-dsh-skin-center] .TJMolG_dirBrowserEntry{color:var(--dsw-alias-label-primary);text-align:left;cursor:pointer;text-overflow:ellipsis;white-space:nowrap;background:0 0;border:0;border-radius:6px;padding:7px 8px;font-size:12.5px;overflow:hidden}body[data-dsh-skin-center] .TJMolG_dirBrowserEntry:hover{background:var(--dsw-alias-bg-layer-2,#7f7f7f1f)}body[data-dsh-skin-center] .TJMolG_dirBrowserNote{color:var(--dsw-alias-label-secondary,#6b7280);padding:6px 8px;font-size:12px}body[data-dsh-skin-center] .TJMolG_dirBrowserError{color:var(--dsw-alias-state-danger,#c53030);word-break:break-all;padding:6px 8px;font-size:12px}body[data-dsh-skin-center] .TJMolG_dirBrowserHidden{color:var(--dsw-alias-label-secondary,#6b7280);cursor:pointer;align-items:center;gap:6px;font-size:12px;display:flex}body[data-dsh-skin-center] .TJMolG_dirBrowserActions{justify-content:flex-end;gap:8px;display:flex}";
|
|
11
11
|
const tagId = "@linxin666/dsh-client-ui-skin-center/src/client/skin-center.module.css";
|
|
12
12
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId) + "]") === null) {
|
|
13
13
|
const tag = document.createElement("style");
|
|
@@ -54,6 +54,19 @@ window.__ModuleLoader__.load({
|
|
|
54
54
|
"customThemeHex": "TJMolG_customThemeHex",
|
|
55
55
|
"customThemeInputRow": "TJMolG_customThemeInputRow",
|
|
56
56
|
"customThemeScheme": "TJMolG_customThemeScheme",
|
|
57
|
+
"dirBrowser": "TJMolG_dirBrowser",
|
|
58
|
+
"dirBrowserActions": "TJMolG_dirBrowserActions",
|
|
59
|
+
"dirBrowserCrumb": "TJMolG_dirBrowserCrumb",
|
|
60
|
+
"dirBrowserCrumbButton": "TJMolG_dirBrowserCrumbButton",
|
|
61
|
+
"dirBrowserCrumbSep": "TJMolG_dirBrowserCrumbSep",
|
|
62
|
+
"dirBrowserCrumbs": "TJMolG_dirBrowserCrumbs",
|
|
63
|
+
"dirBrowserEntry": "TJMolG_dirBrowserEntry",
|
|
64
|
+
"dirBrowserError": "TJMolG_dirBrowserError",
|
|
65
|
+
"dirBrowserHidden": "TJMolG_dirBrowserHidden",
|
|
66
|
+
"dirBrowserList": "TJMolG_dirBrowserList",
|
|
67
|
+
"dirBrowserNote": "TJMolG_dirBrowserNote",
|
|
68
|
+
"dirBrowserOverlay": "TJMolG_dirBrowserOverlay",
|
|
69
|
+
"dirBrowserTitle": "TJMolG_dirBrowserTitle",
|
|
57
70
|
"enableHint": "TJMolG_enableHint",
|
|
58
71
|
"enableLabel": "TJMolG_enableLabel",
|
|
59
72
|
"enableRow": "TJMolG_enableRow",
|
|
@@ -95,6 +108,12 @@ window.__ModuleLoader__.load({
|
|
|
95
108
|
"wallpaperActions": "TJMolG_wallpaperActions",
|
|
96
109
|
"wallpaperCard": "TJMolG_wallpaperCard",
|
|
97
110
|
"wallpaperControls": "TJMolG_wallpaperControls",
|
|
111
|
+
"wallpaperDir": "TJMolG_wallpaperDir",
|
|
112
|
+
"wallpaperDirAdd": "TJMolG_wallpaperDirAdd",
|
|
113
|
+
"wallpaperDirInput": "TJMolG_wallpaperDirInput",
|
|
114
|
+
"wallpaperDirPath": "TJMolG_wallpaperDirPath",
|
|
115
|
+
"wallpaperDirRemove": "TJMolG_wallpaperDirRemove",
|
|
116
|
+
"wallpaperDirs": "TJMolG_wallpaperDirs",
|
|
98
117
|
"wallpaperGrid": "TJMolG_wallpaperGrid",
|
|
99
118
|
"wallpaperName": "TJMolG_wallpaperName",
|
|
100
119
|
"wallpaperPagination": "TJMolG_wallpaperPagination",
|
|
@@ -385,6 +404,31 @@ window.__ModuleLoader__.load({
|
|
|
385
404
|
const BACKDROP_ACTIVE_ATTR = "data-dsh-backdrop-active";
|
|
386
405
|
/** The shared composer-seat neutralizer style's own attribute. */
|
|
387
406
|
const SCENE_NEUTRALIZER_ATTR = "data-dsh-scene-neutralizer";
|
|
407
|
+
/**
|
|
408
|
+
* Attribute of the composer frost follower: a body-level, fixed, empty element
|
|
409
|
+
* sized to the input card's border box while a scene is active.
|
|
410
|
+
*
|
|
411
|
+
* It exists because the frost must NOT be painted on the card. A non-none
|
|
412
|
+
* backdrop-filter (like transform, filter, contain or perspective) makes an
|
|
413
|
+
* element the CONTAINING BLOCK for its fixed-position descendants, and the
|
|
414
|
+
* official shell renders its tooltips inside the composer card: the composer
|
|
415
|
+
* send arrow's bubble is position: fixed, so with the frost on the card it
|
|
416
|
+
* resolved its viewport coordinates against the card, landed far outside it
|
|
417
|
+
* but still inside the conversation scrollport, added its own height to
|
|
418
|
+
* scrollHeight, and every hover clamped the scroll to the new bottom — the
|
|
419
|
+
* whole conversation jumped by hundreds of pixels and snapped back on the
|
|
420
|
+
* next frame (issue #1724). Documented throughout the catalog skins as the
|
|
421
|
+
* reason a card ancestor never carries a filter.
|
|
422
|
+
*
|
|
423
|
+
* A body-level sibling is not an ancestor of the card, so it may carry the
|
|
424
|
+
* blur; because nested backdrop-filters do not compose, the card itself must
|
|
425
|
+
* stay filter-free for the follower's blur to read through its translucent
|
|
426
|
+
* fill. The element is empty and pointer-events:none, and it is a sibling of
|
|
427
|
+
* the app root, never inside the scrollport.
|
|
428
|
+
*/
|
|
429
|
+
const COMPOSER_FROST_ATTR = "data-dsh-composer-frost";
|
|
430
|
+
/** Stacking rung of the follower: above the skin art layers, below the shell. */
|
|
431
|
+
const COMPOSER_FROST_Z_INDEX = "-1";
|
|
388
432
|
/** Conversation-content marker: set while the active conversation has rows. */
|
|
389
433
|
const CONVERSATION_CONTENT_ATTR = "data-dsh-conversation-content";
|
|
390
434
|
/**
|
|
@@ -435,6 +479,7 @@ window.__ModuleLoader__.load({
|
|
|
435
479
|
ensureSceneNeutralizer(doc);
|
|
436
480
|
startContentObserver(doc);
|
|
437
481
|
} else stopContentObserver(doc);
|
|
482
|
+
syncComposerFrost(doc);
|
|
438
483
|
}
|
|
439
484
|
/**
|
|
440
485
|
* Track whether the active conversation scrollport has message rows for the
|
|
@@ -446,6 +491,7 @@ window.__ModuleLoader__.load({
|
|
|
446
491
|
const has = doc.body !== null && doc.body.querySelector(ACTIVE_CONVERSATION_CONTENT_SELECTOR) !== null;
|
|
447
492
|
applyMarker(doc.body, CONVERSATION_CONTENT_ATTR, has);
|
|
448
493
|
applyMarker(doc.documentElement, CONVERSATION_CONTENT_ATTR, has);
|
|
494
|
+
syncComposerFrost(doc);
|
|
449
495
|
}
|
|
450
496
|
/**
|
|
451
497
|
* Coalesce the mutation bursts of a streaming conversation into one content
|
|
@@ -493,12 +539,17 @@ window.__ModuleLoader__.load({
|
|
|
493
539
|
}
|
|
494
540
|
applyMarker(doc.body, CONVERSATION_CONTENT_ATTR, false);
|
|
495
541
|
applyMarker(doc.documentElement, CONVERSATION_CONTENT_ATTR, false);
|
|
542
|
+
syncComposerFrost(doc);
|
|
496
543
|
}
|
|
497
544
|
/**
|
|
498
545
|
* Install the shared composer-seat neutralizer, keyed by head presence so a
|
|
499
546
|
* cleared head (tests) or a re-mount re-creates it. Without the marker the
|
|
500
547
|
* rules are inert, so the style can outlive a single mount without changing
|
|
501
548
|
* any other look.
|
|
549
|
+
*
|
|
550
|
+
* The composer card is deliberately absent from this sheet: its frost rides
|
|
551
|
+
* {@link COMPOSER_FROST_ATTR} instead, because no rule here may put a
|
|
552
|
+
* containing-block property back on the card (issue #1724).
|
|
502
553
|
*/
|
|
503
554
|
function ensureSceneNeutralizer(doc) {
|
|
504
555
|
if (doc.head === null) return;
|
|
@@ -512,17 +563,156 @@ window.__ModuleLoader__.load({
|
|
|
512
563
|
backdrop-filter: none !important;
|
|
513
564
|
-webkit-backdrop-filter: none !important;
|
|
514
565
|
}
|
|
515
|
-
html[data-dsh-backdrop-active][data-dsh-conversation-content] [
|
|
566
|
+
html[data-dsh-backdrop-active][data-dsh-conversation-content] [${COMPOSER_FROST_ATTR}] {
|
|
516
567
|
backdrop-filter: blur(var(--dsh-input-card-blur, 10px)) !important;
|
|
517
568
|
-webkit-backdrop-filter: blur(var(--dsh-input-card-blur, 10px)) !important;
|
|
518
569
|
}
|
|
519
570
|
`;
|
|
520
571
|
doc.head.appendChild(style);
|
|
521
572
|
}
|
|
573
|
+
const frostFollowers = /* @__PURE__ */ new WeakMap();
|
|
574
|
+
/** The composer card the frost tracks, or null while none is mounted. */
|
|
575
|
+
function composerCardOf(doc) {
|
|
576
|
+
return doc.body === null ? null : doc.body.querySelector("[data-composer-card]");
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* Create the follower on first use and keep it sized to the composer card.
|
|
580
|
+
*
|
|
581
|
+
* The element is empty, fixed and inert: it paints nothing of its own, and it
|
|
582
|
+
* receives no pointer events, so it can never take a tap from the composer or
|
|
583
|
+
* the sidebar. Sizing uses one measure per animation frame (the shape
|
|
584
|
+
* shell-rendering.ts uses for the composer height) rather than a per-mutation
|
|
585
|
+
* read, and writes only when the box actually changed.
|
|
586
|
+
*/
|
|
587
|
+
function syncFrostFollower(doc) {
|
|
588
|
+
const follower = frostFollowers.get(doc);
|
|
589
|
+
if (follower === void 0) return;
|
|
590
|
+
const card = composerCardOf(doc);
|
|
591
|
+
if (card === null) return;
|
|
592
|
+
if (follower.observed !== card) {
|
|
593
|
+
if (follower.observed !== null) follower.resizeObserver?.unobserve(follower.observed);
|
|
594
|
+
follower.observed = card;
|
|
595
|
+
follower.resizeObserver?.observe(card);
|
|
596
|
+
follower.applied = "";
|
|
597
|
+
}
|
|
598
|
+
const rect = card.getBoundingClientRect();
|
|
599
|
+
if (rect.width <= 0 || rect.height <= 0) return;
|
|
600
|
+
const radius = doc.defaultView?.getComputedStyle(card).borderRadius ?? "";
|
|
601
|
+
const box = [
|
|
602
|
+
rect.top,
|
|
603
|
+
rect.left,
|
|
604
|
+
rect.width,
|
|
605
|
+
rect.height,
|
|
606
|
+
radius
|
|
607
|
+
].map((value) => typeof value === "number" ? Math.round(value) : value).join("|");
|
|
608
|
+
if (box === follower.applied) return;
|
|
609
|
+
follower.applied = box;
|
|
610
|
+
const style = follower.el.style;
|
|
611
|
+
style.top = `${Math.round(rect.top)}px`;
|
|
612
|
+
style.left = `${Math.round(rect.left)}px`;
|
|
613
|
+
style.width = `${Math.round(rect.width)}px`;
|
|
614
|
+
style.height = `${Math.round(rect.height)}px`;
|
|
615
|
+
style.borderRadius = radius;
|
|
616
|
+
}
|
|
617
|
+
/** Coalesce a burst of geometry changes into one measurement per frame. */
|
|
618
|
+
function scheduleFrostSync(doc) {
|
|
619
|
+
const follower = frostFollowers.get(doc);
|
|
620
|
+
if (follower === void 0 || follower.frame !== null) return;
|
|
621
|
+
const win = doc.defaultView;
|
|
622
|
+
if (win === null || typeof win.requestAnimationFrame !== "function") {
|
|
623
|
+
syncFrostFollower(doc);
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
follower.frame = win.requestAnimationFrame(() => {
|
|
627
|
+
follower.frame = null;
|
|
628
|
+
if (!frostFollowers.has(doc)) return;
|
|
629
|
+
syncFrostFollower(doc);
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
/** Mount the follower while a scene is visible and start tracking the card. */
|
|
633
|
+
function startFrostFollower(doc) {
|
|
634
|
+
if (frostFollowers.has(doc) || doc.body === null) return;
|
|
635
|
+
const el = doc.createElement("div");
|
|
636
|
+
el.setAttribute(COMPOSER_FROST_ATTR, "");
|
|
637
|
+
el.setAttribute("aria-hidden", "true");
|
|
638
|
+
const style = el.style;
|
|
639
|
+
style.position = "fixed";
|
|
640
|
+
style.pointerEvents = "none";
|
|
641
|
+
style.zIndex = COMPOSER_FROST_Z_INDEX;
|
|
642
|
+
style.borderRadius = "inherit";
|
|
643
|
+
const follower = {
|
|
644
|
+
el,
|
|
645
|
+
resizeObserver: null,
|
|
646
|
+
observed: null,
|
|
647
|
+
onResize: null,
|
|
648
|
+
frame: null,
|
|
649
|
+
applied: ""
|
|
650
|
+
};
|
|
651
|
+
doc.body.appendChild(el);
|
|
652
|
+
frostFollowers.set(doc, follower);
|
|
653
|
+
const win = doc.defaultView;
|
|
654
|
+
if (win !== null && typeof win.ResizeObserver === "function") follower.resizeObserver = new win.ResizeObserver(() => scheduleFrostSync(doc));
|
|
655
|
+
if (win !== null && typeof win.addEventListener === "function") {
|
|
656
|
+
follower.onResize = () => scheduleFrostSync(doc);
|
|
657
|
+
win.addEventListener("resize", follower.onResize);
|
|
658
|
+
}
|
|
659
|
+
syncFrostFollower(doc);
|
|
660
|
+
}
|
|
661
|
+
/** Remove the follower and stop every loop that fed it. */
|
|
662
|
+
function stopFrostFollower(doc) {
|
|
663
|
+
const follower = frostFollowers.get(doc);
|
|
664
|
+
if (follower === void 0) return;
|
|
665
|
+
frostFollowers.delete(doc);
|
|
666
|
+
const win = doc.defaultView;
|
|
667
|
+
if (follower.frame !== null && win !== null && typeof win.cancelAnimationFrame === "function") {
|
|
668
|
+
win.cancelAnimationFrame(follower.frame);
|
|
669
|
+
follower.frame = null;
|
|
670
|
+
}
|
|
671
|
+
follower.resizeObserver?.disconnect();
|
|
672
|
+
follower.resizeObserver = null;
|
|
673
|
+
if (follower.onResize !== null) {
|
|
674
|
+
win?.removeEventListener("resize", follower.onResize);
|
|
675
|
+
follower.onResize = null;
|
|
676
|
+
}
|
|
677
|
+
follower.el.remove();
|
|
678
|
+
}
|
|
679
|
+
/**
|
|
680
|
+
* Attach the composer frost to the current scene state. Called from the shared
|
|
681
|
+
* marker sync so the follower exists exactly while a backdrop is visible and
|
|
682
|
+
* the conversation has content — the same gate the card's old frost rule used.
|
|
683
|
+
*
|
|
684
|
+
* The follower tracks the card through a ResizeObserver on the card itself and
|
|
685
|
+
* one interval-free re-schedule on every re-mount, so a conversation switch
|
|
686
|
+
* that replaces the composer keeps the frost aligned without a polling loop.
|
|
687
|
+
*/
|
|
688
|
+
function syncComposerFrost(doc) {
|
|
689
|
+
if (!(doc.body !== null && doc.body.hasAttribute("data-dsh-backdrop-active") && doc.body.hasAttribute("data-dsh-conversation-content"))) {
|
|
690
|
+
stopFrostFollower(doc);
|
|
691
|
+
return;
|
|
692
|
+
}
|
|
693
|
+
startFrostFollower(doc);
|
|
694
|
+
}
|
|
522
695
|
//#endregion
|
|
523
696
|
//#region src/client/wallpaper.ts
|
|
524
697
|
/** The section key the Host schema declares (mirrors src/index.ts). */
|
|
525
698
|
const SKIN_WALLPAPER_NS = "skin-wallpaper";
|
|
699
|
+
/**
|
|
700
|
+
* Map one remote.directoryPicker.pick result onto the outcome the panel
|
|
701
|
+
* switches on. The composed picker refuses the verb with
|
|
702
|
+
* directory-picker/unavailable when it serves the browse capability, so
|
|
703
|
+
* that code is a capability answer, not a failure; every other error still
|
|
704
|
+
* rejects (a transport or assembly fault must never look like a cancel).
|
|
705
|
+
* @param result - the Remote result of the pick call.
|
|
706
|
+
* @returns the pick outcome; throws the Host's message on any other failure.
|
|
707
|
+
*/
|
|
708
|
+
function mapDirPickResult(result) {
|
|
709
|
+
if (result.ok) return result.value === null ? { kind: "cancelled" } : {
|
|
710
|
+
kind: "picked",
|
|
711
|
+
path: result.value
|
|
712
|
+
};
|
|
713
|
+
if (result.error.code === "directory-picker/unavailable") return { kind: "unavailable" };
|
|
714
|
+
throw new Error(result.error.message);
|
|
715
|
+
}
|
|
526
716
|
const clamp = (value, min, max) => Math.max(min, Math.min(max, Math.round(value)));
|
|
527
717
|
/** Style one fixed, non-interactive, under-everything wallpaper layer. */
|
|
528
718
|
function styleLayer(element, zIndex, layer) {
|
|
@@ -569,6 +759,19 @@ window.__ModuleLoader__.load({
|
|
|
569
759
|
return Number.isFinite(numericZIndex) && numericZIndex > MAX_SURFACE_OVERLAY_Z_INDEX;
|
|
570
760
|
}
|
|
571
761
|
/**
|
|
762
|
+
* Whether one media-layer child is the sandboxed scene-player frame.
|
|
763
|
+
*
|
|
764
|
+
* Read from the node (tagName) instead of `instanceof HTMLIFrameElement`: the
|
|
765
|
+
* ambient globals belong to the app's realm, and the wallpaper paths run
|
|
766
|
+
* against elements from documents whose realm those bindings cannot see, where
|
|
767
|
+
* the `instanceof` reference throws rather than answering false.
|
|
768
|
+
* @param node - the candidate element.
|
|
769
|
+
* @returns true when it is an iframe.
|
|
770
|
+
*/
|
|
771
|
+
function isScenePlayer(node) {
|
|
772
|
+
return node !== null && node.tagName === "IFRAME";
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
572
775
|
* Default shell-surface detector for WE wallpaper neutralization (#712). A
|
|
573
776
|
* target must cover most of the visible viewport and paint a nontransparent
|
|
574
777
|
* background. It deliberately avoids equality against a theme token because
|
|
@@ -940,7 +1143,7 @@ window.__ModuleLoader__.load({
|
|
|
940
1143
|
}
|
|
941
1144
|
recoverScenePlayer() {
|
|
942
1145
|
const scenePlayer = this.mediaLayer?.firstElementChild ?? null;
|
|
943
|
-
if (!(scenePlayer
|
|
1146
|
+
if (!isScenePlayer(scenePlayer) || scenePlayer.dataset.dshScenePlayer !== "") return;
|
|
944
1147
|
try {
|
|
945
1148
|
scenePlayer.contentWindow?.postMessage({ type: "dsh-recover-renderer" }, "*");
|
|
946
1149
|
} catch {}
|
|
@@ -1005,7 +1208,7 @@ window.__ModuleLoader__.load({
|
|
|
1005
1208
|
};
|
|
1006
1209
|
onSceneMessage = (event) => {
|
|
1007
1210
|
const scenePlayer = this.mediaLayer?.firstElementChild ?? null;
|
|
1008
|
-
if (!(scenePlayer
|
|
1211
|
+
if (!isScenePlayer(scenePlayer) || scenePlayer.dataset.dshScenePlayer !== "") return;
|
|
1009
1212
|
if (event.source !== scenePlayer.contentWindow) return;
|
|
1010
1213
|
if (event.data?.type !== "dsh-scene-needs-reload") return;
|
|
1011
1214
|
scenePlayer.src = scenePlayer.src;
|
|
@@ -1017,7 +1220,7 @@ window.__ModuleLoader__.load({
|
|
|
1017
1220
|
else this.videoElement.play()?.catch(() => {});
|
|
1018
1221
|
}
|
|
1019
1222
|
const scenePlayer = this.mediaLayer?.firstElementChild ?? null;
|
|
1020
|
-
if (scenePlayer
|
|
1223
|
+
if (isScenePlayer(scenePlayer) && scenePlayer.dataset.dshScenePlayer === "") try {
|
|
1021
1224
|
scenePlayer.contentWindow?.postMessage({
|
|
1022
1225
|
type: "dsh-set-pause",
|
|
1023
1226
|
paused: this.doc.hidden
|
|
@@ -1107,7 +1310,8 @@ window.__ModuleLoader__.load({
|
|
|
1107
1310
|
const child = this.buildMedia(descriptor);
|
|
1108
1311
|
if (child !== null) {
|
|
1109
1312
|
this.mediaLayer.appendChild(child);
|
|
1110
|
-
|
|
1313
|
+
const VideoCtor = this.doc.defaultView?.HTMLVideoElement;
|
|
1314
|
+
if (VideoCtor !== void 0 && child instanceof VideoCtor && child.paused) child.play()?.catch(() => {});
|
|
1111
1315
|
}
|
|
1112
1316
|
} else {
|
|
1113
1317
|
const child = this.mediaLayer.firstElementChild;
|
|
@@ -1124,8 +1328,8 @@ window.__ModuleLoader__.load({
|
|
|
1124
1328
|
/** Push the current sizing mode onto the mounted media element. */
|
|
1125
1329
|
applyFit() {
|
|
1126
1330
|
const child = this.mediaLayer?.firstElementChild ?? null;
|
|
1127
|
-
if (child
|
|
1128
|
-
if (child
|
|
1331
|
+
if (child !== null && child.nodeType === 1) styleCover(child, this.fitValue);
|
|
1332
|
+
if (child !== null && isScenePlayer(child) && child.dataset.dshScenePlayer === "") try {
|
|
1129
1333
|
child.contentWindow?.postMessage({
|
|
1130
1334
|
type: "dsh-set-fit",
|
|
1131
1335
|
fit: this.fitValue
|
|
@@ -1293,7 +1497,7 @@ window.__ModuleLoader__.load({
|
|
|
1293
1497
|
while (stack.length > 0) {
|
|
1294
1498
|
const node = stack.pop();
|
|
1295
1499
|
if (node === void 0) continue;
|
|
1296
|
-
if (node
|
|
1500
|
+
if (node.nodeType === 1 && !node.hasAttribute("data-dsh-wallpaper-surface") && isSurface(node)) {
|
|
1297
1501
|
node.setAttribute("data-dsh-wallpaper-surface", "");
|
|
1298
1502
|
this.taggedSurfaces.add(node);
|
|
1299
1503
|
}
|
|
@@ -1311,7 +1515,7 @@ window.__ModuleLoader__.load({
|
|
|
1311
1515
|
while (stack.length > 0) {
|
|
1312
1516
|
const node = stack.pop();
|
|
1313
1517
|
if (node === void 0) continue;
|
|
1314
|
-
if (node
|
|
1518
|
+
if (node.nodeType === 1 && !node.hasAttribute("data-dsh-wallpaper-surface") && isFade(node, this.doc)) {
|
|
1315
1519
|
node.setAttribute("data-dsh-wallpaper-surface", "");
|
|
1316
1520
|
this.taggedSurfaces.add(node);
|
|
1317
1521
|
}
|
|
@@ -1335,12 +1539,24 @@ window.__ModuleLoader__.load({
|
|
|
1335
1539
|
subtree: true
|
|
1336
1540
|
});
|
|
1337
1541
|
}
|
|
1338
|
-
/**
|
|
1542
|
+
/**
|
|
1543
|
+
* Incrementally tag added subtrees and untag removed subtrees.
|
|
1544
|
+
*
|
|
1545
|
+
* Elements are recognised by nodeType, never by `instanceof HTMLElement`.
|
|
1546
|
+
* Decision: element KIND is read from the node (nodeType / tagName), and a
|
|
1547
|
+
* constructor test, where one is genuinely needed, comes from the owning
|
|
1548
|
+
* document's window (`doc.defaultView.HTMLVideoElement`) — the same rule the
|
|
1549
|
+
* video resume path below already follows. The ambient globals are a single
|
|
1550
|
+
* realm's bindings: they do not describe elements from another document, and
|
|
1551
|
+
* code that reached for them here threw a bare ReferenceError out of an
|
|
1552
|
+
* observer callback. This is a deliberate repository-wide convention, not a
|
|
1553
|
+
* workaround for one sandbox.
|
|
1554
|
+
*/
|
|
1339
1555
|
handleSurfaceMutations(records) {
|
|
1340
1556
|
if (this.disposed || (this.previewing ?? this.applied) === null) return;
|
|
1341
1557
|
for (const record of records) {
|
|
1342
|
-
for (const node of record.addedNodes) if (node
|
|
1343
|
-
for (const node of record.removedNodes) if (node
|
|
1558
|
+
for (const node of record.addedNodes) if (node.nodeType === 1) this.tagAddedSubtree(node);
|
|
1559
|
+
for (const node of record.removedNodes) if (node.nodeType === 1) this.untagRemovedSubtree(node);
|
|
1344
1560
|
}
|
|
1345
1561
|
}
|
|
1346
1562
|
/** Tag newly added elements that qualify as full-viewport surfaces or workspace fades. */
|
|
@@ -1359,7 +1575,7 @@ window.__ModuleLoader__.load({
|
|
|
1359
1575
|
this.taggedSurfaces.add(node);
|
|
1360
1576
|
}
|
|
1361
1577
|
}
|
|
1362
|
-
for (const child of Array.from(node.children)) if (child
|
|
1578
|
+
for (const child of Array.from(node.children)) if (child.nodeType === 1) stack.push(child);
|
|
1363
1579
|
}
|
|
1364
1580
|
}
|
|
1365
1581
|
/** Remove tags from a removed subtree and drop its references. */
|
|
@@ -1372,7 +1588,7 @@ window.__ModuleLoader__.load({
|
|
|
1372
1588
|
node.removeAttribute("data-dsh-wallpaper-surface");
|
|
1373
1589
|
this.taggedSurfaces.delete(node);
|
|
1374
1590
|
}
|
|
1375
|
-
for (const child of Array.from(node.children)) if (child
|
|
1591
|
+
for (const child of Array.from(node.children)) if (child.nodeType === 1) stack.push(child);
|
|
1376
1592
|
}
|
|
1377
1593
|
}
|
|
1378
1594
|
untagSurfaces() {
|
|
@@ -1442,6 +1658,167 @@ window.__ModuleLoader__.load({
|
|
|
1442
1658
|
wallpaper.subscribe(restore);
|
|
1443
1659
|
}
|
|
1444
1660
|
//#endregion
|
|
1661
|
+
//#region src/client/DirBrowserDialog.tsx
|
|
1662
|
+
/**
|
|
1663
|
+
* In-app folder browser: the picking surface for a Host whose directory
|
|
1664
|
+
* picker composes the browse backend. The adaptive chooser resolves that
|
|
1665
|
+
* backend whenever the web server binds beyond loopback (LAN / remote GUI
|
|
1666
|
+
* access) or the host is launched over SSH, because no OS chooser can reach
|
|
1667
|
+
* such a client; the picker then refuses `pick` and serves listing
|
|
1668
|
+
* primitives instead.
|
|
1669
|
+
*
|
|
1670
|
+
* The dialog drives those primitives one level at a time and owns no path
|
|
1671
|
+
* joining: every crumb and every row is an absolute host path the backend
|
|
1672
|
+
* reported, so a client on another machine still picks a real host folder.
|
|
1673
|
+
* Escape and a backdrop click close it; the chosen path goes back through
|
|
1674
|
+
* `onPick` and the caller persists it.
|
|
1675
|
+
*
|
|
1676
|
+
* @module @linxin666/dsh-client-ui-skin-center/DirBrowserDialog
|
|
1677
|
+
*/
|
|
1678
|
+
/** Render the in-app folder browser over the picker's browse primitives. */
|
|
1679
|
+
function DirBrowserDialog({ t, listDir, onPick, onClose }) {
|
|
1680
|
+
const [listing, setListing] = (0, react.useState)(null);
|
|
1681
|
+
const [error, setError] = (0, react.useState)(null);
|
|
1682
|
+
const [busy, setBusy] = (0, react.useState)(true);
|
|
1683
|
+
const [showHidden, setShowHidden] = (0, react.useState)(false);
|
|
1684
|
+
const alive = (0, react.useRef)(true);
|
|
1685
|
+
const panel = (0, react.useRef)(null);
|
|
1686
|
+
(0, react.useEffect)(() => {
|
|
1687
|
+
alive.current = true;
|
|
1688
|
+
return () => {
|
|
1689
|
+
alive.current = false;
|
|
1690
|
+
};
|
|
1691
|
+
}, []);
|
|
1692
|
+
/** Load one level; an absent path loads the host account's home. */
|
|
1693
|
+
const open = (0, react.useCallback)((path) => {
|
|
1694
|
+
setBusy(true);
|
|
1695
|
+
setError(null);
|
|
1696
|
+
listDir(path).then((next) => {
|
|
1697
|
+
if (!alive.current) return;
|
|
1698
|
+
setBusy(false);
|
|
1699
|
+
setListing(next);
|
|
1700
|
+
}, (reason) => {
|
|
1701
|
+
if (!alive.current) return;
|
|
1702
|
+
setBusy(false);
|
|
1703
|
+
setError(reason instanceof Error ? reason.message : String(reason));
|
|
1704
|
+
});
|
|
1705
|
+
}, [listDir]);
|
|
1706
|
+
(0, react.useEffect)(() => {
|
|
1707
|
+
open();
|
|
1708
|
+
}, [open]);
|
|
1709
|
+
(0, react.useEffect)(() => {
|
|
1710
|
+
panel.current?.focus();
|
|
1711
|
+
const onKey = (event) => {
|
|
1712
|
+
if (event.key === "Escape") onClose();
|
|
1713
|
+
};
|
|
1714
|
+
document.addEventListener("keydown", onKey);
|
|
1715
|
+
return () => {
|
|
1716
|
+
document.removeEventListener("keydown", onKey);
|
|
1717
|
+
};
|
|
1718
|
+
}, [onClose]);
|
|
1719
|
+
const entries = (listing?.entries ?? []).filter((entry) => showHidden || !entry.hidden);
|
|
1720
|
+
const failed = error !== null;
|
|
1721
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1722
|
+
className: skin_center_module_css_default.dirBrowserOverlay,
|
|
1723
|
+
onMouseDown: (event) => {
|
|
1724
|
+
if (event.target === event.currentTarget) onClose();
|
|
1725
|
+
},
|
|
1726
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1727
|
+
className: skin_center_module_css_default.dirBrowser,
|
|
1728
|
+
role: "dialog",
|
|
1729
|
+
"aria-modal": "true",
|
|
1730
|
+
"aria-label": t("wallpaperDirBrowseTitle"),
|
|
1731
|
+
tabIndex: -1,
|
|
1732
|
+
ref: panel,
|
|
1733
|
+
children: [
|
|
1734
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1735
|
+
className: skin_center_module_css_default.dirBrowserTitle,
|
|
1736
|
+
children: t("wallpaperDirBrowseTitle")
|
|
1737
|
+
}),
|
|
1738
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1739
|
+
className: skin_center_module_css_default.dirBrowserCrumbs,
|
|
1740
|
+
children: (listing?.crumbs ?? []).map((crumb, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1741
|
+
className: skin_center_module_css_default.dirBrowserCrumb,
|
|
1742
|
+
children: [index > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1743
|
+
className: skin_center_module_css_default.dirBrowserCrumbSep,
|
|
1744
|
+
children: "/"
|
|
1745
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1746
|
+
type: "button",
|
|
1747
|
+
className: skin_center_module_css_default.dirBrowserCrumbButton,
|
|
1748
|
+
title: crumb.path,
|
|
1749
|
+
onClick: () => {
|
|
1750
|
+
open(crumb.path);
|
|
1751
|
+
},
|
|
1752
|
+
children: crumb.name
|
|
1753
|
+
})]
|
|
1754
|
+
}, crumb.path))
|
|
1755
|
+
}),
|
|
1756
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1757
|
+
className: skin_center_module_css_default.dirBrowserList,
|
|
1758
|
+
children: [
|
|
1759
|
+
busy && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1760
|
+
className: skin_center_module_css_default.dirBrowserNote,
|
|
1761
|
+
children: t("loading")
|
|
1762
|
+
}),
|
|
1763
|
+
!busy && failed && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1764
|
+
className: skin_center_module_css_default.dirBrowserError,
|
|
1765
|
+
children: [
|
|
1766
|
+
t("wallpaperDirBrowseFailed"),
|
|
1767
|
+
": ",
|
|
1768
|
+
error
|
|
1769
|
+
]
|
|
1770
|
+
}),
|
|
1771
|
+
!busy && !failed && entries.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1772
|
+
className: skin_center_module_css_default.dirBrowserNote,
|
|
1773
|
+
children: t("wallpaperDirBrowseEmpty")
|
|
1774
|
+
}),
|
|
1775
|
+
!busy && !failed && entries.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1776
|
+
type: "button",
|
|
1777
|
+
className: skin_center_module_css_default.dirBrowserEntry,
|
|
1778
|
+
title: entry.path,
|
|
1779
|
+
onClick: () => {
|
|
1780
|
+
open(entry.path);
|
|
1781
|
+
},
|
|
1782
|
+
children: entry.name
|
|
1783
|
+
}, entry.path)),
|
|
1784
|
+
!busy && !failed && listing?.truncated === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1785
|
+
className: skin_center_module_css_default.dirBrowserNote,
|
|
1786
|
+
children: t("wallpaperDirBrowseTruncated")
|
|
1787
|
+
})
|
|
1788
|
+
]
|
|
1789
|
+
}),
|
|
1790
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
1791
|
+
className: skin_center_module_css_default.dirBrowserHidden,
|
|
1792
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1793
|
+
type: "checkbox",
|
|
1794
|
+
checked: showHidden,
|
|
1795
|
+
onChange: (event) => {
|
|
1796
|
+
setShowHidden(event.target.checked);
|
|
1797
|
+
}
|
|
1798
|
+
}), t("wallpaperDirBrowseHidden")]
|
|
1799
|
+
}),
|
|
1800
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1801
|
+
className: skin_center_module_css_default.dirBrowserActions,
|
|
1802
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1803
|
+
type: "button",
|
|
1804
|
+
className: skin_center_module_css_default.button,
|
|
1805
|
+
onClick: onClose,
|
|
1806
|
+
children: t("cancel")
|
|
1807
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1808
|
+
type: "button",
|
|
1809
|
+
className: skin_center_module_css_default.button,
|
|
1810
|
+
disabled: listing === null,
|
|
1811
|
+
onClick: () => {
|
|
1812
|
+
if (listing !== null) onPick(listing.path);
|
|
1813
|
+
},
|
|
1814
|
+
children: t("wallpaperDirBrowseChoose")
|
|
1815
|
+
})]
|
|
1816
|
+
})
|
|
1817
|
+
]
|
|
1818
|
+
})
|
|
1819
|
+
});
|
|
1820
|
+
}
|
|
1821
|
+
//#endregion
|
|
1445
1822
|
//#region src/client/SliderControl.tsx
|
|
1446
1823
|
/**
|
|
1447
1824
|
* A drag-smooth range slider that decouples the visible value from the
|
|
@@ -1627,7 +2004,6 @@ window.__ModuleLoader__.load({
|
|
|
1627
2004
|
case "video": return "wallpaperTypeVideo";
|
|
1628
2005
|
case "web": return "wallpaperTypeWeb";
|
|
1629
2006
|
case "scene": return "wallpaperTypeScene";
|
|
1630
|
-
case "image": return "wallpaperTypeImage";
|
|
1631
2007
|
default: return "wallpaperTypeApp";
|
|
1632
2008
|
}
|
|
1633
2009
|
}
|
|
@@ -1674,12 +2050,12 @@ window.__ModuleLoader__.load({
|
|
|
1674
2050
|
const [shownVolume, setShownVolume] = useLiveValue$1(volume);
|
|
1675
2051
|
const [dirInput, setDirInput] = (0, react.useState)("");
|
|
1676
2052
|
const [picking, setPicking] = (0, react.useState)(false);
|
|
2053
|
+
const [browsing, setBrowsing] = (0, react.useState)(false);
|
|
1677
2054
|
const [page, setPage] = (0, react.useState)(1);
|
|
1678
2055
|
const [ratingFilter, setRatingFilter] = (0, react.useState)("g");
|
|
1679
2056
|
const [jumpInput, setJumpInput] = (0, react.useState)("");
|
|
1680
2057
|
const [items, setItems] = (0, react.useState)(null);
|
|
1681
2058
|
const [installDir, setInstallDir] = (0, react.useState)(null);
|
|
1682
|
-
const [systemCount, setSystemCount] = (0, react.useState)(0);
|
|
1683
2059
|
const [loadError, setLoadError] = (0, react.useState)(null);
|
|
1684
2060
|
const [actionError, setActionError] = (0, react.useState)(null);
|
|
1685
2061
|
const [workingId, setWorkingId] = (0, react.useState)(null);
|
|
@@ -1704,7 +2080,6 @@ window.__ModuleLoader__.load({
|
|
|
1704
2080
|
setItems(payload.wallpapers);
|
|
1705
2081
|
setPage(1);
|
|
1706
2082
|
setInstallDir(typeof payload.installDir === "string" ? payload.installDir : null);
|
|
1707
|
-
setSystemCount(typeof payload.systemCount === "number" ? payload.systemCount : 0);
|
|
1708
2083
|
const selected = wallpaper.selection();
|
|
1709
2084
|
wallpaper.sync(resolveSelection(payload.wallpapers, selected) ?? null);
|
|
1710
2085
|
}).catch((error) => {
|
|
@@ -1729,18 +2104,33 @@ window.__ModuleLoader__.load({
|
|
|
1729
2104
|
load();
|
|
1730
2105
|
});
|
|
1731
2106
|
};
|
|
1732
|
-
/**
|
|
2107
|
+
/**
|
|
2108
|
+
* Add one picked folder. A native-capability host opens the OS chooser; a
|
|
2109
|
+
* browse-capability host (the adaptive chooser picks it whenever the web
|
|
2110
|
+
* server binds beyond loopback, e.g. LAN remote access) refuses the pick
|
|
2111
|
+
* and gets the in-app browser over the picker's own listing primitives.
|
|
2112
|
+
* Without either capability the manual input stays the fallback.
|
|
2113
|
+
*/
|
|
1733
2114
|
const browseDir = () => {
|
|
1734
2115
|
const pick = wallpaper.pickDir;
|
|
1735
2116
|
if (pick === void 0) return;
|
|
1736
2117
|
setActionError(null);
|
|
1737
2118
|
setPicking(true);
|
|
1738
|
-
pick().then((
|
|
2119
|
+
pick().then((outcome) => {
|
|
1739
2120
|
if (!mounted.current) return;
|
|
1740
2121
|
setPicking(false);
|
|
1741
|
-
if (
|
|
1742
|
-
|
|
1743
|
-
|
|
2122
|
+
if (outcome.kind === "picked") {
|
|
2123
|
+
if (outcome.path.trim() === "") return;
|
|
2124
|
+
wallpaper.addDir(outcome.path);
|
|
2125
|
+
load();
|
|
2126
|
+
return;
|
|
2127
|
+
}
|
|
2128
|
+
if (outcome.kind === "cancelled") return;
|
|
2129
|
+
if (wallpaper.listDir !== void 0) {
|
|
2130
|
+
setBrowsing(true);
|
|
2131
|
+
return;
|
|
2132
|
+
}
|
|
2133
|
+
setActionError(t("wallpaperDirBrowseFailed"));
|
|
1744
2134
|
}).catch((error) => {
|
|
1745
2135
|
if (!mounted.current) return;
|
|
1746
2136
|
setPicking(false);
|
|
@@ -1778,572 +2168,583 @@ window.__ModuleLoader__.load({
|
|
|
1778
2168
|
const activeSelection = selection;
|
|
1779
2169
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1780
2170
|
className: skin_center_module_css_default.wallpaperSection,
|
|
1781
|
-
children: [
|
|
1782
|
-
className: skin_center_module_css_default.enableRow,
|
|
1783
|
-
children: [
|
|
1784
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1785
|
-
className: skin_center_module_css_default.enableLabel,
|
|
1786
|
-
title: t("wallpaperEnable"),
|
|
1787
|
-
children: t("wallpaperTitle")
|
|
1788
|
-
}),
|
|
1789
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1790
|
-
type: "button",
|
|
1791
|
-
role: "switch",
|
|
1792
|
-
"aria-checked": enabled,
|
|
1793
|
-
"aria-label": t("wallpaperEnable"),
|
|
1794
|
-
className: enabled ? skin_center_module_css_default.switch + " " + skin_center_module_css_default.switchOn : skin_center_module_css_default.switch,
|
|
1795
|
-
onClick: () => {
|
|
1796
|
-
wallpaper.setEnabled(!enabled);
|
|
1797
|
-
},
|
|
1798
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: skin_center_module_css_default.switchThumb })
|
|
1799
|
-
}),
|
|
1800
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1801
|
-
className: skin_center_module_css_default.enableHint,
|
|
1802
|
-
children: t("wallpaperHint")
|
|
1803
|
-
})
|
|
1804
|
-
]
|
|
1805
|
-
}), enabled && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2171
|
+
children: [
|
|
1806
2172
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1807
|
-
className: skin_center_module_css_default.
|
|
1808
|
-
children: [loadError !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1809
|
-
className: skin_center_module_css_default.wallpaperStatusError,
|
|
1810
|
-
children: [
|
|
1811
|
-
t("wallpaperLoadError"),
|
|
1812
|
-
": ",
|
|
1813
|
-
loadError
|
|
1814
|
-
]
|
|
1815
|
-
}) : items === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("loading") }) : installDir !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
1816
|
-
t("wallpaperLibraryFound"),
|
|
1817
|
-
" · ",
|
|
1818
|
-
items.length
|
|
1819
|
-
] }) : systemCount > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
1820
|
-
t("wallpaperLibrarySystem"),
|
|
1821
|
-
" · ",
|
|
1822
|
-
items.length
|
|
1823
|
-
] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
1824
|
-
t("wallpaperLibraryManual"),
|
|
1825
|
-
" · ",
|
|
1826
|
-
items.length
|
|
1827
|
-
] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1828
|
-
type: "button",
|
|
1829
|
-
className: skin_center_module_css_default.button,
|
|
1830
|
-
onClick: load,
|
|
1831
|
-
children: t("wallpaperRefresh")
|
|
1832
|
-
})]
|
|
1833
|
-
}),
|
|
1834
|
-
activeSelection !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1835
|
-
className: skin_center_module_css_default.wallpaperControls,
|
|
2173
|
+
className: skin_center_module_css_default.enableRow,
|
|
1836
2174
|
children: [
|
|
1837
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
1838
|
-
className: skin_center_module_css_default.
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
className: skin_center_module_css_default.themeLabel,
|
|
1842
|
-
children: t("wallpaperMode")
|
|
1843
|
-
}),
|
|
1844
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1845
|
-
type: "button",
|
|
1846
|
-
className: skin_center_module_css_default.themeButton + (mode === "live" ? " " + skin_center_module_css_default.themeButtonActive : ""),
|
|
1847
|
-
onClick: () => {
|
|
1848
|
-
wallpaper.setMode("live");
|
|
1849
|
-
},
|
|
1850
|
-
children: t("wallpaperModeLive")
|
|
1851
|
-
}),
|
|
1852
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1853
|
-
type: "button",
|
|
1854
|
-
className: skin_center_module_css_default.themeButton + (mode === "frame" ? " " + skin_center_module_css_default.themeButtonActive : ""),
|
|
1855
|
-
onClick: () => {
|
|
1856
|
-
wallpaper.setMode("frame");
|
|
1857
|
-
},
|
|
1858
|
-
children: t("wallpaperModeFrame")
|
|
1859
|
-
}),
|
|
1860
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1861
|
-
type: "button",
|
|
1862
|
-
className: skin_center_module_css_default.button + " " + skin_center_module_css_default.buttonGhost,
|
|
1863
|
-
onClick: () => {
|
|
1864
|
-
wallpaper.clearSelection();
|
|
1865
|
-
},
|
|
1866
|
-
children: t("wallpaperClear")
|
|
1867
|
-
})
|
|
1868
|
-
]
|
|
2175
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2176
|
+
className: skin_center_module_css_default.enableLabel,
|
|
2177
|
+
title: t("wallpaperEnable"),
|
|
2178
|
+
children: t("wallpaperTitle")
|
|
1869
2179
|
}),
|
|
1870
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
1871
|
-
|
|
2180
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2181
|
+
type: "button",
|
|
2182
|
+
role: "switch",
|
|
2183
|
+
"aria-checked": enabled,
|
|
2184
|
+
"aria-label": t("wallpaperEnable"),
|
|
2185
|
+
className: enabled ? skin_center_module_css_default.switch + " " + skin_center_module_css_default.switchOn : skin_center_module_css_default.switch,
|
|
2186
|
+
onClick: () => {
|
|
2187
|
+
wallpaper.setEnabled(!enabled);
|
|
2188
|
+
},
|
|
2189
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: skin_center_module_css_default.switchThumb })
|
|
2190
|
+
}),
|
|
2191
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2192
|
+
className: skin_center_module_css_default.enableHint,
|
|
2193
|
+
children: t("wallpaperHint")
|
|
2194
|
+
})
|
|
2195
|
+
]
|
|
2196
|
+
}),
|
|
2197
|
+
enabled && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
2198
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2199
|
+
className: skin_center_module_css_default.wallpaperStatus,
|
|
2200
|
+
children: [loadError !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2201
|
+
className: skin_center_module_css_default.wallpaperStatusError,
|
|
1872
2202
|
children: [
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
2203
|
+
t("wallpaperLoadError"),
|
|
2204
|
+
": ",
|
|
2205
|
+
loadError
|
|
2206
|
+
]
|
|
2207
|
+
}) : items === null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("loading") }) : installDir !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
2208
|
+
t("wallpaperLibraryFound"),
|
|
2209
|
+
" · ",
|
|
2210
|
+
items.length
|
|
2211
|
+
] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", { children: [
|
|
2212
|
+
t("wallpaperLibraryManual"),
|
|
2213
|
+
" · ",
|
|
2214
|
+
items.length
|
|
2215
|
+
] }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2216
|
+
type: "button",
|
|
2217
|
+
className: skin_center_module_css_default.button,
|
|
2218
|
+
onClick: load,
|
|
2219
|
+
children: t("wallpaperRefresh")
|
|
2220
|
+
})]
|
|
2221
|
+
}),
|
|
2222
|
+
activeSelection !== "" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2223
|
+
className: skin_center_module_css_default.wallpaperControls,
|
|
2224
|
+
children: [
|
|
2225
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2226
|
+
className: skin_center_module_css_default.themeRow,
|
|
2227
|
+
children: [
|
|
2228
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2229
|
+
className: skin_center_module_css_default.themeLabel,
|
|
2230
|
+
children: t("wallpaperMode")
|
|
2231
|
+
}),
|
|
2232
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2233
|
+
type: "button",
|
|
2234
|
+
className: skin_center_module_css_default.themeButton + (mode === "live" ? " " + skin_center_module_css_default.themeButtonActive : ""),
|
|
2235
|
+
onClick: () => {
|
|
2236
|
+
wallpaper.setMode("live");
|
|
2237
|
+
},
|
|
2238
|
+
children: t("wallpaperModeLive")
|
|
2239
|
+
}),
|
|
2240
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2241
|
+
type: "button",
|
|
2242
|
+
className: skin_center_module_css_default.themeButton + (mode === "frame" ? " " + skin_center_module_css_default.themeButtonActive : ""),
|
|
2243
|
+
onClick: () => {
|
|
2244
|
+
wallpaper.setMode("frame");
|
|
2245
|
+
},
|
|
2246
|
+
children: t("wallpaperModeFrame")
|
|
2247
|
+
}),
|
|
2248
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2249
|
+
type: "button",
|
|
2250
|
+
className: skin_center_module_css_default.button + " " + skin_center_module_css_default.buttonGhost,
|
|
2251
|
+
onClick: () => {
|
|
2252
|
+
wallpaper.clearSelection();
|
|
2253
|
+
},
|
|
2254
|
+
children: t("wallpaperClear")
|
|
2255
|
+
})
|
|
2256
|
+
]
|
|
2257
|
+
}),
|
|
2258
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2259
|
+
className: skin_center_module_css_default.themeRow,
|
|
2260
|
+
children: [
|
|
2261
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2262
|
+
className: skin_center_module_css_default.themeLabel,
|
|
2263
|
+
children: t("wallpaperFit")
|
|
2264
|
+
}),
|
|
2265
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2266
|
+
type: "button",
|
|
2267
|
+
className: skin_center_module_css_default.themeButton + (fit === "cover" ? " " + skin_center_module_css_default.themeButtonActive : ""),
|
|
2268
|
+
onClick: () => {
|
|
2269
|
+
wallpaper.setFit("cover");
|
|
2270
|
+
},
|
|
2271
|
+
children: t("wallpaperFitCover")
|
|
2272
|
+
}),
|
|
2273
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2274
|
+
type: "button",
|
|
2275
|
+
className: skin_center_module_css_default.themeButton + (fit === "contain" ? " " + skin_center_module_css_default.themeButtonActive : ""),
|
|
2276
|
+
onClick: () => {
|
|
2277
|
+
wallpaper.setFit("contain");
|
|
2278
|
+
},
|
|
2279
|
+
children: t("wallpaperFitContain")
|
|
2280
|
+
}),
|
|
2281
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2282
|
+
type: "button",
|
|
2283
|
+
className: skin_center_module_css_default.themeButton + (fit === "fill" ? " " + skin_center_module_css_default.themeButtonActive : ""),
|
|
2284
|
+
onClick: () => {
|
|
2285
|
+
wallpaper.setFit("fill");
|
|
2286
|
+
},
|
|
2287
|
+
children: t("wallpaperFitFill")
|
|
2288
|
+
})
|
|
2289
|
+
]
|
|
2290
|
+
}),
|
|
2291
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2292
|
+
className: skin_center_module_css_default.backgroundRow,
|
|
2293
|
+
children: [
|
|
2294
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2295
|
+
className: skin_center_module_css_default.backgroundHead,
|
|
2296
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2297
|
+
className: skin_center_module_css_default.backgroundLabel,
|
|
2298
|
+
children: t("wallpaperDim")
|
|
2299
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2300
|
+
className: skin_center_module_css_default.backgroundValue,
|
|
2301
|
+
"aria-hidden": "true",
|
|
2302
|
+
children: [shownDim, "%"]
|
|
2303
|
+
})]
|
|
2304
|
+
}),
|
|
2305
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SliderControl, {
|
|
2306
|
+
className: skin_center_module_css_default.backgroundRange,
|
|
2307
|
+
min: 0,
|
|
2308
|
+
max: 90,
|
|
2309
|
+
step: 5,
|
|
2310
|
+
value: dim,
|
|
2311
|
+
ariaValuetext: shownDim + "%",
|
|
2312
|
+
ariaLabel: t("wallpaperDim"),
|
|
2313
|
+
onChanging: setShownDim,
|
|
2314
|
+
onChange: (value) => {
|
|
2315
|
+
wallpaper.setDim(value);
|
|
2316
|
+
}
|
|
2317
|
+
}),
|
|
2318
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2319
|
+
className: skin_center_module_css_default.backgroundHead,
|
|
2320
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2321
|
+
className: skin_center_module_css_default.backgroundLabel,
|
|
2322
|
+
children: t("wallpaperOpacity")
|
|
2323
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2324
|
+
className: skin_center_module_css_default.backgroundValue,
|
|
2325
|
+
"aria-hidden": "true",
|
|
2326
|
+
children: [shownOpacity, "%"]
|
|
2327
|
+
})]
|
|
2328
|
+
}),
|
|
2329
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SliderControl, {
|
|
2330
|
+
className: skin_center_module_css_default.backgroundRange,
|
|
2331
|
+
min: 0,
|
|
2332
|
+
max: 100,
|
|
2333
|
+
step: 5,
|
|
2334
|
+
value: opacity,
|
|
2335
|
+
ariaValuetext: shownOpacity + "%",
|
|
2336
|
+
ariaLabel: t("wallpaperOpacity"),
|
|
2337
|
+
onChanging: setShownOpacity,
|
|
2338
|
+
onChange: (value) => {
|
|
2339
|
+
wallpaper.setOpacity(value);
|
|
2340
|
+
}
|
|
2341
|
+
}),
|
|
2342
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2343
|
+
className: skin_center_module_css_default.backgroundHead,
|
|
2344
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2345
|
+
className: skin_center_module_css_default.backgroundLabel,
|
|
2346
|
+
children: t("wallpaperBlur")
|
|
2347
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2348
|
+
className: skin_center_module_css_default.backgroundValue,
|
|
2349
|
+
"aria-hidden": "true",
|
|
2350
|
+
children: [shownBlur, "px"]
|
|
2351
|
+
})]
|
|
2352
|
+
}),
|
|
2353
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SliderControl, {
|
|
2354
|
+
className: skin_center_module_css_default.backgroundRange,
|
|
2355
|
+
min: 0,
|
|
2356
|
+
max: 60,
|
|
2357
|
+
step: 1,
|
|
2358
|
+
value: blur,
|
|
2359
|
+
ariaValuetext: shownBlur + "px",
|
|
2360
|
+
ariaLabel: t("wallpaperBlur"),
|
|
2361
|
+
onChanging: setShownBlur,
|
|
2362
|
+
onChange: (value) => {
|
|
2363
|
+
wallpaper.setBlur(value);
|
|
2364
|
+
}
|
|
2365
|
+
})
|
|
2366
|
+
]
|
|
2367
|
+
}),
|
|
2368
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2369
|
+
className: skin_center_module_css_default.enableRow,
|
|
2370
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2371
|
+
className: skin_center_module_css_default.enableLabel,
|
|
2372
|
+
children: t("wallpaperPauseHidden")
|
|
2373
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1886
2374
|
type: "button",
|
|
1887
|
-
|
|
2375
|
+
role: "switch",
|
|
2376
|
+
"aria-checked": pauseOnHidden,
|
|
2377
|
+
"aria-label": t("wallpaperPauseHidden"),
|
|
2378
|
+
className: pauseOnHidden ? skin_center_module_css_default.switch + " " + skin_center_module_css_default.switchOn : skin_center_module_css_default.switch,
|
|
1888
2379
|
onClick: () => {
|
|
1889
|
-
wallpaper.
|
|
2380
|
+
wallpaper.setPauseOnHidden(!pauseOnHidden);
|
|
1890
2381
|
},
|
|
1891
|
-
children:
|
|
1892
|
-
})
|
|
1893
|
-
|
|
2382
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: skin_center_module_css_default.switchThumb })
|
|
2383
|
+
})]
|
|
2384
|
+
}),
|
|
2385
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2386
|
+
className: skin_center_module_css_default.enableRow,
|
|
2387
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2388
|
+
className: skin_center_module_css_default.enableLabel,
|
|
2389
|
+
title: t("wallpaperSoundHint"),
|
|
2390
|
+
children: t("wallpaperSound")
|
|
2391
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1894
2392
|
type: "button",
|
|
1895
|
-
|
|
2393
|
+
role: "switch",
|
|
2394
|
+
"aria-checked": sound,
|
|
2395
|
+
"aria-label": t("wallpaperSound"),
|
|
2396
|
+
className: sound ? skin_center_module_css_default.switch + " " + skin_center_module_css_default.switchOn : skin_center_module_css_default.switch,
|
|
1896
2397
|
onClick: () => {
|
|
1897
|
-
wallpaper.
|
|
2398
|
+
wallpaper.setSound(!sound);
|
|
1898
2399
|
},
|
|
1899
|
-
children:
|
|
1900
|
-
})
|
|
1901
|
-
]
|
|
1902
|
-
}),
|
|
1903
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1904
|
-
className: skin_center_module_css_default.backgroundRow,
|
|
1905
|
-
children: [
|
|
1906
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1907
|
-
className: skin_center_module_css_default.backgroundHead,
|
|
1908
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1909
|
-
className: skin_center_module_css_default.backgroundLabel,
|
|
1910
|
-
children: t("wallpaperDim")
|
|
1911
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1912
|
-
className: skin_center_module_css_default.backgroundValue,
|
|
1913
|
-
"aria-hidden": "true",
|
|
1914
|
-
children: [shownDim, "%"]
|
|
1915
|
-
})]
|
|
1916
|
-
}),
|
|
1917
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SliderControl, {
|
|
1918
|
-
className: skin_center_module_css_default.backgroundRange,
|
|
1919
|
-
min: 0,
|
|
1920
|
-
max: 90,
|
|
1921
|
-
step: 5,
|
|
1922
|
-
value: dim,
|
|
1923
|
-
ariaValuetext: shownDim + "%",
|
|
1924
|
-
ariaLabel: t("wallpaperDim"),
|
|
1925
|
-
onChanging: setShownDim,
|
|
1926
|
-
onChange: (value) => {
|
|
1927
|
-
wallpaper.setDim(value);
|
|
1928
|
-
}
|
|
1929
|
-
}),
|
|
1930
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1931
|
-
className: skin_center_module_css_default.backgroundHead,
|
|
1932
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1933
|
-
className: skin_center_module_css_default.backgroundLabel,
|
|
1934
|
-
children: t("wallpaperOpacity")
|
|
1935
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1936
|
-
className: skin_center_module_css_default.backgroundValue,
|
|
1937
|
-
"aria-hidden": "true",
|
|
1938
|
-
children: [shownOpacity, "%"]
|
|
1939
|
-
})]
|
|
1940
|
-
}),
|
|
1941
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SliderControl, {
|
|
1942
|
-
className: skin_center_module_css_default.backgroundRange,
|
|
1943
|
-
min: 0,
|
|
1944
|
-
max: 100,
|
|
1945
|
-
step: 5,
|
|
1946
|
-
value: opacity,
|
|
1947
|
-
ariaValuetext: shownOpacity + "%",
|
|
1948
|
-
ariaLabel: t("wallpaperOpacity"),
|
|
1949
|
-
onChanging: setShownOpacity,
|
|
1950
|
-
onChange: (value) => {
|
|
1951
|
-
wallpaper.setOpacity(value);
|
|
1952
|
-
}
|
|
1953
|
-
}),
|
|
1954
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1955
|
-
className: skin_center_module_css_default.backgroundHead,
|
|
1956
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1957
|
-
className: skin_center_module_css_default.backgroundLabel,
|
|
1958
|
-
children: t("wallpaperBlur")
|
|
1959
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1960
|
-
className: skin_center_module_css_default.backgroundValue,
|
|
1961
|
-
"aria-hidden": "true",
|
|
1962
|
-
children: [shownBlur, "px"]
|
|
1963
|
-
})]
|
|
1964
|
-
}),
|
|
1965
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SliderControl, {
|
|
1966
|
-
className: skin_center_module_css_default.backgroundRange,
|
|
1967
|
-
min: 0,
|
|
1968
|
-
max: 60,
|
|
1969
|
-
step: 1,
|
|
1970
|
-
value: blur,
|
|
1971
|
-
ariaValuetext: shownBlur + "px",
|
|
1972
|
-
ariaLabel: t("wallpaperBlur"),
|
|
1973
|
-
onChanging: setShownBlur,
|
|
1974
|
-
onChange: (value) => {
|
|
1975
|
-
wallpaper.setBlur(value);
|
|
1976
|
-
}
|
|
1977
|
-
})
|
|
1978
|
-
]
|
|
1979
|
-
}),
|
|
1980
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1981
|
-
className: skin_center_module_css_default.enableRow,
|
|
1982
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1983
|
-
className: skin_center_module_css_default.enableLabel,
|
|
1984
|
-
children: t("wallpaperPauseHidden")
|
|
1985
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1986
|
-
type: "button",
|
|
1987
|
-
role: "switch",
|
|
1988
|
-
"aria-checked": pauseOnHidden,
|
|
1989
|
-
"aria-label": t("wallpaperPauseHidden"),
|
|
1990
|
-
className: pauseOnHidden ? skin_center_module_css_default.switch + " " + skin_center_module_css_default.switchOn : skin_center_module_css_default.switch,
|
|
1991
|
-
onClick: () => {
|
|
1992
|
-
wallpaper.setPauseOnHidden(!pauseOnHidden);
|
|
1993
|
-
},
|
|
1994
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: skin_center_module_css_default.switchThumb })
|
|
1995
|
-
})]
|
|
1996
|
-
}),
|
|
1997
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1998
|
-
className: skin_center_module_css_default.enableRow,
|
|
1999
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2000
|
-
className: skin_center_module_css_default.enableLabel,
|
|
2001
|
-
title: t("wallpaperSoundHint"),
|
|
2002
|
-
children: t("wallpaperSound")
|
|
2003
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2004
|
-
type: "button",
|
|
2005
|
-
role: "switch",
|
|
2006
|
-
"aria-checked": sound,
|
|
2007
|
-
"aria-label": t("wallpaperSound"),
|
|
2008
|
-
className: sound ? skin_center_module_css_default.switch + " " + skin_center_module_css_default.switchOn : skin_center_module_css_default.switch,
|
|
2009
|
-
onClick: () => {
|
|
2010
|
-
wallpaper.setSound(!sound);
|
|
2011
|
-
},
|
|
2012
|
-
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: skin_center_module_css_default.switchThumb })
|
|
2013
|
-
})]
|
|
2014
|
-
}),
|
|
2015
|
-
sound && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2016
|
-
className: skin_center_module_css_default.backgroundRow,
|
|
2017
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2018
|
-
className: skin_center_module_css_default.backgroundHead,
|
|
2019
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2020
|
-
className: skin_center_module_css_default.backgroundLabel,
|
|
2021
|
-
children: t("wallpaperVolume")
|
|
2022
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2023
|
-
className: skin_center_module_css_default.backgroundValue,
|
|
2024
|
-
"aria-hidden": "true",
|
|
2025
|
-
children: [shownVolume, "%"]
|
|
2400
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: skin_center_module_css_default.switchThumb })
|
|
2026
2401
|
})]
|
|
2027
|
-
}),
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
className: skin_center_module_css_default.
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
setDirInput(event.target.value);
|
|
2402
|
+
}),
|
|
2403
|
+
sound && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2404
|
+
className: skin_center_module_css_default.backgroundRow,
|
|
2405
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2406
|
+
className: skin_center_module_css_default.backgroundHead,
|
|
2407
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2408
|
+
className: skin_center_module_css_default.backgroundLabel,
|
|
2409
|
+
children: t("wallpaperVolume")
|
|
2410
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2411
|
+
className: skin_center_module_css_default.backgroundValue,
|
|
2412
|
+
"aria-hidden": "true",
|
|
2413
|
+
children: [shownVolume, "%"]
|
|
2414
|
+
})]
|
|
2415
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SliderControl, {
|
|
2416
|
+
className: skin_center_module_css_default.backgroundRange,
|
|
2417
|
+
min: 0,
|
|
2418
|
+
max: 100,
|
|
2419
|
+
step: 5,
|
|
2420
|
+
value: volume,
|
|
2421
|
+
ariaValuetext: shownVolume + "%",
|
|
2422
|
+
ariaLabel: t("wallpaperVolume"),
|
|
2423
|
+
onChanging: setShownVolume,
|
|
2424
|
+
onChange: (value) => {
|
|
2425
|
+
wallpaper.setVolume(value);
|
|
2426
|
+
}
|
|
2427
|
+
})]
|
|
2428
|
+
})
|
|
2429
|
+
]
|
|
2430
|
+
}),
|
|
2431
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2432
|
+
className: skin_center_module_css_default.wallpaperDirs,
|
|
2433
|
+
children: [
|
|
2434
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2435
|
+
className: skin_center_module_css_default.themeLabel,
|
|
2436
|
+
children: t("wallpaperDirs")
|
|
2437
|
+
}),
|
|
2438
|
+
dirs.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2439
|
+
className: skin_center_module_css_default.backgroundHintMuted,
|
|
2440
|
+
children: t("wallpaperDirsEmpty")
|
|
2441
|
+
}),
|
|
2442
|
+
dirs.map((dir) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2443
|
+
className: skin_center_module_css_default.wallpaperDir,
|
|
2444
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2445
|
+
className: skin_center_module_css_default.wallpaperDirPath,
|
|
2446
|
+
title: dir,
|
|
2447
|
+
children: dir
|
|
2448
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2449
|
+
type: "button",
|
|
2450
|
+
className: skin_center_module_css_default.wallpaperDirRemove,
|
|
2451
|
+
"aria-label": t("wallpaperRemove"),
|
|
2452
|
+
onClick: () => {
|
|
2453
|
+
wallpaper.removeDir(dir);
|
|
2454
|
+
load();
|
|
2081
2455
|
},
|
|
2082
|
-
|
|
2083
|
-
|
|
2456
|
+
children: "×"
|
|
2457
|
+
})]
|
|
2458
|
+
}, dir)),
|
|
2459
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2460
|
+
className: skin_center_module_css_default.wallpaperDirAdd,
|
|
2461
|
+
children: [
|
|
2462
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2463
|
+
className: skin_center_module_css_default.wallpaperDirInput,
|
|
2464
|
+
type: "text",
|
|
2465
|
+
value: dirInput,
|
|
2466
|
+
placeholder: t("wallpaperDirPlaceholder"),
|
|
2467
|
+
onChange: (event) => {
|
|
2468
|
+
setDirInput(event.target.value);
|
|
2469
|
+
},
|
|
2470
|
+
onKeyDown: (event) => {
|
|
2471
|
+
if (event.key === "Enter" && dirInput.trim() !== "") {
|
|
2472
|
+
wallpaper.addDir(dirInput);
|
|
2473
|
+
setDirInput("");
|
|
2474
|
+
load();
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
}),
|
|
2478
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2479
|
+
type: "button",
|
|
2480
|
+
className: skin_center_module_css_default.button,
|
|
2481
|
+
disabled: dirInput.trim() === "",
|
|
2482
|
+
onClick: () => {
|
|
2084
2483
|
wallpaper.addDir(dirInput);
|
|
2085
2484
|
setDirInput("");
|
|
2086
2485
|
load();
|
|
2087
|
-
}
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2486
|
+
},
|
|
2487
|
+
children: t("wallpaperDirAdd")
|
|
2488
|
+
}),
|
|
2489
|
+
wallpaper.pickDir !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2490
|
+
type: "button",
|
|
2491
|
+
className: skin_center_module_css_default.button,
|
|
2492
|
+
disabled: picking,
|
|
2493
|
+
title: t("wallpaperDirBrowseHint"),
|
|
2494
|
+
onClick: browseDir,
|
|
2495
|
+
children: picking ? t("loading") : t("wallpaperDirBrowse")
|
|
2496
|
+
})
|
|
2497
|
+
]
|
|
2498
|
+
}),
|
|
2499
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2500
|
+
className: skin_center_module_css_default.backgroundHintMuted,
|
|
2501
|
+
children: t("wallpaperDirsHint")
|
|
2502
|
+
})
|
|
2503
|
+
]
|
|
2504
|
+
}),
|
|
2505
|
+
actionError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2506
|
+
className: skin_center_module_css_default.error,
|
|
2507
|
+
children: actionError
|
|
2508
|
+
}),
|
|
2509
|
+
writeError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2510
|
+
className: skin_center_module_css_default.error,
|
|
2511
|
+
role: "alert",
|
|
2512
|
+
children: [
|
|
2513
|
+
t("wallpaperSaveFailed"),
|
|
2514
|
+
": ",
|
|
2515
|
+
writeError
|
|
2516
|
+
]
|
|
2517
|
+
}),
|
|
2518
|
+
items !== null && items.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2519
|
+
className: skin_center_module_css_default.wallpaperToolbar,
|
|
2520
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2521
|
+
className: skin_center_module_css_default.ratingFilterGroup,
|
|
2522
|
+
role: "tablist",
|
|
2523
|
+
"aria-label": t("wallpaperTitle"),
|
|
2524
|
+
children: [
|
|
2525
|
+
"g",
|
|
2526
|
+
"pg13",
|
|
2527
|
+
"r18"
|
|
2528
|
+
].map((filter) => {
|
|
2529
|
+
const active = ratingFilter === filter;
|
|
2530
|
+
const key = filter === "g" ? "wallpaperRatingG" : filter === "pg13" ? "wallpaperRatingPg13" : "wallpaperRatingR18";
|
|
2531
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2091
2532
|
type: "button",
|
|
2092
|
-
|
|
2093
|
-
|
|
2533
|
+
role: "tab",
|
|
2534
|
+
"aria-selected": active,
|
|
2535
|
+
className: skin_center_module_css_default.ratingFilterButton + (active ? " " + skin_center_module_css_default.ratingFilterActive : ""),
|
|
2094
2536
|
onClick: () => {
|
|
2095
|
-
|
|
2096
|
-
setDirInput("");
|
|
2097
|
-
load();
|
|
2537
|
+
onSelectRatingFilter(filter);
|
|
2098
2538
|
},
|
|
2099
|
-
children: t(
|
|
2100
|
-
})
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2539
|
+
children: t(key)
|
|
2540
|
+
}, filter);
|
|
2541
|
+
})
|
|
2542
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2543
|
+
className: skin_center_module_css_default.pageTotalInfo,
|
|
2544
|
+
children: t("wallpaperPageTotal", {
|
|
2545
|
+
page: String(currentPage),
|
|
2546
|
+
total: String(totalPages)
|
|
2547
|
+
})
|
|
2548
|
+
})]
|
|
2549
|
+
}),
|
|
2550
|
+
items !== null && pagedItems.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2551
|
+
className: skin_center_module_css_default.wallpaperGrid,
|
|
2552
|
+
children: pagedItems.map((item) => {
|
|
2553
|
+
const isApplied = item.id === activeSelection;
|
|
2554
|
+
const isMounted = item.id === activeId;
|
|
2555
|
+
const busy = workingId === item.id;
|
|
2556
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2557
|
+
className: skin_center_module_css_default.wallpaperCard,
|
|
2558
|
+
children: [
|
|
2559
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2560
|
+
className: skin_center_module_css_default.wallpaperThumbWrap,
|
|
2561
|
+
children: [
|
|
2562
|
+
item.previewUrl !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
2563
|
+
className: skin_center_module_css_default.wallpaperThumb,
|
|
2564
|
+
src: item.previewUrl,
|
|
2565
|
+
alt: "",
|
|
2566
|
+
loading: "lazy"
|
|
2567
|
+
}) : item.videoUrl !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("video", {
|
|
2568
|
+
className: skin_center_module_css_default.wallpaperThumb,
|
|
2569
|
+
src: item.videoUrl,
|
|
2570
|
+
preload: "metadata",
|
|
2571
|
+
muted: true,
|
|
2572
|
+
playsInline: true,
|
|
2573
|
+
"aria-hidden": "true"
|
|
2574
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2575
|
+
className: skin_center_module_css_default.wallpaperThumbEmpty,
|
|
2576
|
+
"aria-hidden": "true"
|
|
2577
|
+
}),
|
|
2578
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2579
|
+
className: skin_center_module_css_default.wallpaperType,
|
|
2580
|
+
children: t(typeKey(item))
|
|
2581
|
+
}),
|
|
2582
|
+
item.rating === "r18" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2583
|
+
className: skin_center_module_css_default.wallpaperRating + " " + skin_center_module_css_default.ratingR18,
|
|
2584
|
+
children: "R18"
|
|
2585
|
+
}) : item.rating === "pg13" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2586
|
+
className: skin_center_module_css_default.wallpaperRating + " " + skin_center_module_css_default.ratingPg13,
|
|
2587
|
+
children: "PG-13"
|
|
2588
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2589
|
+
className: skin_center_module_css_default.wallpaperRating + " " + skin_center_module_css_default.ratingG,
|
|
2590
|
+
children: "G"
|
|
2591
|
+
}),
|
|
2592
|
+
isMounted && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2593
|
+
className: skin_center_module_css_default.badge + " " + (trying ? skin_center_module_css_default.badgeTrying : skin_center_module_css_default.badgeActive),
|
|
2594
|
+
children: trying ? t("tryingOn") : t("active")
|
|
2595
|
+
})
|
|
2596
|
+
]
|
|
2597
|
+
}),
|
|
2598
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2599
|
+
className: skin_center_module_css_default.wallpaperName,
|
|
2600
|
+
title: item.title,
|
|
2601
|
+
children: item.title
|
|
2602
|
+
}),
|
|
2603
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2604
|
+
className: skin_center_module_css_default.wallpaperActions,
|
|
2605
|
+
children: [
|
|
2606
|
+
isMounted && trying ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2607
|
+
type: "button",
|
|
2608
|
+
className: skin_center_module_css_default.button + " " + skin_center_module_css_default.buttonPrimary,
|
|
2609
|
+
onClick: () => {
|
|
2610
|
+
wallpaper.exitTryOn();
|
|
2611
|
+
},
|
|
2612
|
+
children: t("exitTryOn")
|
|
2613
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2614
|
+
type: "button",
|
|
2615
|
+
className: skin_center_module_css_default.button + " " + skin_center_module_css_default.buttonPrimary,
|
|
2616
|
+
disabled: !renderable(item) || isMounted && isApplied || busy,
|
|
2617
|
+
onClick: () => {
|
|
2618
|
+
wallpaper.tryOn(descriptorOf(item));
|
|
2619
|
+
},
|
|
2620
|
+
children: t("tryOn")
|
|
2621
|
+
}),
|
|
2622
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2623
|
+
type: "button",
|
|
2624
|
+
className: skin_center_module_css_default.button,
|
|
2625
|
+
disabled: !renderable(item) || isApplied || busy,
|
|
2626
|
+
onClick: () => {
|
|
2627
|
+
wallpaper.applySelection(descriptorOf(item));
|
|
2628
|
+
},
|
|
2629
|
+
children: isApplied ? t("active") : t("apply")
|
|
2630
|
+
}),
|
|
2631
|
+
item.source === "imported" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [item.updateAvailable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2632
|
+
type: "button",
|
|
2633
|
+
className: skin_center_module_css_default.button,
|
|
2634
|
+
disabled: busy,
|
|
2635
|
+
title: t("wallpaperUpdateAvailable"),
|
|
2636
|
+
onClick: () => {
|
|
2637
|
+
runAction(item.id, "/reimport");
|
|
2638
|
+
},
|
|
2639
|
+
children: busy ? t("loading") : t("wallpaperReimport")
|
|
2640
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2641
|
+
type: "button",
|
|
2642
|
+
className: skin_center_module_css_default.button + " " + skin_center_module_css_default.buttonGhost,
|
|
2643
|
+
disabled: busy,
|
|
2644
|
+
onClick: () => {
|
|
2645
|
+
runAction(item.id, "/remove", () => {
|
|
2646
|
+
if (wallpaper.selection() === item.id) wallpaper.clearSelection();
|
|
2647
|
+
});
|
|
2648
|
+
},
|
|
2649
|
+
children: t("wallpaperRemove")
|
|
2650
|
+
})] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2651
|
+
type: "button",
|
|
2652
|
+
className: skin_center_module_css_default.button,
|
|
2653
|
+
disabled: busy,
|
|
2654
|
+
title: t("wallpaperImportHint"),
|
|
2655
|
+
onClick: () => {
|
|
2656
|
+
runAction(item.id, "/import");
|
|
2657
|
+
},
|
|
2658
|
+
children: busy ? t("loading") : t("wallpaperImport")
|
|
2659
|
+
})
|
|
2660
|
+
]
|
|
2661
|
+
})
|
|
2662
|
+
]
|
|
2663
|
+
}, item.id);
|
|
2114
2664
|
})
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
children: actionError
|
|
2120
|
-
}),
|
|
2121
|
-
writeError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2122
|
-
className: skin_center_module_css_default.error,
|
|
2123
|
-
role: "alert",
|
|
2124
|
-
children: [
|
|
2125
|
-
t("wallpaperSaveFailed"),
|
|
2126
|
-
": ",
|
|
2127
|
-
writeError
|
|
2128
|
-
]
|
|
2129
|
-
}),
|
|
2130
|
-
items !== null && items.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2131
|
-
className: skin_center_module_css_default.wallpaperToolbar,
|
|
2132
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2133
|
-
className: skin_center_module_css_default.ratingFilterGroup,
|
|
2134
|
-
role: "tablist",
|
|
2665
|
+
}),
|
|
2666
|
+
totalPages > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2667
|
+
className: skin_center_module_css_default.wallpaperPagination,
|
|
2668
|
+
role: "navigation",
|
|
2135
2669
|
"aria-label": t("wallpaperTitle"),
|
|
2136
2670
|
children: [
|
|
2137
|
-
"
|
|
2138
|
-
"pg13",
|
|
2139
|
-
"r18"
|
|
2140
|
-
].map((filter) => {
|
|
2141
|
-
const active = ratingFilter === filter;
|
|
2142
|
-
const key = filter === "g" ? "wallpaperRatingG" : filter === "pg13" ? "wallpaperRatingPg13" : "wallpaperRatingR18";
|
|
2143
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2671
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2144
2672
|
type: "button",
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
className: skin_center_module_css_default.ratingFilterButton + (active ? " " + skin_center_module_css_default.ratingFilterActive : ""),
|
|
2673
|
+
className: skin_center_module_css_default.pageButton,
|
|
2674
|
+
disabled: currentPage <= 1,
|
|
2148
2675
|
onClick: () => {
|
|
2149
|
-
|
|
2676
|
+
setPage((p) => Math.max(1, p - 1));
|
|
2150
2677
|
},
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2172
|
-
className: skin_center_module_css_default.wallpaperThumbWrap,
|
|
2173
|
-
children: [
|
|
2174
|
-
item.previewUrl !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
2175
|
-
className: skin_center_module_css_default.wallpaperThumb,
|
|
2176
|
-
src: item.previewUrl,
|
|
2177
|
-
alt: "",
|
|
2178
|
-
loading: "lazy"
|
|
2179
|
-
}) : item.videoUrl !== null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("video", {
|
|
2180
|
-
className: skin_center_module_css_default.wallpaperThumb,
|
|
2181
|
-
src: item.videoUrl,
|
|
2182
|
-
preload: "metadata",
|
|
2183
|
-
muted: true,
|
|
2184
|
-
playsInline: true,
|
|
2185
|
-
"aria-hidden": "true"
|
|
2186
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2187
|
-
className: skin_center_module_css_default.wallpaperThumbEmpty,
|
|
2188
|
-
"aria-hidden": "true"
|
|
2189
|
-
}),
|
|
2190
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2191
|
-
className: skin_center_module_css_default.wallpaperType,
|
|
2192
|
-
children: t(typeKey(item))
|
|
2193
|
-
}),
|
|
2194
|
-
item.rating === "r18" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2195
|
-
className: skin_center_module_css_default.wallpaperRating + " " + skin_center_module_css_default.ratingR18,
|
|
2196
|
-
children: "R18"
|
|
2197
|
-
}) : item.rating === "pg13" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2198
|
-
className: skin_center_module_css_default.wallpaperRating + " " + skin_center_module_css_default.ratingPg13,
|
|
2199
|
-
children: "PG-13"
|
|
2200
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2201
|
-
className: skin_center_module_css_default.wallpaperRating + " " + skin_center_module_css_default.ratingG,
|
|
2202
|
-
children: "G"
|
|
2203
|
-
}),
|
|
2204
|
-
isMounted && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2205
|
-
className: skin_center_module_css_default.badge + " " + (trying ? skin_center_module_css_default.badgeTrying : skin_center_module_css_default.badgeActive),
|
|
2206
|
-
children: trying ? t("tryingOn") : t("active")
|
|
2207
|
-
})
|
|
2208
|
-
]
|
|
2209
|
-
}),
|
|
2210
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
2211
|
-
className: skin_center_module_css_default.wallpaperName,
|
|
2212
|
-
title: item.title,
|
|
2213
|
-
children: item.title
|
|
2214
|
-
}),
|
|
2215
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2216
|
-
className: skin_center_module_css_default.wallpaperActions,
|
|
2217
|
-
children: [
|
|
2218
|
-
isMounted && trying ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2219
|
-
type: "button",
|
|
2220
|
-
className: skin_center_module_css_default.button + " " + skin_center_module_css_default.buttonPrimary,
|
|
2221
|
-
onClick: () => {
|
|
2222
|
-
wallpaper.exitTryOn();
|
|
2223
|
-
},
|
|
2224
|
-
children: t("exitTryOn")
|
|
2225
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2226
|
-
type: "button",
|
|
2227
|
-
className: skin_center_module_css_default.button + " " + skin_center_module_css_default.buttonPrimary,
|
|
2228
|
-
disabled: !renderable(item) || isMounted && isApplied || busy,
|
|
2229
|
-
onClick: () => {
|
|
2230
|
-
wallpaper.tryOn(descriptorOf(item));
|
|
2231
|
-
},
|
|
2232
|
-
children: t("tryOn")
|
|
2233
|
-
}),
|
|
2234
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2235
|
-
type: "button",
|
|
2236
|
-
className: skin_center_module_css_default.button,
|
|
2237
|
-
disabled: !renderable(item) || isApplied || busy,
|
|
2238
|
-
onClick: () => {
|
|
2239
|
-
wallpaper.applySelection(descriptorOf(item));
|
|
2240
|
-
},
|
|
2241
|
-
children: isApplied ? t("active") : t("apply")
|
|
2242
|
-
}),
|
|
2243
|
-
item.source === "imported" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [item.updateAvailable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2244
|
-
type: "button",
|
|
2245
|
-
className: skin_center_module_css_default.button,
|
|
2246
|
-
disabled: busy,
|
|
2247
|
-
title: t("wallpaperUpdateAvailable"),
|
|
2248
|
-
onClick: () => {
|
|
2249
|
-
runAction(item.id, "/reimport");
|
|
2250
|
-
},
|
|
2251
|
-
children: busy ? t("loading") : t("wallpaperReimport")
|
|
2252
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2253
|
-
type: "button",
|
|
2254
|
-
className: skin_center_module_css_default.button + " " + skin_center_module_css_default.buttonGhost,
|
|
2255
|
-
disabled: busy,
|
|
2256
|
-
onClick: () => {
|
|
2257
|
-
runAction(item.id, "/remove", () => {
|
|
2258
|
-
if (wallpaper.selection() === item.id) wallpaper.clearSelection();
|
|
2259
|
-
});
|
|
2260
|
-
},
|
|
2261
|
-
children: t("wallpaperRemove")
|
|
2262
|
-
})] }) : item.source === "system" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, {}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2263
|
-
type: "button",
|
|
2264
|
-
className: skin_center_module_css_default.button,
|
|
2265
|
-
disabled: busy,
|
|
2266
|
-
title: t("wallpaperImportHint"),
|
|
2267
|
-
onClick: () => {
|
|
2268
|
-
runAction(item.id, "/import");
|
|
2269
|
-
},
|
|
2270
|
-
children: busy ? t("loading") : t("wallpaperImport")
|
|
2271
|
-
})
|
|
2272
|
-
]
|
|
2273
|
-
})
|
|
2274
|
-
]
|
|
2275
|
-
}, item.id);
|
|
2276
|
-
})
|
|
2277
|
-
}),
|
|
2278
|
-
totalPages > 1 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2279
|
-
className: skin_center_module_css_default.wallpaperPagination,
|
|
2280
|
-
role: "navigation",
|
|
2281
|
-
"aria-label": t("wallpaperTitle"),
|
|
2282
|
-
children: [
|
|
2283
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2284
|
-
type: "button",
|
|
2285
|
-
className: skin_center_module_css_default.pageButton,
|
|
2286
|
-
disabled: currentPage <= 1,
|
|
2287
|
-
onClick: () => {
|
|
2288
|
-
setPage((p) => Math.max(1, p - 1));
|
|
2289
|
-
},
|
|
2290
|
-
"aria-label": t("wallpaperPagePrev"),
|
|
2291
|
-
children: t("wallpaperPagePrev")
|
|
2292
|
-
}),
|
|
2293
|
-
paginationRange(currentPage, totalPages).map((p, idx) => {
|
|
2294
|
-
if (p === "ellipsis") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2295
|
-
className: skin_center_module_css_default.pageEllipsis,
|
|
2296
|
-
children: "…"
|
|
2297
|
-
}, "ellipsis-" + String(idx));
|
|
2298
|
-
const isActive = p === currentPage;
|
|
2299
|
-
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2678
|
+
"aria-label": t("wallpaperPagePrev"),
|
|
2679
|
+
children: t("wallpaperPagePrev")
|
|
2680
|
+
}),
|
|
2681
|
+
paginationRange(currentPage, totalPages).map((p, idx) => {
|
|
2682
|
+
if (p === "ellipsis") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2683
|
+
className: skin_center_module_css_default.pageEllipsis,
|
|
2684
|
+
children: "…"
|
|
2685
|
+
}, "ellipsis-" + String(idx));
|
|
2686
|
+
const isActive = p === currentPage;
|
|
2687
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2688
|
+
type: "button",
|
|
2689
|
+
className: skin_center_module_css_default.pageButton + (isActive ? " " + skin_center_module_css_default.pageButtonActive : ""),
|
|
2690
|
+
"aria-current": isActive ? "page" : void 0,
|
|
2691
|
+
onClick: () => {
|
|
2692
|
+
setPage(p);
|
|
2693
|
+
},
|
|
2694
|
+
children: p
|
|
2695
|
+
}, p);
|
|
2696
|
+
}),
|
|
2697
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2300
2698
|
type: "button",
|
|
2301
|
-
className: skin_center_module_css_default.pageButton
|
|
2302
|
-
|
|
2699
|
+
className: skin_center_module_css_default.pageButton,
|
|
2700
|
+
disabled: currentPage >= totalPages,
|
|
2303
2701
|
onClick: () => {
|
|
2304
|
-
setPage(p);
|
|
2702
|
+
setPage((p) => Math.min(totalPages, p + 1));
|
|
2305
2703
|
},
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2704
|
+
"aria-label": t("wallpaperPageNext"),
|
|
2705
|
+
children: t("wallpaperPageNext")
|
|
2706
|
+
}),
|
|
2707
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
|
|
2708
|
+
className: skin_center_module_css_default.pageJumpForm,
|
|
2709
|
+
onSubmit: (e) => {
|
|
2710
|
+
e.preventDefault();
|
|
2711
|
+
handleJump();
|
|
2712
|
+
},
|
|
2713
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2714
|
+
type: "text",
|
|
2715
|
+
className: skin_center_module_css_default.pageJumpInput,
|
|
2716
|
+
value: jumpInput,
|
|
2717
|
+
"aria-label": t("wallpaperPageJump"),
|
|
2718
|
+
onChange: (e) => {
|
|
2719
|
+
setJumpInput(e.target.value);
|
|
2720
|
+
}
|
|
2721
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2722
|
+
type: "submit",
|
|
2723
|
+
className: skin_center_module_css_default.pageButton,
|
|
2724
|
+
disabled: jumpInput.trim() === "",
|
|
2725
|
+
children: t("wallpaperPageJump")
|
|
2726
|
+
})]
|
|
2727
|
+
})
|
|
2728
|
+
]
|
|
2729
|
+
}),
|
|
2730
|
+
items !== null && (items.length === 0 || pagedItems.length === 0) && loadError === null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2731
|
+
className: skin_center_module_css_default.backgroundHintMuted,
|
|
2732
|
+
children: t("wallpaperEmpty")
|
|
2733
|
+
})
|
|
2734
|
+
] }),
|
|
2735
|
+
browsing && wallpaper.listDir !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DirBrowserDialog, {
|
|
2736
|
+
t,
|
|
2737
|
+
listDir: wallpaper.listDir,
|
|
2738
|
+
onPick: (path) => {
|
|
2739
|
+
setBrowsing(false);
|
|
2740
|
+
wallpaper.addDir(path);
|
|
2741
|
+
load();
|
|
2742
|
+
},
|
|
2743
|
+
onClose: () => {
|
|
2744
|
+
setBrowsing(false);
|
|
2745
|
+
}
|
|
2345
2746
|
})
|
|
2346
|
-
]
|
|
2747
|
+
]
|
|
2347
2748
|
});
|
|
2348
2749
|
}
|
|
2349
2750
|
//#endregion
|
|
@@ -3609,11 +4010,10 @@ window.__ModuleLoader__.load({
|
|
|
3609
4010
|
backgroundHintInert: "Only applies to skins that paint a backdrop (Blue Fantasy / Whale Song). Applies to the official default automatically once such a skin is active.",
|
|
3610
4011
|
wallpaperTitle: "Wallpaper Engine",
|
|
3611
4012
|
wallpaperEnable: "Enable wallpapers",
|
|
3612
|
-
wallpaperHint: "Use your local Wallpaper Engine library as the GUI backdrop: video, web, and scene wallpapers render live (scene wallpapers need WebGL).",
|
|
4013
|
+
wallpaperHint: "Use your local Wallpaper Engine library as the GUI backdrop: video, web, and scene wallpapers render live (scene wallpapers need WebGL). On macOS there is no Wallpaper Engine library, so the feature starts off — turn it on, then add a folder of videos.",
|
|
3613
4014
|
wallpaperLoadError: "Wallpaper library failed to load",
|
|
3614
4015
|
wallpaperLibraryFound: "Wallpaper Engine library detected",
|
|
3615
|
-
wallpaperLibraryManual: "
|
|
3616
|
-
wallpaperLibrarySystem: "macOS wallpapers detected (aerials and Desktop Pictures)",
|
|
4016
|
+
wallpaperLibraryManual: "Folders you added",
|
|
3617
4017
|
wallpaperRefresh: "Refresh",
|
|
3618
4018
|
wallpaperMode: "Render mode",
|
|
3619
4019
|
wallpaperModeLive: "Live",
|
|
@@ -3635,21 +4035,25 @@ window.__ModuleLoader__.load({
|
|
|
3635
4035
|
wallpaperReimport: "Update",
|
|
3636
4036
|
wallpaperRemove: "Remove",
|
|
3637
4037
|
wallpaperUpdateAvailable: "The workshop original changed since import — update the local copy",
|
|
3638
|
-
wallpaperEmpty: "No wallpapers found.
|
|
4038
|
+
wallpaperEmpty: "No wallpapers found. Add a folder of videos below, or subscribe in the Wallpaper Engine workshop on Windows.",
|
|
3639
4039
|
wallpaperTypeVideo: "Video",
|
|
3640
4040
|
wallpaperTypeWeb: "Web",
|
|
3641
4041
|
wallpaperTypeScene: "Scene (static)",
|
|
3642
4042
|
wallpaperTypeApp: "Unsupported",
|
|
3643
|
-
wallpaperTypeImage: "Image",
|
|
3644
4043
|
wallpaperLoadMore: "Load more",
|
|
3645
4044
|
wallpaperDirs: "Manual folders",
|
|
3646
|
-
wallpaperDirsEmpty: "No
|
|
3647
|
-
wallpaperDirsHint: "
|
|
4045
|
+
wallpaperDirsEmpty: "No folders yet — add one to load its videos.",
|
|
4046
|
+
wallpaperDirsHint: "On macOS: pick a folder of .mp4/.webm videos (or a wallpaper project folder). On Windows, folders extend the auto-detected Wallpaper Engine library.",
|
|
3648
4047
|
wallpaperDirPlaceholder: "/path/to/wallpapers or ~/Movies/wallpapers",
|
|
3649
4048
|
wallpaperDirAdd: "Add",
|
|
3650
4049
|
wallpaperDirBrowse: "Browse…",
|
|
3651
4050
|
wallpaperDirBrowseHint: "Pick a folder with the system file manager (Finder / Explorer)",
|
|
3652
4051
|
wallpaperDirBrowseFailed: "Could not open the system folder picker — type the path manually instead",
|
|
4052
|
+
wallpaperDirBrowseTitle: "Select a folder",
|
|
4053
|
+
wallpaperDirBrowseChoose: "Use this folder",
|
|
4054
|
+
wallpaperDirBrowseEmpty: "No subfolders",
|
|
4055
|
+
wallpaperDirBrowseHidden: "Show hidden folders",
|
|
4056
|
+
wallpaperDirBrowseTruncated: "Too many folders to list; only the beginning is shown.",
|
|
3653
4057
|
wallpaperSaveFailed: "Could not save wallpaper settings.",
|
|
3654
4058
|
wallpaperRatingAll: "All",
|
|
3655
4059
|
wallpaperRatingG: "G",
|
|
@@ -3732,11 +4136,10 @@ window.__ModuleLoader__.load({
|
|
|
3732
4136
|
backgroundHintInert: "仅对带背景图插画的皮肤(蓝色幻想 / 鲸吟)生效;官方默认无背景图,该滑块对这些皮肤自动生效。",
|
|
3733
4137
|
wallpaperTitle: "Wallpaper Engine",
|
|
3734
4138
|
wallpaperEnable: "启用动态壁纸",
|
|
3735
|
-
wallpaperHint: "把本机 Wallpaper Engine 壁纸库用作 GUI 背景:视频、网页与场景壁纸均动态渲染(场景壁纸需要 WebGL)。",
|
|
4139
|
+
wallpaperHint: "把本机 Wallpaper Engine 壁纸库用作 GUI 背景:视频、网页与场景壁纸均动态渲染(场景壁纸需要 WebGL)。macOS 没有 Wallpaper Engine 壁纸库,该功能默认关闭;开启后添加一个存放视频的目录即可。",
|
|
3736
4140
|
wallpaperLoadError: "壁纸库加载失败",
|
|
3737
4141
|
wallpaperLibraryFound: "已检测到 Wallpaper Engine 壁纸库",
|
|
3738
|
-
wallpaperLibraryManual: "
|
|
3739
|
-
wallpaperLibrarySystem: "已检测到 macOS 系统壁纸(航拍与桌面图片)",
|
|
4142
|
+
wallpaperLibraryManual: "来自你添加的目录",
|
|
3740
4143
|
wallpaperRefresh: "刷新",
|
|
3741
4144
|
wallpaperMode: "渲染模式",
|
|
3742
4145
|
wallpaperModeLive: "动态",
|
|
@@ -3758,21 +4161,25 @@ window.__ModuleLoader__.load({
|
|
|
3758
4161
|
wallpaperReimport: "更新",
|
|
3759
4162
|
wallpaperRemove: "移除",
|
|
3760
4163
|
wallpaperUpdateAvailable: "工坊原件在导入后有更新——同步更新本地副本",
|
|
3761
|
-
wallpaperEmpty: "
|
|
4164
|
+
wallpaperEmpty: "未发现壁纸。请在下方添加一个存放视频的目录;Windows 上也可先在 Wallpaper Engine 创意工坊订阅。",
|
|
3762
4165
|
wallpaperTypeVideo: "视频",
|
|
3763
4166
|
wallpaperTypeWeb: "网页",
|
|
3764
4167
|
wallpaperTypeScene: "场景(静态)",
|
|
3765
4168
|
wallpaperTypeApp: "不支持",
|
|
3766
|
-
wallpaperTypeImage: "静态图片",
|
|
3767
4169
|
wallpaperLoadMore: "加载更多",
|
|
3768
4170
|
wallpaperDirs: "手动目录",
|
|
3769
|
-
wallpaperDirsEmpty: "
|
|
3770
|
-
wallpaperDirsHint: "
|
|
4171
|
+
wallpaperDirsEmpty: "还没有目录——添加一个以加载其中的视频。",
|
|
4172
|
+
wallpaperDirsHint: "macOS:选择一个存放 .mp4/.webm 视频的目录(或单个壁纸项目文件夹)。Windows:这里添加的目录会扩展自动检测到的 Wallpaper Engine 壁纸库。",
|
|
3771
4173
|
wallpaperDirPlaceholder: "/path/to/wallpapers 或 ~/Movies/wallpapers",
|
|
3772
4174
|
wallpaperDirAdd: "添加",
|
|
3773
4175
|
wallpaperDirBrowse: "浏览…",
|
|
3774
4176
|
wallpaperDirBrowseHint: "通过系统文件管理器(访达 / 资源管理器)选择文件夹",
|
|
3775
4177
|
wallpaperDirBrowseFailed: "无法打开系统目录选择框——请手动输入路径",
|
|
4178
|
+
wallpaperDirBrowseTitle: "选择文件夹",
|
|
4179
|
+
wallpaperDirBrowseChoose: "使用此目录",
|
|
4180
|
+
wallpaperDirBrowseEmpty: "没有子目录",
|
|
4181
|
+
wallpaperDirBrowseHidden: "显示隐藏目录",
|
|
4182
|
+
wallpaperDirBrowseTruncated: "目录过多,仅显示开头部分。",
|
|
3776
4183
|
wallpaperSaveFailed: "壁纸设置保存失败。",
|
|
3777
4184
|
wallpaperRatingAll: "全部",
|
|
3778
4185
|
wallpaperRatingG: "G",
|
|
@@ -3993,14 +4400,14 @@ window.__ModuleLoader__.load({
|
|
|
3993
4400
|
note: "sidebar new-session action (compat seam shields skins from localized labels)"
|
|
3994
4401
|
},
|
|
3995
4402
|
{
|
|
3996
|
-
selector: "[data-dsh-taskboard-view], [data-dsh-taskboard-board]
|
|
4403
|
+
selector: "[data-dsh-taskboard-view], [data-dsh-taskboard-board]",
|
|
3997
4404
|
attrs: [["data-dsh-plugin", "task-board"]],
|
|
3998
|
-
note: "task-board panel/board
|
|
4405
|
+
note: "task-board panel/board (the sidebar row is shell-owned: its glyph carries data-dsh-panel-entry and the family rule below stamps the row)"
|
|
3999
4406
|
},
|
|
4000
4407
|
{
|
|
4001
|
-
selector: "[data-dsh-ssh-view]
|
|
4408
|
+
selector: "[data-dsh-ssh-view]",
|
|
4002
4409
|
attrs: [["data-dsh-plugin", "ssh"]],
|
|
4003
|
-
note: "ssh panel
|
|
4410
|
+
note: "ssh panel (the sidebar row is shell-owned: its glyph carries data-dsh-panel-entry and the family rule below stamps the row)"
|
|
4004
4411
|
},
|
|
4005
4412
|
{
|
|
4006
4413
|
selector: "[data-gitgraph-chip-anchor], [data-gitgraph-dialog]",
|
|
@@ -4013,11 +4420,57 @@ window.__ModuleLoader__.load({
|
|
|
4013
4420
|
note: "pet global root"
|
|
4014
4421
|
},
|
|
4015
4422
|
{
|
|
4016
|
-
selector: "[
|
|
4423
|
+
selector: "[class*=\"panelRow\"]:has([data-dsh-panel-entry])",
|
|
4017
4424
|
attrs: [["data-dsh-part", "sidebar-entry"]],
|
|
4018
|
-
note: "
|
|
4425
|
+
note: "a plugin-registered sidebar row (sidebar.panellist): the shell owns the row box and stamps no hook of its own, so the row is recognised by its css-module class plus the glyph identity the registering plugin outputs — same compat technique as newSession"
|
|
4019
4426
|
}
|
|
4020
4427
|
];
|
|
4428
|
+
/**
|
|
4429
|
+
* The anchors the rule table keys on, as one attribute filter for the
|
|
4430
|
+
* observer: a glyph or row anchor that is SET on an element already in the
|
|
4431
|
+
* tree must re-anchor its ancestors exactly like a newly inserted one.
|
|
4432
|
+
*/
|
|
4433
|
+
const ANCHOR_ATTRIBUTES = [
|
|
4434
|
+
"data-dsh-panel-entry",
|
|
4435
|
+
"data-dsh-surface",
|
|
4436
|
+
"data-dsh-part",
|
|
4437
|
+
"data-dsh-plugin",
|
|
4438
|
+
"data-slot",
|
|
4439
|
+
"data-chat-flow-kind",
|
|
4440
|
+
"data-conversation-scroll",
|
|
4441
|
+
"data-turn-tail",
|
|
4442
|
+
"data-composer-input",
|
|
4443
|
+
"data-queue-dock",
|
|
4444
|
+
"data-rightbar-col",
|
|
4445
|
+
"data-shell-overlay",
|
|
4446
|
+
"data-dsh-taskboard-view",
|
|
4447
|
+
"data-dsh-taskboard-board",
|
|
4448
|
+
"data-dsh-ssh-view",
|
|
4449
|
+
"data-dsh-pet-root",
|
|
4450
|
+
"data-gitgraph-chip-anchor",
|
|
4451
|
+
"data-gitgraph-dialog",
|
|
4452
|
+
"data-decoration",
|
|
4453
|
+
"data-streaming",
|
|
4454
|
+
"data-side",
|
|
4455
|
+
"data-phase"
|
|
4456
|
+
];
|
|
4457
|
+
/**
|
|
4458
|
+
* Safety bound of the ancestor walk, NOT a depth ceiling.
|
|
4459
|
+
*
|
|
4460
|
+
* The walk's normal terminator is the observed root: every node the observer
|
|
4461
|
+
* can report is inside document.body, so the walk returns there (measured —
|
|
4462
|
+
* with this bound removed the walk still stops at body on every delivery).
|
|
4463
|
+
* This constant only fires for a change inside a subtree that is not connected
|
|
4464
|
+
* to the observed root at all, where there is no ancestor to anchor yet and
|
|
4465
|
+
* the node's own childList delivery stamps it once it IS attached.
|
|
4466
|
+
*
|
|
4467
|
+
* It is deliberately far above any real markup depth (the shell's own sidebar
|
|
4468
|
+
* row is three hops deep) instead of a small number that would silently leave
|
|
4469
|
+
* a deeper row unanchored — which is the very defect this walk exists to fix
|
|
4470
|
+
* (issue #1732). A measured sweep anchors through 23 wrapper levels and misses
|
|
4471
|
+
* from 24, i.e. only past the point where the tree is pathological.
|
|
4472
|
+
*/
|
|
4473
|
+
const ANCESTOR_HOPS = 24;
|
|
4021
4474
|
function createSemanticAdapter(doc) {
|
|
4022
4475
|
const rules = SEMANTIC_RULES_V1.map((rule) => ({
|
|
4023
4476
|
rule,
|
|
@@ -4061,6 +4514,25 @@ window.__ModuleLoader__.load({
|
|
|
4061
4514
|
for (const live of rules) live.matchedInPass = 0;
|
|
4062
4515
|
if (doc.documentElement) applyToTree(doc.documentElement);
|
|
4063
4516
|
};
|
|
4517
|
+
/**
|
|
4518
|
+
* Re-anchor one changed node and its ancestor chain.
|
|
4519
|
+
*
|
|
4520
|
+
* applyToTree only walks DOWNWARD: a rule whose selector matches an ancestor
|
|
4521
|
+
* of the changed node (the panel-list row is recognised by the glyph INSIDE
|
|
4522
|
+
* it) is never re-evaluated when that descendant arrives. React mounts the
|
|
4523
|
+
* row first and the registering plugin's glyph a tick later, so without this
|
|
4524
|
+
* the row keeps no anchor at all until something forces a full pass
|
|
4525
|
+
* (issue #1732). The walk is bounded and stops at the document root.
|
|
4526
|
+
*/
|
|
4527
|
+
const reanchorAround = (node) => {
|
|
4528
|
+
const root = doc.body ?? doc.documentElement;
|
|
4529
|
+
let current = node;
|
|
4530
|
+
for (let hop = 0; current !== null && hop <= ANCESTOR_HOPS; hop += 1) {
|
|
4531
|
+
for (const live of rules) if (live.usable) applyRule(live, current);
|
|
4532
|
+
if (current === root || current === doc.documentElement) return;
|
|
4533
|
+
current = current.parentElement;
|
|
4534
|
+
}
|
|
4535
|
+
};
|
|
4064
4536
|
return {
|
|
4065
4537
|
get running() {
|
|
4066
4538
|
return running;
|
|
@@ -4071,12 +4543,23 @@ window.__ModuleLoader__.load({
|
|
|
4071
4543
|
fullPass();
|
|
4072
4544
|
observer = new doc.defaultView.MutationObserver((records) => {
|
|
4073
4545
|
try {
|
|
4074
|
-
for (const record of records)
|
|
4546
|
+
for (const record of records) {
|
|
4547
|
+
for (const node of Array.from(record.addedNodes)) {
|
|
4548
|
+
if (node.nodeType !== 1) continue;
|
|
4549
|
+
const el = node;
|
|
4550
|
+
applyToTree(el);
|
|
4551
|
+
reanchorAround(el.parentElement ?? el);
|
|
4552
|
+
}
|
|
4553
|
+
const target = record.target.nodeType === 1 ? record.target : null;
|
|
4554
|
+
if (target !== null && record.type === "attributes") reanchorAround(target);
|
|
4555
|
+
}
|
|
4075
4556
|
} catch {}
|
|
4076
4557
|
});
|
|
4077
4558
|
observer.observe(doc.body ?? doc.documentElement, {
|
|
4078
4559
|
childList: true,
|
|
4079
|
-
subtree: true
|
|
4560
|
+
subtree: true,
|
|
4561
|
+
attributes: true,
|
|
4562
|
+
attributeFilter: [...ANCHOR_ATTRIBUTES]
|
|
4080
4563
|
});
|
|
4081
4564
|
},
|
|
4082
4565
|
stop() {
|
|
@@ -4145,11 +4628,24 @@ window.__ModuleLoader__.load({
|
|
|
4145
4628
|
opacity: 1 !important;
|
|
4146
4629
|
}
|
|
4147
4630
|
${scoped("[data-phase=\"active\"] [data-slot=\"conversation.input.dock\"] > *")},
|
|
4148
|
-
${scoped("[data-phase=\"active\"] [data-slot=\"conversation.composer.dock\"] > *")}
|
|
4631
|
+
${scoped("[data-phase=\"active\"] [data-slot=\"conversation.composer.dock\"] > *")},
|
|
4632
|
+
${scoped("[data-phase=\"active\"] [data-slot=\"conversation.composer.dock\"] + *")} {
|
|
4149
4633
|
/* One skin-driven accessory surface for task and statistics docks. Skins
|
|
4150
4634
|
automatically follow their existing semantic theme tokens and may
|
|
4151
4635
|
override the --dsh-composer-accessory-* variables for a stronger
|
|
4152
|
-
signature without coupling this adapter to a specific catalog skin.
|
|
4636
|
+
signature without coupling this adapter to a specific catalog skin.
|
|
4637
|
+
|
|
4638
|
+
The third head covers an accessory that is a SIBLING of the dock slot
|
|
4639
|
+
host rather than a child of it. The slot host is display: contents, so
|
|
4640
|
+
the shell lays its children out as items of the surrounding dock row
|
|
4641
|
+
and the child heads reach them; 0.1.7's context meter was placed beside
|
|
4642
|
+
that host instead of inside it, so it shared the row with the
|
|
4643
|
+
statistics pill but stayed bare (issue #1715) -- one line, two looks.
|
|
4644
|
+
The adjacent-sibling head keeps the row uniform without naming any
|
|
4645
|
+
hashed class or localized label, and covers whatever future accessory
|
|
4646
|
+
the shell seats next to the dock. The plate lands on the accessory's
|
|
4647
|
+
own seat, never on a trigger inside it, so a component's own hover
|
|
4648
|
+
feedback keeps painting on top. */
|
|
4153
4649
|
background: var(--dsh-composer-accessory-bg, var(--dsw-specific-tip, var(--dsw-alias-bg-layer-1))) !important;
|
|
4154
4650
|
color: var(--dsh-composer-accessory-color, var(--dsw-alias-label-tertiary)) !important;
|
|
4155
4651
|
border: var(--dsh-composer-accessory-border, none) !important;
|
|
@@ -4158,7 +4654,11 @@ window.__ModuleLoader__.load({
|
|
|
4158
4654
|
backdrop-filter: blur(var(--dsh-composer-accessory-blur, var(--dsh-input-card-blur, 10px))) !important;
|
|
4159
4655
|
-webkit-backdrop-filter: blur(var(--dsh-composer-accessory-blur, var(--dsh-input-card-blur, 10px))) !important;
|
|
4160
4656
|
}
|
|
4161
|
-
${scoped("[data-phase=\"active\"] [data-slot=\"conversation.composer.dock\"] > *")}
|
|
4657
|
+
${scoped("[data-phase=\"active\"] [data-slot=\"conversation.composer.dock\"] > *")},
|
|
4658
|
+
${scoped("[data-phase=\"active\"] [data-slot=\"conversation.composer.dock\"] + *")} {
|
|
4659
|
+
/* Same metrics for a dock child and for the sibling seat beside the dock
|
|
4660
|
+
slot host, so the two accessories of one row end up the same height:
|
|
4661
|
+
the plate must not make one of them look like a different control. */
|
|
4162
4662
|
margin-top: var(--dsh-composer-accessory-gap, 4px);
|
|
4163
4663
|
margin-bottom: var(--dsh-composer-accessory-gap, 4px);
|
|
4164
4664
|
padding-top: 2px;
|
|
@@ -4770,6 +5270,7 @@ window.__ModuleLoader__.load({
|
|
|
4770
5270
|
let catalog = null;
|
|
4771
5271
|
let diagnostics = [];
|
|
4772
5272
|
const listeners = /* @__PURE__ */ new Set();
|
|
5273
|
+
const teardowns = /* @__PURE__ */ new Set();
|
|
4773
5274
|
const emit = () => {
|
|
4774
5275
|
for (const listener of listeners) listener();
|
|
4775
5276
|
};
|
|
@@ -4784,6 +5285,10 @@ window.__ModuleLoader__.load({
|
|
|
4784
5285
|
const store = {
|
|
4785
5286
|
controller,
|
|
4786
5287
|
adapter,
|
|
5288
|
+
doc,
|
|
5289
|
+
window: doc.defaultView ?? window,
|
|
5290
|
+
apiBase,
|
|
5291
|
+
fetchImpl,
|
|
4787
5292
|
catalog: () => catalog,
|
|
4788
5293
|
diagnostics: () => diagnostics,
|
|
4789
5294
|
refreshCatalog,
|
|
@@ -4802,6 +5307,9 @@ window.__ModuleLoader__.load({
|
|
|
4802
5307
|
adapter.stop();
|
|
4803
5308
|
disposeShellRendering();
|
|
4804
5309
|
controller.shutdown();
|
|
5310
|
+
for (const teardown of [...teardowns]) teardown();
|
|
5311
|
+
teardowns.clear();
|
|
5312
|
+
listeners.clear();
|
|
4805
5313
|
}
|
|
4806
5314
|
};
|
|
4807
5315
|
{
|
|
@@ -4829,20 +5337,133 @@ window.__ModuleLoader__.load({
|
|
|
4829
5337
|
await controller.switchTo(null, null).catch(() => {});
|
|
4830
5338
|
}
|
|
4831
5339
|
})();
|
|
4832
|
-
|
|
4833
|
-
const win = doc.defaultView;
|
|
4834
|
-
const onSkinApplied = (e) => {
|
|
4835
|
-
const detail = e.detail;
|
|
4836
|
-
if (detail && typeof detail.id === "string") refreshCatalog().then(async () => {
|
|
4837
|
-
const entry = store.find(detail.id);
|
|
4838
|
-
if (entry !== null) await controller.switchTo(detail.id, entry);
|
|
4839
|
-
});
|
|
4840
|
-
};
|
|
4841
|
-
win.addEventListener("dsh-skin-applied", onSkinApplied);
|
|
4842
|
-
listeners.add(() => win.removeEventListener("dsh-skin-applied", onSkinApplied));
|
|
4843
|
-
}
|
|
5340
|
+
teardowns.add(trackSkinAppliedEvents(store));
|
|
4844
5341
|
return store;
|
|
4845
5342
|
}
|
|
5343
|
+
/**
|
|
5344
|
+
* Install the skin-applied contract: a window event carrying the applied skin
|
|
5345
|
+
* id makes this page converge on that skin immediately.
|
|
5346
|
+
*
|
|
5347
|
+
* The v1 runtime booted through a full document reload, so any page that
|
|
5348
|
+
* switched the skin could assume the next load picked the new selection up -
|
|
5349
|
+
* the workshop card simply reloaded. The v2 runtime applies in place, so the
|
|
5350
|
+
* page that is already open has to converge on the write, and the workshop
|
|
5351
|
+
* installs the skin files itself. Legacy-bridge pages announce through that
|
|
5352
|
+
* reload event; wiring it here keeps one converge path instead of two.
|
|
5353
|
+
* @param store - the runtime store the event converges.
|
|
5354
|
+
* @param win - the window whose document the runtime owns.
|
|
5355
|
+
* @returns the idempotent listener teardown.
|
|
5356
|
+
*/
|
|
5357
|
+
function trackSkinAppliedEvents(store) {
|
|
5358
|
+
const win = store.window;
|
|
5359
|
+
const onApplied = (event) => {
|
|
5360
|
+
const detail = event.detail;
|
|
5361
|
+
if (detail === null || typeof detail !== "object") return;
|
|
5362
|
+
const id = detail.id;
|
|
5363
|
+
if (typeof id !== "string" || id === "") return;
|
|
5364
|
+
convergeOnSkin(store, id);
|
|
5365
|
+
};
|
|
5366
|
+
win.addEventListener("dsh-skin-applied", onApplied);
|
|
5367
|
+
return () => {
|
|
5368
|
+
win.removeEventListener("dsh-skin-applied", onApplied);
|
|
5369
|
+
};
|
|
5370
|
+
}
|
|
5371
|
+
/** Polling cadence for a selection changed outside this page. */
|
|
5372
|
+
const SELECTION_POLL_MS = 2e3;
|
|
5373
|
+
/** Read the persisted selection; undefined when the endpoint cannot answer. */
|
|
5374
|
+
async function readPersistedSelection(store) {
|
|
5375
|
+
let res;
|
|
5376
|
+
try {
|
|
5377
|
+
res = await store.fetchImpl(`${store.apiBase}/active`);
|
|
5378
|
+
} catch {
|
|
5379
|
+
return;
|
|
5380
|
+
}
|
|
5381
|
+
if (!res.ok) return void 0;
|
|
5382
|
+
let payload;
|
|
5383
|
+
try {
|
|
5384
|
+
payload = await res.json();
|
|
5385
|
+
} catch {
|
|
5386
|
+
return;
|
|
5387
|
+
}
|
|
5388
|
+
if (payload.ok !== true) return void 0;
|
|
5389
|
+
return typeof payload.active === "string" && payload.active !== "" ? payload.active : null;
|
|
5390
|
+
}
|
|
5391
|
+
/**
|
|
5392
|
+
* Converge on the skin the persisted selection names, re-reading the catalog
|
|
5393
|
+
* first when the id is not in the current snapshot (a workshop install lands
|
|
5394
|
+
* after the page read the catalog).
|
|
5395
|
+
*/
|
|
5396
|
+
async function convergeOnSelection(store, id) {
|
|
5397
|
+
if (id !== null && store.find(id) === null) try {
|
|
5398
|
+
await store.refreshCatalog();
|
|
5399
|
+
} catch {}
|
|
5400
|
+
if (id === null) {
|
|
5401
|
+
await store.controller.switchTo(null, null);
|
|
5402
|
+
return;
|
|
5403
|
+
}
|
|
5404
|
+
const entry = store.find(id);
|
|
5405
|
+
if (entry === null) return;
|
|
5406
|
+
await store.controller.switchTo(id, entry);
|
|
5407
|
+
}
|
|
5408
|
+
/** Converge on an announced skin id, re-reading the catalog when it is new. */
|
|
5409
|
+
async function convergeOnSkin(store, id) {
|
|
5410
|
+
await convergeOnSelection(store, id);
|
|
5411
|
+
}
|
|
5412
|
+
/**
|
|
5413
|
+
* Follow the persisted selection while this page is open.
|
|
5414
|
+
*
|
|
5415
|
+
* Applying a skin and saving that choice are one action in the GUI, but the
|
|
5416
|
+
* choice is persisted for the NEXT page load. The conventional route is the
|
|
5417
|
+
* official settings form: it writes the settings row this plugin owns, the
|
|
5418
|
+
* Host applies it, and nothing inside this page is told. That row is seeded
|
|
5419
|
+
* from the skin center's own configuration, so the two are the same choice
|
|
5420
|
+
* written twice, and this page reads it back rather than assuming it owns
|
|
5421
|
+
* every write (issue #1740).
|
|
5422
|
+
*
|
|
5423
|
+
* The poll only reads: it never writes the selection back, it skips a value
|
|
5424
|
+
* already applied, and it stops while the page is hidden.
|
|
5425
|
+
* @param store - the booted runtime store.
|
|
5426
|
+
* @param intervalMs - polling cadence in milliseconds.
|
|
5427
|
+
* @returns the idempotent teardown of the poll.
|
|
5428
|
+
*/
|
|
5429
|
+
function watchPersistedSelection(store, intervalMs = SELECTION_POLL_MS) {
|
|
5430
|
+
let applied;
|
|
5431
|
+
let timer = null;
|
|
5432
|
+
let stopped = false;
|
|
5433
|
+
const tick = async () => {
|
|
5434
|
+
if (stopped) return;
|
|
5435
|
+
const next = await readPersistedSelection(store);
|
|
5436
|
+
if (stopped || next === void 0 || next === applied) return;
|
|
5437
|
+
applied = next;
|
|
5438
|
+
await convergeOnSelection(store, next);
|
|
5439
|
+
};
|
|
5440
|
+
const stopTimer = () => {
|
|
5441
|
+
if (timer !== null) store.window.clearInterval(timer);
|
|
5442
|
+
timer = null;
|
|
5443
|
+
};
|
|
5444
|
+
const startTimer = () => {
|
|
5445
|
+
if (stopped || timer !== null) return;
|
|
5446
|
+
timer = store.window.setInterval(() => {
|
|
5447
|
+
tick();
|
|
5448
|
+
}, intervalMs);
|
|
5449
|
+
};
|
|
5450
|
+
(async () => {
|
|
5451
|
+
applied = await readPersistedSelection(store);
|
|
5452
|
+
if (!stopped) startTimer();
|
|
5453
|
+
})();
|
|
5454
|
+
const onVisibility = () => {
|
|
5455
|
+
if (store.doc.visibilityState === "visible") {
|
|
5456
|
+
startTimer();
|
|
5457
|
+
tick();
|
|
5458
|
+
} else stopTimer();
|
|
5459
|
+
};
|
|
5460
|
+
store.doc.addEventListener("visibilitychange", onVisibility);
|
|
5461
|
+
return () => {
|
|
5462
|
+
stopped = true;
|
|
5463
|
+
stopTimer();
|
|
5464
|
+
store.doc.removeEventListener("visibilitychange", onVisibility);
|
|
5465
|
+
};
|
|
5466
|
+
}
|
|
4846
5467
|
//#endregion
|
|
4847
5468
|
//#region src/client/preview-coordinator.ts
|
|
4848
5469
|
var PreviewCoordinator = class {
|
|
@@ -5106,6 +5727,13 @@ window.__ModuleLoader__.load({
|
|
|
5106
5727
|
if (!this.config.applied) throw new Error("custom theme activation was not persisted");
|
|
5107
5728
|
}
|
|
5108
5729
|
async deactivate() {
|
|
5730
|
+
if (!this.config.applied) {
|
|
5731
|
+
this.previewingValue = false;
|
|
5732
|
+
this.suspended = false;
|
|
5733
|
+
this.syncDom();
|
|
5734
|
+
this.publish();
|
|
5735
|
+
return;
|
|
5736
|
+
}
|
|
5109
5737
|
this.config = {
|
|
5110
5738
|
...this.config,
|
|
5111
5739
|
applied: false
|
|
@@ -5114,6 +5742,7 @@ window.__ModuleLoader__.load({
|
|
|
5114
5742
|
this.suspended = false;
|
|
5115
5743
|
this.syncDom();
|
|
5116
5744
|
this.publish();
|
|
5745
|
+
if (this.scope.getSnapshot().status === "unavailable") return;
|
|
5117
5746
|
await this.queueWrite("applied", false);
|
|
5118
5747
|
if (this.config.applied) throw new Error("custom theme deactivation was not persisted");
|
|
5119
5748
|
}
|
|
@@ -5171,6 +5800,19 @@ window.__ModuleLoader__.load({
|
|
|
5171
5800
|
};
|
|
5172
5801
|
this.publish();
|
|
5173
5802
|
}
|
|
5803
|
+
/**
|
|
5804
|
+
* Identity of the settings section a refused write addressed, for the error a
|
|
5805
|
+
* user sees. It carries the Host's own status and revision — which entry
|
|
5806
|
+
* answered, whether it can be written, and at which revision — and never any
|
|
5807
|
+
* stored configuration value, so a refused save can be diagnosed from the
|
|
5808
|
+
* message alone without exposing what the user configured.
|
|
5809
|
+
* @returns a short diagnostic fragment naming the section and its state.
|
|
5810
|
+
*/
|
|
5811
|
+
sectionState() {
|
|
5812
|
+
const snapshot = this.scope.getSnapshot();
|
|
5813
|
+
const revision = snapshot.revision === void 0 ? "none" : String(snapshot.revision);
|
|
5814
|
+
return `${SKIN_CUSTOM_THEME_NS}: status ${snapshot.status}, writable ${String(snapshot.writable)}, mode ${snapshot.mode}, revision ${revision}`;
|
|
5815
|
+
}
|
|
5174
5816
|
queueWrite(field, value) {
|
|
5175
5817
|
this.pendingWrites += 1;
|
|
5176
5818
|
const pending = new Promise((resolve, reject) => {
|
|
@@ -5192,7 +5834,7 @@ window.__ModuleLoader__.load({
|
|
|
5192
5834
|
const write = this.writeQueue.shift();
|
|
5193
5835
|
if (write === void 0) break;
|
|
5194
5836
|
try {
|
|
5195
|
-
if (!await this.scope.set(write.field, write.value)) throw new Error(
|
|
5837
|
+
if (!await this.scope.set(write.field, write.value)) throw new Error(`the Host did not accept the custom theme setting (${this.sectionState()})`);
|
|
5196
5838
|
} catch (error) {
|
|
5197
5839
|
settled.push({
|
|
5198
5840
|
write,
|
|
@@ -5270,6 +5912,59 @@ window.__ModuleLoader__.load({
|
|
|
5270
5912
|
};
|
|
5271
5913
|
}
|
|
5272
5914
|
//#endregion
|
|
5915
|
+
//#region src/client/settings-entry-id.ts
|
|
5916
|
+
/** Profile entry id the family aggregate's generated row carries. */
|
|
5917
|
+
const AGGREGATE_ENTRY_ID = "web-ui-skin-center";
|
|
5918
|
+
/**
|
|
5919
|
+
* This package's own declared plugin name, which is also the entry id its
|
|
5920
|
+
* standalone bundle patch row carries (cordis.patch.yml) and the `name` the
|
|
5921
|
+
* plugin exports (src/index.ts). It is the one safe guess when the describe
|
|
5922
|
+
* mirror cannot answer, because it names the row this package itself installs
|
|
5923
|
+
* rather than a row that belongs to a different package.
|
|
5924
|
+
*/
|
|
5925
|
+
const OWN_ENTRY_ID = "ui-skin-center";
|
|
5926
|
+
/**
|
|
5927
|
+
* Profile entry ids this package's rows can be served under: its own standalone
|
|
5928
|
+
* row, the family aggregate's generated row, and the legacy background
|
|
5929
|
+
* namespace a profile that named the row after it still serves. The served
|
|
5930
|
+
* mirror decides which one is real; this list only ranks the candidates.
|
|
5931
|
+
*/
|
|
5932
|
+
const SKIN_CENTER_ENTRY_IDS = [
|
|
5933
|
+
OWN_ENTRY_ID,
|
|
5934
|
+
AGGREGATE_ENTRY_ID,
|
|
5935
|
+
"skin-background"
|
|
5936
|
+
];
|
|
5937
|
+
/**
|
|
5938
|
+
* The entry id of the first skin-center row the Host says it serves, or `null`
|
|
5939
|
+
* when the mirror cannot say. `null` means "unknown", never "none": the mirror
|
|
5940
|
+
* is asynchronous, so an unreadable snapshot is a pre-boot window, not proof
|
|
5941
|
+
* that this package is absent.
|
|
5942
|
+
* @param forms - the shared configuration forms service.
|
|
5943
|
+
* @param candidates - entry ids to look for, in preference order.
|
|
5944
|
+
* @returns the served entry id, or `null` when the mirror cannot answer.
|
|
5945
|
+
*/
|
|
5946
|
+
function servedEntryId(forms, candidates = SKIN_CENTER_ENTRY_IDS) {
|
|
5947
|
+
let served;
|
|
5948
|
+
try {
|
|
5949
|
+
served = forms.describe().getSnapshot().view?.namespaces.map((view) => view.ns);
|
|
5950
|
+
} catch {
|
|
5951
|
+
served = void 0;
|
|
5952
|
+
}
|
|
5953
|
+
if (served === void 0) return null;
|
|
5954
|
+
return candidates.find((id) => served.includes(id)) ?? null;
|
|
5955
|
+
}
|
|
5956
|
+
/**
|
|
5957
|
+
* The entry id to bind, which is this package's OWN row whenever the mirror
|
|
5958
|
+
* cannot name a skin-center row. Addressing another package's row is the #17
|
|
5959
|
+
* failure; addressing this one's own row is safe because an entry that is not
|
|
5960
|
+
* served reports itself `unavailable` instead of silently accepting a write.
|
|
5961
|
+
* @param forms - the shared configuration forms service.
|
|
5962
|
+
* @returns the entry id whose form the plugin should address.
|
|
5963
|
+
*/
|
|
5964
|
+
function boundEntryId(forms) {
|
|
5965
|
+
return servedEntryId(forms) ?? OWN_ENTRY_ID;
|
|
5966
|
+
}
|
|
5967
|
+
//#endregion
|
|
5273
5968
|
//#region src/client/telemetry.ts
|
|
5274
5969
|
const VISITOR_KEY = "dsh-web-ui-telemetry-visitor";
|
|
5275
5970
|
const DAY_KEY_PREFIX = "dsh-web-ui-telemetry-day:";
|
|
@@ -5277,7 +5972,7 @@ window.__ModuleLoader__.load({
|
|
|
5277
5972
|
/** The building package's version, when the bundle carries it. */
|
|
5278
5973
|
function bakedVersion() {
|
|
5279
5974
|
try {
|
|
5280
|
-
return "0.4.
|
|
5975
|
+
return "0.4.4";
|
|
5281
5976
|
} catch {
|
|
5282
5977
|
return;
|
|
5283
5978
|
}
|
|
@@ -5344,30 +6039,6 @@ window.__ModuleLoader__.load({
|
|
|
5344
6039
|
/** Locale namespace owned by this plugin. */
|
|
5345
6040
|
const NS = "skinCenter";
|
|
5346
6041
|
/**
|
|
5347
|
-
* Profile entry id the family aggregate's generated row carries.
|
|
5348
|
-
*/
|
|
5349
|
-
const AGGREGATE_ENTRY_ID = "web-ui-skin-center";
|
|
5350
|
-
/**
|
|
5351
|
-
* Profile entry ids this package's two patch rows carry: the aggregate's
|
|
5352
|
-
* generated row and the standalone bundle patch's row (`ui-skin-center`), plus
|
|
5353
|
-
* the legacy background namespace as the last resort.
|
|
5354
|
-
*/
|
|
5355
|
-
const SKIN_CENTER_ENTRY_IDS = [
|
|
5356
|
-
AGGREGATE_ENTRY_ID,
|
|
5357
|
-
"ui-skin-center",
|
|
5358
|
-
SKIN_BACKGROUND_NS
|
|
5359
|
-
];
|
|
5360
|
-
function servedEntryId(forms) {
|
|
5361
|
-
let served;
|
|
5362
|
-
try {
|
|
5363
|
-
served = forms.describe().getSnapshot().view?.namespaces.map((view) => view.ns);
|
|
5364
|
-
} catch {
|
|
5365
|
-
served = void 0;
|
|
5366
|
-
}
|
|
5367
|
-
if (served === void 0) return AGGREGATE_ENTRY_ID;
|
|
5368
|
-
return SKIN_CENTER_ENTRY_IDS.find((id) => served.includes(id)) ?? "skin-background";
|
|
5369
|
-
}
|
|
5370
|
-
/**
|
|
5371
6042
|
* The configuration form of this plugin's own profile entry.
|
|
5372
6043
|
*
|
|
5373
6044
|
* `ctx.configForms` addresses one form per profile entry id and carries no
|
|
@@ -5375,9 +6046,15 @@ window.__ModuleLoader__.load({
|
|
|
5375
6046
|
* namespace-to-entry mapping the settings group owns: `skin-background` is
|
|
5376
6047
|
* the namespace this package has always owned, and the bridge resolves it to
|
|
5377
6048
|
* whichever entry id the profile gave this row. A deployment without the
|
|
5378
|
-
* group serves no such mapping
|
|
5379
|
-
*
|
|
5380
|
-
* a
|
|
6049
|
+
* group serves no such mapping, so the shared forms service is asked which row
|
|
6050
|
+
* it actually serves. Guessing ANOTHER package's entry id is what produced
|
|
6051
|
+
* dsh-skins#17: a standalone install (`ui-skin-center`) bound the aggregate's row
|
|
6052
|
+
* (`web-ui-skin-center`) whenever the describe mirror had not landed yet, and
|
|
6053
|
+
* every `settings.mutate` then addressed an entry the Host does not serve, so
|
|
6054
|
+
* applying and restoring a skin both failed with a rejected write and a
|
|
6055
|
+
* rolled-back switch. The mirror is the only authority on the entry id, so the
|
|
6056
|
+
* one guess that survives an unreadable mirror is this package's OWN row: an
|
|
6057
|
+
* entry that is not served reports itself unavailable, which each feature
|
|
5381
6058
|
* already handles by keeping its defaults and reporting a failed save.
|
|
5382
6059
|
* @param ctx - client root context.
|
|
5383
6060
|
* @returns the entry form carrying every preference family.
|
|
@@ -5385,16 +6062,40 @@ window.__ModuleLoader__.load({
|
|
|
5385
6062
|
function bindConfigForm(ctx) {
|
|
5386
6063
|
const binder = ctx.get("webUiSettings");
|
|
5387
6064
|
if (binder !== void 0 && typeof binder.bind === "function") return binder.bind({ namespace: SKIN_BACKGROUND_NS });
|
|
5388
|
-
return ctx.configForms.get(
|
|
6065
|
+
return ctx.configForms.get(boundEntryId(ctx.configForms));
|
|
5389
6066
|
}
|
|
5390
|
-
/**
|
|
6067
|
+
/**
|
|
6068
|
+
* Required services: slots + locale (plugin card), theme (preview toggle),
|
|
6069
|
+
* configForms (settings sections), and the Remote faces the wallpaper feature
|
|
6070
|
+
* calls.
|
|
6071
|
+
*
|
|
6072
|
+
* `remote.directoryPicker` is named explicitly because a generated Remote
|
|
6073
|
+
* namespace is its own Cordis service (`remote.<namespace>`), NOT a property
|
|
6074
|
+
* of `remote`: `ctx.remote.x` is only readable while `remote.x` is injected,
|
|
6075
|
+
* otherwise cordis' context proxy throws
|
|
6076
|
+
* `cannot get property "remote.x" without inject`. `connection` owns the
|
|
6077
|
+
* carrier the namespace service rides on, so it is required first.
|
|
6078
|
+
*
|
|
6079
|
+
* The skin center is mounted inside the dsh-web-all aggregate, whose client
|
|
6080
|
+
* children bring their own nested fiber; naming the namespace here parks this
|
|
6081
|
+
* plugin until the picker namespace is really mounted, which is what makes
|
|
6082
|
+
* `pickDir` available on a Host that serves it (and keeps the browse button
|
|
6083
|
+
* off a deployment that does not).
|
|
6084
|
+
*
|
|
6085
|
+
* Both composed backends register the same namespace, so the namespace's
|
|
6086
|
+
* presence says nothing about which interaction the Host serves: a native
|
|
6087
|
+
* backend opens the OS chooser, a browse backend refuses `pick` and serves
|
|
6088
|
+
* listing primitives instead. The panel decides on the answer, not on
|
|
6089
|
+
* presence (see WallpaperPanel's folder browser).
|
|
6090
|
+
*/
|
|
5391
6091
|
const inject = [
|
|
5392
6092
|
"slots",
|
|
5393
6093
|
"locale",
|
|
5394
6094
|
"theme",
|
|
5395
6095
|
"configForms",
|
|
5396
6096
|
"connection",
|
|
5397
|
-
"remote"
|
|
6097
|
+
"remote",
|
|
6098
|
+
"remote.directoryPicker"
|
|
5398
6099
|
];
|
|
5399
6100
|
/** Self-report item for the install heartbeat. */
|
|
5400
6101
|
const SELF_ITEM = [{ name: "@linxin666/dsh-client-ui-skin-center" }];
|
|
@@ -5512,6 +6213,7 @@ window.__ModuleLoader__.load({
|
|
|
5512
6213
|
installBootRestore(wallpaper);
|
|
5513
6214
|
const runtime = bootSkinRuntime({ suppressBackgroundMedia: () => wallpaper.enabled() && wallpaper.isDisplaying() });
|
|
5514
6215
|
ctx.effect(() => () => runtime.shutdown(), "ui-skin-center: runtime shutdown");
|
|
6216
|
+
ctx.effect(() => watchPersistedSelection(runtime), "ui-skin-center: follow the persisted selection");
|
|
5515
6217
|
ctx.effect(() => wallpaper.subscribe(() => {
|
|
5516
6218
|
runtime.controller.refresh();
|
|
5517
6219
|
}), "ui-skin-center: wallpaper priority refresh");
|
|
@@ -5558,8 +6260,9 @@ window.__ModuleLoader__.load({
|
|
|
5558
6260
|
dirs: () => wallpaper.dirs(),
|
|
5559
6261
|
addDir: (dir) => wallpaper.addDir(dir),
|
|
5560
6262
|
removeDir: (dir) => wallpaper.removeDir(dir),
|
|
5561
|
-
pickDir: async () =>
|
|
5562
|
-
|
|
6263
|
+
pickDir: async () => mapDirPickResult(await ctx.remote.directoryPicker.pick()),
|
|
6264
|
+
listDir: async (path) => {
|
|
6265
|
+
const result = await ctx.remote.directoryPicker.list(path);
|
|
5563
6266
|
if (!result.ok) throw new Error(result.error.message);
|
|
5564
6267
|
return result.value;
|
|
5565
6268
|
},
|