feat: added dataPath for projects
This commit is contained in:
@@ -401,6 +401,14 @@ export class DatabaseConnection {
|
||||
console.log('Tags table created successfully');
|
||||
}
|
||||
|
||||
// Migration: Add data_path column to projects table
|
||||
const dataPathCol = await this.localClient.execute(
|
||||
"SELECT name FROM pragma_table_info('projects') WHERE name = 'data_path'"
|
||||
);
|
||||
if (dataPathCol.rows.length === 0) {
|
||||
await this.localClient.execute("ALTER TABLE projects ADD COLUMN data_path TEXT");
|
||||
}
|
||||
|
||||
// Create default project if none exists
|
||||
const existingProjects = await this.localClient.execute('SELECT COUNT(*) as count FROM projects');
|
||||
if (existingProjects.rows[0] && (existingProjects.rows[0].count as number) === 0) {
|
||||
|
||||
@@ -6,6 +6,7 @@ export const projects = sqliteTable('projects', {
|
||||
name: text('name').notNull(),
|
||||
slug: text('slug').notNull().unique(),
|
||||
description: text('description'),
|
||||
dataPath: text('data_path'), // Custom path for project data (null = default userData/projects/{id})
|
||||
createdAt: integer('created_at', { mode: 'timestamp' }).notNull(),
|
||||
updatedAt: integer('updated_at', { mode: 'timestamp' }).notNull(),
|
||||
isActive: integer('is_active', { mode: 'boolean' }).notNull().default(false),
|
||||
|
||||
@@ -49,14 +49,20 @@ export interface MediaMetadata {
|
||||
|
||||
export class MediaEngine extends EventEmitter {
|
||||
private currentProjectId: string = 'default';
|
||||
private projectBaseDir: string | null = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
private getMediaBaseDir(): string {
|
||||
private getProjectBaseDir(): string {
|
||||
if (this.projectBaseDir) return this.projectBaseDir;
|
||||
const userDataPath = app.getPath('userData');
|
||||
return path.join(userDataPath, 'projects', this.currentProjectId, 'media');
|
||||
return path.join(userDataPath, 'projects', this.currentProjectId);
|
||||
}
|
||||
|
||||
private getMediaBaseDir(): string {
|
||||
return path.join(this.getProjectBaseDir(), 'media');
|
||||
}
|
||||
|
||||
private getMediaDir(): string {
|
||||
@@ -85,8 +91,9 @@ export class MediaEngine extends EventEmitter {
|
||||
return path.join(dir, `${id}${extension}`);
|
||||
}
|
||||
|
||||
setProjectContext(projectId: string): void {
|
||||
setProjectContext(projectId: string, baseDir?: string): void {
|
||||
this.currentProjectId = projectId;
|
||||
this.projectBaseDir = baseDir || null;
|
||||
}
|
||||
|
||||
getProjectContext(): string {
|
||||
@@ -101,8 +108,7 @@ export class MediaEngine extends EventEmitter {
|
||||
* Get the thumbnails directory for the current project
|
||||
*/
|
||||
private getThumbnailsDir(): string {
|
||||
const userDataPath = app.getPath('userData');
|
||||
return path.join(userDataPath, 'projects', this.currentProjectId, 'thumbnails');
|
||||
return path.join(this.getProjectBaseDir(), 'thumbnails');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,6 +12,7 @@ import { posts, projects } from '../database/schema';
|
||||
export interface ProjectMetadata {
|
||||
name: string;
|
||||
description?: string;
|
||||
dataPath?: string; // Custom path for project data
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -31,6 +32,7 @@ export const DEFAULT_CATEGORIES = ['article', 'picture', 'aside', 'page'];
|
||||
*/
|
||||
export class MetaEngine extends EventEmitter {
|
||||
private currentProjectId: string = 'default';
|
||||
private projectBaseDir: string | null = null;
|
||||
private tags: Set<string> = new Set();
|
||||
private categories: Set<string> = new Set();
|
||||
private projectMetadata: ProjectMetadata | null = null;
|
||||
@@ -40,13 +42,18 @@ export class MetaEngine extends EventEmitter {
|
||||
super();
|
||||
}
|
||||
|
||||
private getProjectBaseDir(): string {
|
||||
if (this.projectBaseDir) return this.projectBaseDir;
|
||||
const userDataPath = app.getPath('userData');
|
||||
return path.join(userDataPath, 'projects', this.currentProjectId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the meta directory path for the current project.
|
||||
* Format: {userData}/projects/{projectId}/meta/
|
||||
* Format: {baseDir}/meta/
|
||||
*/
|
||||
getMetaDir(): string {
|
||||
const userDataPath = app.getPath('userData');
|
||||
return path.join(userDataPath, 'projects', this.currentProjectId, 'meta');
|
||||
return path.join(this.getProjectBaseDir(), 'meta');
|
||||
}
|
||||
|
||||
private getTagsFilePath(): string {
|
||||
@@ -61,8 +68,9 @@ export class MetaEngine extends EventEmitter {
|
||||
return path.join(this.getMetaDir(), 'project.json');
|
||||
}
|
||||
|
||||
setProjectContext(projectId: string): void {
|
||||
setProjectContext(projectId: string, baseDir?: string): void {
|
||||
this.currentProjectId = projectId;
|
||||
this.projectBaseDir = baseDir || null;
|
||||
// Reset in-memory cache when project changes
|
||||
this.tags.clear();
|
||||
this.categories.clear();
|
||||
@@ -334,14 +342,14 @@ export class MetaEngine extends EventEmitter {
|
||||
/**
|
||||
* Fetch the current project's data from the database.
|
||||
*/
|
||||
private async fetchProjectFromDatabase(): Promise<{ name: string; description: string | null } | null> {
|
||||
private async fetchProjectFromDatabase(): Promise<{ name: string; description: string | null; dataPath: string | null } | null> {
|
||||
const db = getDatabase().getLocal();
|
||||
const project = await db
|
||||
.select({ name: projects.name, description: projects.description })
|
||||
.select({ name: projects.name, description: projects.description, dataPath: projects.dataPath })
|
||||
.from(projects)
|
||||
.where(eq(projects.id, this.currentProjectId))
|
||||
.get();
|
||||
|
||||
|
||||
return project || null;
|
||||
}
|
||||
|
||||
@@ -459,6 +467,18 @@ export class MetaEngine extends EventEmitter {
|
||||
// Handle project metadata
|
||||
if (projectMetadataFileExists) {
|
||||
await this.loadProjectMetadata();
|
||||
|
||||
// If project.json has a dataPath, sync it back to the database
|
||||
if (this.projectMetadata?.dataPath !== undefined) {
|
||||
const projectData = await this.fetchProjectFromDatabase();
|
||||
if (projectData && projectData.dataPath !== this.projectMetadata.dataPath) {
|
||||
const db = getDatabase().getLocal();
|
||||
await db.update(projects)
|
||||
.set({ dataPath: this.projectMetadata.dataPath || null })
|
||||
.where(eq(projects.id, this.currentProjectId));
|
||||
console.log(`[MetaEngine] Synced dataPath from project.json to database: ${this.projectMetadata.dataPath || '(default)'}`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No file exists, fetch project data from database and create file
|
||||
const projectData = await this.fetchProjectFromDatabase();
|
||||
@@ -468,6 +488,7 @@ export class MetaEngine extends EventEmitter {
|
||||
this.projectMetadata = {
|
||||
name: projectData.name,
|
||||
description: projectData.description || undefined,
|
||||
dataPath: projectData.dataPath || undefined,
|
||||
};
|
||||
await this.saveProjectMetadata();
|
||||
}
|
||||
|
||||
@@ -142,9 +142,16 @@ export class PostEngine extends EventEmitter {
|
||||
await client.execute({ sql: 'DELETE FROM posts_fts WHERE id = ?', args: [id] });
|
||||
}
|
||||
|
||||
private getPostsBaseDir(): string {
|
||||
private projectBaseDir: string | null = null;
|
||||
|
||||
private getProjectBaseDir(): string {
|
||||
if (this.projectBaseDir) return this.projectBaseDir;
|
||||
const userDataPath = app.getPath('userData');
|
||||
return path.join(userDataPath, 'projects', this.currentProjectId, 'posts');
|
||||
return path.join(userDataPath, 'projects', this.currentProjectId);
|
||||
}
|
||||
|
||||
private getPostsBaseDir(): string {
|
||||
return path.join(this.getProjectBaseDir(), 'posts');
|
||||
}
|
||||
|
||||
private getPostsDir(): string {
|
||||
@@ -172,8 +179,9 @@ export class PostEngine extends EventEmitter {
|
||||
return path.join(dir, `${slug}.md`);
|
||||
}
|
||||
|
||||
setProjectContext(projectId: string): void {
|
||||
setProjectContext(projectId: string, baseDir?: string): void {
|
||||
this.currentProjectId = projectId;
|
||||
this.projectBaseDir = baseDir || null;
|
||||
}
|
||||
|
||||
getProjectContext(): string {
|
||||
|
||||
@@ -12,6 +12,7 @@ export interface ProjectData {
|
||||
name: string;
|
||||
slug: string;
|
||||
description?: string;
|
||||
dataPath?: string; // Custom path for project data (undefined = default)
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
isActive: boolean;
|
||||
@@ -29,18 +30,41 @@ export class ProjectEngine extends EventEmitter {
|
||||
.replace(/^-|-$/g, '');
|
||||
}
|
||||
|
||||
private async ensureProjectDirectories(projectId: string): Promise<void> {
|
||||
/**
|
||||
* Get the base directory for a project's data.
|
||||
* If the project has a custom dataPath, use that; otherwise use the default.
|
||||
*/
|
||||
getProjectBaseDir(projectId: string, dataPath?: string | null): string {
|
||||
if (dataPath) {
|
||||
return dataPath;
|
||||
}
|
||||
const userDataPath = app.getPath('userData');
|
||||
const projectDir = path.join(userDataPath, 'projects', projectId);
|
||||
return path.join(userDataPath, 'projects', projectId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default base directory (in userData) for a project.
|
||||
*/
|
||||
getDefaultProjectBaseDir(projectId: string): string {
|
||||
const userDataPath = app.getPath('userData');
|
||||
return path.join(userDataPath, 'projects', projectId);
|
||||
}
|
||||
|
||||
private async ensureProjectDirectories(projectId: string, dataPath?: string | null): Promise<void> {
|
||||
const projectDir = this.getProjectBaseDir(projectId, dataPath);
|
||||
const postsDir = path.join(projectDir, 'posts');
|
||||
const mediaDir = path.join(projectDir, 'media');
|
||||
const thumbnailsDir = path.join(projectDir, 'thumbnails');
|
||||
const metaDir = path.join(projectDir, 'meta');
|
||||
|
||||
await fs.mkdir(projectDir, { recursive: true });
|
||||
await fs.mkdir(postsDir, { recursive: true });
|
||||
await fs.mkdir(mediaDir, { recursive: true });
|
||||
await fs.mkdir(thumbnailsDir, { recursive: true });
|
||||
await fs.mkdir(metaDir, { recursive: true });
|
||||
}
|
||||
|
||||
async createProject(data: { name: string; description?: string; slug?: string }): Promise<ProjectData> {
|
||||
async createProject(data: { name: string; description?: string; slug?: string; dataPath?: string }): Promise<ProjectData> {
|
||||
const db = getDatabase().getLocal();
|
||||
const now = new Date();
|
||||
const id = uuidv4();
|
||||
@@ -61,13 +85,14 @@ export class ProjectEngine extends EventEmitter {
|
||||
name: data.name,
|
||||
slug: finalSlug,
|
||||
description: data.description,
|
||||
dataPath: data.dataPath,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
isActive: false,
|
||||
};
|
||||
|
||||
// Create directories using project ID (not slug)
|
||||
await this.ensureProjectDirectories(id);
|
||||
await this.ensureProjectDirectories(id, data.dataPath);
|
||||
|
||||
// Insert into database
|
||||
const dbProject: NewProject = {
|
||||
@@ -75,6 +100,7 @@ export class ProjectEngine extends EventEmitter {
|
||||
name: project.name,
|
||||
slug: project.slug,
|
||||
description: project.description,
|
||||
dataPath: project.dataPath,
|
||||
createdAt: project.createdAt,
|
||||
updatedAt: project.updatedAt,
|
||||
isActive: project.isActive,
|
||||
@@ -106,6 +132,7 @@ export class ProjectEngine extends EventEmitter {
|
||||
name: updated.name,
|
||||
slug: updated.slug,
|
||||
description: updated.description,
|
||||
dataPath: updated.dataPath,
|
||||
updatedAt: updated.updatedAt,
|
||||
isActive: updated.isActive,
|
||||
})
|
||||
@@ -116,6 +143,7 @@ export class ProjectEngine extends EventEmitter {
|
||||
name: updated.name,
|
||||
slug: updated.slug,
|
||||
description: updated.description || undefined,
|
||||
dataPath: updated.dataPath || undefined,
|
||||
createdAt: updated.createdAt,
|
||||
updatedAt: updated.updatedAt,
|
||||
isActive: updated.isActive ?? false,
|
||||
@@ -196,6 +224,7 @@ export class ProjectEngine extends EventEmitter {
|
||||
name: dbProject.name,
|
||||
slug: dbProject.slug,
|
||||
description: dbProject.description || undefined,
|
||||
dataPath: dbProject.dataPath || undefined,
|
||||
createdAt: dbProject.createdAt,
|
||||
updatedAt: dbProject.updatedAt,
|
||||
isActive: dbProject.isActive ?? false,
|
||||
@@ -211,6 +240,7 @@ export class ProjectEngine extends EventEmitter {
|
||||
name: p.name,
|
||||
slug: p.slug,
|
||||
description: p.description || undefined,
|
||||
dataPath: p.dataPath || undefined,
|
||||
createdAt: p.createdAt,
|
||||
updatedAt: p.updatedAt,
|
||||
isActive: p.isActive ?? false,
|
||||
@@ -231,6 +261,7 @@ export class ProjectEngine extends EventEmitter {
|
||||
name: dbProject.name,
|
||||
slug: dbProject.slug,
|
||||
description: dbProject.description || undefined,
|
||||
dataPath: dbProject.dataPath || undefined,
|
||||
createdAt: dbProject.createdAt,
|
||||
updatedAt: dbProject.updatedAt,
|
||||
isActive: dbProject.isActive ?? false,
|
||||
@@ -255,13 +286,21 @@ export class ProjectEngine extends EventEmitter {
|
||||
return project;
|
||||
}
|
||||
|
||||
getProjectPaths(projectId: string): { posts: string; media: string } {
|
||||
const userDataPath = app.getPath('userData');
|
||||
getProjectPaths(projectId: string, dataPath?: string | null): { posts: string; media: string } {
|
||||
const baseDir = this.getProjectBaseDir(projectId, dataPath);
|
||||
return {
|
||||
posts: path.join(userDataPath, 'projects', projectId, 'posts'),
|
||||
media: path.join(userDataPath, 'projects', projectId, 'media'),
|
||||
posts: path.join(baseDir, 'posts'),
|
||||
media: path.join(baseDir, 'media'),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get project paths by looking up the project's dataPath from the database.
|
||||
*/
|
||||
async getProjectPathsResolved(projectId: string): Promise<{ posts: string; media: string }> {
|
||||
const project = await this.getProject(projectId);
|
||||
return this.getProjectPaths(projectId, project?.dataPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Singleton instance
|
||||
|
||||
@@ -110,16 +110,24 @@ function isValidHexColor(color: string): boolean {
|
||||
*/
|
||||
export class TagEngine extends EventEmitter {
|
||||
private currentProjectId: string = 'default';
|
||||
private projectBaseDir: string | null = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
private getProjectBaseDir(): string {
|
||||
if (this.projectBaseDir) return this.projectBaseDir;
|
||||
const userDataPath = app.getPath('userData');
|
||||
return path.join(userDataPath, 'projects', this.currentProjectId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current project context
|
||||
*/
|
||||
setProjectContext(projectId: string): void {
|
||||
setProjectContext(projectId: string, baseDir?: string): void {
|
||||
this.currentProjectId = projectId;
|
||||
this.projectBaseDir = baseDir || null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,8 +141,7 @@ export class TagEngine extends EventEmitter {
|
||||
* Get the tags file path for filesystem persistence
|
||||
*/
|
||||
private getTagsFilePath(): string {
|
||||
const userDataPath = app.getPath('userData');
|
||||
return path.join(userDataPath, 'projects', this.currentProjectId, 'meta', 'tags-metadata.json');
|
||||
return path.join(this.getProjectBaseDir(), 'meta', 'tags-metadata.json');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -47,44 +47,46 @@ export function registerIpcHandlers(): void {
|
||||
ipcMain.handle('projects:getActive', async () => {
|
||||
const projectEngine = getProjectEngine();
|
||||
const project = await projectEngine.getActiveProject();
|
||||
|
||||
|
||||
// Ensure all engines have the correct project context
|
||||
if (project) {
|
||||
const baseDir = projectEngine.getProjectBaseDir(project.id, project.dataPath);
|
||||
const postEngine = getPostEngine();
|
||||
const mediaEngine = getMediaEngine();
|
||||
const metaEngine = getMetaEngine();
|
||||
const tagEngine = getTagEngine();
|
||||
postEngine.setProjectContext(project.id);
|
||||
mediaEngine.setProjectContext(project.id);
|
||||
metaEngine.setProjectContext(project.id);
|
||||
tagEngine.setProjectContext(project.id);
|
||||
|
||||
postEngine.setProjectContext(project.id, baseDir);
|
||||
mediaEngine.setProjectContext(project.id, baseDir);
|
||||
metaEngine.setProjectContext(project.id, baseDir);
|
||||
tagEngine.setProjectContext(project.id, baseDir);
|
||||
|
||||
// Sync meta on startup
|
||||
await metaEngine.syncOnStartup();
|
||||
}
|
||||
|
||||
|
||||
return project;
|
||||
});
|
||||
|
||||
ipcMain.handle('projects:setActive', async (_, id: string) => {
|
||||
const projectEngine = getProjectEngine();
|
||||
const project = await projectEngine.setActiveProject(id);
|
||||
|
||||
|
||||
// Update all engines to use the new project context
|
||||
if (project) {
|
||||
const baseDir = projectEngine.getProjectBaseDir(project.id, project.dataPath);
|
||||
const postEngine = getPostEngine();
|
||||
const mediaEngine = getMediaEngine();
|
||||
const metaEngine = getMetaEngine();
|
||||
const tagEngine = getTagEngine();
|
||||
postEngine.setProjectContext(project.id);
|
||||
mediaEngine.setProjectContext(project.id);
|
||||
metaEngine.setProjectContext(project.id);
|
||||
tagEngine.setProjectContext(project.id);
|
||||
|
||||
postEngine.setProjectContext(project.id, baseDir);
|
||||
mediaEngine.setProjectContext(project.id, baseDir);
|
||||
metaEngine.setProjectContext(project.id, baseDir);
|
||||
tagEngine.setProjectContext(project.id, baseDir);
|
||||
|
||||
// Sync meta on project switch
|
||||
await metaEngine.syncOnStartup();
|
||||
}
|
||||
|
||||
|
||||
return project;
|
||||
});
|
||||
|
||||
@@ -383,7 +385,7 @@ export function registerIpcHandlers(): void {
|
||||
const projectEngine = getProjectEngine();
|
||||
const activeProject = await projectEngine.getActiveProject();
|
||||
const projectId = activeProject?.id || 'default';
|
||||
const paths = projectEngine.getProjectPaths(projectId);
|
||||
const paths = projectEngine.getProjectPaths(projectId, activeProject?.dataPath);
|
||||
|
||||
const fullConfig: DropboxSyncConfig = {
|
||||
accessToken: config.accessToken,
|
||||
@@ -480,7 +482,7 @@ export function registerIpcHandlers(): void {
|
||||
const projectEngine = getProjectEngine();
|
||||
const activeProject = await projectEngine.getActiveProject();
|
||||
const projectId = activeProject?.id || 'default';
|
||||
const paths = projectEngine.getProjectPaths(projectId);
|
||||
const paths = projectEngine.getProjectPaths(projectId, activeProject?.dataPath);
|
||||
return {
|
||||
database: getDatabase().getDataPaths().database,
|
||||
posts: paths.posts,
|
||||
@@ -492,6 +494,22 @@ export function registerIpcHandlers(): void {
|
||||
return shell.openPath(folderPath);
|
||||
});
|
||||
|
||||
ipcMain.handle('app:selectFolder', async (_, title?: string) => {
|
||||
const result = await dialog.showOpenDialog({
|
||||
title: title || 'Select Folder',
|
||||
properties: ['openDirectory', 'createDirectory'],
|
||||
});
|
||||
if (result.canceled || result.filePaths.length === 0) {
|
||||
return null;
|
||||
}
|
||||
return result.filePaths[0];
|
||||
});
|
||||
|
||||
ipcMain.handle('app:getDefaultProjectPath', async (_, projectId: string) => {
|
||||
const projectEngine = getProjectEngine();
|
||||
return projectEngine.getDefaultProjectBaseDir(projectId);
|
||||
});
|
||||
|
||||
ipcMain.handle('app:showItemInFolder', async (_, itemPath: string) => {
|
||||
return shell.showItemInFolder(itemPath);
|
||||
});
|
||||
@@ -553,7 +571,7 @@ export function registerIpcHandlers(): void {
|
||||
return engine.getProjectMetadata();
|
||||
});
|
||||
|
||||
ipcMain.handle('meta:updateProjectMetadata', async (_, updates: { name?: string; description?: string }) => {
|
||||
ipcMain.handle('meta:updateProjectMetadata', async (_, updates: { name?: string; description?: string; dataPath?: string }) => {
|
||||
const engine = getMetaEngine();
|
||||
await engine.updateProjectMetadata(updates);
|
||||
return engine.getProjectMetadata();
|
||||
|
||||
@@ -5,6 +5,7 @@ import { getDatabase } from './database';
|
||||
import { registerIpcHandlers, registerChatHandlers, initializeChatHandlers, cleanupChatHandlers } from './ipc';
|
||||
import { media } from './database/schema';
|
||||
import { eq } from 'drizzle-orm';
|
||||
import { getMediaEngine } from './engine/MediaEngine';
|
||||
|
||||
let mainWindow: BrowserWindow | null = null;
|
||||
|
||||
@@ -22,6 +23,15 @@ protocol.registerSchemesAsPrivileged([
|
||||
corsEnabled: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
scheme: 'bds-thumb',
|
||||
privileges: {
|
||||
standard: true,
|
||||
secure: true,
|
||||
supportFetchAPI: true,
|
||||
corsEnabled: true,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
function createWindow(): void {
|
||||
@@ -383,6 +393,39 @@ async function initialize(): Promise<void> {
|
||||
}
|
||||
});
|
||||
|
||||
// Register custom protocol for serving thumbnail images
|
||||
// URLs like bds-thumb://media-id will serve the small thumbnail webp
|
||||
protocol.handle('bds-thumb', async (request) => {
|
||||
try {
|
||||
const url = new URL(request.url);
|
||||
const mediaId = url.hostname;
|
||||
|
||||
const engine = getMediaEngine();
|
||||
const thumbnails = await engine.getThumbnailPaths(mediaId);
|
||||
|
||||
if (thumbnails.small) {
|
||||
return net.fetch(`file://${thumbnails.small}`);
|
||||
}
|
||||
|
||||
// Fallback to full image if thumbnail doesn't exist
|
||||
const database = getDatabase().getLocal();
|
||||
const mediaItem = await database
|
||||
.select()
|
||||
.from(media)
|
||||
.where(eq(media.id, mediaId))
|
||||
.get();
|
||||
|
||||
if (mediaItem && mediaItem.filePath) {
|
||||
return net.fetch(`file://${mediaItem.filePath}`);
|
||||
}
|
||||
|
||||
return new Response('Thumbnail not found', { status: 404 });
|
||||
} catch (error) {
|
||||
console.error('Error serving thumbnail:', error);
|
||||
return new Response('Internal server error', { status: 500 });
|
||||
}
|
||||
});
|
||||
|
||||
// Register IPC handlers
|
||||
registerIpcHandlers();
|
||||
|
||||
|
||||
@@ -99,6 +99,8 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||
getDataPaths: () => ipcRenderer.invoke('app:getDataPaths'),
|
||||
openFolder: (folderPath: string) => ipcRenderer.invoke('app:openFolder', folderPath),
|
||||
showItemInFolder: (itemPath: string) => ipcRenderer.invoke('app:showItemInFolder', itemPath),
|
||||
selectFolder: (title?: string) => ipcRenderer.invoke('app:selectFolder', title),
|
||||
getDefaultProjectPath: (projectId: string) => ipcRenderer.invoke('app:getDefaultProjectPath', projectId),
|
||||
},
|
||||
|
||||
// Meta (tags, categories, and project metadata)
|
||||
@@ -112,7 +114,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
||||
syncOnStartup: () => ipcRenderer.invoke('meta:syncOnStartup'),
|
||||
getProjectMetadata: () => ipcRenderer.invoke('meta:getProjectMetadata'),
|
||||
setProjectMetadata: (metadata: { name: string; description?: string }) => ipcRenderer.invoke('meta:setProjectMetadata', metadata),
|
||||
updateProjectMetadata: (updates: { name?: string; description?: string }) => ipcRenderer.invoke('meta:updateProjectMetadata', updates),
|
||||
updateProjectMetadata: (updates: { name?: string; description?: string; dataPath?: string }) => ipcRenderer.invoke('meta:updateProjectMetadata', updates),
|
||||
},
|
||||
|
||||
// Tag Management (advanced tag operations)
|
||||
@@ -267,6 +269,8 @@ export interface ElectronAPI {
|
||||
getDataPaths: () => Promise<{ database: string; posts: string; media: string }>;
|
||||
openFolder: (folderPath: string) => Promise<string>;
|
||||
showItemInFolder: (itemPath: string) => Promise<void>;
|
||||
selectFolder: (title?: string) => Promise<string | null>;
|
||||
getDefaultProjectPath: (projectId: string) => Promise<string>;
|
||||
};
|
||||
meta: {
|
||||
getTags: () => Promise<string[]>;
|
||||
|
||||
Reference in New Issue
Block a user