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:
tommy0103 committed 2026-07-09 10:20:18 +08:00
1 parent e04c07de05
commit 80c125a572
69 files changed
+572 -78

No files matched your search

+1
View File
@@ -7,3 +7,4 @@ release/
.dev.docs .dev.docs
.claude/ .claude/
dist/ dist/
app/out/
+31
View File
@@ -0,0 +1,31 @@
import { resolve } from 'node:path';
import { defineConfig, externalizeDepsPlugin } from 'electron-vite';
import vue from '@vitejs/plugin-vue';
// Kept CommonJS for the first electron-vite boot (no "type":"module" yet); the
// TS + ESM migration is a later stage. Each main-process module is its own input
// so it is emitted to out/main/<name>.js and the CommonJS require("./x") calls
// between them (and `new Worker(__dirname/indexer-worker.js)`) resolve at runtime.
export default defineConfig({
main: {
plugins: [externalizeDepsPlugin()],
build: {
rollupOptions: {
input: {
index: resolve('src/main/index.js'),
indexer: resolve('src/main/indexer.js'),
'indexer-service': resolve('src/main/indexer-service.js'),
'indexer-worker': resolve('src/main/indexer-worker.js'),
'indexer-worker-client': resolve('src/main/indexer-worker-client.js'),
'recap-capture-query': resolve('src/main/recap-capture-query.js'),
},
},
},
},
preload: {
plugins: [externalizeDepsPlugin()],
},
renderer: {
plugins: [vue()],
},
});
+470
View File
@@ -15,11 +15,171 @@
"@vitejs/plugin-vue": "^5.0.0", "@vitejs/plugin-vue": "^5.0.0",
"electron": "^33.0.0", "electron": "^33.0.0",
"electron-builder": "^25.0.0", "electron-builder": "^25.0.0",
"electron-vite": "^5.0.0",
"vite": "^6.0.0", "vite": "^6.0.0",
"vue": "^3.4.0", "vue": "^3.4.0",
"vue-router": "^4.3.0" "vue-router": "^4.3.0"
} }
}, },
"node_modules/@babel/code-frame": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
"integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-validator-identifier": "^7.29.7",
"js-tokens": "^4.0.0",
"picocolors": "^1.1.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/compat-data": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
"integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/core": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
"integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.29.7",
"@babel/generator": "^7.29.7",
"@babel/helper-compilation-targets": "^7.29.7",
"@babel/helper-module-transforms": "^7.29.7",
"@babel/helpers": "^7.29.7",
"@babel/parser": "^7.29.7",
"@babel/template": "^7.29.7",
"@babel/traverse": "^7.29.7",
"@babel/types": "^7.29.7",
"@jridgewell/remapping": "^2.3.5",
"convert-source-map": "^2.0.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
"json5": "^2.2.3",
"semver": "^6.3.1"
},
"engines": {
"node": ">=6.9.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/babel"
}
},
"node_modules/@babel/generator": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
"integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/parser": "^7.29.7",
"@babel/types": "^7.29.7",
"@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28",
"jsesc": "^3.0.2"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-compilation-targets": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
"integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/compat-data": "^7.29.7",
"@babel/helper-validator-option": "^7.29.7",
"browserslist": "^4.24.0",
"lru-cache": "^5.1.1",
"semver": "^6.3.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"dev": true,
"license": "ISC",
"dependencies": {
"yallist": "^3.0.2"
}
},
"node_modules/@babel/helper-compilation-targets/node_modules/yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true,
"license": "ISC"
},
"node_modules/@babel/helper-globals": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
"integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-imports": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
"integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/traverse": "^7.29.7",
"@babel/types": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-transforms": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
"integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-module-imports": "^7.29.7",
"@babel/helper-validator-identifier": "^7.29.7",
"@babel/traverse": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0"
}
},
"node_modules/@babel/helper-plugin-utils": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
"integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-string-parser": { "node_modules/@babel/helper-string-parser": {
"version": "7.29.7", "version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
@@ -40,6 +200,30 @@
"node": ">=6.9.0" "node": ">=6.9.0"
} }
}, },
"node_modules/@babel/helper-validator-option": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
"integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helpers": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
"integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/template": "^7.29.7",
"@babel/types": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/parser": { "node_modules/@babel/parser": {
"version": "7.29.7", "version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
@@ -56,6 +240,56 @@
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/@babel/plugin-transform-arrow-functions": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.29.7.tgz",
"integrity": "sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/helper-plugin-utils": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/template": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
"integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.29.7",
"@babel/parser": "^7.29.7",
"@babel/types": "^7.29.7"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz",
"integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/code-frame": "^7.29.7",
"@babel/generator": "^7.29.7",
"@babel/helper-globals": "^7.29.7",
"@babel/parser": "^7.29.7",
"@babel/template": "^7.29.7",
"@babel/types": "^7.29.7",
"debug": "^4.3.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/types": { "node_modules/@babel/types": {
"version": "7.29.7", "version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
@@ -1008,6 +1242,38 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1" "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
} }
}, },
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.0",
"@jridgewell/trace-mapping": "^0.3.24"
}
},
"node_modules/@jridgewell/remapping": {
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
"integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.24"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": { "node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.5", "version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
@@ -1015,6 +1281,17 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.31",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
"integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@malept/cross-spawn-promise": { "node_modules/@malept/cross-spawn-promise": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz",
@@ -2230,6 +2507,19 @@
], ],
"license": "MIT" "license": "MIT"
}, },
"node_modules/baseline-browser-mapping": {
"version": "2.10.42",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.42.tgz",
"integrity": "sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"baseline-browser-mapping": "dist/cli.cjs"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/better-sqlite3": { "node_modules/better-sqlite3": {
"version": "11.10.0", "version": "11.10.0",
"resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz", "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.10.0.tgz",
@@ -2300,6 +2590,40 @@
"node": "18 || 20 || >=22" "node": "18 || 20 || >=22"
} }
}, },
"node_modules/browserslist": {
"version": "4.28.5",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.5.tgz",
"integrity": "sha512-Cu2E6QejHWzuDMTkuwgpABFgDfZrXLQq5V13YOACZx4mFAG4IwGTbTfHPMr4WtxlHoXSM8FIuRwYYCz5XiabaQ==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/browserslist"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"baseline-browser-mapping": "^2.10.42",
"caniuse-lite": "^1.0.30001800",
"electron-to-chromium": "^1.5.387",
"node-releases": "^2.0.50",
"update-browserslist-db": "^1.2.3"
},
"bin": {
"browserslist": "cli.js"
},
"engines": {
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
"node_modules/buffer": { "node_modules/buffer": {
"version": "5.7.1", "version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
@@ -2418,6 +2742,16 @@
"node": ">= 10.0.0" "node": ">= 10.0.0"
} }
}, },
"node_modules/cac": {
"version": "6.7.14",
"resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
"integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
}
},
"node_modules/cacache": { "node_modules/cacache": {
"version": "16.1.3", "version": "16.1.3",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz",
@@ -2562,6 +2896,27 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/caniuse-lite": {
"version": "1.0.30001803",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001803.tgz",
"integrity": "sha512-g/uHREV2ZpK9qMalCsWaxmA6ol+DX8GYhuf3T40RKoP+oL7vhRJh8LNt73PCjpnR6l14FzfPrB5Yux4PKm2meg==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/caniuse-lite"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "CC-BY-4.0"
},
"node_modules/chalk": { "node_modules/chalk": {
"version": "4.1.2", "version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -2885,6 +3240,13 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/convert-source-map": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
"dev": true,
"license": "MIT"
},
"node_modules/core-util-is": { "node_modules/core-util-is": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
@@ -3487,6 +3849,43 @@
"node": ">= 10.0.0" "node": ">= 10.0.0"
} }
}, },
"node_modules/electron-to-chromium": {
"version": "1.5.389",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.389.tgz",
"integrity": "sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==",
"dev": true,
"license": "ISC"
},
"node_modules/electron-vite": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/electron-vite/-/electron-vite-5.0.0.tgz",
"integrity": "sha512-OHp/vjdlubNlhNkPkL/+3JD34ii5ov7M0GpuXEVdQeqdQ3ulvVR7Dg/rNBLfS5XPIFwgoBLDf9sjjrL+CuDyRQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/core": "^7.28.4",
"@babel/plugin-transform-arrow-functions": "^7.27.1",
"cac": "^6.7.14",
"esbuild": "^0.25.11",
"magic-string": "^0.30.19",
"picocolors": "^1.1.1"
},
"bin": {
"electron-vite": "bin/electron-vite.js"
},
"engines": {
"node": "^20.19.0 || >=22.12.0"
},
"peerDependencies": {
"@swc/core": "^1.0.0",
"vite": "^5.0.0 || ^6.0.0 || ^7.0.0"
},
"peerDependenciesMeta": {
"@swc/core": {
"optional": true
}
}
},
"node_modules/emoji-regex": { "node_modules/emoji-regex": {
"version": "8.0.0", "version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
@@ -3944,6 +4343,16 @@
"node": "^12.13.0 || ^14.15.0 || >=16.0.0" "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
} }
}, },
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/get-caller-file": { "node_modules/get-caller-file": {
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
@@ -4537,6 +4946,13 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true,
"license": "MIT"
},
"node_modules/js-yaml": { "node_modules/js-yaml": {
"version": "4.2.0", "version": "4.2.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.2.0.tgz",
@@ -4560,6 +4976,19 @@
"js-yaml": "bin/js-yaml.js" "js-yaml": "bin/js-yaml.js"
} }
}, },
"node_modules/jsesc": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
"dev": true,
"license": "MIT",
"bin": {
"jsesc": "bin/jsesc"
},
"engines": {
"node": ">=6"
}
},
"node_modules/json-buffer": { "node_modules/json-buffer": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
@@ -5206,6 +5635,16 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/node-releases": {
"version": "2.0.50",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.50.tgz",
"integrity": "sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/nopt": { "node_modules/nopt": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz",
@@ -6533,6 +6972,37 @@
"node": ">= 4.0.0" "node": ">= 4.0.0"
} }
}, },
"node_modules/update-browserslist-db": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
"integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/browserslist"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"escalade": "^3.2.0",
"picocolors": "^1.1.1"
},
"bin": {
"update-browserslist-db": "cli.js"
},
"peerDependencies": {
"browserslist": ">= 4.21.0"
}
},
"node_modules/uri-js": { "node_modules/uri-js": {
"version": "4.4.1", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+14 -19
View File
@@ -2,18 +2,16 @@
"name": "obelisk", "name": "obelisk",
"version": "0.1.0", "version": "0.1.0",
"description": "Memory management for Obelisk — let Claude Code search its own memory", "description": "Memory management for Obelisk — let Claude Code search its own memory",
"main": "main.js", "main": "out/main/index.js",
"scripts": { "scripts": {
"start": "electron .", "start": "electron-vite preview",
"dev": "node scripts/dev.js", "dev": "electron-vite dev",
"dev:renderer": "vite renderer", "build": "electron-vite build && electron-builder",
"build:renderer": "vite build renderer", "pack": "electron-vite build && electron-builder --dir",
"build": "npm run build:renderer && electron-builder", "dist": "electron-vite build && electron-builder --mac --win --linux",
"pack": "npm run build:renderer && electron-builder --dir", "dist:mac": "electron-vite build && electron-builder --mac",
"dist": "npm run build:renderer && electron-builder --mac --win --linux", "dist:win": "electron-vite build && electron-builder --win",
"dist:mac": "npm run build:renderer && electron-builder --mac", "dist:linux": "electron-vite build && electron-builder --linux"
"dist:win": "npm run build:renderer && electron-builder --win",
"dist:linux": "npm run build:renderer && electron-builder --linux"
}, },
"build": { "build": {
"appId": "com.obelisk.app", "appId": "com.obelisk.app",
@@ -45,14 +43,10 @@
"category": "Development" "category": "Development"
}, },
"files": [ "files": [
"main.js", "out/**"
"preload.js", ],
"recap-capture-query.js", "asarUnpack": [
"indexer.js", "**/node_modules/better-sqlite3/**"
"indexer-service.js",
"indexer-worker.js",
"indexer-worker-client.js",
"dist-renderer/**/*"
], ],
"extraResources": [ "extraResources": [
{ {
@@ -72,6 +66,7 @@
"@vitejs/plugin-vue": "^5.0.0", "@vitejs/plugin-vue": "^5.0.0",
"electron": "^33.0.0", "electron": "^33.0.0",
"electron-builder": "^25.0.0", "electron-builder": "^25.0.0",
"electron-vite": "^5.0.0",
"vite": "^6.0.0", "vite": "^6.0.0",
"vue": "^3.4.0", "vue": "^3.4.0",
"vue-router": "^4.3.0" "vue-router": "^4.3.0"
-11
View File
@@ -1,11 +0,0 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
base: './',
build: {
outDir: '../dist-renderer',
emptyOutDir: true,
},
})
+8 -8
View File
@@ -241,7 +241,7 @@ async function stopBackgroundResources({ stopWorker = false } = {}) {
} }
function createWindow() { function createWindow() {
const isDev = process.argv.includes('--dev'); const isDev = process.argv.includes('--dev') || !!process.env.ELECTRON_RENDERER_URL;
const shouldOpenDevTools = process.argv.includes('--devtools'); const shouldOpenDevTools = process.argv.includes('--devtools');
const win = new BrowserWindow({ const win = new BrowserWindow({
@@ -253,7 +253,7 @@ function createWindow() {
trafficLightPosition: { x: 14, y: 10 }, trafficLightPosition: { x: 14, y: 10 },
backgroundColor: '#0a0b14', backgroundColor: '#0a0b14',
webPreferences: { webPreferences: {
preload: path.join(__dirname, 'preload.js'), preload: path.join(__dirname, '..', 'preload', 'index.js'),
contextIsolation: true, contextIsolation: true,
nodeIntegration: false, nodeIntegration: false,
devTools: isDev || shouldOpenDevTools, devTools: isDev || shouldOpenDevTools,
@@ -268,12 +268,12 @@ function createWindow() {
}); });
if (isDev) { if (isDev) {
win.loadURL(process.env.OBELISK_DEV_SERVER_URL || 'http://localhost:5173'); win.loadURL(process.env.ELECTRON_RENDERER_URL || process.env.OBELISK_DEV_SERVER_URL || 'http://localhost:5173');
if (shouldOpenDevTools) { if (shouldOpenDevTools) {
win.webContents.openDevTools(); win.webContents.openDevTools();
} }
} else { } else {
win.loadFile(path.join(__dirname, 'dist-renderer', 'index.html')); win.loadFile(path.join(__dirname, '..', 'renderer', 'index.html'));
} }
} }
@@ -607,7 +607,7 @@ async function createExportCapture(parentWin, query) {
height: EXPORT_HEIGHT, height: EXPORT_HEIGHT,
show: false, show: false,
webPreferences: { webPreferences: {
preload: path.join(__dirname, 'preload.js'), preload: path.join(__dirname, '..', 'preload', 'index.js'),
contextIsolation: true, contextIsolation: true,
nodeIntegration: false, nodeIntegration: false,
offscreen: true, offscreen: true,
@@ -615,10 +615,10 @@ async function createExportCapture(parentWin, query) {
}, },
}); });
const isDev = process.argv.includes('--dev'); const isDev = process.argv.includes('--dev') || !!process.env.ELECTRON_RENDERER_URL;
const url = isDev const url = isDev
? `http://localhost:5173/#/recap-export?${query}` ? `${process.env.ELECTRON_RENDERER_URL || 'http://localhost:5173'}/#/recap-export?${query}`
: `file://${path.join(__dirname, 'dist-renderer', 'index.html')}#/recap-export?${query}`; : `file://${path.join(__dirname, '..', 'renderer', 'index.html')}#/recap-export?${query}`;
await exportWin.loadURL(url); await exportWin.loadURL(url);
await waitForExportReady(exportWin.webContents); await waitForExportReady(exportWin.webContents);
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1 -1
View File
@@ -14,7 +14,7 @@ const DEFAULT_HISTORY_PATH = path.join(DEFAULT_CLAUDE_DIR, 'history.jsonl');
function resolveSchemaPath() { function resolveSchemaPath() {
const candidates = [ const candidates = [
path.join(__dirname, 'schema.sql'), path.join(__dirname, 'schema.sql'),
path.join(__dirname, '..', 'scripts', 'schema.sql'), path.join(__dirname, '..', '..', '..', 'scripts', 'schema.sql'),
process.resourcesPath ? path.join(process.resourcesPath, 'scripts', 'schema.sql') : null, process.resourcesPath ? path.join(process.resourcesPath, 'scripts', 'schema.sql') : null,
].filter(Boolean); ].filter(Boolean);
const found = candidates.find(p => fs.existsSync(p)); const found = candidates.find(p => fs.existsSync(p));
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+8
View File
@@ -0,0 +1,8 @@
--- 2026-06-18 Activity noise folding phase ---
- Task: fold noise sessions/projects in Activity.vue (monthly + daily lists)
- Noise rules already used: NOISE_PROJECT_RE = /^(od-conn-test|[0-9a-f]{6,})/i in App.vue; SessionList: !s.title means noise
- Activity.vue has 3 group types per block: newWorkspaces / newSessions / continued
- Approach: per-block compute noise split, render normal first then single fold banner toggling all noise in that block
- Default collapsed; banner says 'N hidden — likely test/throwaway runs'
+1 -1
View File
@@ -6,7 +6,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path'; import { join } from 'node:path';
const require = createRequire(import.meta.url); const require = createRequire(import.meta.url);
const { createIndexerService } = require('../app/indexer-service.js'); const { createIndexerService } = require('../app/src/main/indexer-service.js');
function manualTimers() { function manualTimers() {
const timers = new Set(); const timers = new Set();
+1 -1
View File
@@ -3,7 +3,7 @@ import assert from 'node:assert/strict';
import { createRequire } from 'node:module'; import { createRequire } from 'node:module';
const require = createRequire(import.meta.url); 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 () => { test('worker build client resolves build results from a worker thread', async () => {
const instances = []; const instances = [];
+1 -1
View File
@@ -6,7 +6,7 @@ import { tmpdir } from 'node:os';
import { join } from 'node:path'; import { join } from 'node:path';
const require = createRequire(import.meta.url); 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'); const { DatabaseSync } = require('node:sqlite');
class TestDatabase { class TestDatabase {
+16 -16
View File
@@ -12,7 +12,7 @@ async function loadMainForWindowFlags(flags) {
const originalArgv = process.argv; const originalArgv = process.argv;
const originalHome = process.env.HOME; const originalHome = process.env.HOME;
const originalLoad = Module._load; 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()}`); const home = join(tmpdir(), `obelisk-window-flags-${Date.now()}-${Math.random()}`);
mkdirSync(join(home, '.obelisk'), { recursive: true }); mkdirSync(join(home, '.obelisk'), { recursive: true });
writeFileSync(join(home, '.obelisk', 'obelisk.sqlite'), ''); writeFileSync(join(home, '.obelisk', 'obelisk.sqlite'), '');
@@ -93,7 +93,7 @@ async function loadMainForWindowFlags(flags) {
}; };
try { try {
require('../app/main.js'); require('../app/src/main/index.js');
await new Promise(resolve => setImmediate(resolve)); await new Promise(resolve => setImmediate(resolve));
return windows; return windows;
} finally { } finally {
@@ -137,7 +137,7 @@ test('main process watches Codex sessions directory instead of Codex root', asyn
process.env.HOME = home; process.env.HOME = home;
const serviceOptions = []; const serviceOptions = [];
const mainPath = require.resolve('../app/main.js'); const mainPath = require.resolve('../app/src/main/index.js');
delete require.cache[mainPath]; delete require.cache[mainPath];
class FakeDatabase { class FakeDatabase {
@@ -202,7 +202,7 @@ test('main process watches Codex sessions directory instead of Codex root', asyn
}; };
try { try {
require('../app/main.js'); require('../app/src/main/index.js');
await new Promise(resolve => setImmediate(resolve)); await new Promise(resolve => setImmediate(resolve));
assert.equal(serviceOptions.length, 1); 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 ipcHandlers = new Map();
const queries = []; const queries = [];
const mainPath = require.resolve('../app/main.js'); const mainPath = require.resolve('../app/src/main/index.js');
delete require.cache[mainPath]; delete require.cache[mainPath];
class FakeDatabase { class FakeDatabase {
@@ -305,7 +305,7 @@ test('session IPC hides Codex rows by default and supports explicit source opt-i
}; };
try { try {
require('../app/main.js'); require('../app/src/main/index.js');
await new Promise(resolve => setImmediate(resolve)); await new Promise(resolve => setImmediate(resolve));
ipcHandlers.get('db:getSessions')(null, {}); ipcHandlers.get('db:getSessions')(null, {});
@@ -373,7 +373,7 @@ test('main process migrates an existing app database before source-filtered IPC
legacy.close(); legacy.close();
const ipcHandlers = new Map(); const ipcHandlers = new Map();
const mainPath = require.resolve('../app/main.js'); const mainPath = require.resolve('../app/src/main/index.js');
delete require.cache[mainPath]; delete require.cache[mainPath];
class FakeBrowserWindow { class FakeBrowserWindow {
@@ -454,7 +454,7 @@ test('main process migrates an existing app database before source-filtered IPC
}; };
try { try {
require('../app/main.js'); require('../app/src/main/index.js');
await new Promise(resolve => setImmediate(resolve)); await new Promise(resolve => setImmediate(resolve));
const sessions = ipcHandlers.get('db:getSessions')(null, {}); 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()}`); const home = join(tmpdir(), `obelisk-main-window-${Date.now()}`);
mkdirSync(join(home, '.obelisk'), { recursive: true }); mkdirSync(join(home, '.obelisk'), { recursive: true });
writeFileSync(join(home, '.obelisk', 'obelisk.sqlite'), ''); 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]; delete require.cache[mainPath];
process.env.HOME = home; process.env.HOME = home;
Object.defineProperty(process, 'platform', { value: 'darwin' }); Object.defineProperty(process, 'platform', { value: 'darwin' });
@@ -561,7 +561,7 @@ test('closing the last macOS window releases background resources until activati
}; };
try { try {
require('../app/main.js'); require('../app/src/main/index.js');
await new Promise(resolve => setImmediate(resolve)); await new Promise(resolve => setImmediate(resolve));
assert.equal(windows.length, 1); assert.equal(windows.length, 1);
@@ -618,7 +618,7 @@ test('settings rebuild reopens the database from the configured Claude path', as
const buildCalls = []; const buildCalls = [];
const serviceEvents = []; const serviceEvents = [];
const originalLoad = Module._load; const originalLoad = Module._load;
const mainPath = require.resolve('../app/main.js'); const mainPath = require.resolve('../app/src/main/index.js');
delete require.cache[mainPath]; delete require.cache[mainPath];
class FakeDatabase { class FakeDatabase {
@@ -703,7 +703,7 @@ test('settings rebuild reopens the database from the configured Claude path', as
}; };
try { try {
require('../app/main.js'); require('../app/src/main/index.js');
await new Promise(resolve => setImmediate(resolve)); await new Promise(resolve => setImmediate(resolve));
const rebuild = ipcHandlers.get('settings:rebuildIndex'); 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 closedDbPaths = [];
const serviceEvents = []; const serviceEvents = [];
const originalLoad = Module._load; const originalLoad = Module._load;
const mainPath = require.resolve('../app/main.js'); const mainPath = require.resolve('../app/src/main/index.js');
delete require.cache[mainPath]; delete require.cache[mainPath];
class FakeDatabase { class FakeDatabase {
@@ -834,7 +834,7 @@ test('settings rebuild keeps the existing database after a worker failure', asyn
}; };
try { try {
require('../app/main.js'); require('../app/src/main/index.js');
await new Promise(resolve => setImmediate(resolve)); await new Promise(resolve => setImmediate(resolve));
const rebuild = ipcHandlers.get('settings:rebuildIndex'); 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 ipcHandlers = new Map();
const serviceEvents = []; const serviceEvents = [];
const originalLoad = Module._load; const originalLoad = Module._load;
const mainPath = require.resolve('../app/main.js'); const mainPath = require.resolve('../app/src/main/index.js');
delete require.cache[mainPath]; delete require.cache[mainPath];
class FakeDatabase { class FakeDatabase {
@@ -963,7 +963,7 @@ test('settings rebuild cancels an in-flight background build instead of waiting
}; };
try { try {
require('../app/main.js'); require('../app/src/main/index.js');
await new Promise(resolve => setImmediate(resolve)); await new Promise(resolve => setImmediate(resolve));
const rebuild = ipcHandlers.get('settings:rebuildIndex'); const rebuild = ipcHandlers.get('settings:rebuildIndex');
+1 -1
View File
@@ -3,7 +3,7 @@ import assert from 'node:assert/strict';
import { createRequire } from 'node:module'; import { createRequire } from 'node:module';
const require = createRequire(import.meta.url); 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', () => { test('recap export query includes the selected recap filename', () => {
const query = buildRecapExportQuery({ const query = buildRecapExportQuery({
+17 -17
View File
@@ -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 () => { test('cover card schema uses claim instead of subtitle', async () => {
const pattern = await read('references/recap/pattern1-cover.md'); const pattern = await read('references/recap/pattern1-cover.md');
const writing = await read('references/recap/writing1-cover.md'); const writing = await read('references/recap/writing1-cover.md');
const component = await read('app/renderer/src/components/recap/CoverCard.vue'); const component = await read('app/src/renderer/src/components/recap/CoverCard.vue');
const detail = await read('app/renderer/src/views/RecapDetail.vue'); const detail = await read('app/src/renderer/src/views/RecapDetail.vue');
const list = await read('app/renderer/src/views/RecapList.vue'); const list = await read('app/src/renderer/src/views/RecapList.vue');
const mock = await read('app/renderer/src/mock/recap-2026-W24.json'); const mock = await read('app/src/renderer/src/mock/recap-2026-W24.json');
assert.match(pattern, /claim that lists three topics/i); assert.match(pattern, /claim that lists three topics/i);
assert.doesNotMatch(pattern, /subtitle/); 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 () => { test('thinking path schema uses turn instead of outcome', async () => {
const pattern = await read('references/recap/pattern2-thinking.md'); const pattern = await read('references/recap/pattern2-thinking.md');
const writing = await read('references/recap/writing2-thinking.md'); const writing = await read('references/recap/writing2-thinking.md');
const component = await read('app/renderer/src/components/recap/PathCard.vue'); const component = await read('app/src/renderer/src/components/recap/PathCard.vue');
const mock = await read('app/renderer/src/mock/recap-2026-W24.json'); const mock = await read('app/src/renderer/src/mock/recap-2026-W24.json');
assert.match(pattern, /prompt, turn, and an `evidence` anchor/i); assert.match(pattern, /prompt, turn, and an `evidence` anchor/i);
assert.doesNotMatch(pattern, /outcome/); 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 () => { test('vibe card schema uses voice_lines instead of observations', async () => {
const writing = await read('references/recap/writing3-vibe.md'); const writing = await read('references/recap/writing3-vibe.md');
const component = await read('app/renderer/src/components/recap/VibeCard.vue'); const component = await read('app/src/renderer/src/components/recap/VibeCard.vue');
const detail = await read('app/renderer/src/views/RecapDetail.vue'); const detail = await read('app/src/renderer/src/views/RecapDetail.vue');
const mock = await read('app/renderer/src/mock/recap-2026-W24.json'); const mock = await read('app/src/renderer/src/mock/recap-2026-W24.json');
assert.match(writing, /voice_lines: Array/); assert.match(writing, /voice_lines: Array/);
assert.match(writing, /"voice_lines": \[/); 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 () => { test('workflow card uses reaction instead of outcome for row copy', async () => {
const pattern = await read('references/recap/pattern4-workflow.md'); const pattern = await read('references/recap/pattern4-workflow.md');
const writing = await read('references/recap/writing4-workflow.md'); const writing = await read('references/recap/writing4-workflow.md');
const component = await read('app/renderer/src/components/recap/WorkflowCard.vue'); const component = await read('app/src/renderer/src/components/recap/WorkflowCard.vue');
const mock = await read('app/renderer/src/mock/recap-2026-W24.json'); const mock = await read('app/src/renderer/src/mock/recap-2026-W24.json');
assert.match(pattern, /items\[\]\.reaction/i); assert.match(pattern, /items\[\]\.reaction/i);
assert.match(writing, /reaction: string/); 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 () => { test('workflow card schema uses deck instead of summary for the visible line', async () => {
const writing = await read('references/recap/writing4-workflow.md'); const writing = await read('references/recap/writing4-workflow.md');
const component = await read('app/renderer/src/components/recap/WorkflowCard.vue'); const component = await read('app/src/renderer/src/components/recap/WorkflowCard.vue');
const detail = await read('app/renderer/src/views/RecapDetail.vue'); const detail = await read('app/src/renderer/src/views/RecapDetail.vue');
const mock = await read('app/renderer/src/mock/recap-2026-W24.json'); const mock = await read('app/src/renderer/src/mock/recap-2026-W24.json');
assert.match(writing, /deck\?: string/); assert.match(writing, /deck\?: string/);
assert.match(writing, /"deck": "你召唤了机器军团。结果各有不同。"/); 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 () => { test('closing card schema uses receipts instead of stats', async () => {
const pattern = await read('references/recap/pattern5-closing.md'); const pattern = await read('references/recap/pattern5-closing.md');
const writing = await read('references/recap/writing5-closing.md'); const writing = await read('references/recap/writing5-closing.md');
const component = await read('app/renderer/src/components/recap/ClosingCard.vue'); const component = await read('app/src/renderer/src/components/recap/ClosingCard.vue');
const detail = await read('app/renderer/src/views/RecapDetail.vue'); const detail = await read('app/src/renderer/src/views/RecapDetail.vue');
const mock = await read('app/renderer/src/mock/recap-2026-W24.json'); const mock = await read('app/src/renderer/src/mock/recap-2026-W24.json');
assert.match(pattern, /one or two compact receipts/i); assert.match(pattern, /one or two compact receipts/i);
assert.doesNotMatch(pattern, /receipt stats/); assert.doesNotMatch(pattern, /receipt stats/);
+1 -1
View File
@@ -5,7 +5,7 @@ import {
createSessionLiveState, createSessionLiveState,
consumeSessionDirty, consumeSessionDirty,
noteSessionUpdated, 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', () => { test('session live state marks non-visible updated sessions as dirty', () => {
const live = createSessionLiveState(); const live = createSessionLiveState();
+1 -1
View File
@@ -1,6 +1,6 @@
import { test } from 'node:test'; import { test } from 'node:test';
import assert from 'node:assert/strict'; 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', () => { test('session sidebar projects follow database recency order instead of label order', () => {
const sessions = [ const sessions = [