feat(export): wire ExportService — tray '내보내기...' menu + dialog
Tray now has 4th callback that opens directory chooser, exports all notes via ExportService with includeMedia=true default. Dialog message warns about raw_text plain-text + recommends private location. Native toast on success/failure with note + media counts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import electron from 'electron';
|
||||
const { app, BrowserWindow, Notification } = electron;
|
||||
const { app, BrowserWindow, Notification, dialog } = electron;
|
||||
import '@shared/types';
|
||||
import { existsSync, writeFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
import { createTray } from './tray.js';
|
||||
import { MediaGc } from './services/MediaGc.js';
|
||||
import { BackupService } from './services/BackupService.js';
|
||||
import { ExportService } from './services/ExportService.js';
|
||||
|
||||
const HIDDEN_ARG = '--hidden';
|
||||
const startedHidden = process.argv.includes(HIDDEN_ARG);
|
||||
@@ -102,6 +103,8 @@ app.whenReady().then(async () => {
|
||||
const gc = new MediaGc(db, store);
|
||||
void gc.run().then((r) => logger.info('media.gc', { ...r } as Record<string, unknown>));
|
||||
|
||||
const exportSvc = new ExportService(repo, store);
|
||||
|
||||
const backup = new BackupService(db, join(paths.profileDir, 'backups'));
|
||||
void backup.runDaily()
|
||||
.then((r) => logger.info('backup.daily', { ...r } as Record<string, unknown>))
|
||||
@@ -142,6 +145,40 @@ app.whenReady().then(async () => {
|
||||
silent: true
|
||||
}).show();
|
||||
}
|
||||
},
|
||||
async () => {
|
||||
const win = getInboxWindow();
|
||||
const dialogOpts: Electron.OpenDialogOptions = {
|
||||
title: '내보낼 폴더 선택',
|
||||
message: '선택한 폴더에 노트를 마크다운으로 내보냅니다. 이미지가 함께 포함됩니다. raw_text 가 평문으로 보관되니 비공개 위치를 권장합니다.',
|
||||
buttonLabel: '여기에 내보내기',
|
||||
properties: ['openDirectory', 'createDirectory']
|
||||
};
|
||||
const result = win
|
||||
? await dialog.showOpenDialog(win, dialogOpts)
|
||||
: await dialog.showOpenDialog(dialogOpts);
|
||||
if (result.canceled || result.filePaths.length === 0) return;
|
||||
try {
|
||||
const r = await exportSvc.export(result.filePaths[0]!, { includeMedia: true });
|
||||
logger.info('export.done', {
|
||||
outDir: r.outDir,
|
||||
noteCount: r.noteCount,
|
||||
mediaCount: r.mediaCount,
|
||||
bytes: r.bytes
|
||||
});
|
||||
new Notification({
|
||||
title: 'Inkling',
|
||||
body: `내보내기 완료 — 노트 ${r.noteCount}개, 이미지 ${r.mediaCount}개`,
|
||||
silent: true
|
||||
}).show();
|
||||
} catch (e) {
|
||||
logger.warn('export.failed', { reason: String(e) });
|
||||
new Notification({
|
||||
title: 'Inkling',
|
||||
body: '내보내기를 완료하지 못했습니다.',
|
||||
silent: true
|
||||
}).show();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -7,13 +7,15 @@ let tray: TrayType | null = null;
|
||||
function buildMenu(
|
||||
showInbox: () => void,
|
||||
showCapture: () => void,
|
||||
runBackup: () => void
|
||||
runBackup: () => void,
|
||||
runExport: () => void
|
||||
) {
|
||||
const items: MenuItemConstructorOptions[] = [
|
||||
{ label: '구출한 메모 보기', click: showInbox },
|
||||
{ label: '기억 구출하기', click: showCapture },
|
||||
{ type: 'separator' },
|
||||
{ label: '지금 백업', click: runBackup }
|
||||
{ label: '지금 백업', click: runBackup },
|
||||
{ label: '내보내기...', click: runExport }
|
||||
];
|
||||
if (app.isPackaged) {
|
||||
const { openAtLogin } = app.getLoginItemSettings();
|
||||
@@ -39,12 +41,13 @@ function buildMenu(
|
||||
export function createTray(
|
||||
showInbox: () => void,
|
||||
showCapture: () => void,
|
||||
runBackup: () => void
|
||||
runBackup: () => void,
|
||||
runExport: () => void
|
||||
): TrayType {
|
||||
const icon = nativeImage.createEmpty();
|
||||
tray = new Tray(icon);
|
||||
tray.setToolTip('Inkling');
|
||||
tray.setContextMenu(buildMenu(showInbox, showCapture, runBackup));
|
||||
tray.setContextMenu(buildMenu(showInbox, showCapture, runBackup, runExport));
|
||||
tray.on('click', showInbox);
|
||||
return tray;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user