feat(v029): AiProviderSection AI 자동 처리 토글 + OFF 시 안내문

This commit is contained in:
altair823
2026-05-09 16:31:24 +09:00
parent 49fbed050a
commit c21fca57dd
2 changed files with 62 additions and 2 deletions

View File

@@ -10,6 +10,8 @@ export function AiProviderSection(): React.ReactElement {
const [error, setError] = useState<string | null>(null);
const [saveResult, setSaveResult] = useState<string | null>(null);
const [recheckResult, setRecheckResult] = useState<string | null>(null);
// v0.2.9 Cut B Task 15: AI 자동 처리 토글.
const [aiEnabled, setAiEnabledState] = useState<boolean | null>(null);
useEffect(() => {
void (async () => {
@@ -18,9 +20,16 @@ export function AiProviderSection(): React.ReactElement {
setEndpoint(s.endpoint);
setModel(s.model);
}
const settings = await inboxApi.getSettings();
setAiEnabledState(settings.ai_enabled ?? true);
})();
}, []);
async function onToggleAi(checked: boolean): Promise<void> {
await inboxApi.setAiEnabled(checked);
setAiEnabledState(checked);
}
async function onSave(): Promise<void> {
const r = endpointSchema.safeParse(endpoint);
if (!r.success) {
@@ -51,6 +60,25 @@ export function AiProviderSection(): React.ReactElement {
return (
<div>
{/* v0.2.9 Cut B Task 15 — AI 자동 처리 토글 (가장 위, 스위치 의미가 가장 큰 결정) */}
{aiEnabled !== null && (
<label style={{ display: 'flex', gap: 8, alignItems: 'center', marginBottom: 12, fontSize: 13 }}>
<input
type="checkbox"
checked={aiEnabled}
onChange={(e) => void onToggleAi(e.target.checked)}
/>
AI
</label>
)}
{aiEnabled === false && (
<p style={{ fontSize: 12, color: '#666', marginBottom: 12 }}>
. // .<br />
<a href="https://ollama.com/download" target="_blank" rel="noopener noreferrer">
Ollama
</a>
</p>
)}
<label style={{ display: 'block', marginBottom: 8, fontSize: 12, color: '#666' }}>
Endpoint
<input

View File

@@ -1,13 +1,17 @@
// @vitest-environment jsdom
import { describe, it, expect, vi, beforeEach } from 'vitest';
import '@testing-library/jest-dom/vitest';
import { render, screen, fireEvent, cleanup } from '@testing-library/react';
import { render, screen, fireEvent, cleanup, waitFor } from '@testing-library/react';
vi.mock('../../src/renderer/inbox/api.js', () => ({
inboxApi: {
loadOllamaSettings: vi.fn(async () => ({ endpoint: 'http://localhost:11434', model: 'gemma2:2b' })),
saveOllamaSettings: vi.fn(async () => ({ ok: true })),
ollamaRecheck: vi.fn(async () => ({ ok: true }))
ollamaRecheck: vi.fn(async () => ({ ok: true })),
getSettings: vi.fn(async () => ({ ai_enabled: true })),
setAiEnabled: vi.fn(async () => ({ ok: true })),
getDisabledCount: vi.fn(async () => 0),
enqueueDisabled: vi.fn(async () => ({ count: 0 }))
}
}));
@@ -41,4 +45,32 @@ describe('AiProviderSection', () => {
fireEvent.click(screen.getByRole('button', { name: /지금 재확인/ }));
expect(inboxApi.ollamaRecheck).toHaveBeenCalled();
});
// v0.2.9 Cut B Task 15 — AI 자동 처리 토글 + OFF 안내문.
it('renders AI 자동 처리 toggle (default true)', async () => {
const { inboxApi } = await import('../../src/renderer/inbox/api.js');
vi.mocked(inboxApi.getSettings).mockResolvedValue({ ai_enabled: true } as never);
render(<AiProviderSection />);
const toggle = await screen.findByLabelText(/AI 자동 처리 사용/);
expect((toggle as HTMLInputElement).checked).toBe(true);
});
it('toggling calls setAiEnabled', async () => {
const { inboxApi } = await import('../../src/renderer/inbox/api.js');
vi.mocked(inboxApi.getSettings).mockResolvedValue({ ai_enabled: true } as never);
vi.mocked(inboxApi.setAiEnabled).mockResolvedValue({ ok: true } as never);
render(<AiProviderSection />);
const toggle = await screen.findByLabelText(/AI 자동 처리 사용/);
fireEvent.click(toggle);
await waitFor(() => expect(inboxApi.setAiEnabled).toHaveBeenCalledWith(false));
});
it('shows OFF state explanation when ai_enabled=false', async () => {
const { inboxApi } = await import('../../src/renderer/inbox/api.js');
vi.mocked(inboxApi.getSettings).mockResolvedValue({ ai_enabled: false } as never);
render(<AiProviderSection />);
await screen.findByLabelText(/AI 자동 처리 사용/);
expect(screen.getByText(/원문만 저장 모드/)).toBeInTheDocument();
expect(screen.getByRole('link', { name: /ollama\.com|설치/ })).toBeInTheDocument();
});
});