diff --git a/api/modules/index.ts b/api/modules/index.ts index 2acb2b2..f3b2fb3 100644 --- a/api/modules/index.ts +++ b/api/modules/index.ts @@ -1,51 +1,59 @@ -import { readdirSync, statSync } from 'fs' -import { join, dirname } from 'path' -import { fileURLToPath } from 'url' import type { ApiModule } from '../infra/types.js' -const __filename = fileURLToPath(import.meta.url) -const __dirname = dirname(__filename) - -const moduleFactoryPattern = /^create\w+Module$/ - async function discoverModules(): Promise { + return await getStaticModules() +} + +async function getStaticModules(): Promise { const modules: ApiModule[] = [] - const entries = readdirSync(__dirname) - for (const entry of entries) { - const entryPath = join(__dirname, entry) + try { + const { createTodoModule } = await import('./todo/index.js') + modules.push(createTodoModule()) + } catch (e) { + console.warn('[ModuleLoader] Failed to load todo module:', e) + } - try { - const stats = statSync(entryPath) - if (!stats.isDirectory()) { - continue - } + try { + const { createTimeTrackingModule } = await import('./time-tracking/index.js') + modules.push(createTimeTrackingModule()) + } catch (e) { + console.warn('[ModuleLoader] Failed to load time-tracking module:', e) + } - const moduleIndexPath = join(entryPath, 'index.ts') - let moduleIndexStats: ReturnType - try { - moduleIndexStats = statSync(moduleIndexPath) - } catch { - continue - } - if (!moduleIndexStats.isFile()) { - continue - } + try { + const { createRecycleBinModule } = await import('./recycle-bin/index.js') + modules.push(createRecycleBinModule()) + } catch (e) { + console.warn('[ModuleLoader] Failed to load recycle-bin module:', e) + } - const moduleExports = await import(`./${entry}/index.js`) + try { + const { createPyDemosModule } = await import('./pydemos/index.js') + modules.push(createPyDemosModule()) + } catch (e) { + console.warn('[ModuleLoader] Failed to load pydemos module:', e) + } - for (const exportName of Object.keys(moduleExports)) { - if (moduleFactoryPattern.test(exportName)) { - const factory = moduleExports[exportName] - if (typeof factory === 'function') { - const module = factory() as ApiModule - modules.push(module) - } - } - } - } catch (error) { - console.warn(`[ModuleLoader] Failed to load module '${entry}':`, error) - } + try { + const { createDocumentParserModule } = await import('./document-parser/index.js') + modules.push(createDocumentParserModule()) + } catch (e) { + console.warn('[ModuleLoader] Failed to load document-parser module:', e) + } + + try { + const { createAiModule } = await import('./ai/index.js') + modules.push(createAiModule()) + } catch (e) { + console.warn('[ModuleLoader] Failed to load ai module:', e) + } + + try { + const { createRemoteModule } = await import('./remote/index.js') + modules.push(createRemoteModule()) + } catch (e) { + console.warn('[ModuleLoader] Failed to load remote module:', e) } modules.sort((a, b) => { diff --git a/api/watcher/watcher.ts b/api/watcher/watcher.ts index 93d7f0c..734481b 100644 --- a/api/watcher/watcher.ts +++ b/api/watcher/watcher.ts @@ -1,6 +1,6 @@ import chokidar, { FSWatcher } from 'chokidar'; import path from 'path'; -import { NOTEBOOK_ROOT } from '../config/paths.js'; +import { config } from '../config/index.js'; import { eventBus } from '../events/eventBus.js'; import { logger } from '../utils/logger.js'; import { toPosixPath } from '../../shared/utils/path.js'; @@ -10,16 +10,17 @@ let watcher: FSWatcher | null = null; export const startWatcher = (): void => { if (watcher) return; - logger.info(`Starting file watcher for: ${NOTEBOOK_ROOT}`); + const notebookRoot = config.notebookRoot; + logger.info(`Starting file watcher for: ${notebookRoot}`); - watcher = chokidar.watch(NOTEBOOK_ROOT, { + watcher = chokidar.watch(notebookRoot, { ignored: /(^|[\/\\])\../, persistent: true, ignoreInitial: true, }); const broadcast = (event: string, changedPath: string) => { - const rel = path.relative(NOTEBOOK_ROOT, changedPath); + const rel = path.relative(notebookRoot, changedPath); if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) return; logger.info(`File event: ${event} - ${rel}`); eventBus.broadcast({ event, path: toPosixPath(rel) }); diff --git a/electron/main.ts b/electron/main.ts index cdced87..0856203 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -13,16 +13,6 @@ log.initialize(); const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -process.env.NOTEBOOK_ROOT = path.join(app.getPath('documents'), 'XCDesktop'); - -if (!fs.existsSync(process.env.NOTEBOOK_ROOT)) { - try { - fs.mkdirSync(process.env.NOTEBOOK_ROOT, { recursive: true }); - } catch (err) { - log.error('Failed to create notebook directory:', err); - } -} - electronState.setDevelopment(!app.isPackaged); let opencodeProcess: ChildProcess | null = null; @@ -443,6 +433,16 @@ async function startServer() { } app.whenReady().then(async () => { + process.env.NOTEBOOK_ROOT = path.join(app.getPath('documents'), 'XCDesktop'); + + if (!fs.existsSync(process.env.NOTEBOOK_ROOT)) { + try { + fs.mkdirSync(process.env.NOTEBOOK_ROOT, { recursive: true }); + } catch (err) { + log.error('Failed to create notebook directory:', err); + } + } + await startServer(); await createWindow(); diff --git a/package.json b/package.json index bbd3e72..2f60ff8 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "watch:electron": "tsup --config tsup.electron.ts --watch", "electron:dev": "concurrently -k \"npm run server:dev\" \"npm run client:dev\" \"wait-on tcp:3001 tcp:5173 && npm run watch:electron\"", "build:electron": "tsup --config tsup.electron.ts", - "build:api": "tsup electron/server.ts --format esm --out-dir dist-api --clean --no-splitting --target esnext", + "build:api": "tsup electron/server.ts --format esm --out-dir dist-api --clean --target esnext --external electron", "electron:build": "npm run build && npm run build:electron && npm run build:api && electron-builder" }, "dependencies": {