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:
@@ -0,0 +1,48 @@
|
||||
import { splitMentionSegments } from '$lib/utils/mention-badge';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
describe('splitMentionSegments', () => {
|
||||
it('returns a single plain run when there is no mention', () => {
|
||||
const segments = splitMentionSegments('# not a heading here');
|
||||
|
||||
expect(segments).toEqual([{ mention: null, text: '# not a heading here' }]);
|
||||
});
|
||||
|
||||
it('splits text around a mention', () => {
|
||||
const segments = splitMentionSegments('look at [main.c](file:///src/main.c) please');
|
||||
|
||||
expect(segments.map((segment) => segment.text)).toEqual([
|
||||
'look at ',
|
||||
'[main.c](file:///src/main.c)',
|
||||
' please'
|
||||
]);
|
||||
expect(segments[1].mention).toEqual({ name: 'main.c', path: '/src/main.c' });
|
||||
});
|
||||
|
||||
it('decodes percent-encoded paths', () => {
|
||||
const segments = splitMentionSegments('[a b.txt](file:///tmp/a%20b.txt)');
|
||||
|
||||
expect(segments[0].mention?.path).toBe('/tmp/a b.txt');
|
||||
});
|
||||
|
||||
it('keeps the directory marker so the folder icon is picked', () => {
|
||||
const segments = splitMentionSegments('[src](file:///repo/src/)');
|
||||
|
||||
expect(segments[0].mention?.path).toBe('/repo/src/');
|
||||
});
|
||||
|
||||
it('handles adjacent mentions with no text between them', () => {
|
||||
const segments = splitMentionSegments('[a](file:///a)[b](file:///b)');
|
||||
|
||||
expect(segments).toHaveLength(2);
|
||||
expect(segments.every((segment) => segment.mention !== null)).toBe(true);
|
||||
});
|
||||
|
||||
it('preserves the exact source when segments are joined back', () => {
|
||||
const source = 'see [a](file:///a) and [b](file:///b/) done';
|
||||
|
||||
expect(splitMentionSegments(source).reduce((acc, segment) => acc + segment.text, '')).toBe(
|
||||
source
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user