mirror of
https://github.com/amitwh/markdown-converter.git
synced 2026-10-01 09:19:34 +05:30
feat(ai-assist): first-use confirmation + wire Check-for-Updates to updater
Inline AI first-use confirmation (v4.13.0):
- inline-ai-controller accepts optional confirmFirstUse + getProviderLabel
deps. On the first action of the session, calls confirmFirstUse with
{providerLabel, selectionLength, action}; if it returns false, aborts
and shows error state. Once confirmed, never re-prompts for the
session.
- new 'ai-assistant:confirm-info' invoke handler returns
{provider, model} (no key material) so the renderer can label the
confirm dialog.
- electronAPI.aiAssistant.confirmInfo() exposed via preload.
- renderer.js wires window.confirm() with the provider label as the
v1 prompt UX; can be upgraded to a custom modal later without
touching the controller.
Check-for-Updates menu (v4.13.0):
- Help > Check for Updates now calls ensureAutoUpdater().check()
instead of opening the Releases page in the browser.
- Falls back to the browser page if the check throws (dev runs with
no app-update.yml, network failures, etc.) so the user always has
a way to update manually.
4 new controller tests cover: confirm called once with provider label
and selection length; declining aborts + surfaces error state; confirm
not asked twice in a session; missing confirmFirstUse dep skips the
prompt and streams normally.
Amit Haridas
This commit is contained in:
@@ -269,6 +269,75 @@ describe('createInlineAiController — detach', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('createInlineAiController — first-use confirmation', () => {
|
||||
test('confirmFirstUse is called with provider label and selection length', () => {
|
||||
const confirmFirstUse = jest.fn().mockReturnValue(true);
|
||||
const editor = makeEditor('hello world longer');
|
||||
editor._setSelection(0, 5);
|
||||
const { api } = makeApi();
|
||||
const controller = require('../src/renderer/inline-ai-controller').createInlineAiController({
|
||||
getEditorView: () => editor,
|
||||
electronAPI: api,
|
||||
getProviderLabel: () => 'openai (gpt-4o)',
|
||||
confirmFirstUse,
|
||||
});
|
||||
controller.showForSelection();
|
||||
getPopoverButton('rewrite').click();
|
||||
expect(confirmFirstUse).toHaveBeenCalledTimes(1);
|
||||
expect(confirmFirstUse).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
providerLabel: 'openai (gpt-4o)',
|
||||
selectionLength: 5, // selection is 'hello' (5 chars)
|
||||
action: 'rewrite',
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test('declining the confirm aborts and surfaces an error state', () => {
|
||||
const confirmFirstUse = jest.fn().mockReturnValue(false);
|
||||
const editor = makeEditor('hello world');
|
||||
editor._setSelection(0, 5);
|
||||
const { api, _state } = makeApi();
|
||||
const controller = require('../src/renderer/inline-ai-controller').createInlineAiController({
|
||||
getEditorView: () => editor,
|
||||
electronAPI: api,
|
||||
confirmFirstUse,
|
||||
});
|
||||
controller.showForSelection();
|
||||
getPopoverButton('rewrite').click();
|
||||
expect(_state.started).toHaveLength(0);
|
||||
const popovers = document.querySelectorAll('.inline-ai-popover');
|
||||
expect(popovers[popovers.length - 1].dataset.state).toBe('error');
|
||||
});
|
||||
|
||||
test('confirm is only asked once per session', () => {
|
||||
const confirmFirstUse = jest.fn().mockReturnValue(true);
|
||||
const editor = makeEditor('hello world');
|
||||
editor._setSelection(0, 5);
|
||||
const { api } = makeApi();
|
||||
const controller = require('../src/renderer/inline-ai-controller').createInlineAiController({
|
||||
getEditorView: () => editor,
|
||||
electronAPI: api,
|
||||
confirmFirstUse,
|
||||
});
|
||||
controller.showForSelection();
|
||||
getPopoverButton('rewrite').click();
|
||||
// Reset selection for a second action — confirm should NOT be asked again
|
||||
editor._setSelection(0, 5);
|
||||
getPopoverButton('shorten').click();
|
||||
expect(confirmFirstUse).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test('no confirmFirstUse dep means no prompt (still streams)', () => {
|
||||
const editor = makeEditor('hello world');
|
||||
editor._setSelection(0, 5);
|
||||
const { controller, _state } = mount({ editor });
|
||||
controller.showForSelection();
|
||||
getPopoverButton('rewrite').click();
|
||||
expect(_state.started).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createInlineAiController — validation', () => {
|
||||
test('throws when getEditorView is missing', () => {
|
||||
expect(() =>
|
||||
|
||||
Reference in New Issue
Block a user