From 4b16b873c6d7eff51ba012a8205b21d793ccc07b Mon Sep 17 00:00:00 2001 From: altair823 Date: Sat, 25 Apr 2026 11:58:59 +0900 Subject: [PATCH] feat(main): add Electron entry + Inbox window shell MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task 2 of the slice plan. Creates the minimal Electron main process: app entry that opens an Inbox BrowserWindow on whenReady, an inboxWindow module that handles show/hide/close- to-tray semantics, an HTML placeholder renderer ("Inkling Inbox (renderer pending)"), and the minimum @shared/types augmentation for app.isQuitting (Task 3 expands this file). Plan tsconfig template adjustment: TypeScript 6 deprecates baseUrl. Dropped it and made paths entries explicitly relative ("./src/...") so they resolve from tsconfig.json's directory. Updated both the in-repo tsconfig.json and Task 1 Step 3 in the plan to match. Verification: `npm run typecheck` exits 0. Full `npm run dev` sanity check is deferred until Task 3 (preload) and Task 19 (quickcapture HTML) land — electron-vite.config.ts wires both entries that don't yet exist. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../2026-04-24-inkling-vertical-slice.md | 5 +- src/main/index.ts | 12 +++++ src/main/windows/inboxWindow.ts | 46 +++++++++++++++++++ src/renderer/inbox/index.html | 12 +++++ src/shared/types.ts | 6 +++ tsconfig.json | 5 +- 6 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 src/main/index.ts create mode 100644 src/main/windows/inboxWindow.ts create mode 100644 src/renderer/inbox/index.html create mode 100644 src/shared/types.ts diff --git a/docs/superpowers/plans/2026-04-24-inkling-vertical-slice.md b/docs/superpowers/plans/2026-04-24-inkling-vertical-slice.md index 968de85..b4bd534 100644 --- a/docs/superpowers/plans/2026-04-24-inkling-vertical-slice.md +++ b/docs/superpowers/plans/2026-04-24-inkling-vertical-slice.md @@ -181,10 +181,9 @@ If a version is not yet published, run `npm view version` and use the late "resolveJsonModule": true, "isolatedModules": true, "types": ["node"], - "baseUrl": ".", "paths": { - "@shared/*": ["src/shared/*"], - "@main/*": ["src/main/*"] + "@shared/*": ["./src/shared/*"], + "@main/*": ["./src/main/*"] } }, "include": ["src/**/*", "tests/**/*"], diff --git a/src/main/index.ts b/src/main/index.ts new file mode 100644 index 0000000..de1f4ee --- /dev/null +++ b/src/main/index.ts @@ -0,0 +1,12 @@ +import { app, BrowserWindow } from 'electron'; +import '@shared/types'; +import { createInboxWindow } from './windows/inboxWindow.js'; + +app.whenReady().then(() => { + createInboxWindow(); + app.on('activate', () => { + if (BrowserWindow.getAllWindows().length === 0) createInboxWindow(); + }); +}); + +app.on('before-quit', () => { app.isQuitting = true; }); diff --git a/src/main/windows/inboxWindow.ts b/src/main/windows/inboxWindow.ts new file mode 100644 index 0000000..7579c5d --- /dev/null +++ b/src/main/windows/inboxWindow.ts @@ -0,0 +1,46 @@ +import { BrowserWindow, app } from 'electron'; +import { join } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +let inboxWindow: BrowserWindow | null = null; +const __dirname = fileURLToPath(new URL('.', import.meta.url)); + +export function getInboxWindow(): BrowserWindow | null { + return inboxWindow; +} + +export function createInboxWindow(): BrowserWindow { + if (inboxWindow && !inboxWindow.isDestroyed()) { + inboxWindow.show(); + inboxWindow.focus(); + return inboxWindow; + } + + inboxWindow = new BrowserWindow({ + width: 900, + height: 720, + show: false, + webPreferences: { + preload: join(__dirname, '../preload/index.js'), + contextIsolation: true, + nodeIntegration: false, + sandbox: false + } + }); + + if (process.env.ELECTRON_RENDERER_URL) { + inboxWindow.loadURL(`${process.env.ELECTRON_RENDERER_URL}/inbox/index.html`); + } else { + inboxWindow.loadFile(join(__dirname, '../renderer/inbox/index.html')); + } + + inboxWindow.on('close', (e) => { + if (!app.isQuitting) { + e.preventDefault(); + inboxWindow?.hide(); + } + }); + + inboxWindow.once('ready-to-show', () => inboxWindow?.show()); + return inboxWindow; +} diff --git a/src/renderer/inbox/index.html b/src/renderer/inbox/index.html new file mode 100644 index 0000000..e031d2c --- /dev/null +++ b/src/renderer/inbox/index.html @@ -0,0 +1,12 @@ + + + + + + Inkling + + +
+ + + diff --git a/src/shared/types.ts b/src/shared/types.ts new file mode 100644 index 0000000..431d541 --- /dev/null +++ b/src/shared/types.ts @@ -0,0 +1,6 @@ +declare global { + namespace Electron { + interface App { isQuitting?: boolean; } + } +} +export {}; diff --git a/tsconfig.json b/tsconfig.json index 5dd1776..d19c09a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,10 +13,9 @@ "resolveJsonModule": true, "isolatedModules": true, "types": ["node"], - "baseUrl": ".", "paths": { - "@shared/*": ["src/shared/*"], - "@main/*": ["src/main/*"] + "@shared/*": ["./src/shared/*"], + "@main/*": ["./src/main/*"] } }, "include": ["src/**/*", "tests/**/*"],