twskin 0.5.4 → 0.5.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.md +9 -0
- package/dist/commands.d.ts +5 -0
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +42 -1
- package/dist/commands.js.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +4 -0
- package/dist/context.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +21 -1
- package/dist/main.js.map +1 -1
- package/dist/semver.d.ts +8 -0
- package/dist/semver.d.ts.map +1 -0
- package/dist/semver.js +35 -0
- package/dist/semver.js.map +1 -0
- package/dist/system.d.ts.map +1 -1
- package/dist/system.js +5 -1
- package/dist/system.js.map +1 -1
- package/dist/theme-catalog.d.ts +78 -0
- package/dist/theme-catalog.d.ts.map +1 -0
- package/dist/theme-catalog.js +324 -0
- package/dist/theme-catalog.js.map +1 -0
- package/dist/theme-download.d.ts +9 -1
- package/dist/theme-download.d.ts.map +1 -1
- package/dist/theme-download.js +36 -23
- package/dist/theme-download.js.map +1 -1
- package/dist/themes.d.ts.map +1 -1
- package/dist/themes.js +24 -2
- package/dist/themes.js.map +1 -1
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/theme-distribution.md +19 -1
- package/package.json +1 -1
- package/runtime/injector.mjs +133 -1
- package/runtime/manifest.json +7 -7
- package/runtime/skin.js +231 -22
- package/runtime/styles/manager.css +81 -19
package/runtime/skin.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
const TOKEN_MAP = self.DREAM_SKIN_TOKEN_MAP;
|
|
8
8
|
const COMPONENT_MAP = self.DREAM_SKIN_COMPONENT_MAP;
|
|
9
9
|
const CATALOG = __CATALOG__;
|
|
10
|
+
let themeSyncState = __THEME_SYNC_STATE__;
|
|
10
11
|
const DEFAULT_THEME = __DEFAULT_THEME__;
|
|
11
12
|
const VERSION = __VERSION__;
|
|
12
13
|
const BASE_CSS = __BASE_CSS__;
|
|
@@ -664,25 +665,39 @@ ${selector} {
|
|
|
664
665
|
const configToggle = document.createElement("button");
|
|
665
666
|
configToggle.className = "ds-config-toggle";
|
|
666
667
|
configToggle.type = "button";
|
|
667
|
-
configToggle.textContent = "
|
|
668
|
+
configToggle.textContent = "当前主题";
|
|
668
669
|
configToggle.title = "查看当前主题配置";
|
|
670
|
+
const globalToggle = document.createElement("button");
|
|
671
|
+
globalToggle.className = "ds-global-toggle";
|
|
672
|
+
globalToggle.type = "button";
|
|
673
|
+
globalToggle.setAttribute("aria-label", "主题库设置");
|
|
674
|
+
globalToggle.setAttribute("aria-pressed", "false");
|
|
675
|
+
globalToggle.title = "主题库设置";
|
|
676
|
+
globalToggle.innerHTML = '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 15.5a3.5 3.5 0 1 0 0-7 3.5 3.5 0 0 0 0 7Z"/><path d="m19.4 15 .1 1.7-2.1 2.1-1.7-.1-1.2 1-.4 1.6h-3l-.4-1.6-1.2-1-1.7.1-2.1-2.1.1-1.7-1-1.2L3 13.4v-3l1.6-.4 1-1.2-.1-1.7 2.1-2.1 1.7.1 1.2-1 .4-1.6h3l.4 1.6 1.2 1 1.7-.1 2.1 2.1-.1 1.7 1 1.2 1.6.4v3l-1.6.4-1 1.2Z"/></svg>';
|
|
677
|
+
const syncButton = document.createElement("button");
|
|
678
|
+
syncButton.className = "ds-sync";
|
|
679
|
+
syncButton.type = "button";
|
|
680
|
+
syncButton.textContent = "检查更新";
|
|
681
|
+
syncButton.title = "检查官方主题更新";
|
|
669
682
|
const reloadButton = document.createElement("button");
|
|
670
|
-
reloadButton.className = "ds-reload";
|
|
683
|
+
reloadButton.className = "ds-local-reload";
|
|
671
684
|
reloadButton.type = "button";
|
|
672
|
-
reloadButton.
|
|
685
|
+
reloadButton.textContent = "重新扫描本地主题";
|
|
673
686
|
reloadButton.title = "重新加载主题目录(新主题热加载)";
|
|
674
687
|
const closeButton = document.createElement("button");
|
|
675
688
|
closeButton.className = "ds-close";
|
|
676
689
|
closeButton.type = "button";
|
|
677
690
|
closeButton.textContent = "×";
|
|
678
691
|
closeButton.title = "关闭 (Esc)";
|
|
679
|
-
headerActions.append(
|
|
692
|
+
headerActions.append(configToggle, globalToggle, closeButton);
|
|
680
693
|
header.append(headerText, headerActions);
|
|
681
694
|
const panelBody = document.createElement("div");
|
|
682
695
|
panelBody.className = "ds-body";
|
|
683
696
|
|
|
684
697
|
const galleryView = document.createElement("div");
|
|
685
698
|
galleryView.className = "ds-gallery-view";
|
|
699
|
+
const syncBanner = document.createElement("div");
|
|
700
|
+
syncBanner.className = "ds-sync-banner";
|
|
686
701
|
const configView = document.createElement("div");
|
|
687
702
|
configView.className = "ds-config-view";
|
|
688
703
|
const configHead = document.createElement("div");
|
|
@@ -709,6 +724,58 @@ ${selector} {
|
|
|
709
724
|
blurSwitch.setAttribute("role", "switch");
|
|
710
725
|
blurSwitch.setAttribute("aria-label", "面板模糊");
|
|
711
726
|
blurRow.append(blurCopy, blurSwitch);
|
|
727
|
+
const globalView = document.createElement("div");
|
|
728
|
+
globalView.className = "ds-global-view";
|
|
729
|
+
const globalHead = document.createElement("div");
|
|
730
|
+
globalHead.className = "ds-global-head";
|
|
731
|
+
const globalTitle = document.createElement("div");
|
|
732
|
+
globalTitle.className = "ds-global-title";
|
|
733
|
+
globalTitle.textContent = "主题库设置";
|
|
734
|
+
const globalDesc = document.createElement("div");
|
|
735
|
+
globalDesc.className = "ds-global-desc";
|
|
736
|
+
globalDesc.textContent = "管理所有主题共用的更新与本地目录";
|
|
737
|
+
globalHead.append(globalTitle, globalDesc);
|
|
738
|
+
const checkRow = document.createElement("div");
|
|
739
|
+
checkRow.className = "ds-setting-row";
|
|
740
|
+
const checkCopy = document.createElement("div");
|
|
741
|
+
checkCopy.className = "ds-setting-copy";
|
|
742
|
+
const checkLabel = document.createElement("div");
|
|
743
|
+
checkLabel.className = "ds-setting-label";
|
|
744
|
+
checkLabel.textContent = "检查官方主题更新";
|
|
745
|
+
const checkDesc = document.createElement("div");
|
|
746
|
+
checkDesc.className = "ds-setting-desc";
|
|
747
|
+
checkDesc.setAttribute("aria-live", "polite");
|
|
748
|
+
checkCopy.append(checkLabel, checkDesc);
|
|
749
|
+
checkRow.append(checkCopy, syncButton);
|
|
750
|
+
const updateRow = document.createElement("div");
|
|
751
|
+
updateRow.className = "ds-setting-row";
|
|
752
|
+
const updateCopy = document.createElement("div");
|
|
753
|
+
updateCopy.className = "ds-setting-copy";
|
|
754
|
+
const updateLabel = document.createElement("div");
|
|
755
|
+
updateLabel.className = "ds-setting-label";
|
|
756
|
+
updateLabel.textContent = "自动更新官方主题";
|
|
757
|
+
const updateDesc = document.createElement("div");
|
|
758
|
+
updateDesc.className = "ds-setting-desc";
|
|
759
|
+
updateDesc.textContent = "发现兼容的新主题或更新时自动下载安装";
|
|
760
|
+
updateCopy.append(updateLabel, updateDesc);
|
|
761
|
+
const updateSwitch = document.createElement("button");
|
|
762
|
+
updateSwitch.className = "ds-switch";
|
|
763
|
+
updateSwitch.type = "button";
|
|
764
|
+
updateSwitch.setAttribute("role", "switch");
|
|
765
|
+
updateSwitch.setAttribute("aria-label", "自动更新官方主题");
|
|
766
|
+
updateRow.append(updateCopy, updateSwitch);
|
|
767
|
+
const localThemesRow = document.createElement("div");
|
|
768
|
+
localThemesRow.className = "ds-setting-row ds-local-themes-row";
|
|
769
|
+
const localThemesCopy = document.createElement("div");
|
|
770
|
+
localThemesCopy.className = "ds-setting-copy";
|
|
771
|
+
const localThemesLabel = document.createElement("div");
|
|
772
|
+
localThemesLabel.className = "ds-setting-label";
|
|
773
|
+
localThemesLabel.textContent = "本地主题";
|
|
774
|
+
const localThemesDesc = document.createElement("div");
|
|
775
|
+
localThemesDesc.className = "ds-setting-desc";
|
|
776
|
+
localThemesDesc.textContent = "新增或修改 themes/ 目录后重新读取";
|
|
777
|
+
localThemesCopy.append(localThemesLabel, localThemesDesc);
|
|
778
|
+
localThemesRow.append(localThemesCopy, reloadButton);
|
|
712
779
|
const brightnessRow = document.createElement("div");
|
|
713
780
|
brightnessRow.className = "ds-setting-row ds-setting-row-slider";
|
|
714
781
|
const brightnessHead = document.createElement("div");
|
|
@@ -769,8 +836,21 @@ ${selector} {
|
|
|
769
836
|
resetActions.className = "ds-reset-actions";
|
|
770
837
|
resetActions.append(resetCancel, resetConfig);
|
|
771
838
|
configHead.append(configName, copyConfig);
|
|
839
|
+
const restoreRow = document.createElement("div");
|
|
840
|
+
restoreRow.className = "ds-setting-row ds-restore-row";
|
|
841
|
+
const restoreCopy = document.createElement("div");
|
|
842
|
+
restoreCopy.className = "ds-setting-copy";
|
|
843
|
+
const restoreLabel = document.createElement("div");
|
|
844
|
+
restoreLabel.className = "ds-setting-label";
|
|
845
|
+
restoreLabel.textContent = "恢复原生外观";
|
|
846
|
+
const restoreDesc = document.createElement("div");
|
|
847
|
+
restoreDesc.className = "ds-setting-desc";
|
|
848
|
+
restoreDesc.textContent = "停止应用主题化,并恢复 TRAE 默认界面";
|
|
849
|
+
restoreCopy.append(restoreLabel, restoreDesc);
|
|
850
|
+
restoreRow.append(restoreCopy, resetActions);
|
|
772
851
|
configView.append(configHead, brightnessRow, mainOverlayRow, blurRow, configCode);
|
|
773
|
-
|
|
852
|
+
globalView.append(globalHead, syncBanner, checkRow, updateRow, localThemesRow, restoreRow);
|
|
853
|
+
panelBody.append(galleryView, configView, globalView);
|
|
774
854
|
|
|
775
855
|
// 分类 Tab(固定在头部下,不随列表滚动;选择持久化)
|
|
776
856
|
const CATEGORY_LS = `${LS_PREFIX}category`;
|
|
@@ -811,25 +891,31 @@ ${selector} {
|
|
|
811
891
|
const footerText = document.createElement("span");
|
|
812
892
|
footerText.className = "ds-footer-text";
|
|
813
893
|
footerText.textContent = `TRAE Work Skin v${VERSION}`;
|
|
814
|
-
footer.append(footerText
|
|
894
|
+
footer.append(footerText);
|
|
815
895
|
panel.append(header, tabsBar, panelBody, footer);
|
|
816
896
|
|
|
817
|
-
let
|
|
818
|
-
const
|
|
819
|
-
|
|
820
|
-
//
|
|
821
|
-
tabsBar.style.display =
|
|
822
|
-
galleryView.style.display =
|
|
823
|
-
configView.style.display =
|
|
824
|
-
|
|
825
|
-
configToggle.
|
|
897
|
+
let activeView = "gallery";
|
|
898
|
+
const showView = (view) => {
|
|
899
|
+
activeView = view;
|
|
900
|
+
// 详情页只呈现对应所有权范围的设置,画廊则恢复主题库操作与分类列表。
|
|
901
|
+
tabsBar.style.display = view === "gallery" ? "" : "none";
|
|
902
|
+
galleryView.style.display = view === "gallery" ? "" : "none";
|
|
903
|
+
configView.style.display = view === "theme" ? "block" : "none";
|
|
904
|
+
globalView.style.display = view === "global" ? "block" : "none";
|
|
905
|
+
configToggle.textContent = view === "gallery" ? "当前主题" : "主题";
|
|
906
|
+
configToggle.title = view === "gallery" ? "查看当前主题配置" : "返回主题列表";
|
|
907
|
+
globalToggle.classList.toggle("ds-global-toggle-active", view === "global");
|
|
908
|
+
globalToggle.setAttribute("aria-pressed", String(view === "global"));
|
|
909
|
+
globalToggle.title = view === "global" ? "返回主题列表" : "主题库设置";
|
|
910
|
+
renderThemeSyncState();
|
|
826
911
|
};
|
|
912
|
+
const showConfig = (open) => showView(open ? "theme" : "gallery");
|
|
827
913
|
renderCurrentConfig = (theme = findTheme(
|
|
828
914
|
(() => { try { return localStorage.getItem(LS_KEY); } catch { return null; } })(),
|
|
829
915
|
)) => {
|
|
830
916
|
if (!theme) return;
|
|
831
917
|
const config = publicThemeConfig(theme);
|
|
832
|
-
configName.textContent = `${theme.name} · ${theme.id}`;
|
|
918
|
+
configName.textContent = `${theme.name} · ${theme.id} · v${theme.version || "0.0.0"}`;
|
|
833
919
|
currentLabel.textContent = `当前:${theme.name}`;
|
|
834
920
|
configCode.textContent = JSON.stringify(config, null, 2);
|
|
835
921
|
};
|
|
@@ -840,6 +926,94 @@ ${selector} {
|
|
|
840
926
|
blurSwitch.setAttribute("aria-checked", String(Boolean(enabled)));
|
|
841
927
|
blurSwitch.title = enabled ? "关闭毛玻璃效果" : "启用毛玻璃效果";
|
|
842
928
|
};
|
|
929
|
+
const requestThemeSync = (action, autoUpdate) => {
|
|
930
|
+
const id = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
931
|
+
window.__TRAE_DREAM_SKIN_SYNC_REQUEST__ = { id, action, ...(typeof autoUpdate === "boolean" ? { autoUpdate } : {}) };
|
|
932
|
+
themeSyncState = {
|
|
933
|
+
...themeSyncState,
|
|
934
|
+
phase: action === "check" ? "checking" : action === "auto-update" ? "idle" : "downloading",
|
|
935
|
+
message: action === "check" ? "正在检查主题更新" : "正在同步官方主题",
|
|
936
|
+
};
|
|
937
|
+
renderThemeSyncState();
|
|
938
|
+
};
|
|
939
|
+
const syncSummary = () => {
|
|
940
|
+
if (themeSyncState.phase === "checking") return "正在检查主题更新…";
|
|
941
|
+
if (themeSyncState.phase === "downloading" || themeSyncState.phase === "installing") return themeSyncState.message || "正在同步官方主题…";
|
|
942
|
+
if (themeSyncState.phase === "update-available") return `发现 ${themeSyncState.newThemes || 0} 个新主题、${themeSyncState.updatedThemes || 0} 个更新`;
|
|
943
|
+
if (themeSyncState.phase === "success") return themeSyncState.message || "主题同步完成";
|
|
944
|
+
if (themeSyncState.phase === "error") return themeSyncState.message || "主题更新失败";
|
|
945
|
+
return "";
|
|
946
|
+
};
|
|
947
|
+
const SUCCESS_FEEDBACK_DURATION_MS = 4000;
|
|
948
|
+
let successFeedbackTimer = null;
|
|
949
|
+
const hasRecentSyncSuccess = () => {
|
|
950
|
+
if (themeSyncState.phase !== "success") return false;
|
|
951
|
+
const completedAt = Date.parse(themeSyncState.lastSuccessfulSyncAt || "");
|
|
952
|
+
return Number.isFinite(completedAt) && Date.now() - completedAt < SUCCESS_FEEDBACK_DURATION_MS;
|
|
953
|
+
};
|
|
954
|
+
const renderThemeSyncState = () => {
|
|
955
|
+
const phase = themeSyncState.phase || "idle";
|
|
956
|
+
const summary = syncSummary();
|
|
957
|
+
const updateCount = Number(themeSyncState.updateCount || 0);
|
|
958
|
+
const showSuccessFeedback = hasRecentSyncSuccess();
|
|
959
|
+
syncBanner.replaceChildren();
|
|
960
|
+
syncBanner.className = `ds-sync-banner ds-sync-${phase}`;
|
|
961
|
+
if (!summary || phase === "fresh" || phase === "idle" || (phase === "success" && !showSuccessFeedback) || activeView !== "global") {
|
|
962
|
+
syncBanner.style.display = "none";
|
|
963
|
+
} else {
|
|
964
|
+
syncBanner.style.display = "flex";
|
|
965
|
+
const text = document.createElement("span");
|
|
966
|
+
text.className = "ds-sync-copy";
|
|
967
|
+
text.textContent = summary;
|
|
968
|
+
syncBanner.append(text);
|
|
969
|
+
if (phase === "update-available") {
|
|
970
|
+
const install = document.createElement("button");
|
|
971
|
+
install.type = "button";
|
|
972
|
+
install.className = "ds-sync-action";
|
|
973
|
+
install.textContent = "立即更新";
|
|
974
|
+
install.addEventListener("click", (event) => {
|
|
975
|
+
event.stopPropagation();
|
|
976
|
+
requestThemeSync("sync");
|
|
977
|
+
});
|
|
978
|
+
syncBanner.append(install);
|
|
979
|
+
} else if (phase === "error") {
|
|
980
|
+
const retry = document.createElement("button");
|
|
981
|
+
retry.type = "button";
|
|
982
|
+
retry.className = "ds-sync-action";
|
|
983
|
+
retry.textContent = "重试";
|
|
984
|
+
retry.addEventListener("click", (event) => {
|
|
985
|
+
event.stopPropagation();
|
|
986
|
+
requestThemeSync("check");
|
|
987
|
+
});
|
|
988
|
+
syncBanner.append(retry);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
syncButton.classList.toggle("ds-sync-pending", updateCount > 0);
|
|
992
|
+
syncButton.disabled = phase === "checking" || phase === "downloading" || phase === "installing";
|
|
993
|
+
syncButton.title = summary || "检查官方主题更新";
|
|
994
|
+
globalToggle.classList.toggle("ds-global-pending", updateCount > 0);
|
|
995
|
+
let checkStatusCopy = "尚未检查";
|
|
996
|
+
if (phase === "checking") checkStatusCopy = "正在检查更新…";
|
|
997
|
+
else if (phase === "update-available") checkStatusCopy = `发现 ${updateCount} 项更新`;
|
|
998
|
+
else if (phase === "success" && showSuccessFeedback) checkStatusCopy = "已完成同步";
|
|
999
|
+
else if (phase === "error") checkStatusCopy = "检查失败,可重试";
|
|
1000
|
+
else if (themeSyncState.lastCheckedAt) checkStatusCopy = `上次检查:${new Date(themeSyncState.lastCheckedAt).toLocaleString()}`;
|
|
1001
|
+
checkDesc.textContent = checkStatusCopy;
|
|
1002
|
+
if (successFeedbackTimer) {
|
|
1003
|
+
clearTimeout(successFeedbackTimer);
|
|
1004
|
+
successFeedbackTimer = null;
|
|
1005
|
+
}
|
|
1006
|
+
if (showSuccessFeedback) {
|
|
1007
|
+
const remaining = SUCCESS_FEEDBACK_DURATION_MS - (Date.now() - Date.parse(themeSyncState.lastSuccessfulSyncAt));
|
|
1008
|
+
successFeedbackTimer = setTimeout(() => {
|
|
1009
|
+
successFeedbackTimer = null;
|
|
1010
|
+
renderThemeSyncState();
|
|
1011
|
+
}, Math.max(0, remaining));
|
|
1012
|
+
}
|
|
1013
|
+
updateSwitch.setAttribute("aria-checked", String(Boolean(themeSyncState.autoUpdateThemes)));
|
|
1014
|
+
updateSwitch.title = themeSyncState.autoUpdateThemes ? "关闭官方主题自动更新" : "开启官方主题自动更新";
|
|
1015
|
+
footerText.textContent = `TRAE Work Skin v${VERSION}`;
|
|
1016
|
+
};
|
|
843
1017
|
renderBrightnessControl = (theme = findTheme(
|
|
844
1018
|
(() => { try { return localStorage.getItem(LS_KEY); } catch { return null; } })(),
|
|
845
1019
|
), value = theme ? backgroundBrightness(theme) : 1) => {
|
|
@@ -866,8 +1040,24 @@ ${selector} {
|
|
|
866
1040
|
};
|
|
867
1041
|
configToggle.addEventListener("click", (event) => {
|
|
868
1042
|
event.stopPropagation();
|
|
869
|
-
|
|
870
|
-
|
|
1043
|
+
if (activeView === "gallery") {
|
|
1044
|
+
renderCurrentConfig();
|
|
1045
|
+
showView("theme");
|
|
1046
|
+
} else {
|
|
1047
|
+
showView("gallery");
|
|
1048
|
+
}
|
|
1049
|
+
});
|
|
1050
|
+
globalToggle.addEventListener("click", (event) => {
|
|
1051
|
+
event.stopPropagation();
|
|
1052
|
+
showView(activeView === "global" ? "gallery" : "global");
|
|
1053
|
+
});
|
|
1054
|
+
syncButton.addEventListener("click", (event) => {
|
|
1055
|
+
event.stopPropagation();
|
|
1056
|
+
requestThemeSync("check");
|
|
1057
|
+
});
|
|
1058
|
+
updateSwitch.addEventListener("click", (event) => {
|
|
1059
|
+
event.stopPropagation();
|
|
1060
|
+
requestThemeSync("auto-update", !themeSyncState.autoUpdateThemes);
|
|
871
1061
|
});
|
|
872
1062
|
copyConfig.addEventListener("click", async (event) => {
|
|
873
1063
|
event.stopPropagation();
|
|
@@ -976,7 +1166,7 @@ ${selector} {
|
|
|
976
1166
|
card.dataset.category = theme.category || "其他";
|
|
977
1167
|
const badge = document.createElement("span");
|
|
978
1168
|
badge.className = "ds-badge";
|
|
979
|
-
badge.textContent = "
|
|
1169
|
+
badge.textContent = "当前主题";
|
|
980
1170
|
const preview = document.createElement("div");
|
|
981
1171
|
preview.className = "ds-preview";
|
|
982
1172
|
preview.style.backgroundImage = `url("${theme.art}")`;
|
|
@@ -986,6 +1176,10 @@ ${selector} {
|
|
|
986
1176
|
name.className = "ds-name";
|
|
987
1177
|
name.textContent = theme.name;
|
|
988
1178
|
meta.appendChild(name);
|
|
1179
|
+
const version = document.createElement("span");
|
|
1180
|
+
version.className = "ds-version";
|
|
1181
|
+
version.textContent = `v${theme.version || "0.0.0"}`;
|
|
1182
|
+
meta.appendChild(version);
|
|
989
1183
|
if (theme.desc) {
|
|
990
1184
|
const desc = document.createElement("span");
|
|
991
1185
|
desc.className = "ds-desc";
|
|
@@ -998,6 +1192,7 @@ ${selector} {
|
|
|
998
1192
|
galleryView.appendChild(card);
|
|
999
1193
|
}
|
|
1000
1194
|
applyCategoryFilter();
|
|
1195
|
+
renderThemeSyncState();
|
|
1001
1196
|
|
|
1002
1197
|
const button = document.createElement("button");
|
|
1003
1198
|
button.id = BUTTON_ID;
|
|
@@ -1025,12 +1220,26 @@ ${selector} {
|
|
|
1025
1220
|
});
|
|
1026
1221
|
reloadButton.addEventListener("click", (e) => {
|
|
1027
1222
|
e.stopPropagation();
|
|
1028
|
-
if (reloadButton.classList.contains("ds-reload-loading")) return;
|
|
1029
|
-
reloadButton.classList.add("ds-reload-loading");
|
|
1223
|
+
if (reloadButton.classList.contains("ds-local-reload-loading")) return;
|
|
1224
|
+
reloadButton.classList.add("ds-local-reload-loading");
|
|
1225
|
+
const original = reloadButton.textContent;
|
|
1226
|
+
reloadButton.textContent = "正在扫描…";
|
|
1030
1227
|
// 守护进程轮询到这个标记后会重建主题目录、重新注入并重新打开面板
|
|
1031
1228
|
window.__TRAE_DREAM_SKIN_RELOAD_REQUEST__ = Date.now();
|
|
1032
|
-
setTimeout(() =>
|
|
1229
|
+
setTimeout(() => {
|
|
1230
|
+
reloadButton.classList.remove("ds-local-reload-loading");
|
|
1231
|
+
reloadButton.textContent = original;
|
|
1232
|
+
}, 6000);
|
|
1033
1233
|
});
|
|
1234
|
+
window.__TRAE_DREAM_SKIN_SYNC_LISTENER__ && window.removeEventListener("trae-dream-skin-sync-state", window.__TRAE_DREAM_SKIN_SYNC_LISTENER__);
|
|
1235
|
+
window.__TRAE_DREAM_SKIN_SYNC_LISTENER__ = () => {
|
|
1236
|
+
const next = window.__TRAE_DREAM_SKIN_SYNC_STATE__;
|
|
1237
|
+
if (next && typeof next === "object") {
|
|
1238
|
+
themeSyncState = next;
|
|
1239
|
+
renderThemeSyncState();
|
|
1240
|
+
}
|
|
1241
|
+
};
|
|
1242
|
+
window.addEventListener("trae-dream-skin-sync-state", window.__TRAE_DREAM_SKIN_SYNC_LISTENER__);
|
|
1034
1243
|
document.addEventListener("click", (e) => {
|
|
1035
1244
|
if (isPanelOpen() && !panel.contains(e.target)) setPanelOpen(false);
|
|
1036
1245
|
});
|
|
@@ -60,27 +60,48 @@ body.trae-skin-v2 #trae-dream-skin-button {
|
|
|
60
60
|
#trae-dream-skin-panel .ds-header-actions { flex: 0 0 auto; display: flex; align-items: center; gap: 5px; }
|
|
61
61
|
#trae-dream-skin-panel .ds-config-toggle,
|
|
62
62
|
#trae-dream-skin-panel .ds-close,
|
|
63
|
-
#trae-dream-skin-panel .ds-reload
|
|
63
|
+
#trae-dream-skin-panel .ds-local-reload,
|
|
64
|
+
#trae-dream-skin-panel .ds-global-toggle {
|
|
64
65
|
border: 1px solid rgba(255, 255, 255, 0.18); border-radius: 6px;
|
|
65
66
|
color: inherit; background: rgba(255, 255, 255, 0.08);
|
|
66
67
|
font: inherit; cursor: pointer;
|
|
67
68
|
}
|
|
68
69
|
#trae-dream-skin-panel .ds-config-toggle { padding: 3px 7px; }
|
|
70
|
+
#trae-dream-skin-panel .ds-sync {
|
|
71
|
+
position: relative; flex: 0 0 auto; padding: 4px 8px; border-radius: 6px;
|
|
72
|
+
border: 1px solid rgba(124, 156, 255, 0.52); color: inherit;
|
|
73
|
+
background: rgba(124, 156, 255, 0.13); font: inherit; cursor: pointer;
|
|
74
|
+
}
|
|
75
|
+
#trae-dream-skin-panel .ds-sync.ds-sync-pending::after {
|
|
76
|
+
content: ""; position: absolute; top: -3px; right: -3px;
|
|
77
|
+
width: 6px; height: 6px; border-radius: 50%; background: #ff8d98;
|
|
78
|
+
border: 1px solid rgba(20, 22, 34, 0.9);
|
|
79
|
+
}
|
|
69
80
|
#trae-dream-skin-panel .ds-close,
|
|
70
|
-
#trae-dream-skin-panel .ds-reload
|
|
81
|
+
#trae-dream-skin-panel .ds-local-reload,
|
|
82
|
+
#trae-dream-skin-panel .ds-global-toggle {
|
|
71
83
|
box-sizing: border-box; width: 23px; height: 23px; padding: 0;
|
|
72
84
|
display: flex; align-items: center; justify-content: center;
|
|
73
85
|
font-size: 14px; line-height: 1;
|
|
74
86
|
}
|
|
87
|
+
#trae-dream-skin-panel .ds-local-reload {
|
|
88
|
+
width: auto; height: auto; padding: 4px 7px; font-size: 10px; line-height: 1.2;
|
|
89
|
+
}
|
|
90
|
+
#trae-dream-skin-panel .ds-global-toggle { position: relative; padding: 0; }
|
|
91
|
+
#trae-dream-skin-panel .ds-global-toggle svg { width: 14px; height: 14px; fill: none; stroke: currentColor; stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; }
|
|
92
|
+
#trae-dream-skin-panel .ds-global-toggle.ds-global-toggle-active { background: rgba(124, 156, 255, 0.18); border-color: rgba(124, 156, 255, 0.56); }
|
|
93
|
+
#trae-dream-skin-panel .ds-global-toggle.ds-global-toggle-active:hover { background: rgba(124, 156, 255, 0.24); }
|
|
94
|
+
#trae-dream-skin-panel .ds-global-toggle.ds-global-pending::after {
|
|
95
|
+
content: ""; position: absolute; top: -3px; right: -3px; width: 6px; height: 6px;
|
|
96
|
+
border-radius: 50%; background: #ff8d98; border: 1px solid rgba(20, 22, 34, 0.9);
|
|
97
|
+
}
|
|
75
98
|
#trae-dream-skin-panel .ds-config-toggle:hover,
|
|
99
|
+
#trae-dream-skin-panel .ds-sync:hover:not(:disabled),
|
|
76
100
|
#trae-dream-skin-panel .ds-close:hover,
|
|
77
|
-
#trae-dream-skin-panel .ds-reload:hover
|
|
78
|
-
|
|
79
|
-
#trae-dream-skin-panel .ds-
|
|
80
|
-
#trae-dream-skin-panel .ds-reload.ds-reload-loading {
|
|
81
|
-
#trae-dream-skin-panel .ds-reload.ds-reload-loading .ds-reload-icon {
|
|
82
|
-
animation: trae-skin-spin 0.8s linear infinite;
|
|
83
|
-
}
|
|
101
|
+
#trae-dream-skin-panel .ds-local-reload:hover,
|
|
102
|
+
#trae-dream-skin-panel .ds-global-toggle:hover { background: rgba(255, 255, 255, 0.14); }
|
|
103
|
+
#trae-dream-skin-panel .ds-sync:disabled { opacity: 0.55; cursor: wait; }
|
|
104
|
+
#trae-dream-skin-panel .ds-local-reload.ds-local-reload-loading { opacity: 0.55; cursor: wait; }
|
|
84
105
|
#trae-dream-skin-panel .ds-body {
|
|
85
106
|
flex: 1 1 auto; overflow-y: auto; padding: 12px;
|
|
86
107
|
scrollbar-width: thin; scrollbar-color: rgba(255, 255, 255, 0.22) transparent;
|
|
@@ -92,8 +113,25 @@ body.trae-skin-v2 #trae-dream-skin-button {
|
|
|
92
113
|
#trae-dream-skin-panel .ds-gallery-view {
|
|
93
114
|
display: grid; grid-template-columns: 1fr; gap: 4px;
|
|
94
115
|
}
|
|
116
|
+
#trae-dream-skin-panel .ds-sync-banner {
|
|
117
|
+
display: none; align-items: center; justify-content: space-between; gap: 8px;
|
|
118
|
+
margin: 0 0 9px; padding: 7px 8px; border-radius: 8px;
|
|
119
|
+
border: 1px solid rgba(124, 156, 255, 0.38); background: rgba(124, 156, 255, 0.1);
|
|
120
|
+
font-size: 10px;
|
|
121
|
+
}
|
|
122
|
+
#trae-dream-skin-panel .ds-sync-banner.ds-sync-error {
|
|
123
|
+
border-color: rgba(255, 102, 117, 0.58); background: rgba(255, 82, 99, 0.1);
|
|
124
|
+
}
|
|
125
|
+
#trae-dream-skin-panel .ds-sync-copy { min-width: 0; flex: 1 1 auto; }
|
|
126
|
+
#trae-dream-skin-panel .ds-sync-action {
|
|
127
|
+
flex: 0 0 auto; padding: 3px 7px; border-radius: 5px;
|
|
128
|
+
border: 1px solid rgba(255, 255, 255, 0.2); color: inherit;
|
|
129
|
+
background: rgba(255, 255, 255, 0.1); font: inherit; cursor: pointer;
|
|
130
|
+
}
|
|
131
|
+
#trae-dream-skin-panel .ds-sync-action:hover { background: rgba(255, 255, 255, 0.18); }
|
|
95
132
|
#trae-dream-skin-panel .ds-gallery-view,
|
|
96
|
-
#trae-dream-skin-panel .ds-config-view
|
|
133
|
+
#trae-dream-skin-panel .ds-config-view,
|
|
134
|
+
#trae-dream-skin-panel .ds-global-view { animation: trae-skin-fade-in 0.15s ease; }
|
|
97
135
|
/* 分类 Tab */
|
|
98
136
|
#trae-dream-skin-panel .ds-tabs {
|
|
99
137
|
flex: 0 0 auto; display: flex; gap: 4px;
|
|
@@ -131,7 +169,8 @@ body.trae-skin-v2 #trae-dream-skin-panel .ds-tab.ds-tab-active {
|
|
|
131
169
|
min-width: 0; opacity: 0.5;
|
|
132
170
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
133
171
|
}
|
|
134
|
-
#trae-dream-skin-panel .ds-config-view
|
|
172
|
+
#trae-dream-skin-panel .ds-config-view,
|
|
173
|
+
#trae-dream-skin-panel .ds-global-view { display: none; }
|
|
135
174
|
#trae-dream-skin-panel .ds-config-head {
|
|
136
175
|
display: flex; align-items: center; justify-content: space-between;
|
|
137
176
|
gap: 8px; margin: 2px 2px 8px;
|
|
@@ -140,6 +179,9 @@ body.trae-skin-v2 #trae-dream-skin-panel .ds-tab.ds-tab-active {
|
|
|
140
179
|
min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
141
180
|
font-weight: 600;
|
|
142
181
|
}
|
|
182
|
+
#trae-dream-skin-panel .ds-global-head { margin: 2px 2px 8px; }
|
|
183
|
+
#trae-dream-skin-panel .ds-global-title { font-weight: 600; }
|
|
184
|
+
#trae-dream-skin-panel .ds-global-desc { margin-top: 1px; font-size: 10px; opacity: 0.58; }
|
|
143
185
|
#trae-dream-skin-panel .ds-setting-row {
|
|
144
186
|
display: flex; align-items: center; justify-content: space-between;
|
|
145
187
|
gap: 12px; margin: 0 2px 9px; padding: 8px 9px;
|
|
@@ -273,6 +315,10 @@ body.trae-skin-v2 #trae-dream-skin-panel .ds-tab.ds-tab-active {
|
|
|
273
315
|
display: block; font-size: 12px; font-weight: 600;
|
|
274
316
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
275
317
|
}
|
|
318
|
+
#trae-dream-skin-panel .ds-version {
|
|
319
|
+
display: block; margin-top: 1px; font-size: 9px; opacity: 0.48;
|
|
320
|
+
font-variant-numeric: tabular-nums;
|
|
321
|
+
}
|
|
276
322
|
#trae-dream-skin-panel .ds-desc {
|
|
277
323
|
display: block; margin-top: 1px; font-size: 10px; opacity: 0.65;
|
|
278
324
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
|
|
@@ -280,10 +326,12 @@ body.trae-skin-v2 #trae-dream-skin-panel .ds-tab.ds-tab-active {
|
|
|
280
326
|
#trae-dream-skin-panel .ds-badge {
|
|
281
327
|
flex: 0 0 auto;
|
|
282
328
|
display: none; align-items: center; gap: 3px;
|
|
283
|
-
padding:
|
|
284
|
-
background: #7c9cff; color: #0a0a0a;
|
|
329
|
+
padding: 0; color: #7c9cff;
|
|
285
330
|
font-size: 10px; font-weight: 600;
|
|
286
331
|
}
|
|
332
|
+
#trae-dream-skin-panel .ds-badge::before {
|
|
333
|
+
content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentColor;
|
|
334
|
+
}
|
|
287
335
|
#trae-dream-skin-panel .ds-card.ds-active .ds-badge { display: flex; }
|
|
288
336
|
body.trae-skin-v2 #trae-dream-skin-panel {
|
|
289
337
|
color: var(--trae-skin-text-primary);
|
|
@@ -296,10 +344,7 @@ body.trae-skin-v2 #trae-dream-skin-panel .ds-card.ds-active {
|
|
|
296
344
|
border-color: var(--trae-skin-accent);
|
|
297
345
|
background: var(--trae-skin-accent-subtle);
|
|
298
346
|
}
|
|
299
|
-
body.trae-skin-v2 #trae-dream-skin-panel .ds-badge {
|
|
300
|
-
background: var(--trae-skin-accent);
|
|
301
|
-
color: var(--trae-skin-accent-on);
|
|
302
|
-
}
|
|
347
|
+
body.trae-skin-v2 #trae-dream-skin-panel .ds-badge { color: var(--trae-skin-accent); }
|
|
303
348
|
body.trae-skin-v2 #trae-dream-skin-panel .ds-header { border-bottom-color: var(--trae-skin-border-subtle); }
|
|
304
349
|
body.trae-skin-v2 #trae-dream-skin-panel .ds-footer { border-top-color: var(--trae-skin-border-subtle); }
|
|
305
350
|
body.trae-skin-v2 #trae-dream-skin-panel .ds-body::-webkit-scrollbar-thumb { background: var(--trae-skin-scrollbar-thumb); }
|
|
@@ -307,7 +352,8 @@ body.trae-skin-v2 #trae-dream-skin-panel .ds-body::-webkit-scrollbar-thumb:hover
|
|
|
307
352
|
body.trae-skin-effects-max #trae-dream-skin-panel .ds-header { padding-right: 16px; }
|
|
308
353
|
body.trae-skin-v2 #trae-dream-skin-panel .ds-config-toggle,
|
|
309
354
|
body.trae-skin-v2 #trae-dream-skin-panel .ds-close,
|
|
310
|
-
body.trae-skin-v2 #trae-dream-skin-panel .ds-reload,
|
|
355
|
+
body.trae-skin-v2 #trae-dream-skin-panel .ds-local-reload,
|
|
356
|
+
body.trae-skin-v2 #trae-dream-skin-panel .ds-global-toggle,
|
|
311
357
|
body.trae-skin-v2 #trae-dream-skin-panel .ds-copy-config,
|
|
312
358
|
body.trae-skin-v2 #trae-dream-skin-panel .ds-reset-config,
|
|
313
359
|
body.trae-skin-v2 #trae-dream-skin-panel .ds-reset-cancel {
|
|
@@ -316,14 +362,21 @@ body.trae-skin-v2 #trae-dream-skin-panel .ds-reset-cancel {
|
|
|
316
362
|
border-color: var(--trae-skin-border-default);
|
|
317
363
|
}
|
|
318
364
|
body.trae-skin-v2 #trae-dream-skin-panel .ds-config-toggle:hover,
|
|
365
|
+
body.trae-skin-v2 #trae-dream-skin-panel .ds-sync:hover:not(:disabled),
|
|
319
366
|
body.trae-skin-v2 #trae-dream-skin-panel .ds-close:hover,
|
|
320
|
-
body.trae-skin-v2 #trae-dream-skin-panel .ds-reload:hover,
|
|
367
|
+
body.trae-skin-v2 #trae-dream-skin-panel .ds-local-reload:hover,
|
|
368
|
+
body.trae-skin-v2 #trae-dream-skin-panel .ds-global-toggle:hover,
|
|
321
369
|
body.trae-skin-v2 #trae-dream-skin-panel .ds-copy-config:hover,
|
|
322
370
|
body.trae-skin-v2 #trae-dream-skin-panel .ds-reset-config:hover,
|
|
323
371
|
body.trae-skin-v2 #trae-dream-skin-panel .ds-reset-cancel:hover {
|
|
324
372
|
color: var(--trae-skin-accent);
|
|
325
373
|
border-color: var(--trae-skin-accent);
|
|
326
374
|
}
|
|
375
|
+
body.trae-skin-v2 #trae-dream-skin-panel .ds-global-toggle.ds-global-toggle-active {
|
|
376
|
+
color: var(--trae-skin-accent);
|
|
377
|
+
border-color: var(--trae-skin-accent);
|
|
378
|
+
background: var(--trae-skin-accent-subtle);
|
|
379
|
+
}
|
|
327
380
|
body.trae-skin-v2 #trae-dream-skin-panel .ds-reset-config.ds-reset-armed {
|
|
328
381
|
color: var(--trae-skin-error);
|
|
329
382
|
border-color: var(--trae-skin-error);
|
|
@@ -337,6 +390,15 @@ body.trae-skin-v2 #trae-dream-skin-panel .ds-setting-row {
|
|
|
337
390
|
background: var(--trae-skin-info-subtle);
|
|
338
391
|
border-color: var(--trae-skin-border-subtle);
|
|
339
392
|
}
|
|
393
|
+
body.trae-skin-v2 #trae-dream-skin-panel .ds-sync-banner {
|
|
394
|
+
border-color: var(--trae-skin-border-default); background: var(--trae-skin-info-subtle);
|
|
395
|
+
}
|
|
396
|
+
body.trae-skin-v2 #trae-dream-skin-panel .ds-sync {
|
|
397
|
+
border-color: var(--trae-skin-border-strong); background: var(--trae-skin-accent-subtle);
|
|
398
|
+
}
|
|
399
|
+
body.trae-skin-v2 #trae-dream-skin-panel .ds-sync-banner.ds-sync-error {
|
|
400
|
+
border-color: var(--trae-skin-error); background: color-mix(in srgb, var(--trae-skin-error) 12%, transparent);
|
|
401
|
+
}
|
|
340
402
|
body.trae-skin-v2 #trae-dream-skin-panel .ds-switch[aria-checked="true"] {
|
|
341
403
|
background: var(--trae-skin-accent);
|
|
342
404
|
border-color: var(--trae-skin-border-strong);
|