diff --git a/src/ui/options/settings.ts b/src/ui/options/settings.ts index 58568ad11..617829950 100644 --- a/src/ui/options/settings.ts +++ b/src/ui/options/settings.ts @@ -154,17 +154,29 @@ function wireImportExportHandlers(registry: SettingsRegistry): void { }); } +// The stored prefix-only value of each control while inline mode shows it as on. +const prefixOnlyBeforeInline = new WeakMap(); + function applyInlineSuggestionLocks(registry: SettingsRegistry, enabled: boolean): void { + const prefixOnly = registry[KEY_PREFIX_ONLY_MODE]; if (enabled) { const locks: Array<[string, unknown]> = [ [KEY_AUTOCOMPLETE_ON_TAB, true], [KEY_NUM_SUGGESTIONS, 10], - [KEY_PREFIX_ONLY_MODE, true], ]; for (const [key, value] of locks) { // A select control gives its value as a string. if (String(registry[key].get()) !== String(value)) registry[key].set(value); } + // ConfigAssembler forces prefix-only mode while inline mode is on. Thus show it without a write, + // so that the user's value comes back when inline mode goes off. + if (prefixOnly.get() !== true) { + prefixOnlyBeforeInline.set(prefixOnly, prefixOnly.get()); + prefixOnly.set(true, true); + } + } else if (prefixOnlyBeforeInline.has(prefixOnly)) { + prefixOnly.set(prefixOnlyBeforeInline.get(prefixOnly), true); + prefixOnlyBeforeInline.delete(prefixOnly); } registry[KEY_AUTOCOMPLETE_ON_TAB].setDisabled(enabled); registry[KEY_PREFIX_ONLY_MODE].setDisabled(enabled); diff --git a/tests/e2e/e2e-helpers.ts b/tests/e2e/e2e-helpers.ts index 0d7091c80..23c1ac75b 100644 --- a/tests/e2e/e2e-helpers.ts +++ b/tests/e2e/e2e-helpers.ts @@ -129,7 +129,7 @@ function cacheChromeExtensionHost(candidate: string | null | undefined): void { function isRetriableBackgroundContextError(error: unknown): boolean { const message = error instanceof Error ? error.message : String(error); - return /Execution context was destroyed|Execution context is not available in detached frame or worker|Cannot find context with specified id|Session closed|Target closed|Connection closed|background worker is unavailable|Waiting failed|NoSuchFrameError|Browsing Context with id .* not found/i.test( + return /Execution context was destroyed|Execution context is not available in detached frame or worker|Cannot find context with specified id|Session closed|Target closed|Connection closed|background worker is unavailable|Waiting failed|Timed out after waiting \d+ms|NoSuchFrameError|Browsing Context with id .* not found/i.test( message, ); } diff --git a/tests/options.page.test.ts b/tests/options.page.test.ts index a353ba8f4..33a9ad794 100644 --- a/tests/options.page.test.ts +++ b/tests/options.page.test.ts @@ -197,6 +197,26 @@ describe("options page settings handlers", () => { expect(writes).toEqual([...expected]); }, ); + + test("inline mode shows prefix-only mode as on but keeps the stored value", () => { + stubConfigChangeSender(); + const registry = fakeRegistry({ + [KEY_INLINE_SUGGESTION]: false, + [KEY_AUTOCOMPLETE_ON_TAB]: true, + [KEY_NUM_SUGGESTIONS]: "10", + [KEY_PREFIX_ONLY_MODE]: false, + }); + wireRuntimeSettingsHandlers(registry); + const prefixOnly = registry[KEY_PREFIX_ONLY_MODE]; + + registry[KEY_INLINE_SUGGESTION].set(true); + expect(prefixOnly.get()).toBe(true); + + registry[KEY_INLINE_SUGGESTION].set(false); + expect(prefixOnly.get()).toBe(false); + // ConfigAssembler forces prefix-only mode while inline mode is on, so no write is needed. + expect(prefixOnly.calls.every((call) => call.silent)).toBe(true); + }); }); async function flushPage(rounds = 30): Promise {