1 parent
de4d70970c
commit
648556f762
13 files changed
+147
-59
No files matched your search
@@ -310,7 +310,10 @@ function applyPreferences() {
|
||||
const fixedStyle = layoutInterfaceStyle(p);
|
||||
const skinDepth = resolvedSkinDepth(p);
|
||||
const previousLook = `${root.dataset.theme}|${root.dataset.interfaceSkin}|${root.dataset.skinDepth}|${root.dataset.skinGradients}`;
|
||||
const visual = fixedStyle
|
||||
// A predefined style fixes the layout unless editing is allowed; then the
|
||||
// user's own values (seeded from the style when editing was enabled) win,
|
||||
// while the style's palette, tokens and gradients stay in place.
|
||||
const visual = fixedStyle && !p.presetScaleUnlocked
|
||||
? { ...p, ...fixedStyle.preferences }
|
||||
: p;
|
||||
root.dataset.theme = resolveTheme(p.theme);
|
||||
@@ -450,7 +453,9 @@ function applyPreferences() {
|
||||
if (skinNote) {
|
||||
const styleLabel = state.prefs.language === "pl" ? (state.translations._text?.[paletteStyle?.label] || paletteStyle?.label) : paletteStyle?.label;
|
||||
skinNote.textContent = paletteStyle
|
||||
? `${styleLabel} — ${tr("fixed_interface_style_note", "predefined interface style. Enable preset editing below to create a customizable copy on the first change.")}`
|
||||
? `${styleLabel} — ${p.presetScaleUnlocked
|
||||
? tr("edited_interface_style_note", "predefined interface style with your own layout. Changing a colour creates a RustBox custom copy.")
|
||||
: tr("fixed_interface_style_note", "predefined interface style. Allow editing below to adjust its layout and typography.")}`
|
||||
: tr("custom_interface_style_note", "RustBox is fully customizable: colors, spacing, panes, typography and advanced interface controls are available below.");
|
||||
}
|
||||
}
|
||||
@@ -684,7 +689,7 @@ function presetCssHex(styles, name, fallback) {
|
||||
|
||||
function customAppearancePayload(prefs) {
|
||||
const keys = [
|
||||
"interfaceSkin", "skinDepth", "skinGradients", "presetScaleUnlocked", "colorPreset", "accentColor", "backgroundColor", "panelColor", "textColor",
|
||||
"theme", "interfaceSkin", "skinDepth", "skinGradients", "presetScaleUnlocked", "colorPreset", "accentColor", "backgroundColor", "panelColor", "textColor",
|
||||
"density", "preview", "scale", "fontFamily", "fontSize", "rowHeight", "folderPaneWidth", "messagePaneWidth",
|
||||
"borderRadius", "contrast", "iconSize", "toolbarDensity", "sidebarItemHeight", "readerMaxWidth", "composerWidth",
|
||||
"selectionColor", "unreadFontWeight", "unreadBadgeStyle", "scrollbarDensity", "compactHeaders", "showBorders", "monochrome",
|
||||
@@ -717,20 +722,54 @@ function schedulePromotedAppearanceSave() {
|
||||
}, 180);
|
||||
}
|
||||
|
||||
function promotePresetToCustom(key, value) {
|
||||
const paletteStyle = activeInterfaceStyle(state.prefs);
|
||||
if (!paletteStyle || !presetEditablePreferenceKeys.has(key)) return false;
|
||||
// At the "colors" depth the layout already follows the user's own settings,
|
||||
// so only a palette change needs a custom copy of the style.
|
||||
const layoutStyle = layoutInterfaceStyle(state.prefs);
|
||||
if (!layoutStyle && !presetColorPreferenceKeys.has(key)) return false;
|
||||
if (!state.prefs.presetScaleUnlocked) return false;
|
||||
const fixedStyle = paletteStyle;
|
||||
const presetLayoutPreferenceKeys = [...presetEditablePreferenceKeys].filter((key) => !presetColorPreferenceKeys.has(key));
|
||||
|
||||
// "Allow editing predefined style": seed the user's own layout values from the
|
||||
// style, so editing starts from exactly what is on screen and never mixes in
|
||||
// values saved earlier for another style.
|
||||
async function setPresetEditing(enabled) {
|
||||
const layoutStyle = layoutInterfaceStyle(state.prefs);
|
||||
const payload = { preset_scale_unlocked: enabled };
|
||||
if (enabled && layoutStyle) {
|
||||
for (const key of presetLayoutPreferenceKeys) {
|
||||
if (!(key in layoutStyle.preferences)) continue;
|
||||
state.prefs[key] = layoutStyle.preferences[key];
|
||||
if (preferenceMap[key]) payload[preferenceMap[key]] = layoutStyle.preferences[key];
|
||||
}
|
||||
}
|
||||
state.prefs.presetScaleUnlocked = enabled;
|
||||
applyPreferences();
|
||||
try {
|
||||
await request("/settings/display", { method: "PUT", body: JSON.stringify(payload) });
|
||||
} catch (error) {
|
||||
if (!suppressPaneWidthValidationError(error)) toast(error.message, "error");
|
||||
}
|
||||
}
|
||||
|
||||
// Colours of a predefined style come from its light and dark palettes and
|
||||
// cannot be overridden one by one without mixing two palettes. Changing one
|
||||
// therefore turns the style into a RustBox custom copy of the palette that is
|
||||
// on screen now, pinned to the current light/dark mode so it stays coherent.
|
||||
async function promotePresetToCustom(key, value) {
|
||||
const paletteStyle = activeInterfaceStyle(state.prefs);
|
||||
if (!paletteStyle || !presetColorPreferenceKeys.has(key) || !state.prefs.presetScaleUnlocked) return false;
|
||||
const currentTheme = resolveTheme(state.prefs.theme);
|
||||
const pinTheme = state.prefs.theme !== currentTheme;
|
||||
const label = trText(paletteStyle.label);
|
||||
const message = `${trText("Colours of a predefined style cannot be changed in place.")}\n\n${trText("RustBox will switch to its custom style with a copy of the current colours, layout and typography of")} “${label}”.${pinTheme ? `\n${trText("The theme will be fixed to the current mode")} (${trText(currentTheme === "light" ? "Light" : "Dark")}), ${trText("because the copied colours belong to it.")}` : ""}\n\n${trText("Shapes, fonts, app bar and gradients specific to the style are not part of the copy.")}`;
|
||||
const confirmed = await appConfirm(message, { title: trText("Create a custom copy?"), confirmLabel: trText("Create custom copy") });
|
||||
if (!confirmed) {
|
||||
applyPreferences();
|
||||
return true;
|
||||
}
|
||||
const layoutStyle = layoutInterfaceStyle(state.prefs);
|
||||
const styles = getComputedStyle(root);
|
||||
state.prefs = {
|
||||
...state.prefs,
|
||||
...(layoutStyle ? layoutStyle.preferences : {}),
|
||||
// With editing allowed the user's layout already holds the style's values
|
||||
// (seeded by setPresetEditing); without a fixed layout it is the user's own.
|
||||
...(layoutStyle && !state.prefs.presetScaleUnlocked ? layoutStyle.preferences : {}),
|
||||
theme: currentTheme,
|
||||
interfaceSkin: "rustbox",
|
||||
presetScaleUnlocked: false,
|
||||
colorPreset: "custom",
|
||||
@@ -738,12 +777,13 @@ function promotePresetToCustom(key, value) {
|
||||
backgroundColor: presetCssHex(styles, "--bg", state.prefs.backgroundColor),
|
||||
panelColor: presetCssHex(styles, "--panel", state.prefs.panelColor),
|
||||
textColor: presetCssHex(styles, "--text", state.prefs.textColor),
|
||||
selectionColor: presetCssHex(styles, "--selected", fixedStyle.preferences.selectionColor || state.prefs.selectionColor),
|
||||
folderBackground: presetCssHex(styles, "--folder-bg", state.prefs.folderBackground || "#101310"),
|
||||
messageBackground: presetCssHex(styles, "--message-bg", state.prefs.messageBackground || "#181c19"),
|
||||
readerBackground: presetCssHex(styles, "--reader-bg", state.prefs.readerBackground || "#181c19"),
|
||||
[key]: value,
|
||||
selectionColor: presetCssHex(styles, "--selected", state.prefs.selectionColor),
|
||||
folderBackground: presetCssHex(styles, "--folder-bg", ""),
|
||||
messageBackground: presetCssHex(styles, "--message-bg", ""),
|
||||
readerBackground: presetCssHex(styles, "--reader-bg", ""),
|
||||
};
|
||||
state.prefs[key] = value;
|
||||
if (["accentColor", "backgroundColor", "panelColor", "textColor"].includes(key)) state.prefs.colorPreset = "custom";
|
||||
presetPromotionActive = true;
|
||||
applyPreferences();
|
||||
schedulePromotedAppearanceSave();
|
||||
@@ -751,7 +791,21 @@ function promotePresetToCustom(key, value) {
|
||||
}
|
||||
|
||||
function savePreference(key, value) {
|
||||
if (promotePresetToCustom(key, value)) return;
|
||||
if (key === "presetScaleUnlocked") { setPresetEditing(Boolean(value)); return; }
|
||||
// Edited layout values belong to the style they were seeded from: a new
|
||||
// style starts locked again, and a new depth re-seeds from the same style.
|
||||
if (key === "interfaceSkin" && state.prefs.presetScaleUnlocked && value !== state.prefs.interfaceSkin) {
|
||||
state.prefs.presetScaleUnlocked = false;
|
||||
savePreference("presetScaleUnlocked", false);
|
||||
}
|
||||
if (key === "skinDepth" && state.prefs.presetScaleUnlocked && value !== state.prefs.skinDepth) {
|
||||
state.prefs.skinDepth = value;
|
||||
setPresetEditing(true);
|
||||
}
|
||||
if (activeInterfaceStyle(state.prefs) && state.prefs.presetScaleUnlocked && presetColorPreferenceKeys.has(key)) {
|
||||
promotePresetToCustom(key, value);
|
||||
return;
|
||||
}
|
||||
state.prefs[key] = value;
|
||||
if (presetPromotionActive && presetEditablePreferenceKeys.has(key)) {
|
||||
if (["accentColor", "backgroundColor", "panelColor", "textColor"].includes(key)) state.prefs.colorPreset = "custom";
|
||||
|
||||
Reference in new issue
Block a user