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

+4 -2
View File
@@ -127,9 +127,11 @@ html[data-font-family="mono"] {
--font-sans: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", monospace;
}
/* Not derived from var(--line): a custom property referring to itself is a
cycle, which made --line empty (no borders) wherever no palette overrode it. */
html[data-contrast="soft"] {
--line: color-mix(in srgb, var(--line) 70%, transparent);
--line-strong: color-mix(in srgb, var(--line-strong) 78%, transparent);
--line: color-mix(in srgb, var(--text) 11%, var(--panel));
--line-strong: color-mix(in srgb, var(--text) 20%, var(--panel));
}
html[data-contrast="high"] {
+3 -2
View File
@@ -47,7 +47,7 @@
"unavailable": "Unavailable",
"show_full_addresses": "Show full addresses",
"collapse_addresses": "Collapse addresses",
"fixed_interface_style_note": "predefined interface style. Enable preset editing below to create a customizable copy on the first change.",
"fixed_interface_style_note": "predefined interface style. Allow editing below to adjust its layout and typography.",
"custom_interface_style_note": "RustBox is fully customizable: colors, spacing, panes, typography and advanced interface controls are available below.",
"reply": "Reply",
"reply_all": "Reply all",
@@ -74,5 +74,6 @@
"theme_dark_from": "Dark from",
"account_color": "Account color",
"account_color_note": "A stripe on the app bar and a dot next to the address show which account is active.",
"contrast_warning": "Low contrast"
"contrast_warning": "Low contrast",
"edited_interface_style_note": "predefined interface style with your own layout. Changing a colour creates a RustBox custom copy."
}
+12 -4
View File
@@ -618,7 +618,6 @@
"Uploading files…": "Wysyłanie plików…",
"Upload complete": "Wysyłanie zakończone",
"Allow editing predefined style": "Zezwól na edycję gotowego stylu",
"When enabled, the first appearance change creates and saves a RustBox custom copy of the selected preset.": "Po włączeniu pierwsza zmiana wyglądu utworzy i zapisze edytowalną kopię wybranego stylu jako RustBox custom.",
"Maximum attachment size": "Maksymalny rozmiar załącznika",
"Server-wide limit set with": "Globalny limit serwera ustawiany przez",
"in rustbox.toml.": "w rustbox.toml.",
@@ -781,9 +780,17 @@
"More": "Więcej",
"Snooze…": "Odłóż…",
"View snoozed messages…": "Pokaż odłożone wiadomości…",
"Open full screen": "Otwórz na pełnym ekranie"
"Open full screen": "Otwórz na pełnym ekranie",
"Layout and typography can be adjusted while the style keeps its colours, shapes and gradients. Changing a colour creates a RustBox custom copy.": "Układ i typografię można zmieniać, a styl zachowuje swoje kolory, kształty i gradienty. Zmiana koloru tworzy własną kopię RustBox.",
"Colours of a predefined style cannot be changed in place.": "Kolorów gotowego stylu nie da się zmieniać bezpośrednio.",
"RustBox will switch to its custom style with a copy of the current colours, layout and typography of": "RustBox przełączy się na styl własny z kopią obecnych kolorów, układu i typografii stylu",
"The theme will be fixed to the current mode": "Motyw zostanie ustawiony na obecny tryb",
"because the copied colours belong to it.": "bo skopiowane kolory należą do niego.",
"Shapes, fonts, app bar and gradients specific to the style are not part of the copy.": "Kształty, czcionki, pasek aplikacji i gradienty charakterystyczne dla stylu nie wchodzą do kopii.",
"Create a custom copy?": "Utworzyć własną kopię?",
"Create custom copy": "Utwórz własną kopię"
},
"fixed_interface_style_note": "gotowy styl interfejsu. Włącz edycję presetu poniżej, aby przy pierwszej zmianie utworzyć jego edytowalną kopię.",
"fixed_interface_style_note": "gotowy styl interfejsu. Włącz edycję poniżej, aby dopasować jego układ i typografię.",
"custom_interface_style_note": "RustBox można w pełni dostosować: kolory, odstępy, panele, typografię i zaawansowane ustawienia interfejsu.",
"reply": "Odpowiedz",
"reply_all": "Odpowiedz wszystkim",
@@ -810,5 +817,6 @@
"theme_dark_from": "Ciemny od",
"account_color": "Kolor konta",
"account_color_note": "Pasek u góry aplikacji i kropka przy adresie pokazują, które konto jest aktywne.",
"contrast_warning": "Niski kontrast"
"contrast_warning": "Niski kontrast",
"edited_interface_style_note": "gotowy styl interfejsu z Twoim układem. Zmiana koloru tworzy własną kopię RustBox."
}
+2 -2
View File
@@ -718,8 +718,8 @@
style.</small></label>
<label id="pref-preset-scale-unlocked-row" class="checkbox-row hidden"><input
id="pref-preset-scale-unlocked" type="checkbox"><span>Allow editing predefined style</span><small
class="muted">When enabled, the first appearance change creates and saves a RustBox custom copy of the
selected preset.</small></label>
class="muted">Layout and typography can be adjusted while the style keeps its colours, shapes and
gradients. Changing a colour creates a RustBox custom copy.</small></label>
<label><span data-i18n="color_preset">Color preset</span><select id="pref-color-preset">
<option value="sage">Sage</option>
<option value="blue">Blue</option>
+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";