v0.2.25
RustBox CI / rustbox (push) Canceled after 0s

This commit is contained in:
Mateusz Gruszczyński committed 2026-11-04 23:59:38 +01:00
1 parent de4d70970c
commit 648556f762
13 files changed
+147 -59

No files matched your search

+73 -19
View File
@@ -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";