f525772725
* fix(desktop): keep config/structured code blocks fenced instead of unwrapping to prose The desktop markdown preprocessor has a "prose fence" heuristic that strips the fence off blocks it thinks are wrapped prose. Its `proseLines >= 3 && codeSignals === 0` rule fires on ANY 3+ line plaintext block with no JS/SQL tokens -- which is exactly what an SSH config, a .env dump, or any INI/key-value listing looks like. The result was that a fenced ```-block of SSH config rendered as a flat paragraph instead of a code block. Add isLikelyStructuredText() and use it as a veto in both isLikelyProseFence() and isLikelyProseCodeBlock(): a block is treated as structured (and kept fenced) when it has indented continuation lines, or when it has no sentence-ending punctuation and a majority of lines are `Key value` / `Key: value` directives. Real wrapped prose has sentence-shaped lines and no per-line indentation, so it still unwraps as before. The bullet-prose case in isLikelyProseCodeBlock is checked first so markdown bullet lists remain prose. Tests: markdown-code.test.ts gains SSH-config / flat-config / .env regression cases for both functions, plus direct isLikelyStructuredText coverage, and re-asserts that genuine paragraph prose still unwraps. * fix(desktop): tighten config-line detection to not match punctuation-less prose The first CONFIG_LINE_RE matched any 'word word' line, so a wrapped prose fragment with no sentence punctuation (e.g. 'the quick brown fox jumps') was misread as a config directive and its fence kept. Split into an explicit-separator form (Key: value / Key = value) plus a short 2-3 token 'Key value' directive form; a real sentence line has more tokens, so punctuation-less prose is no longer treated as config.
98 lines
3.2 KiB
TypeScript
98 lines
3.2 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
import { isLikelyProseCodeBlock, isLikelyProseFence, isLikelyStructuredText } from './markdown-code'
|
|
|
|
describe('isLikelyProseCodeBlock', () => {
|
|
it('detects prose that Streamdown mislabels as an unknown language', () => {
|
|
expect(
|
|
isLikelyProseCodeBlock(
|
|
'heads',
|
|
[
|
|
'- Pure white (`#ffffff`), roughness 0.55, no emissive',
|
|
'- Black wireframe edges at 35% opacity',
|
|
'',
|
|
'Want the bunny gone, or want me to keep riffing on it?'
|
|
].join('\n')
|
|
)
|
|
).toBe(true)
|
|
})
|
|
|
|
it('keeps real code blocks', () => {
|
|
expect(isLikelyProseCodeBlock('ts', 'const value = { bunny: true };\nreturn value')).toBe(false)
|
|
})
|
|
|
|
it('keeps an SSH config block fenced (regression: rendered as flat prose)', () => {
|
|
const ssh = ['Host 192.168.0.159', ' HostName 192.168.0.159', ' User teknium', ' Port 22'].join('\n')
|
|
|
|
expect(isLikelyProseCodeBlock('', ssh)).toBe(false)
|
|
expect(isLikelyProseCodeBlock('text', ssh)).toBe(false)
|
|
})
|
|
|
|
it('keeps a flat key-value config fenced', () => {
|
|
expect(isLikelyProseCodeBlock('', ['Host myserver', 'User teknium', 'Port 22'].join('\n'))).toBe(false)
|
|
})
|
|
|
|
it('keeps an .env-style dump fenced', () => {
|
|
expect(isLikelyProseCodeBlock('', ['API_KEY=abc123', 'PORT=8080', 'DEBUG=true'].join('\n'))).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('isLikelyStructuredText', () => {
|
|
it('flags indented config stanzas', () => {
|
|
expect(
|
|
isLikelyStructuredText(['Host x', ' HostName 10.0.0.1', ' Port 22'].join('\n'))
|
|
).toBe(true)
|
|
})
|
|
|
|
it('flags flat key-value / settings listings', () => {
|
|
expect(isLikelyStructuredText(['Host myserver', 'User teknium', 'Port 22'].join('\n'))).toBe(true)
|
|
expect(isLikelyStructuredText(['API_KEY=abc123', 'PORT=8080', 'DEBUG=true'].join('\n'))).toBe(true)
|
|
})
|
|
|
|
it('does NOT flag real wrapped prose', () => {
|
|
expect(
|
|
isLikelyStructuredText(
|
|
[
|
|
'This is the first sentence of a paragraph.',
|
|
'Here is a second line that continues the thought.',
|
|
'And a third concluding line follows here.'
|
|
].join('\n')
|
|
)
|
|
).toBe(false)
|
|
})
|
|
|
|
it('does NOT flag prose without a config shape', () => {
|
|
expect(
|
|
isLikelyStructuredText(
|
|
['the quick brown fox jumps', 'over the lazy sleeping dog', 'while the sun sets slowly'].join('\n')
|
|
)
|
|
).toBe(false)
|
|
})
|
|
|
|
it('ignores single-line blocks', () => {
|
|
expect(isLikelyStructuredText('Port 22')).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('isLikelyProseFence', () => {
|
|
it('keeps an SSH config block fenced', () => {
|
|
const ssh = ['Host 192.168.0.159', ' HostName 192.168.0.159', ' User teknium', ' Port 22'].join('\n')
|
|
|
|
expect(isLikelyProseFence('', ssh)).toBe(false)
|
|
expect(isLikelyProseFence('text', ssh)).toBe(false)
|
|
})
|
|
|
|
it('still unwraps a plain-language paragraph fence', () => {
|
|
expect(
|
|
isLikelyProseFence(
|
|
'',
|
|
[
|
|
'This is the first sentence of a paragraph.',
|
|
'Here is a second line that continues the thought.',
|
|
'And a third concluding line follows here.'
|
|
].join('\n')
|
|
)
|
|
).toBe(true)
|
|
})
|
|
})
|