Skip to content
Draft
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
5 changes: 3 additions & 2 deletions apps/desktop/src/app/DesktopApp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as Schema from "effect/Schema";

import * as NetService from "@t3tools/shared/Net";
import * as Crypto from "effect/Crypto";
import { i18n } from "@t3tools/shared/i18n";
import * as ElectronApp from "../electron/ElectronApp.ts";
import * as ElectronDialog from "../electron/ElectronDialog.ts";
import * as ElectronProtocol from "../electron/ElectronProtocol.ts";
Expand Down Expand Up @@ -131,8 +132,8 @@ const handleFatalStartupError = Effect.fn("desktop.startup.handleFatalStartupErr
const wasQuitting = yield* Ref.getAndSet(state.quitting, true);
if (!wasQuitting) {
yield* electronDialog.showErrorBox(
"T3 Code failed to start",
`Stage: ${stage}\n${message}${detail}`,
i18n.t("desktop.startup.failedTitle"),
i18n.t("desktop.startup.failedDetail", { stage, message, detail }),
);
}
yield* shutdown.request;
Expand Down
10 changes: 6 additions & 4 deletions apps/desktop/src/backend/DesktopBackendPool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ import * as FileSystem from "effect/FileSystem";
import { HttpClient } from "effect/unstable/http";
import { ChildProcessSpawner } from "effect/unstable/process";

import { i18n } from "@t3tools/shared/i18n";

import * as DesktopBackendConfiguration from "./DesktopBackendConfiguration.ts";
import * as DesktopBackendManager from "./DesktopBackendManager.ts";
import * as DesktopObservability from "../app/DesktopObservability.ts";
Expand Down Expand Up @@ -242,8 +244,8 @@ export const layer = Layer.effect(
{ reason },
);
yield* electronDialog.showErrorBox(
"WSL backend is still unavailable",
`${reason}\n\nT3 Code will use the Windows backend for this launch and retry WSL the next time the app starts.`,
i18n.t("desktop.wsl.unavailableTitle"),
i18n.t("desktop.wsl.unavailableMessage", { reason }),
);
yield* appSettings.applyWslWindowsFallbackInMemory;
return true;
Expand All @@ -253,8 +255,8 @@ export const layer = Layer.effect(
reason,
});
yield* electronDialog.showErrorBox(
"WSL backend couldn't start",
`${reason}\n\nFalling back to the Windows backend so T3 Code can open. Re-enable the WSL backend from Settings > Connections once the WSL distro is fixed.`,
i18n.t("desktop.wsl.couldNotStartTitle"),
i18n.t("desktop.wsl.couldNotStartMessage", { reason }),
);
// Fully disable the WSL backend — both flags, matching the "Switch to
// Windows" recovery path — so the manager's next restart re-resolves the
Expand Down
12 changes: 8 additions & 4 deletions apps/desktop/src/window/DesktopApplicationMenu.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import * as Option from "effect/Option";

import type * as Electron from "electron";

import { i18n } from "@t3tools/shared/i18n";

import * as ElectronApp from "../electron/ElectronApp.ts";
import * as ElectronDialog from "../electron/ElectronDialog.ts";
import * as ElectronMenu from "../electron/ElectronMenu.ts";
Expand Down Expand Up @@ -136,12 +138,14 @@ describe("DesktopApplicationMenu", () => {
yield* configureMenu(selectedAction, applicationMenuTemplate);

const template = yield* Deferred.await(applicationMenuTemplate);
const fileMenu = template.find((item) => item.label === "File");
const fileMenu = template.find((item) => item.label === i18n.t("desktop.menu.file"));
assert.isDefined(fileMenu);
if (!Array.isArray(fileMenu.submenu)) {
throw new Error("Expected File menu submenu to be an array.");
}
const settingsItem = fileMenu.submenu.find((item) => item.label === "Settings...");
const settingsItem = fileMenu.submenu.find(
(item) => item.label === i18n.t("desktop.menu.settings"),
);
assert.isDefined(settingsItem);
const settingsClick = settingsItem.click;
if (typeof settingsClick !== "function") {
Expand All @@ -165,7 +169,7 @@ describe("DesktopApplicationMenu", () => {
yield* configureMenu(selectedAction, applicationMenuTemplate);

const template = yield* Deferred.await(applicationMenuTemplate);
const viewMenu = template.find((item) => item.label === "View");
const viewMenu = template.find((item) => item.label === i18n.t("desktop.menu.view"));
assert.isDefined(viewMenu);
if (!Array.isArray(viewMenu.submenu)) {
throw new Error("Expected View menu submenu to be an array.");
Expand All @@ -175,7 +179,7 @@ describe("DesktopApplicationMenu", () => {
viewMenu.submenu.find((item) => item.role?.toLowerCase().includes("zoom")),
);

const zoomIn = viewMenu.submenu.find((item) => item.label === "Zoom In");
const zoomIn = viewMenu.submenu.find((item) => item.label === i18n.t("desktop.menu.zoomIn"));
assert.isDefined(zoomIn);
assert.equal(zoomIn.accelerator, "CmdOrCtrl+=");
if (typeof zoomIn.click !== "function") {
Expand Down
54 changes: 34 additions & 20 deletions apps/desktop/src/window/DesktopApplicationMenu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import * as Schema from "effect/Schema";

import type * as Electron from "electron";

import { i18n } from "@t3tools/shared/i18n";

import { makeComponentLogger } from "../app/DesktopObservability.ts";
import * as ElectronApp from "../electron/ElectronApp.ts";
import * as ElectronDialog from "../electron/ElectronDialog.ts";
Expand Down Expand Up @@ -65,17 +67,17 @@ const checkForUpdatesFromMenu = Effect.gen(function* () {
if (updateState.status === "up-to-date") {
yield* electronDialog.showMessageBox({
type: "info",
title: "You're up to date!",
message: `T3 Code ${updateState.currentVersion} is currently the newest version available.`,
buttons: ["OK"],
title: i18n.t("desktop.update.upToDateTitle"),
message: i18n.t("desktop.update.upToDateMessage", { version: updateState.currentVersion }),
buttons: [i18n.t("action.ok")],
});
} else if (updateState.status === "error") {
yield* electronDialog.showMessageBox({
type: "warning",
title: "Update check failed",
message: "Could not check for updates.",
detail: updateState.message ?? "An unknown error occurred. Please try again later.",
buttons: ["OK"],
title: i18n.t("desktop.update.checkFailedTitle"),
message: i18n.t("desktop.update.checkFailedMessage"),
detail: updateState.message ?? i18n.t("desktop.update.checkFailedDetail"),
buttons: [i18n.t("action.ok")],
});
}
}).pipe(Effect.withSpan("desktop.menu.checkForUpdates"));
Expand All @@ -90,10 +92,10 @@ const handleCheckForUpdatesMenuClick = Effect.gen(function* () {
});
yield* electronDialog.showMessageBox({
type: "info",
title: "Updates unavailable",
message: "Automatic updates are not available right now.",
title: i18n.t("desktop.update.unavailableTitle"),
message: i18n.t("desktop.update.unavailableMessage"),
detail: disabledReason.value,
buttons: ["OK"],
buttons: [i18n.t("action.ok")],
});
return;
}
Expand Down Expand Up @@ -146,12 +148,12 @@ export const make = Effect.gen(function* () {
submenu: [
{ role: "about" },
{
label: "Check for Updates...",
label: i18n.t("desktop.menu.checkForUpdates"),
click: checkForUpdatesClick,
},
{ type: "separator" },
{
label: "Settings...",
label: i18n.t("desktop.menu.settings"),
accelerator: "CmdOrCtrl+,",
click: settingsClick,
},
Expand All @@ -169,13 +171,13 @@ export const make = Effect.gen(function* () {

template.push(
{
label: "File",
label: i18n.t("desktop.menu.file"),
submenu: [
...(environment.platform === "darwin"
? []
: [
{
label: "Settings...",
label: i18n.t("desktop.menu.settings"),
accelerator: "CmdOrCtrl+,",
click: settingsClick,
},
Expand All @@ -186,7 +188,7 @@ export const make = Effect.gen(function* () {
},
{ role: "editMenu" },
{
label: "View",
label: i18n.t("desktop.menu.view"),
submenu: [
{ role: "reload" },
{ role: "forceReload" },
Expand All @@ -198,15 +200,27 @@ export const make = Effect.gen(function* () {
page and the app UI appears stuck. These always zoom the main
window (see DesktopWindow.zoomMain).
*/
{ label: "Actual Size", accelerator: "CmdOrCtrl+0", click: zoomClick("reset") },
{ label: "Zoom In", accelerator: "CmdOrCtrl+=", click: zoomClick("in") },
{
label: "Zoom In",
label: i18n.t("desktop.menu.actualSize"),
accelerator: "CmdOrCtrl+0",
click: zoomClick("reset"),
},
{
label: i18n.t("desktop.menu.zoomIn"),
accelerator: "CmdOrCtrl+=",
click: zoomClick("in"),
},
{
label: i18n.t("desktop.menu.zoomIn"),
accelerator: "CmdOrCtrl+Plus",
visible: false,
click: zoomClick("in"),
},
{ label: "Zoom Out", accelerator: "CmdOrCtrl+-", click: zoomClick("out") },
{
label: i18n.t("desktop.menu.zoomOut"),
accelerator: "CmdOrCtrl+-",
click: zoomClick("out"),
},
{ type: "separator" },
{ role: "togglefullscreen" },
],
Expand All @@ -216,7 +230,7 @@ export const make = Effect.gen(function* () {
role: "help",
submenu: [
{
label: "Check for Updates...",
label: i18n.t("desktop.menu.checkForUpdates"),
click: checkForUpdatesClick,
},
],
Expand Down
9 changes: 7 additions & 2 deletions apps/desktop/src/window/DesktopWindow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as NodeServices from "@effect/platform-node/NodeServices";
import { assert, describe, it } from "@effect/vitest";
import * as Deferred from "effect/Deferred";
import { DesktopSnapShotId } from "@t3tools/contracts";
import { i18n } from "@t3tools/shared/i18n";
import * as Effect from "effect/Effect";
import * as Fiber from "effect/Fiber";
import * as Layer from "effect/Layer";
Expand Down Expand Up @@ -519,8 +520,12 @@ describe("DesktopWindow", () => {
);
const imageMenu = (yield* Queue.take(menus)).input;
assert.isUndefined(imageMenu.frame);
const copyImage = imageMenu.template.find((item) => item.label === "Copy Image");
const copyLink = imageMenu.template.find((item) => item.label === "Copy Link");
const copyImage = imageMenu.template.find(
(item) => item.label === i18n.t("desktop.contextMenu.copyImage"),
);
const copyLink = imageMenu.template.find(
(item) => item.label === i18n.t("desktop.contextMenu.copyLink"),
);
assert.isDefined(copyImage?.click);
assert.isDefined(copyLink?.click);
copyImage.click({} as Electron.MenuItem, undefined, {} as Electron.KeyboardEvent);
Expand Down
12 changes: 8 additions & 4 deletions apps/desktop/src/window/DesktopWindow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as Ref from "effect/Ref";
import * as Electron from "electron";

import { type DesktopSnapShotEvent, DEFAULT_CLIENT_SETTINGS } from "@t3tools/contracts";
import { i18n } from "@t3tools/shared/i18n";

import * as DesktopAssets from "../app/DesktopAssets.ts";
import * as DesktopEnvironment from "../app/DesktopEnvironment.ts";
Expand Down Expand Up @@ -188,7 +189,7 @@ function buildConnectingSplashDataUrl(shouldUseDarkColors: boolean): string {
const label = shouldUseDarkColors ? "#9ca3af" : "#6b7280";
const accent = shouldUseDarkColors ? "#f8fafc" : "#1f2937";
const track = shouldUseDarkColors ? "rgba(248,250,252,0.18)" : "rgba(31,41,55,0.18)";
const html = `<!doctype html><html><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'"><style>html,body{margin:0;height:100%}body{background:${background};color:${label};font-family:system-ui,-apple-system,'Segoe UI',sans-serif;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:18px;-webkit-user-select:none;user-select:none;-webkit-app-region:drag}.spinner{width:26px;height:26px;border:3px solid ${track};border-top-color:${accent};border-radius:50%;animation:spin .8s linear infinite}.label{font-size:13px}@keyframes spin{to{transform:rotate(360deg)}}</style></head><body><div class="spinner"></div><div class="label">Connecting to WSL…</div></body></html>`;
const html = `<!doctype html><html><head><meta charset="utf-8"><meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'"><style>html,body{margin:0;height:100%}body{background:${background};color:${label};font-family:system-ui,-apple-system,'Segoe UI',sans-serif;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:18px;-webkit-user-select:none;user-select:none;-webkit-app-region:drag}.spinner{width:26px;height:26px;border:3px solid ${track};border-top-color:${accent};border-radius:50%;animation:spin .8s linear infinite}.label{font-size:13px}@keyframes spin{to{transform:rotate(360deg)}}</style></head><body><div class="spinner"></div><div class="label">${i18n.t("desktop.splash.connecting")}</div></body></html>`;
return `data:text/html;charset=utf-8,${encodeURIComponent(html)}`;
}

Expand Down Expand Up @@ -531,15 +532,18 @@ export const make = Effect.gen(function* () {
});
}
if (params.dictionarySuggestions.length === 0) {
menuTemplate.push({ label: "No suggestions", enabled: false });
menuTemplate.push({
label: i18n.t("desktop.contextMenu.noSuggestions"),
enabled: false,
});
}
menuTemplate.push({ type: "separator" });
}

if (Option.isSome(ElectronShell.parseSafeExternalUrl(params.linkURL))) {
menuTemplate.push(
{
label: "Copy Link",
label: i18n.t("desktop.contextMenu.copyLink"),
click: () => {
void runPromise(electronShell.copyText(params.linkURL));
},
Expand All @@ -550,7 +554,7 @@ export const make = Effect.gen(function* () {

if (params.mediaType === "image") {
menuTemplate.push({
label: "Copy Image",
label: i18n.t("desktop.contextMenu.copyImage"),
click: () => {
if (!contents.isDestroyed()) contents.copyImageAt(params.x, params.y);
},
Expand Down
Loading
Loading