1 parent
de4d70970c
commit
648556f762
13 files changed
+147
-59
No files matched your search
@@ -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"] {
|
||||
|
||||
@@ -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
@@ -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
@@ -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>
|
||||
|
||||
@@ -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