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
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ npm run dev
- Last browser session tabs are restored on startup (up to 20 tabs).
- Renderer supports a coder-focused orange light/dark theme toggle (persisted per user).

## Storage Foundation

- Main-process SQLite storage foundation is initialized from `src/main/storage`.
- Versioned migrations create core tables for bookmarks, history, and session tabs.
- Repository interfaces are ready for feature-layer integration.

## Commit Format Checker

This project enforces Conventional Commits through Husky + commitlint.
Expand Down Expand Up @@ -85,7 +91,7 @@ Before shipping a production app, make sure you have:
Use this gate before cutting a release branch or tag.

1. CI policy:
- PRs to `develop` and `main` must pass quality, smoke, security audit, and Windows package smoke jobs.
- PRs to `develop` and `main` must pass quality, smoke, security audit, and Linux package smoke jobs.
2. Local verification:
- `npm run quality`
- `npm run test:smoke`
Expand Down
19 changes: 19 additions & 0 deletions src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
import { IPC_CHANNELS } from '../shared/ipc-contract';
import type { BrowserBounds, TabSnapshot, TabsStateSnapshot } from '../shared/ipc';
import { isHttpNavigationUrl } from '../shared/url';
import { initializeStorageLayer } from './storage';
import type { StorageLayer } from './storage';

const VITE_DEV_SERVER_URL = process.env.ELECTRON_RENDERER_URL;
const RENDERER_DIST = path.join(__dirname, '../renderer');
Expand Down Expand Up @@ -41,6 +43,7 @@ let attachedView: BrowserView | null = null;
let nextTabId = 1;
let activeTabId: number | null = null;
let tabs: ManagedTab[] = [];
let storageLayer: StorageLayer | null = null;
let persistedStateStore: Store<PersistedStateSchema> | null = null;
let browserBounds: BrowserBounds = {
x: 0,
Expand Down Expand Up @@ -685,6 +688,15 @@ process.on('unhandledRejection', (reason) => {
app.whenReady().then(() => {
configureSessionSecurity();
configureWebContentsSecurity();

try {
storageLayer = initializeStorageLayer({
userDataPath: app.getPath('userData'),
});
} catch (error) {
console.error('[main] failed to initialize SQLite storage layer', error);
}

restoreTabsSession();

createMainWindow();
Expand All @@ -705,6 +717,13 @@ app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
destroyAllTabs();
closeFloatWindow();
storageLayer?.close();
storageLayer = null;
app.quit();
}
});

app.on('before-quit', () => {
storageLayer?.close();
storageLayer = null;
});
12 changes: 12 additions & 0 deletions src/main/storage/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export { initializeStorageLayer, getStorageFilePath } from './sqlite-storage';
export type {
BookmarkRecord,
BookmarksRepository,
HistoryRecord,
HistoryRepository,
SessionRepository,
SessionSnapshot,
SessionTabRecord,
StorageLayer,
StorageOptions,
} from './types';
34 changes: 34 additions & 0 deletions src/main/storage/migrations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { StorageMigration } from './types';

export const STORAGE_MIGRATIONS: StorageMigration[] = [
{
version: 1,
name: 'create_core_storage_tables',
statements: [
`CREATE TABLE IF NOT EXISTS bookmarks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
url TEXT NOT NULL UNIQUE,
title TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
)`,
`CREATE TABLE IF NOT EXISTS history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
url TEXT NOT NULL UNIQUE,
title TEXT NOT NULL,
visit_count INTEGER NOT NULL DEFAULT 1,
last_visited_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
)`,
`CREATE TABLE IF NOT EXISTS session_tabs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
tab_order INTEGER NOT NULL,
url TEXT,
is_active INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
)`,
'CREATE INDEX IF NOT EXISTS idx_bookmarks_updated_at ON bookmarks(updated_at)',
'CREATE INDEX IF NOT EXISTS idx_history_last_visited_at ON history(last_visited_at)',
'CREATE INDEX IF NOT EXISTS idx_session_tabs_order ON session_tabs(tab_order)',
],
},
];
Loading
Loading