feat(v027): createTray wiring 3-callback + refreshTray 호출부 슬림
This commit is contained in:
@@ -19,7 +19,7 @@ import { ProviderHolder } from './ai/ProviderHolder.js';
|
||||
import { AiWorker } from './ai/AiWorker.js';
|
||||
import { registerCaptureApi } from './ipc/captureApi.js';
|
||||
import { registerInboxApi, pushNoteUpdated, pushOllamaStatus } from './ipc/inboxApi.js';
|
||||
import { registerSettingsApi } from './ipc/settingsApi.js';
|
||||
import { registerSettingsApi, navigateInbox } from './ipc/settingsApi.js';
|
||||
import { createInboxWindow, getInboxWindow } from './windows/inboxWindow.js';
|
||||
import {
|
||||
createQuickCaptureWindow, showQuickCapture, getQuickCaptureWindow
|
||||
@@ -121,7 +121,6 @@ app.whenReady().then(async () => {
|
||||
onUpdate: (status) => {
|
||||
logger.info('ai.health', { ...status } as Record<string, unknown>);
|
||||
pushOllamaStatus(getInboxWindow, status);
|
||||
refreshTray({ ollamaOk: status.ok });
|
||||
},
|
||||
onTelemetry: (ev) => {
|
||||
if (ev.kind === 'ollama_unreachable') {
|
||||
@@ -139,7 +138,8 @@ app.whenReady().then(async () => {
|
||||
onUpdate: (note) => {
|
||||
pushNoteUpdated(getInboxWindow, note);
|
||||
// F4-C: AI 처리 완료 = 새 캡처가 inbox 에 합류한 시점, tray 도 즉시 갱신.
|
||||
refreshTray({ todayCount: repo.countToday(), failedCount: repo.countFailed() });
|
||||
// v0.2.7 Phase 3 — failedCount 메뉴 항목 제거됨 → todayCount 만 갱신.
|
||||
refreshTray({ todayCount: repo.countToday() });
|
||||
},
|
||||
logger,
|
||||
telemetry
|
||||
@@ -231,188 +231,20 @@ app.whenReady().then(async () => {
|
||||
});
|
||||
});
|
||||
|
||||
// v0.2.7 Phase 3 (Task 16) — TrayCallbacks 슬림: 10 → 3.
|
||||
// 백업/내보내기/복원/동기화/사용 로그/Ollama 재확인/AI 재처리/Ollama 설정/정보 →
|
||||
// 모두 설정 페이지로 이전 (registerSettingsApi 의 IPC 핸들러가 본문 보유).
|
||||
createTray({
|
||||
showInbox: () => createInboxWindow(),
|
||||
showCapture: () => showQuickCapture(),
|
||||
runBackup: async () => {
|
||||
try {
|
||||
const r = await backup.runDaily();
|
||||
new Notification({
|
||||
title: 'Inkling',
|
||||
body: r.snapshotted
|
||||
? `백업 완료 — ${r.removed?.length ?? 0}개 정리`
|
||||
: `오늘 백업이 이미 있습니다`,
|
||||
silent: true
|
||||
}).show();
|
||||
} catch (e) {
|
||||
logger.warn('backup.manual.failed', { reason: String(e) });
|
||||
new Notification({
|
||||
title: 'Inkling',
|
||||
body: '백업을 만들지 못했습니다.',
|
||||
silent: true
|
||||
}).show();
|
||||
}
|
||||
},
|
||||
runExport: 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();
|
||||
}
|
||||
},
|
||||
runImport: async () => {
|
||||
const win = getInboxWindow();
|
||||
const dirOpts: Electron.OpenDialogOptions = {
|
||||
title: '복원할 백업 폴더 선택',
|
||||
message: 'F5 export 형식의 폴더를 선택하세요. notes/ 하위의 마크다운 파일이 적재됩니다.',
|
||||
buttonLabel: '여기서 복원',
|
||||
properties: ['openDirectory']
|
||||
};
|
||||
const dirResult = win
|
||||
? await dialog.showOpenDialog(win, dirOpts)
|
||||
: await dialog.showOpenDialog(dirOpts);
|
||||
if (dirResult.canceled || dirResult.filePaths.length === 0) return;
|
||||
const sourceDir = dirResult.filePaths[0]!;
|
||||
let plan;
|
||||
try {
|
||||
plan = await importSvc.preview(sourceDir);
|
||||
} catch (e) {
|
||||
logger.warn('import.preview.failed', { reason: String(e) });
|
||||
new Notification({
|
||||
title: 'Inkling',
|
||||
body: '백업 폴더를 읽지 못했습니다.',
|
||||
silent: true
|
||||
}).show();
|
||||
return;
|
||||
}
|
||||
const detail = `총 ${plan.total}개 노트\n · 신규 ${plan.newCount}개\n · 동일 (스킵) ${plan.unchangedCount}개\n · 충돌→새 id (${plan.forkedCount}개, raw_text 보존)\n\n이미지 ${plan.mediaCount}개 복사 예정.`;
|
||||
const confirmOpts: Electron.MessageBoxOptions = {
|
||||
type: 'question',
|
||||
buttons: ['복원', '취소'],
|
||||
defaultId: 0,
|
||||
cancelId: 1,
|
||||
title: 'Inkling 복원',
|
||||
message: '복원 미리보기',
|
||||
detail
|
||||
};
|
||||
const confirm = win
|
||||
? await dialog.showMessageBox(win, confirmOpts)
|
||||
: await dialog.showMessageBox(confirmOpts);
|
||||
if (confirm.response !== 0) return;
|
||||
try {
|
||||
const r = await importSvc.run(sourceDir);
|
||||
logger.info('import.done', {
|
||||
total: r.total,
|
||||
new: r.newCount,
|
||||
unchanged: r.unchangedCount,
|
||||
forked: r.forkedCount,
|
||||
media: r.mediaCount
|
||||
});
|
||||
new Notification({
|
||||
title: 'Inkling',
|
||||
body: `복원 완료 — 신규 ${r.newCount}개, 스킵 ${r.unchangedCount}개, 충돌 ${r.forkedCount}개`,
|
||||
silent: true
|
||||
}).show();
|
||||
} catch (e) {
|
||||
logger.warn('import.run.failed', { reason: String(e) });
|
||||
new Notification({
|
||||
title: 'Inkling',
|
||||
body: '복원을 완료하지 못했습니다.',
|
||||
silent: true
|
||||
}).show();
|
||||
}
|
||||
},
|
||||
runSync: async () => {
|
||||
// runSync — 트레이 "지금 동기화"
|
||||
try {
|
||||
const r = await syncSvc.sync();
|
||||
if (!r.ok) {
|
||||
logger.warn('sync.failed', { reason: r.reason });
|
||||
const body = r.reason === 'not_configured'
|
||||
? `${syncSvc.getSyncDir()} 에서 git init + remote 설정이 필요합니다.`
|
||||
: '동기화를 완료하지 못했습니다.';
|
||||
new Notification({ title: 'Inkling', body, silent: true }).show();
|
||||
return;
|
||||
}
|
||||
if (r.changed) {
|
||||
logger.info('sync.done', { sha: r.sha, pushed: r.pushed });
|
||||
new Notification({ title: 'Inkling', body: '동기화 완료', silent: true }).show();
|
||||
} else {
|
||||
new Notification({ title: 'Inkling', body: '변경 사항 없음', silent: true }).show();
|
||||
}
|
||||
} catch (e) {
|
||||
logger.warn('sync.exception', { reason: String(e) });
|
||||
new Notification({ title: 'Inkling', body: '동기화를 완료하지 못했습니다.', silent: true }).show();
|
||||
}
|
||||
},
|
||||
runExportTelemetry: async () => {
|
||||
const win = getInboxWindow();
|
||||
const dialogOpts: Electron.OpenDialogOptions = {
|
||||
title: '사용 로그를 내보낼 폴더 선택',
|
||||
message: '선택한 폴더에 events.jsonl + stats.md 가 생성됩니다. 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 telemetry.exportTo(result.filePaths[0]!);
|
||||
logger.info('telemetry.export', { eventCount: r.eventCount, outDir: result.filePaths[0] });
|
||||
new Notification({
|
||||
title: 'Inkling',
|
||||
body: `사용 로그 내보내기 완료 — ${r.eventCount}개 이벤트`,
|
||||
silent: true
|
||||
}).show();
|
||||
} catch (e) {
|
||||
logger.warn('telemetry.export.failed', { reason: String(e) });
|
||||
new Notification({
|
||||
title: 'Inkling',
|
||||
body: '사용 로그 내보내기를 완료하지 못했습니다.',
|
||||
silent: true
|
||||
}).show();
|
||||
}
|
||||
},
|
||||
runOllamaRecheck: () => { void health.runOnce({ manual: true }); },
|
||||
runRetryAllFailed: () => { void capture.retryAllFailed(); },
|
||||
runOpenOllamaSettings: () => {
|
||||
const win = getInboxWindow();
|
||||
if (win) win.webContents.send('inbox:openOllamaSettings');
|
||||
}
|
||||
showSettings: () => navigateInbox('settings')
|
||||
});
|
||||
|
||||
// F4-C 환경 앵커 — tray tooltip + 메뉴 첫 항목을 오늘 KST 캡처 수로 갱신.
|
||||
// 초기 1회 + 60s interval. AiWorker.onUpdate 도 별도 갱신 트리거.
|
||||
// cleanup 은 위 통합 before-quit 핸들러에서 처리.
|
||||
refreshTray({ todayCount: repo.countToday(), failedCount: repo.countFailed() });
|
||||
// v0.2.7 Phase 3 — failedCount 메뉴 항목 제거됨 → todayCount 만 갱신.
|
||||
refreshTray({ todayCount: repo.countToday() });
|
||||
trayInterval = setInterval(() => {
|
||||
refreshTray({ todayCount: repo.countToday() });
|
||||
}, 60_000);
|
||||
|
||||
Reference in New Issue
Block a user