build(app): migrate to electron-vite (main/preload/renderer), keep electron-builder (Phase 5d-3a)
Restructure app into src/{main,preload,renderer}; electron.vite.config.ts builds
all three (each main module its own input so CJS requires + the indexer worker
resolve; better-sqlite3 externalized; Vue plugin for renderer). main/index.js
paths updated for the out/ layout + ELECTRON_RENDERER_URL. Still JS/CJS — TS+ESM
and core consumption are the next stages. Verified: npm run dev launches clean;
electron-vite build succeeds; root suite 119/119.
This commit is contained in:
@@ -6,7 +6,7 @@ import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const { createIndexerService } = require('../app/indexer-service.js');
|
||||
const { createIndexerService } = require('../app/src/main/indexer-service.js');
|
||||
|
||||
function manualTimers() {
|
||||
const timers = new Set();
|
||||
|
||||
@@ -3,7 +3,7 @@ import assert from 'node:assert/strict';
|
||||
import { createRequire } from 'node:module';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const { createWorkerBuildIndex } = require('../app/indexer-worker-client.js');
|
||||
const { createWorkerBuildIndex } = require('../app/src/main/indexer-worker-client.js');
|
||||
|
||||
test('worker build client resolves build results from a worker thread', async () => {
|
||||
const instances = [];
|
||||
|
||||
@@ -6,7 +6,7 @@ import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const { buildIndex } = require('../app/indexer.js');
|
||||
const { buildIndex } = require('../app/src/main/indexer.js');
|
||||
const { DatabaseSync } = require('node:sqlite');
|
||||
|
||||
class TestDatabase {
|
||||
|
||||
@@ -12,7 +12,7 @@ async function loadMainForWindowFlags(flags) {
|
||||
const originalArgv = process.argv;
|
||||
const originalHome = process.env.HOME;
|
||||
const originalLoad = Module._load;
|
||||
const mainPath = require.resolve('../app/main.js');
|
||||
const mainPath = require.resolve('../app/src/main/index.js');
|
||||
const home = join(tmpdir(), `obelisk-window-flags-${Date.now()}-${Math.random()}`);
|
||||
mkdirSync(join(home, '.obelisk'), { recursive: true });
|
||||
writeFileSync(join(home, '.obelisk', 'obelisk.sqlite'), '');
|
||||
@@ -93,7 +93,7 @@ async function loadMainForWindowFlags(flags) {
|
||||
};
|
||||
|
||||
try {
|
||||
require('../app/main.js');
|
||||
require('../app/src/main/index.js');
|
||||
await new Promise(resolve => setImmediate(resolve));
|
||||
return windows;
|
||||
} finally {
|
||||
@@ -137,7 +137,7 @@ test('main process watches Codex sessions directory instead of Codex root', asyn
|
||||
process.env.HOME = home;
|
||||
|
||||
const serviceOptions = [];
|
||||
const mainPath = require.resolve('../app/main.js');
|
||||
const mainPath = require.resolve('../app/src/main/index.js');
|
||||
delete require.cache[mainPath];
|
||||
|
||||
class FakeDatabase {
|
||||
@@ -202,7 +202,7 @@ test('main process watches Codex sessions directory instead of Codex root', asyn
|
||||
};
|
||||
|
||||
try {
|
||||
require('../app/main.js');
|
||||
require('../app/src/main/index.js');
|
||||
await new Promise(resolve => setImmediate(resolve));
|
||||
|
||||
assert.equal(serviceOptions.length, 1);
|
||||
@@ -229,7 +229,7 @@ test('session IPC hides Codex rows by default and supports explicit source opt-i
|
||||
|
||||
const ipcHandlers = new Map();
|
||||
const queries = [];
|
||||
const mainPath = require.resolve('../app/main.js');
|
||||
const mainPath = require.resolve('../app/src/main/index.js');
|
||||
delete require.cache[mainPath];
|
||||
|
||||
class FakeDatabase {
|
||||
@@ -305,7 +305,7 @@ test('session IPC hides Codex rows by default and supports explicit source opt-i
|
||||
};
|
||||
|
||||
try {
|
||||
require('../app/main.js');
|
||||
require('../app/src/main/index.js');
|
||||
await new Promise(resolve => setImmediate(resolve));
|
||||
|
||||
ipcHandlers.get('db:getSessions')(null, {});
|
||||
@@ -373,7 +373,7 @@ test('main process migrates an existing app database before source-filtered IPC
|
||||
legacy.close();
|
||||
|
||||
const ipcHandlers = new Map();
|
||||
const mainPath = require.resolve('../app/main.js');
|
||||
const mainPath = require.resolve('../app/src/main/index.js');
|
||||
delete require.cache[mainPath];
|
||||
|
||||
class FakeBrowserWindow {
|
||||
@@ -454,7 +454,7 @@ test('main process migrates an existing app database before source-filtered IPC
|
||||
};
|
||||
|
||||
try {
|
||||
require('../app/main.js');
|
||||
require('../app/src/main/index.js');
|
||||
await new Promise(resolve => setImmediate(resolve));
|
||||
|
||||
const sessions = ipcHandlers.get('db:getSessions')(null, {});
|
||||
@@ -480,7 +480,7 @@ test('closing the last macOS window releases background resources until activati
|
||||
const home = join(tmpdir(), `obelisk-main-window-${Date.now()}`);
|
||||
mkdirSync(join(home, '.obelisk'), { recursive: true });
|
||||
writeFileSync(join(home, '.obelisk', 'obelisk.sqlite'), '');
|
||||
const mainPath = require.resolve('../app/main.js');
|
||||
const mainPath = require.resolve('../app/src/main/index.js');
|
||||
delete require.cache[mainPath];
|
||||
process.env.HOME = home;
|
||||
Object.defineProperty(process, 'platform', { value: 'darwin' });
|
||||
@@ -561,7 +561,7 @@ test('closing the last macOS window releases background resources until activati
|
||||
};
|
||||
|
||||
try {
|
||||
require('../app/main.js');
|
||||
require('../app/src/main/index.js');
|
||||
await new Promise(resolve => setImmediate(resolve));
|
||||
|
||||
assert.equal(windows.length, 1);
|
||||
@@ -618,7 +618,7 @@ test('settings rebuild reopens the database from the configured Claude path', as
|
||||
const buildCalls = [];
|
||||
const serviceEvents = [];
|
||||
const originalLoad = Module._load;
|
||||
const mainPath = require.resolve('../app/main.js');
|
||||
const mainPath = require.resolve('../app/src/main/index.js');
|
||||
delete require.cache[mainPath];
|
||||
|
||||
class FakeDatabase {
|
||||
@@ -703,7 +703,7 @@ test('settings rebuild reopens the database from the configured Claude path', as
|
||||
};
|
||||
|
||||
try {
|
||||
require('../app/main.js');
|
||||
require('../app/src/main/index.js');
|
||||
await new Promise(resolve => setImmediate(resolve));
|
||||
|
||||
const rebuild = ipcHandlers.get('settings:rebuildIndex');
|
||||
@@ -750,7 +750,7 @@ test('settings rebuild keeps the existing database after a worker failure', asyn
|
||||
const closedDbPaths = [];
|
||||
const serviceEvents = [];
|
||||
const originalLoad = Module._load;
|
||||
const mainPath = require.resolve('../app/main.js');
|
||||
const mainPath = require.resolve('../app/src/main/index.js');
|
||||
delete require.cache[mainPath];
|
||||
|
||||
class FakeDatabase {
|
||||
@@ -834,7 +834,7 @@ test('settings rebuild keeps the existing database after a worker failure', asyn
|
||||
};
|
||||
|
||||
try {
|
||||
require('../app/main.js');
|
||||
require('../app/src/main/index.js');
|
||||
await new Promise(resolve => setImmediate(resolve));
|
||||
|
||||
const rebuild = ipcHandlers.get('settings:rebuildIndex');
|
||||
@@ -878,7 +878,7 @@ test('settings rebuild cancels an in-flight background build instead of waiting
|
||||
const ipcHandlers = new Map();
|
||||
const serviceEvents = [];
|
||||
const originalLoad = Module._load;
|
||||
const mainPath = require.resolve('../app/main.js');
|
||||
const mainPath = require.resolve('../app/src/main/index.js');
|
||||
delete require.cache[mainPath];
|
||||
|
||||
class FakeDatabase {
|
||||
@@ -963,7 +963,7 @@ test('settings rebuild cancels an in-flight background build instead of waiting
|
||||
};
|
||||
|
||||
try {
|
||||
require('../app/main.js');
|
||||
require('../app/src/main/index.js');
|
||||
await new Promise(resolve => setImmediate(resolve));
|
||||
|
||||
const rebuild = ipcHandlers.get('settings:rebuildIndex');
|
||||
|
||||
@@ -3,7 +3,7 @@ import assert from 'node:assert/strict';
|
||||
import { createRequire } from 'node:module';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const { buildRecapExportQuery, cleanRecapFilename } = require('../app/recap-capture-query.js');
|
||||
const { buildRecapExportQuery, cleanRecapFilename } = require('../app/src/main/recap-capture-query.js');
|
||||
|
||||
test('recap export query includes the selected recap filename', () => {
|
||||
const query = buildRecapExportQuery({
|
||||
|
||||
@@ -155,10 +155,10 @@ test('cover card retrieval and writing choose one dominant human claim', async (
|
||||
test('cover card schema uses claim instead of subtitle', async () => {
|
||||
const pattern = await read('references/recap/pattern1-cover.md');
|
||||
const writing = await read('references/recap/writing1-cover.md');
|
||||
const component = await read('app/renderer/src/components/recap/CoverCard.vue');
|
||||
const detail = await read('app/renderer/src/views/RecapDetail.vue');
|
||||
const list = await read('app/renderer/src/views/RecapList.vue');
|
||||
const mock = await read('app/renderer/src/mock/recap-2026-W24.json');
|
||||
const component = await read('app/src/renderer/src/components/recap/CoverCard.vue');
|
||||
const detail = await read('app/src/renderer/src/views/RecapDetail.vue');
|
||||
const list = await read('app/src/renderer/src/views/RecapList.vue');
|
||||
const mock = await read('app/src/renderer/src/mock/recap-2026-W24.json');
|
||||
|
||||
assert.match(pattern, /claim that lists three topics/i);
|
||||
assert.doesNotMatch(pattern, /subtitle/);
|
||||
@@ -197,8 +197,8 @@ test('thinking card retrieval searches for turns instead of implementation timel
|
||||
test('thinking path schema uses turn instead of outcome', async () => {
|
||||
const pattern = await read('references/recap/pattern2-thinking.md');
|
||||
const writing = await read('references/recap/writing2-thinking.md');
|
||||
const component = await read('app/renderer/src/components/recap/PathCard.vue');
|
||||
const mock = await read('app/renderer/src/mock/recap-2026-W24.json');
|
||||
const component = await read('app/src/renderer/src/components/recap/PathCard.vue');
|
||||
const mock = await read('app/src/renderer/src/mock/recap-2026-W24.json');
|
||||
|
||||
assert.match(pattern, /prompt, turn, and an `evidence` anchor/i);
|
||||
assert.doesNotMatch(pattern, /outcome/);
|
||||
@@ -230,9 +230,9 @@ test('vibe card retrieval finds small user voice, not a correction audit', async
|
||||
|
||||
test('vibe card schema uses voice_lines instead of observations', async () => {
|
||||
const writing = await read('references/recap/writing3-vibe.md');
|
||||
const component = await read('app/renderer/src/components/recap/VibeCard.vue');
|
||||
const detail = await read('app/renderer/src/views/RecapDetail.vue');
|
||||
const mock = await read('app/renderer/src/mock/recap-2026-W24.json');
|
||||
const component = await read('app/src/renderer/src/components/recap/VibeCard.vue');
|
||||
const detail = await read('app/src/renderer/src/views/RecapDetail.vue');
|
||||
const mock = await read('app/src/renderer/src/mock/recap-2026-W24.json');
|
||||
|
||||
assert.match(writing, /voice_lines: Array/);
|
||||
assert.match(writing, /"voice_lines": \[/);
|
||||
@@ -273,8 +273,8 @@ test('workflow card retrieval scopes real workflow runs and user reactions', asy
|
||||
test('workflow card uses reaction instead of outcome for row copy', async () => {
|
||||
const pattern = await read('references/recap/pattern4-workflow.md');
|
||||
const writing = await read('references/recap/writing4-workflow.md');
|
||||
const component = await read('app/renderer/src/components/recap/WorkflowCard.vue');
|
||||
const mock = await read('app/renderer/src/mock/recap-2026-W24.json');
|
||||
const component = await read('app/src/renderer/src/components/recap/WorkflowCard.vue');
|
||||
const mock = await read('app/src/renderer/src/mock/recap-2026-W24.json');
|
||||
|
||||
assert.match(pattern, /items\[\]\.reaction/i);
|
||||
assert.match(writing, /reaction: string/);
|
||||
@@ -288,9 +288,9 @@ test('workflow card uses reaction instead of outcome for row copy', async () =>
|
||||
|
||||
test('workflow card schema uses deck instead of summary for the visible line', async () => {
|
||||
const writing = await read('references/recap/writing4-workflow.md');
|
||||
const component = await read('app/renderer/src/components/recap/WorkflowCard.vue');
|
||||
const detail = await read('app/renderer/src/views/RecapDetail.vue');
|
||||
const mock = await read('app/renderer/src/mock/recap-2026-W24.json');
|
||||
const component = await read('app/src/renderer/src/components/recap/WorkflowCard.vue');
|
||||
const detail = await read('app/src/renderer/src/views/RecapDetail.vue');
|
||||
const mock = await read('app/src/renderer/src/mock/recap-2026-W24.json');
|
||||
|
||||
assert.match(writing, /deck\?: string/);
|
||||
assert.match(writing, /"deck": "你召唤了机器军团。结果各有不同。"/);
|
||||
@@ -323,9 +323,9 @@ test('closing card retrieval and writing keep a small personal receipt', async (
|
||||
test('closing card schema uses receipts instead of stats', async () => {
|
||||
const pattern = await read('references/recap/pattern5-closing.md');
|
||||
const writing = await read('references/recap/writing5-closing.md');
|
||||
const component = await read('app/renderer/src/components/recap/ClosingCard.vue');
|
||||
const detail = await read('app/renderer/src/views/RecapDetail.vue');
|
||||
const mock = await read('app/renderer/src/mock/recap-2026-W24.json');
|
||||
const component = await read('app/src/renderer/src/components/recap/ClosingCard.vue');
|
||||
const detail = await read('app/src/renderer/src/views/RecapDetail.vue');
|
||||
const mock = await read('app/src/renderer/src/mock/recap-2026-W24.json');
|
||||
|
||||
assert.match(pattern, /one or two compact receipts/i);
|
||||
assert.doesNotMatch(pattern, /receipt stats/);
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
createSessionLiveState,
|
||||
consumeSessionDirty,
|
||||
noteSessionUpdated,
|
||||
} from '../app/renderer/src/session-live.mjs';
|
||||
} from '../app/src/renderer/src/session-live.mjs';
|
||||
|
||||
test('session live state marks non-visible updated sessions as dirty', () => {
|
||||
const live = createSessionLiveState();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { buildSidebarProjects } from '../app/renderer/src/sidebar-projects.mjs';
|
||||
import { buildSidebarProjects } from '../app/src/renderer/src/sidebar-projects.mjs';
|
||||
|
||||
test('session sidebar projects follow database recency order instead of label order', () => {
|
||||
const sessions = [
|
||||
|
||||
Reference in New Issue
Block a user