The bottom-push rule anchored on #root :has(> [data-slot=conversation]): a :has() match cache invalidates on every #root subtree mutation, and the streaming chat mutates that subtree constantly — every layout-push frame (the bottom drag writes --dsh-sidebar-height per frame) paid a whole-document selector re-evaluation. The shell's locator (which already holds the measured column node for the ResizeObserver) now tags it with [data-dsh-center-col]; the rule evaluates once per node. The tag retargets with the ref on boot swap / HMR node replacement and is removed on unmount; the drag lane pins exactly-one-tagged-node and its identity as the conversation slot wrapper's parent. The dead [data-pane] branch (renamed away in alpha hosts) is gone. TextEditor: the CodeMirror updateListener re-stringified the WHOLE document into React state on every keystroke (O(docLength) per key, re-rendering the shell per key). The listener now only flips the dirty flag; the preview's draft is snapshotted from the live view when entering preview (or on a content swap), and save() already reads the live document.
34 lines
1.3 KiB
TypeScript
34 lines
1.3 KiB
TypeScript
/**
|
|
* layout.css must let the DSH conversation column shrink below its content
|
|
* size. Without min-height:0 a long unbreakable URL grows the grid item
|
|
* past the viewport and clips the composer.
|
|
*/
|
|
import { readFileSync } from 'node:fs'
|
|
import { describe, expect, it } from 'vitest'
|
|
|
|
const css = readFileSync('src/client/layout.css', 'utf8')
|
|
|
|
describe('layout.css conversation column', () => {
|
|
it('targets the AppFrame center column through the shell-written tag', () => {
|
|
// The bottom-push rule anchors on the [data-dsh-center-col] tag the
|
|
// Sidebar shell's locator writes onto the measured column node (see
|
|
// Sidebar.tsx locate) — not on a `:has()` selector, whose cache
|
|
// invalidates on every #root subtree mutation (the streaming chat).
|
|
expect(css).toContain('[data-dsh-center-col]')
|
|
expect(css).not.toContain(':has(')
|
|
})
|
|
|
|
it('allows the center column to shrink and wrap long tokens', () => {
|
|
expect(css).toMatch(/min-height:\s*0/)
|
|
expect(css).toMatch(/overflow-wrap:\s*anywhere/)
|
|
})
|
|
|
|
it('leaves overflow ownership to the host conversation descendants', () => {
|
|
const conversationRule = css.match(
|
|
/#root \[data-dsh-center-col\][\s\S]*?\{([\s\S]*?)\n\}/,
|
|
)?.[1]
|
|
expect(conversationRule).toBeDefined()
|
|
expect(conversationRule).not.toMatch(/(?:^|[;\s])overflow\s*:/)
|
|
})
|
|
})
|