Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions app/src/assets/home.css
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,24 @@ body {
font-family: "Inter", system-ui, sans-serif;
}

/* Settings → Appearance picks the backdrop; the glow above is "Gradient". The wallpaper keeps only its light and takes
the theme's accent as its color (luminosity blend), then is tinted toward the theme background so text keeps its
contrast in every theme, light ones included. */
body[data-bg="Solid"] { background: var(--bg); }
body[data-bg="Wallpaper"] {
background:
radial-gradient(ellipse at center, color-mix(in srgb, var(--bg) 78%, transparent), color-mix(in srgb, var(--bg) 45%, transparent) 80%),
url("newtab-wallpaper.jpg") center / cover no-repeat fixed,
var(--accent);
background-blend-mode: normal, luminosity, normal;
}
/* The wallpaper is a dark picture: a light theme washes it out further so dark text stays readable up to the edges. */
body[data-bg="Wallpaper"][data-tone="light"] {
background-image:
radial-gradient(ellipse at center, color-mix(in srgb, var(--bg) 92%, transparent), color-mix(in srgb, var(--bg) 72%, transparent) 80%),
url("newtab-wallpaper.jpg");
}

main {
display: flex;
flex-direction: column;
Expand Down
Binary file added app/src/assets/newtab-wallpaper.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 6 additions & 1 deletion app/src/assets/newtab.html
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
const MAX_TILES = 8;
let pinned = [];
let recent = [];
let showSites = true;

function card(name, url) {
const b = document.createElement('button');
Expand Down Expand Up @@ -77,6 +78,10 @@
const v = parseInt(t.accent.slice(1), 16);
r.setProperty('--accent-text', (0.2126 * (v >> 16 & 255) + 0.7152 * (v >> 8 & 255) + 0.0722 * (v & 255)) / 255 > 0.45 ? '#0A0F1A' : '#FFFFFF');
$('engine').textContent = `${msg.searchEngine.name}`;
document.body.dataset.bg = msg.newTabBackground || 'Wallpaper';
const b = parseInt(t.background.slice(1), 16);
document.body.dataset.tone = (0.2126 * (b >> 16 & 255) + 0.7152 * (b >> 8 & 255) + 0.0722 * (b & 255)) / 255 > 0.5 ? 'light' : 'dark';
showSites = msg.newTabSites !== false;
pinned = [...msg.shortcuts, ...(msg.favorites || [])];
render();
}
Expand All @@ -89,7 +94,7 @@
function render() {
const seen = new Set();
const sites = [...pinned, ...recent].filter((s) => !seen.has(s.url) && seen.add(s.url)).slice(0, MAX_TILES);
$('sites').hidden = sites.length === 0;
$('sites').hidden = !showSites || sites.length === 0;
$('sites').replaceChildren(...sites.map((s) => card(s.name, s.url)));
}
post('ready', {});
Expand Down
17 changes: 16 additions & 1 deletion app/src/assets/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ <h2 data-i18n="settings.appearance">Aparência</h2>
<div><label for="overscan" data-i18n="settings.overscan">Margem da tela (overscan)</label><small data-i18n="settings.overscanHint">Para TVs que cortam as bordas</small></div>
<select id="overscan"><option value="0">0%</option><option value="2">2%</option><option value="4">4%</option><option value="6">6%</option><option value="8">8%</option></select>
</div>
<div class="row">
<div><label for="newTabBackground" data-i18n="settings.ntBackground">Fundo da nova aba</label></div>
<select id="newTabBackground">
<option value="Wallpaper" data-i18n="settings.ntWallpaper">Papel de parede</option>
<option value="Gradient" data-i18n="settings.ntGradient">Brilho da cor do tema</option>
<option value="Solid" data-i18n="settings.ntSolid">Cor sólida</option>
</select>
</div>
<div class="row">
<div><span id="newTabSitesLabel" data-i18n="settings.ntSites">Acesso rápido na nova aba</span><small data-i18n="settings.ntSitesHint">Seus atalhos, favoritos e sites recentes</small></div>
<label class="switch"><input type="checkbox" id="newTabSites" data-ui aria-labelledby="newTabSitesLabel"><span></span></label>
</div>
<div class="row">
<div><span id="highContrastLabel" data-i18n="settings.contrast">Alto contraste</span></div>
<label class="switch"><input type="checkbox" id="highContrast" data-ui aria-labelledby="highContrastLabel"><span></span></label>
Expand Down Expand Up @@ -299,10 +311,11 @@ <h2 data-i18n="settings.about">Sobre e atualizações</h2>
post('ui-settings', {
uiScale: Number($('uiScale').value), overscan: Number($('overscan').value),
highContrast: $('highContrast').checked, language: $('language').value, crashReports: $('crashReports').checked,
newTabBackground: $('newTabBackground').value, newTabSites: $('newTabSites').checked,
});
flash('uiStatus', i18n.t('settings.saved'));
};
for (const id of ['uiScale', 'overscan', 'highContrast', 'language', 'crashReports']) $(id).addEventListener('change', sendUi);
for (const id of ['uiScale', 'overscan', 'highContrast', 'language', 'crashReports', 'newTabBackground', 'newTabSites']) $(id).addEventListener('change', sendUi);

function showUpdate(msg) {
$('appUpdate').textContent = msg.appUpdate || '';
Expand Down Expand Up @@ -355,6 +368,8 @@ <h2 data-i18n="settings.about">Sobre e atualizações</h2>
$('uiScale').value = String(msg.uiScale ?? 1);
$('overscan').value = String(msg.overscan ?? 0);
$('highContrast').checked = !!msg.highContrast;
$('newTabBackground').value = msg.newTabBackground || 'Wallpaper';
$('newTabSites').checked = msg.newTabSites !== false;
$('language').value = msg.language ?? 'pt-BR';
$('crashReports').checked = !!msg.crashReports;
$('streamingUa').checked = !!msg.streamingDesktopUa;
Expand Down
6 changes: 5 additions & 1 deletion app/src/main/pageBridge.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Channels } from "../shared/ipc";
import { Pages } from "../shared/pages";
import {
DEFAULT_SHORTCUTS, parseSearchEngine, parseShortcut, parseTheme, SEARCH_ENGINES, THEME_PRESETS, type AppSettings,
DEFAULT_SHORTCUTS, NEW_TAB_BACKGROUNDS, parseSearchEngine, parseShortcut, parseTheme, SEARCH_ENGINES, THEME_PRESETS, type AppSettings,
withoutInputMode, withoutPermissions,
} from "../shared/settings";
import { currentShortcutMod } from "./platform";
Expand Down Expand Up @@ -62,6 +62,8 @@ export class PageBridge {
highContrast: s.highContrast,
crashReports: s.crashReports,
streamingDesktopUa: s.streamingDesktopUa,
newTabBackground: s.newTabBackground,
newTabSites: s.newTabSites,
vibration: s.vibration,
appVersion: this.deps.appVersion,
appUpdate: this.deps.updateStatus(),
Expand Down Expand Up @@ -145,6 +147,8 @@ export class PageBridge {
if (typeof m.highContrast === "boolean") patch.highContrast = m.highContrast;
if (m.language === "pt-BR" || m.language === "en") patch.language = m.language;
if (typeof m.crashReports === "boolean") patch.crashReports = m.crashReports;
if ((NEW_TAB_BACKGROUNDS as readonly unknown[]).includes(m.newTabBackground)) patch.newTabBackground = m.newTabBackground as AppSettings["newTabBackground"];
if (typeof m.newTabSites === "boolean") patch.newTabSites = m.newTabSites;
store.update((s) => ({ ...s, ...patch }));
this.shell.broadcast();
this.reply(tab, { type: "init", ...this.payloadFields() });
Expand Down
12 changes: 12 additions & 0 deletions app/src/shared/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,12 @@ const en: Record<string, string> = {
"settings.overscan": "Screen margin (overscan)",
"settings.overscanHint": "Keep content away from the edges of TVs that crop the picture",
"settings.contrast": "High contrast",
"settings.ntBackground": "New tab background",
"settings.ntWallpaper": "Wallpaper",
"settings.ntGradient": "Theme color glow",
"settings.ntSolid": "Solid color",
"settings.ntSites": "Quick links on the new tab",
"settings.ntSitesHint": "Your shortcuts, favorites and recent sites",
"settings.language": "Language",
"settings.navigation": "Navigation",
"settings.searchEngine": "Search engine",
Expand Down Expand Up @@ -373,6 +379,12 @@ const ptBR: Record<string, string> = {
"settings.overscan": "Margem da tela (overscan)",
"settings.overscanHint": "Mantém o conteúdo longe das bordas em TVs que cortam a imagem",
"settings.contrast": "Alto contraste",
"settings.ntBackground": "Fundo da nova aba",
"settings.ntWallpaper": "Papel de parede",
"settings.ntGradient": "Brilho da cor do tema",
"settings.ntSolid": "Cor sólida",
"settings.ntSites": "Acesso rápido na nova aba",
"settings.ntSitesHint": "Seus atalhos, favoritos e sites recentes",
"settings.language": "Idioma",
"settings.navigation": "Navegação",
"settings.searchEngine": "Mecanismo de busca",
Expand Down
15 changes: 15 additions & 0 deletions app/src/shared/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export const THEME_PRESETS: Record<string, Theme> = {
Ocean: { accent: "#4CC2FF", background: "#0B1220", surface: "#121C2E", text: "#EAF2FF" },
Ember: { accent: "#FF7A45", background: "#15100E", surface: "#1F1815", text: "#FFF1EA" },
Violet: { accent: "#B388FF", background: "#100D18", surface: "#191424", text: "#F2EDFF" },
Midnight: { accent: "#7AA2FF", background: "#0A0E1A", surface: "#131A2B", text: "#E6ECFF" },
Graphite: { accent: "#A9B8D0", background: "#141414", surface: "#232323", text: "#EDEDED" },
Rose: { accent: "#FF6B9A", background: "#160E12", surface: "#21161B", text: "#FFEFF4" },
Sunset: { accent: "#FFB224", background: "#15110A", surface: "#201A10", text: "#FFF6E5" },
Light: LIGHT_THEME,
};

Expand Down Expand Up @@ -97,6 +101,10 @@ export function parseShortcut(value: unknown): Shortcut | undefined {
return typeof s.name === "string" && s.name.trim() && isWebUrl(s.url) ? { name: s.name.trim(), url: s.url.trim() } : undefined;
}

/** What the new tab draws behind the logo and the search: the bundled wallpaper, the accent glow, or the plain color. */
export const NEW_TAB_BACKGROUNDS = ["Wallpaper", "Gradient", "Solid"] as const;
export type NewTabBackground = (typeof NEW_TAB_BACKGROUNDS)[number];

export type StartupMode = "NewTab" | "Home" | "Restore" | "Custom";
export type NewTabMode = "NewTabPage" | "Home" | "Custom";
export type TrackingLevel = "Basic" | "Balanced" | "Strict";
Expand Down Expand Up @@ -142,6 +150,9 @@ export interface AppSettings {
* sites whose rendition improves without the TV marker. Off until a row in docs/DRM-UAS.md shows a gain.
*/
streamingDesktopUa: boolean;
newTabBackground: NewTabBackground;
/** The row of quick-link tiles under the search on the new tab. */
newTabSites: boolean;
}

export function defaultSettings(): AppSettings {
Expand Down Expand Up @@ -172,6 +183,8 @@ export function defaultSettings(): AppSettings {
vibration: false,
sitePermissions: {},
streamingDesktopUa: false,
newTabBackground: "Wallpaper",
newTabSites: true,
};
}

Expand Down Expand Up @@ -260,6 +273,8 @@ export function parseSettings(input: unknown): AppSettings {
vibration: bool(r.vibration, d.vibration),
sitePermissions: permissions,
streamingDesktopUa: bool(r.streamingDesktopUa, d.streamingDesktopUa),
newTabBackground: oneOf(NEW_TAB_BACKGROUNDS, r.newTabBackground, d.newTabBackground),
newTabSites: bool(r.newTabSites, d.newTabSites),
};
}

Expand Down
16 changes: 15 additions & 1 deletion app/tests/e2e/shell.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { _electron as electron, expect, test, type ElectronApplication } from "@playwright/test";
import { existsSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";

Expand Down Expand Up @@ -113,3 +113,17 @@ test("first run shows onboarding", async () => {
rmSync(fresh, { recursive: true, force: true });
}
});

test("settings change the new tab: backdrop and quick links", async () => {
const pageEval = <T>(code: string) => app.evaluate(({}, c) => globalThis.joy.pageEval(c), code) as Promise<T>;
await expect.poll(tabs, { timeout: 30_000 }).toHaveLength(1);
await command({ type: "navigate", url: "joychromium://settings" });
await expect.poll(() => pageEval<string>("document.getElementById('newTabBackground').value"), { timeout: 15_000 }).toBe("Wallpaper");
await pageEval("(() => { const s = document.getElementById('newTabBackground'); s.value = 'Solid'; s.dispatchEvent(new Event('change')); })()");
await pageEval("document.getElementById('newTabSites').click()");
await expect.poll(() => JSON.parse(readFileSync(join(data, "settings.json"), "utf8")).newTabSites).toBe(false);
expect(JSON.parse(readFileSync(join(data, "settings.json"), "utf8")).newTabBackground).toBe("Solid");

await command({ type: "navigate", url: "joychromium://newtab" });
await expect.poll(() => pageEval<string>("document.body.dataset.bg"), { timeout: 15_000 }).toBe("Solid");
});
7 changes: 7 additions & 0 deletions app/tests/unit/services.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,13 @@ describe("settings store", () => {
expect(parseSettings({}).shortcuts).toEqual([]);
});

it("parses the new tab look and falls back on anything unknown", () => {
expect(parseSettings({}).newTabBackground).toBe("Wallpaper");
expect(parseSettings({}).newTabSites).toBe(true);
expect(parseSettings({ newTabBackground: "Solid", newTabSites: false })).toMatchObject({ newTabBackground: "Solid", newTabSites: false });
expect(parseSettings({ newTabBackground: "Video", newTabSites: "no" })).toMatchObject({ newTabBackground: "Wallpaper", newTabSites: true });
});

it("writes atomically and keeps the previous file as a backup", () => {
const dir = temp();
try {
Expand Down
Loading