diff --git a/src/main/index.ts b/src/main/index.ts
index e38197f..9768e0f 100644
--- a/src/main/index.ts
+++ b/src/main/index.ts
@@ -19,6 +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 { createInboxWindow, getInboxWindow } from './windows/inboxWindow.js';
import {
createQuickCaptureWindow, showQuickCapture, getQuickCaptureWindow
@@ -162,6 +163,7 @@ app.whenReady().then(async () => {
repo, continuity, capture, health, intent,
getInboxWindow, settings: settingsSvc, providerHolder
});
+ registerSettingsApi();
const hotkeys = new HotkeyService();
const reg = hotkeys.register({
diff --git a/src/main/ipc/settingsApi.ts b/src/main/ipc/settingsApi.ts
new file mode 100644
index 0000000..712da9f
--- /dev/null
+++ b/src/main/ipc/settingsApi.ts
@@ -0,0 +1,23 @@
+import electron from 'electron';
+const { ipcMain, app } = electron;
+
+/**
+ * v0.2.7 자동 실행 설정 IPC.
+ *
+ * 임시 채널명 (`settings:get-autostart` / `settings:set-autostart`).
+ * Task 22 에서 정식 이름 (`settings:autostart-state` / `settings:autostart-set`) 으로 rename 예정.
+ *
+ * args=['--hidden'] 명시 — 자동 실행 시 백그라운드 모드로 시작 (Quick Capture only).
+ */
+export function registerSettingsApi(): void {
+ ipcMain.handle('settings:get-autostart', () => {
+ const r = app.getLoginItemSettings({ args: ['--hidden'] });
+ return { openAtLogin: r.openAtLogin };
+ });
+
+ ipcMain.handle('settings:set-autostart', (_e, open: boolean) => {
+ app.setLoginItemSettings({ openAtLogin: open, args: ['--hidden'] });
+ const r = app.getLoginItemSettings({ args: ['--hidden'] });
+ return { openAtLogin: r.openAtLogin };
+ });
+}
diff --git a/src/preload/index.ts b/src/preload/index.ts
index 4281f5a..c6ec4ed 100644
--- a/src/preload/index.ts
+++ b/src/preload/index.ts
@@ -52,6 +52,9 @@ const api: InklingApi = {
ipcRenderer.on('inbox:openOllamaSettings', handler);
return () => ipcRenderer.removeListener('inbox:openOllamaSettings', handler);
},
+ // v0.2.7 자동 실행 (임시 채널 — Task 22 에서 settings:autostart-state / settings:autostart-set 으로 rename)
+ getAutostart: () => ipcRenderer.invoke('settings:get-autostart'),
+ setAutostart: (open: boolean) => ipcRenderer.invoke('settings:set-autostart', open),
}
};
diff --git a/src/renderer/inbox/components/SettingsPage.tsx b/src/renderer/inbox/components/SettingsPage.tsx
index c9d921c..d5e4ed6 100644
--- a/src/renderer/inbox/components/SettingsPage.tsx
+++ b/src/renderer/inbox/components/SettingsPage.tsx
@@ -1,6 +1,7 @@
import React from 'react';
import { useInbox } from '../store.js';
import { AiProviderSection } from './settings/AiProviderSection.js';
+import { AutostartSection } from './settings/AutostartSection.js';
export function SettingsPage(): React.ReactElement {
const setShowSettings = useInbox((s) => s.setShowSettings);
@@ -27,7 +28,7 @@ export function SettingsPage(): React.ReactElement {
자동 실행
- {/* AutostartSection — Task 9 + Task 23/24 */}
+
백업 / 복원
diff --git a/src/renderer/inbox/components/settings/AutostartSection.tsx b/src/renderer/inbox/components/settings/AutostartSection.tsx
new file mode 100644
index 0000000..000ecbc
--- /dev/null
+++ b/src/renderer/inbox/components/settings/AutostartSection.tsx
@@ -0,0 +1,31 @@
+import React, { useEffect, useState } from 'react';
+import { inboxApi } from '../../api.js';
+
+export function AutostartSection(): React.ReactElement {
+ const [openAtLogin, setOpenAtLogin] = useState(null);
+
+ useEffect(() => {
+ void (async () => {
+ const r = await inboxApi.getAutostart();
+ setOpenAtLogin(r.openAtLogin);
+ })();
+ }, []);
+
+ async function onToggle(e: React.ChangeEvent): Promise {
+ const r = await inboxApi.setAutostart(e.target.checked);
+ setOpenAtLogin(r.openAtLogin);
+ }
+
+ if (openAtLogin === null) {
+ return 로딩 중...
;
+ }
+
+ return (
+
+
+
+ );
+}
diff --git a/src/shared/types.ts b/src/shared/types.ts
index dd79b2c..82c076d 100644
--- a/src/shared/types.ts
+++ b/src/shared/types.ts
@@ -92,6 +92,9 @@ export interface InboxApi {
loadOllamaSettings(): Promise<{ endpoint: string; model: string } | null>;
saveOllamaSettings(v: { endpoint: string; model: string }): Promise<{ ok: true } | { ok: false; reason: string }>;
onOpenOllamaSettings(cb: () => void): () => void;
+ // v0.2.7 자동 실행 (임시 채널 — Task 22 에서 정식 이름으로 rename)
+ getAutostart(): Promise<{ openAtLogin: boolean }>;
+ setAutostart(open: boolean): Promise<{ openAtLogin: boolean }>;
}
export interface InklingApi {
diff --git a/tests/unit/AutostartSection.test.tsx b/tests/unit/AutostartSection.test.tsx
new file mode 100644
index 0000000..7a4c4fe
--- /dev/null
+++ b/tests/unit/AutostartSection.test.tsx
@@ -0,0 +1,34 @@
+// @vitest-environment jsdom
+import { describe, it, expect, vi, beforeEach } from 'vitest';
+import '@testing-library/jest-dom/vitest';
+import { render, screen, fireEvent, waitFor, cleanup } from '@testing-library/react';
+
+vi.mock('../../src/renderer/inbox/api.js', () => ({
+ inboxApi: {
+ getAutostart: vi.fn(async () => ({ openAtLogin: true })),
+ setAutostart: vi.fn(async (open: boolean) => ({ openAtLogin: open }))
+ }
+}));
+
+import { AutostartSection } from '../../src/renderer/inbox/components/settings/AutostartSection';
+
+describe('AutostartSection', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ cleanup();
+ });
+
+ it('renders toggle reflecting current state', async () => {
+ render();
+ const toggle = await screen.findByRole('checkbox');
+ expect(toggle).toBeChecked();
+ });
+
+ it('clicking toggle calls setAutostart', async () => {
+ const { inboxApi } = await import('../../src/renderer/inbox/api.js');
+ render();
+ const toggle = await screen.findByRole('checkbox');
+ fireEvent.click(toggle);
+ await waitFor(() => expect(inboxApi.setAutostart).toHaveBeenCalledWith(false));
+ });
+});
diff --git a/tests/unit/SettingsPage.test.tsx b/tests/unit/SettingsPage.test.tsx
index 2805021..491d0be 100644
--- a/tests/unit/SettingsPage.test.tsx
+++ b/tests/unit/SettingsPage.test.tsx
@@ -10,7 +10,9 @@ vi.mock('../../src/renderer/inbox/api.js', () => ({
inboxApi: {
loadOllamaSettings: vi.fn(async () => null),
saveOllamaSettings: vi.fn(async () => ({ ok: true })),
- ollamaRecheck: vi.fn(async () => ({ ok: true }))
+ ollamaRecheck: vi.fn(async () => ({ ok: true })),
+ getAutostart: vi.fn(async () => ({ openAtLogin: false })),
+ setAutostart: vi.fn(async (open: boolean) => ({ openAtLogin: open }))
}
}));