ui: UI/chat form follow ups (#26743)
* ui: split the markdown rendering setting per surface User content and thinking get their own toggle again, so turning off markdown for a message leaves reasoning blocks formatted. Both default to markdown. A stored renderContentAsRawText unfolds onto the user key and is dropped from the config. File mentions render as badges in the raw text path too, through a narrow pass over [name](file://path) that leaves everything else untouched. * ui: let the rich chat input scroll past its max height The contenteditable renderer caps its height with max-height but had no overflow rule, so a long buffer overflowed into the input area wrapper and got clipped by its overflow-hidden, leaving no way to reach the bottom of the message. The textarea renderer scrolls natively and was never affected. * ui: apply the new lint and format config * ui: move the render keys unfolding into the migration service Address review from @allozaur: the settings store no longer rewrites persisted config on load, the raw text toggle now unfolds onto the per-surface render keys in migration.service.ts, next to the other config migrations. The mention scanner flag and the directory path suffix become named constants.
This commit is contained in:
@@ -1,66 +0,0 @@
|
||||
// Guards the legacy render-key migration: `renderUserContentAsMarkdown`
|
||||
// and `renderThinkingAsMarkdown` (opt-INTO markdown) fold into the single
|
||||
// `renderContentAsRawText` setting, with any explicit raw-text preference
|
||||
// winning when the legacy keys disagree. Legacy keys are removed from the
|
||||
// persisted config so they do not stay orphaned in localStorage.
|
||||
|
||||
import { CONFIG_LOCALSTORAGE_KEY } from '$lib/constants/storage';
|
||||
import { config, settingsStore } from '$lib/stores/settings.svelte';
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
|
||||
function seedConfig(stored: Record<string, unknown>) {
|
||||
localStorage.setItem(CONFIG_LOCALSTORAGE_KEY, JSON.stringify(stored));
|
||||
settingsStore.initialize();
|
||||
}
|
||||
|
||||
function persisted(): Record<string, unknown> {
|
||||
return JSON.parse(localStorage.getItem(CONFIG_LOCALSTORAGE_KEY) ?? '{}');
|
||||
}
|
||||
|
||||
describe('renderContentAsRawText migration', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.removeItem(CONFIG_LOCALSTORAGE_KEY);
|
||||
settingsStore.initialize();
|
||||
});
|
||||
|
||||
it('maps renderUserContentAsMarkdown=false to raw text', () => {
|
||||
seedConfig({ renderUserContentAsMarkdown: false });
|
||||
expect(config().renderContentAsRawText).toBe(true);
|
||||
});
|
||||
|
||||
it('maps renderUserContentAsMarkdown=true to markdown', () => {
|
||||
seedConfig({ renderUserContentAsMarkdown: true });
|
||||
expect(config().renderContentAsRawText).toBe(false);
|
||||
});
|
||||
|
||||
it('maps renderThinkingAsMarkdown=false to raw text', () => {
|
||||
seedConfig({ renderThinkingAsMarkdown: false });
|
||||
expect(config().renderContentAsRawText).toBe(true);
|
||||
});
|
||||
|
||||
it('lets any explicit raw-text preference win when the legacy keys disagree', () => {
|
||||
seedConfig({ renderThinkingAsMarkdown: false, renderUserContentAsMarkdown: true });
|
||||
expect(config().renderContentAsRawText).toBe(true);
|
||||
});
|
||||
|
||||
it('honors the intermediate renderUserContentAsRawText key from the PR branch', () => {
|
||||
seedConfig({ renderUserContentAsRawText: true });
|
||||
expect(config().renderContentAsRawText).toBe(true);
|
||||
});
|
||||
|
||||
it('keeps an already-migrated value and cleans up the legacy keys', () => {
|
||||
seedConfig({ renderContentAsRawText: false, renderUserContentAsMarkdown: false });
|
||||
expect(config().renderContentAsRawText).toBe(false);
|
||||
|
||||
const stored = persisted();
|
||||
|
||||
expect(stored.renderUserContentAsMarkdown).toBeUndefined();
|
||||
expect(stored.renderThinkingAsMarkdown).toBeUndefined();
|
||||
expect(stored.renderUserContentAsRawText).toBeUndefined();
|
||||
});
|
||||
|
||||
it('defaults to markdown when no legacy key exists', () => {
|
||||
seedConfig({});
|
||||
expect(config().renderContentAsRawText).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,64 @@
|
||||
// Guards the unfolding of `renderContentAsRawText` back onto the two
|
||||
// per-surface render keys. The single toggle carried user content and
|
||||
// thinking at once, so only the user key is restored from it and thinking
|
||||
// returns to its own default. The toggle is removed from the persisted
|
||||
// config so it does not stay orphaned in localStorage.
|
||||
|
||||
import { CONFIG_LOCALSTORAGE_KEY } from '$lib/constants/storage';
|
||||
import { MigrationService } from '$lib/services/migration.service';
|
||||
import { config, settingsStore } from '$lib/stores/settings.svelte';
|
||||
import { beforeEach, describe, expect, it } from 'vitest';
|
||||
|
||||
const RENDER_KEYS_MIGRATION_ID = 'render-keys-unfold-v1';
|
||||
|
||||
async function seedConfig(stored: Record<string, unknown>) {
|
||||
localStorage.setItem(CONFIG_LOCALSTORAGE_KEY, JSON.stringify(stored));
|
||||
|
||||
const migration = MigrationService.getMigrations().find((m) => m.id === RENDER_KEYS_MIGRATION_ID);
|
||||
|
||||
await migration?.run();
|
||||
settingsStore.initialize();
|
||||
}
|
||||
|
||||
function persisted(): Record<string, unknown> {
|
||||
return JSON.parse(localStorage.getItem(CONFIG_LOCALSTORAGE_KEY) ?? '{}');
|
||||
}
|
||||
|
||||
describe('renderContentAsRawText unfolding', () => {
|
||||
beforeEach(() => {
|
||||
localStorage.removeItem(CONFIG_LOCALSTORAGE_KEY);
|
||||
MigrationService.resetState();
|
||||
settingsStore.initialize();
|
||||
});
|
||||
|
||||
it('maps raw text to user content as plain text', async () => {
|
||||
await seedConfig({ renderContentAsRawText: true });
|
||||
expect(config().renderUserContentAsMarkdown).toBe(false);
|
||||
});
|
||||
|
||||
it('maps markdown to user content as markdown', async () => {
|
||||
await seedConfig({ renderContentAsRawText: false });
|
||||
expect(config().renderUserContentAsMarkdown).toBe(true);
|
||||
});
|
||||
|
||||
it('leaves thinking on its own default', async () => {
|
||||
await seedConfig({ renderContentAsRawText: true });
|
||||
expect(config().renderThinkingAsMarkdown).toBe(true);
|
||||
});
|
||||
|
||||
it('keeps an explicit user preference over the toggle', async () => {
|
||||
await seedConfig({ renderContentAsRawText: true, renderUserContentAsMarkdown: true });
|
||||
expect(config().renderUserContentAsMarkdown).toBe(true);
|
||||
});
|
||||
|
||||
it('drops the toggle from the persisted config', async () => {
|
||||
await seedConfig({ renderContentAsRawText: true });
|
||||
expect(persisted().renderContentAsRawText).toBeUndefined();
|
||||
});
|
||||
|
||||
it('leaves both surfaces on markdown when nothing is stored', async () => {
|
||||
await seedConfig({});
|
||||
expect(config().renderUserContentAsMarkdown).toBe(true);
|
||||
expect(config().renderThinkingAsMarkdown).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user