@svelterm/core 0.1.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (166) hide show
  1. package/CHANGELOG.md +465 -0
  2. package/README.md +42 -29
  3. package/dist/src/cli/build.d.ts +13 -0
  4. package/dist/src/cli/build.js +119 -0
  5. package/dist/src/cli/bundle.d.ts +25 -0
  6. package/dist/src/cli/bundle.js +61 -0
  7. package/dist/src/cli/dev.d.ts +10 -0
  8. package/dist/src/cli/dev.js +152 -0
  9. package/dist/src/cli/devtools.d.ts +9 -0
  10. package/dist/src/cli/devtools.js +47 -0
  11. package/dist/src/cli/init.d.ts +8 -0
  12. package/dist/src/cli/init.js +153 -0
  13. package/dist/src/cli/main.d.ts +9 -0
  14. package/dist/src/cli/main.js +52 -0
  15. package/dist/src/cli/svt-bin.d.ts +2 -0
  16. package/dist/src/cli/svt-bin.js +6 -0
  17. package/dist/src/cli/svt.d.ts +14 -0
  18. package/dist/src/cli/svt.js +76 -0
  19. package/dist/src/components/text-buffer.js +8 -5
  20. package/dist/src/css/animation-runner.d.ts +15 -6
  21. package/dist/src/css/animation-runner.js +80 -29
  22. package/dist/src/css/animation.d.ts +12 -0
  23. package/dist/src/css/animation.js +21 -0
  24. package/dist/src/css/calc.js +4 -3
  25. package/dist/src/css/color.d.ts +19 -0
  26. package/dist/src/css/color.js +371 -62
  27. package/dist/src/css/compute.d.ts +31 -4
  28. package/dist/src/css/compute.js +273 -34
  29. package/dist/src/css/defaults.d.ts +1 -1
  30. package/dist/src/css/defaults.js +9 -0
  31. package/dist/src/css/easing.d.ts +9 -0
  32. package/dist/src/css/easing.js +95 -0
  33. package/dist/src/css/incremental.d.ts +1 -1
  34. package/dist/src/css/incremental.js +2 -2
  35. package/dist/src/css/interpolate.d.ts +13 -0
  36. package/dist/src/css/interpolate.js +41 -0
  37. package/dist/src/css/parser.js +59 -3
  38. package/dist/src/css/pseudo-elements.d.ts +9 -0
  39. package/dist/src/css/pseudo-elements.js +97 -0
  40. package/dist/src/css/selector.d.ts +17 -2
  41. package/dist/src/css/selector.js +128 -13
  42. package/dist/src/css/specificity.js +17 -6
  43. package/dist/src/css/values.d.ts +6 -1
  44. package/dist/src/css/values.js +13 -6
  45. package/dist/src/debug/context.d.ts +13 -0
  46. package/dist/src/debug/context.js +11 -0
  47. package/dist/src/debug/css.d.ts +12 -0
  48. package/dist/src/debug/css.js +28 -0
  49. package/dist/src/debug/dom.d.ts +17 -0
  50. package/dist/src/debug/dom.js +92 -0
  51. package/dist/src/devtools/DevTools.compiled.js +327 -0
  52. package/dist/src/devtools/DevTools.css.js +1 -0
  53. package/dist/src/devtools/client.d.ts +36 -0
  54. package/dist/src/devtools/client.js +76 -0
  55. package/dist/src/framelog.d.ts +54 -0
  56. package/dist/src/framelog.js +99 -0
  57. package/dist/src/headless.js +12 -4
  58. package/dist/src/index.d.ts +66 -3
  59. package/dist/src/index.js +610 -81
  60. package/dist/src/input/checkable.d.ts +8 -0
  61. package/dist/src/input/checkable.js +66 -0
  62. package/dist/src/input/details.d.ts +6 -0
  63. package/dist/src/input/details.js +34 -0
  64. package/dist/src/input/focus.d.ts +6 -0
  65. package/dist/src/input/focus.js +27 -9
  66. package/dist/src/input/keyboard.d.ts +2 -2
  67. package/dist/src/input/keyboard.js +32 -5
  68. package/dist/src/input/label.d.ts +8 -0
  69. package/dist/src/input/label.js +53 -0
  70. package/dist/src/input/modal.d.ts +9 -0
  71. package/dist/src/input/modal.js +28 -0
  72. package/dist/src/input/mouse.d.ts +2 -2
  73. package/dist/src/input/mouse.js +15 -2
  74. package/dist/src/input/select.d.ts +12 -0
  75. package/dist/src/input/select.js +63 -0
  76. package/dist/src/input/selection.d.ts +48 -0
  77. package/dist/src/input/selection.js +150 -0
  78. package/dist/src/layout/engine.d.ts +2 -0
  79. package/dist/src/layout/engine.js +1092 -142
  80. package/dist/src/layout/flex.js +4 -4
  81. package/dist/src/layout/size.js +3 -2
  82. package/dist/src/layout/text.d.ts +3 -2
  83. package/dist/src/layout/text.js +96 -17
  84. package/dist/src/layout/unicode.d.ts +20 -0
  85. package/dist/src/layout/unicode.js +121 -0
  86. package/dist/src/render/animation-clock.d.ts +57 -0
  87. package/dist/src/render/animation-clock.js +221 -0
  88. package/dist/src/render/ansi-text.d.ts +26 -0
  89. package/dist/src/render/ansi-text.js +131 -0
  90. package/dist/src/render/ansi.d.ts +18 -0
  91. package/dist/src/render/ansi.js +64 -19
  92. package/dist/src/render/border.js +166 -17
  93. package/dist/src/render/buffer.d.ts +1 -0
  94. package/dist/src/render/buffer.js +5 -2
  95. package/dist/src/render/clock.d.ts +35 -0
  96. package/dist/src/render/clock.js +67 -0
  97. package/dist/src/render/color-depth.d.ts +8 -0
  98. package/dist/src/render/color-depth.js +59 -0
  99. package/dist/src/render/context.d.ts +1 -0
  100. package/dist/src/render/context.js +17 -21
  101. package/dist/src/render/cursor-emit.d.ts +18 -0
  102. package/dist/src/render/cursor-emit.js +50 -0
  103. package/dist/src/render/diff.d.ts +12 -0
  104. package/dist/src/render/diff.js +120 -0
  105. package/dist/src/render/generation.d.ts +9 -0
  106. package/dist/src/render/generation.js +14 -0
  107. package/dist/src/render/graphics-layer.d.ts +27 -0
  108. package/dist/src/render/graphics-layer.js +86 -0
  109. package/dist/src/render/image.d.ts +27 -0
  110. package/dist/src/render/image.js +113 -0
  111. package/dist/src/render/incremental-paint.d.ts +7 -3
  112. package/dist/src/render/incremental-paint.js +52 -79
  113. package/dist/src/render/inline.d.ts +59 -0
  114. package/dist/src/render/inline.js +219 -0
  115. package/dist/src/render/kitty-graphics.d.ts +24 -0
  116. package/dist/src/render/kitty-graphics.js +58 -0
  117. package/dist/src/render/paint-text.js +68 -22
  118. package/dist/src/render/paint.d.ts +8 -1
  119. package/dist/src/render/paint.js +358 -31
  120. package/dist/src/render/png.d.ts +13 -0
  121. package/dist/src/render/png.js +145 -0
  122. package/dist/src/render/scrollbar.d.ts +8 -2
  123. package/dist/src/render/scrollbar.js +71 -14
  124. package/dist/src/render/snapshot.js +3 -1
  125. package/dist/src/renderer/default.d.ts +7 -0
  126. package/dist/src/renderer/default.js +11 -0
  127. package/dist/src/renderer/index.d.ts +8 -2
  128. package/dist/src/renderer/index.js +4 -2
  129. package/dist/src/renderer/node.d.ts +109 -0
  130. package/dist/src/renderer/node.js +165 -1
  131. package/dist/src/terminal/capabilities.d.ts +33 -0
  132. package/dist/src/terminal/capabilities.js +66 -0
  133. package/dist/src/terminal/clipboard.d.ts +9 -0
  134. package/dist/src/terminal/clipboard.js +39 -0
  135. package/dist/src/terminal/io.d.ts +82 -0
  136. package/dist/src/terminal/io.js +155 -0
  137. package/dist/src/terminal/screen.d.ts +3 -10
  138. package/dist/src/terminal/screen.js +5 -28
  139. package/dist/src/terminal/stdin-router.d.ts +8 -5
  140. package/dist/src/terminal/stdin-router.js +22 -11
  141. package/dist/src/utils/node-map.d.ts +24 -0
  142. package/dist/src/utils/node-map.js +75 -0
  143. package/dist/src/vite/config.d.ts +62 -0
  144. package/dist/src/vite/config.js +191 -0
  145. package/docs/compatibility.md +67 -0
  146. package/docs/debug/devtools.md +40 -0
  147. package/docs/debug/svt.md +50 -0
  148. package/docs/distribution.md +106 -0
  149. package/docs/elements.md +120 -0
  150. package/docs/getting-started.md +177 -0
  151. package/docs/guide/css.md +187 -0
  152. package/docs/guide/input.md +143 -0
  153. package/docs/guide/layout.md +171 -0
  154. package/docs/guide/theming.md +94 -0
  155. package/docs/how-it-works.md +115 -0
  156. package/docs/inline-mode.md +77 -0
  157. package/docs/layout.md +112 -0
  158. package/docs/motion.md +91 -0
  159. package/docs/reference/README.md +65 -0
  160. package/docs/reference/css/properties/border-corner.md +82 -0
  161. package/docs/reference/css/properties/border-style.md +168 -0
  162. package/docs/reference.md +227 -0
  163. package/docs/selectors.md +80 -0
  164. package/docs/terminal-css.md +149 -0
  165. package/docs/terminals.md +83 -0
  166. package/package.json +28 -7
@@ -0,0 +1,94 @@
1
+ # Theming
2
+
3
+ Svelterm uses CSS variables for theming — the same mechanism as the web. Define color tokens on a root element, reference them throughout your components, switch themes by changing a class.
4
+
5
+ ## Basic theme
6
+
7
+ ```css
8
+ :root {
9
+ --primary: cyan;
10
+ --accent: yellow;
11
+ --muted: gray;
12
+ --bg: black;
13
+ --fg: white;
14
+ }
15
+
16
+ .title { color: var(--primary); }
17
+ .value { color: var(--accent); }
18
+ .hint { color: var(--muted); opacity: dim; }
19
+ ```
20
+
21
+ ## Multiple themes
22
+
23
+ Define theme palettes as class selectors. Switch by changing the class on a parent element:
24
+
25
+ ```svelte
26
+ <script>
27
+ let theme = $state('dark-ocean')
28
+ </script>
29
+
30
+ <style>
31
+ .dark-ocean {
32
+ --primary: cyan; --accent: yellow; --border: cyan;
33
+ }
34
+ .dark-forest {
35
+ --primary: green; --accent: yellow; --border: green;
36
+ }
37
+ .light-paper {
38
+ --primary: #2244aa; --accent: #886600; --border: #2244aa;
39
+ }
40
+ </style>
41
+
42
+ <div class="app {theme}">
43
+ <span style:color="var(--primary)">{theme}</span>
44
+ <button onclick={() => theme = 'dark-forest'}>Switch</button>
45
+ </div>
46
+ ```
47
+
48
+ ## Light and dark mode
49
+
50
+ Svelterm detects the terminal's background color via OSC 11 at startup, then polls every second. The detected scheme feeds into `@media (prefers-color-scheme)`:
51
+
52
+ ```css
53
+ @media (prefers-color-scheme: dark) {
54
+ :root { --bg: black; --fg: white; --primary: cyan; }
55
+ }
56
+
57
+ @media (prefers-color-scheme: light) {
58
+ :root { --bg: #eeeeee; --fg: #111111; --primary: #0088aa; }
59
+ }
60
+ ```
61
+
62
+ This works automatically — no code needed. If the user switches their terminal's color scheme, the app re-renders with the correct palette within a second.
63
+
64
+ ### Color contrast
65
+
66
+ Some CSS colors have poor contrast on light backgrounds (yellow, cyan, gold). Use the media query to provide darker variants:
67
+
68
+ ```css
69
+ .warning { color: yellow; }
70
+
71
+ @media (prefers-color-scheme: light) {
72
+ .warning { color: #997700; }
73
+ }
74
+ ```
75
+
76
+ ## Dual-target theming
77
+
78
+ CSS variables work in both terminal and browser. Use the same tokens, with target-specific values:
79
+
80
+ ```css
81
+ :root {
82
+ --primary: cyan;
83
+ --bg: black;
84
+ }
85
+
86
+ @media (display-mode: screen) {
87
+ :root {
88
+ --primary: #00b4d8;
89
+ --bg: #1a1a2e;
90
+ }
91
+ }
92
+ ```
93
+
94
+ The terminal gets ANSI `cyan`. The browser gets hex `#00b4d8`. Same component, same variable names, different rendered values.
@@ -0,0 +1,115 @@
1
+ # How it works
2
+
3
+ svelterm is a Svelte custom renderer plus a CSS engine plus a terminal
4
+ painter. There is no DOM anywhere — not emulated, not shimmed. This page
5
+ walks the path from your component source to ANSI bytes on a terminal.
6
+
7
+ ## No DOM
8
+
9
+ Svelte's compiler doesn't hardcode the DOM: compiled components call a
10
+ renderer interface — create an element, insert a node, set an attribute,
11
+ set text, listen for an event. In a browser that interface is backed by
12
+ `document`. svelterm (via the experimental
13
+ [custom renderer API](https://github.com/paoloricciuti/svelte/tree/svelte-custom-renderer))
14
+ supplies a different backing: every call builds and mutates a tree of
15
+ **`TermNode`** objects — plain values holding a tag, attributes, children,
16
+ event listeners, and a little render cache. No `document`, no `window`, no
17
+ JSDOM.
18
+
19
+ Everything Svelte does — reactivity, `$state`, effects, `{#if}`/`{#each}`
20
+ patching, component composition, scoped styles — works unchanged, because
21
+ Svelte only ever asks the renderer to make small tree mutations. Your
22
+ component doesn't know it's in a terminal; a handful of DOM-compat
23
+ properties on `TermNode` (`textContent`, `nodeValue`, `checked`, `value`)
24
+ cover the places Svelte's generated code reads nodes directly.
25
+
26
+ The renderer itself is stateless; each node carries its render context
27
+ (`node.ctx`), attached on insert and cleared on remove, so trees can be
28
+ built detached and adopted later.
29
+
30
+ ## The pipeline
31
+
32
+ Each frame runs a browser-shaped pipeline against the `TermNode` tree,
33
+ with one radical simplification: the unit of geometry is a character
34
+ cell, not a pixel.
35
+
36
+ ```
37
+ component source
38
+ │ svelte compiler (customRenderer)
39
+
40
+ TermNode tree ◄── Svelte reactivity mutates it
41
+ │ resolve styles cascade, specificity, selectors, var(), @media
42
+
43
+ ResolvedStyle per node
44
+ │ layout block/inline flow, flex, grid, tables → cells
45
+
46
+ LayoutBox per node { x, y, width, height } in cells
47
+ │ paint borders, text, backgrounds, form glyphs
48
+
49
+ CellBuffer every cell: { char, fg, bg, bold, … }
50
+ │ diff vs previous buffer
51
+
52
+ minimal ANSI cursor moves + SGR codes, sync-wrapped
53
+ ```
54
+
55
+ - **Style resolution** matches the stylesheet's selectors against the
56
+ tree and folds the cascade into one `ResolvedStyle` struct per element
57
+ — the same job as a browser's computed style, minus pixel-derived
58
+ properties (which parse and drop; see
59
+ [compatibility](./compatibility.md)).
60
+ - **Layout** implements block/inline flow, flexbox, grid, and table
61
+ algorithms over integer cells. Every length rounds to whole cells;
62
+ borders are one cell thick.
63
+ - **Paint** writes glyphs and colours into a `CellBuffer` — including
64
+ box-drawing borders, list markers, form-control glyphs like `[x]`, and
65
+ `::before`/`::after` runs.
66
+ - **Diff** compares against the previous buffer and emits only the
67
+ changed cells as ANSI (cursor positioning + colour codes), wrapped in
68
+ a synchronized-update sequence so the terminal repaints atomically.
69
+
70
+ ## Incremental updates
71
+
72
+ Re-running the whole pipeline per keystroke would waste most of its
73
+ work, so mutations are classified at the point Svelte makes them:
74
+
75
+ - same-length text change → **paint only**
76
+ - attribute change → **re-resolve styles** for the node and its
77
+ descendants (any attribute can affect selector matching)
78
+ - size-affecting change → **layout**, scoped to a subtree or bubbled to
79
+ the nearest fixed-size ancestor
80
+
81
+ A microtask-batched queue coalesces a burst of mutations into one
82
+ render, which runs only the stages the batch needs — an incremental
83
+ style pass, an incremental layout pass, and a repaint clipped to the
84
+ damaged region.
85
+
86
+ ## Input, without a browser
87
+
88
+ Raw stdin bytes are parsed into key events (including modifiers and
89
+ escape sequences), SGR mouse events with cell coordinates, and
90
+ bracketed paste. Mouse positions hit-test against the layout boxes to
91
+ find the target node; events then dispatch through the tree with
92
+ W3C capture/bubble semantics, `preventDefault()` and all. Focus is a
93
+ document-order traversal of focusable elements driven by `Tab`, with
94
+ `:focus`/`:hover` implemented as attribute-backed pseudo-classes.
95
+ Default actions (link opening, checkbox toggling, select cycling,
96
+ label activation) live in the run loop, mirroring browser behaviour.
97
+
98
+ ## Time
99
+
100
+ An animation clock discovers elements whose resolved style declares an
101
+ animation or whose transition targets changed, applies the current
102
+ interpolated values onto their resolved styles (~30fps), and enqueues
103
+ paint-only or layout invalidation per frame depending on what the
104
+ animation touches — see [motion](./motion.md).
105
+
106
+ ## The terminal is an interface too
107
+
108
+ All output goes through a small `TerminalIO` interface rather than
109
+ `process.stdout` directly. `ProcessIO` backs real terminals (raw mode,
110
+ alternate screen, resize signals, OSC 11 colour-scheme queries);
111
+ `InProcessIO` backs anything that can accept a byte stream — which is
112
+ how the playground works: the same engine, compiled for the browser,
113
+ writes its ANSI into an xterm.js instance. The browser pane next to it
114
+ is simply the same component compiled with Svelte's normal DOM renderer.
115
+ One source, two render targets — that contrast is the whole point.
@@ -0,0 +1,77 @@
1
+ # Inline mode
2
+
3
+ Fullscreen apps own the alternate screen. CLI tools want the opposite:
4
+ output that streams downward, scrolls into the terminal's real
5
+ scrollback, and leaves the screen intact on exit — the Claude Code /
6
+ Gemini CLI shape. That's inline mode:
7
+
8
+ ```typescript
9
+ run(App, { mode: 'inline' })
10
+ ```
11
+
12
+ The app renders at the shell's cursor position in the main buffer. The
13
+ **live area** sizes itself to the app's content (clamped to the terminal
14
+ height) and updates with cell-level diffs; everything above it belongs to
15
+ the terminal and is never touched again. All cursor movement is relative
16
+ — svelterm never needs to know where on the screen it's running. On exit
17
+ the rendered output stays put and the shell prompt continues below it.
18
+
19
+ ## FrameLog
20
+
21
+ The companion for streaming sessions: an append-only log of frames,
22
+ each a mounted component. Archiving a finished frame hands its rows to
23
+ the terminal's scrollback and unmounts the component — memory tracks
24
+ what's live, not the whole session.
25
+
26
+ ```svelte
27
+ <script>
28
+ import { createFrameLog } from '@svelterm/core/app'
29
+ import Message from './Message.svelte'
30
+
31
+ function start(el) {
32
+ const log = createFrameLog(el)
33
+ const run = setTimeout(async () => {
34
+ log.append(Message, { role: 'user', text: 'hello' })
35
+ const props = $state({ role: 'assistant', text: '', streaming: true })
36
+ const id = log.append(Message, props)
37
+ for await (const chunk of stream()) props.text += chunk
38
+ props.streaming = false
39
+ log.archive(id) // this turn scrolls into history, components freed
40
+ }, 0)
41
+ return () => clearTimeout(run)
42
+ }
43
+ </script>
44
+
45
+ <div class="log" {@attach start}></div>
46
+ <StatusBar />
47
+ ```
48
+
49
+ - **`append(Component, props)`** mounts a frame and returns its id. Pass
50
+ a `$state` object as props to stream updates into it (mutate it, or
51
+ call `update(id, partial)` which assigns onto the same object).
52
+ - **`archive(id)`** archives every frame up to and including `id` —
53
+ frames leave from the top, in order. Their rows stay on the terminal
54
+ exactly as rendered.
55
+ - **`remove(id)`** deletes a frame outright (redaction); the live area
56
+ reflows to close the gap.
57
+ - Get the host element with `{@attach ...}` — `bind:this` is not
58
+ available under the custom renderer. Kick off async work with
59
+ `setTimeout(fn, 0)` so state writes don't re-trigger the attachment.
60
+
61
+ Try it: `DEMO=inline npm run demo` in the svelterm repo.
62
+
63
+ ## Constraints
64
+
65
+ - The live area must fit the terminal height — archive finished content
66
+ to keep it short. Content past the bottom clips.
67
+ - **Mouse works in the live area**: the zone's screen position comes
68
+ from a cursor-position query, so clicks, hover, and scroll map into
69
+ the app; clicks on shell history above it are ignored. (On a terminal
70
+ that doesn't answer CPR, mouse events are dropped.) Keyboard input,
71
+ focus, and the input cursor work as usual.
72
+ - Archived rows never re-wrap: after a resize they keep their old
73
+ wrapping, like any other scrollback. The live area re-lays-out and
74
+ repaints in place on width changes (best effort — some terminals
75
+ re-wrap the live rows too; the next frame repaints them).
76
+ - Every change re-renders the live area fully (it's content-sized, so
77
+ anything can move). Keep live frames modest; archive the rest.
package/docs/layout.md ADDED
@@ -0,0 +1,112 @@
1
+ # Layout
2
+
3
+ svelterm implements the CSS layout models that make sense on a cell grid:
4
+ block/inline flow, flexbox, grid, tables, and positioning. Lengths are
5
+ cells; behaviour follows the specs linked below unless a grid deviation is
6
+ noted.
7
+
8
+ ## Box model
9
+
10
+ [Box model](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_box_model)
11
+ as standard: `width`/`height` (+ `min-`/`max-`), `padding`, `margin`
12
+ (including `margin: auto` centring and vertical
13
+ [margin collapse](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_box_model/Mastering_margin_collapsing)),
14
+ `box-sizing: border-box | content-box`. Borders are 1 cell thick when
15
+ present.
16
+
17
+ `overflow: hidden | scroll | auto` clips, and scrollable boxes really
18
+ scroll — mouse wheel, with fading scrollbar overlays. `text-overflow:
19
+ ellipsis` needs the usual `white-space: nowrap; overflow: hidden`.
20
+
21
+ Scrolling is O(visible): subtrees outside the clip are culled from the
22
+ paint walk, so a 10,000-row list repaints in ~1.5 ms per scroll step
23
+ (initial layout still visits every row once). No windowing API needed —
24
+ put long content in an `overflow: auto` box and scroll it.
25
+
26
+ When the whole viewport scrolls (a fullscreen list, streaming inline
27
+ output), the diff detects the vertical translation and emits a DECSTBM
28
+ scroll-region command instead of rewriting every cell — an 80×40 screen
29
+ scrolled one line is ~13× less output, which shows on slow links.
30
+
31
+ ## Display and flow
32
+
33
+ `display: block, inline, inline-block, flex, grid, none, contents` and
34
+ the full set of table display types. Inline elements flow horizontally
35
+ and wrap; whitespace between inline siblings is preserved, between block
36
+ siblings collapsed — matching browser text flow.
37
+
38
+ ## Flexbox
39
+
40
+ [Flexbox](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_flexible_box_layout)
41
+ support: `flex-direction` (all four), `flex-wrap`, the `flex` shorthand
42
+ and `flex-grow`/`flex-shrink`/`flex-basis`, `gap`, `justify-content`
43
+ (including the `space-*` distributions), `align-items`, `align-self`,
44
+ `order`.
45
+
46
+ ```css
47
+ .toolbar { display: flex; gap: 1ch; justify-content: space-between; }
48
+ .spacer { flex: 1; }
49
+ ```
50
+
51
+ ## Grid
52
+
53
+ [Grid](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout)
54
+ support:
55
+
56
+ - `grid-template-columns` / `grid-template-rows` with `cell`/`ch`, `%`,
57
+ `fr`, `repeat()`, and `minmax()`.
58
+ - Placement: `grid-column` and `grid-row` (`start`, `start / end`,
59
+ `span n`), numeric `grid-area` (`r1 / c1 / r2 / c2`).
60
+ - Named areas: `grid-template-areas` with `grid-area: name`; `.` is a
61
+ hole; a repeated name spans its rectangle. Without a column template,
62
+ areas split the width evenly.
63
+
64
+ ```css
65
+ .app {
66
+ display: grid;
67
+ grid-template-columns: 20ch 1fr;
68
+ grid-template-rows: 1ch 1fr 1ch;
69
+ grid-template-areas:
70
+ "header header"
71
+ "nav main"
72
+ "footer footer";
73
+ }
74
+ .nav { grid-area: nav; }
75
+ ```
76
+
77
+ Deviations: auto-flow is row-based (`grid-auto-flow: column` is not
78
+ implemented); `minmax()` minimums on `fr` tracks are enforced without
79
+ redistribution; spanning content doesn't stretch individual tracks.
80
+
81
+ ## Tables
82
+
83
+ Full [CSS table layout](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_table):
84
+ `<table>`/`<thead>`/`<tbody>`/`<tfoot>`/`<caption>`/`<colgroup>`,
85
+ `colspan`/`rowspan`, `table-layout: auto | fixed`, `border-spacing`,
86
+ `caption-side`, `empty-cells`, and `border-collapse: collapse` drawn with
87
+ shared box-drawing grid lines. Anonymous boxes are generated for stray
88
+ content. `vertical-align: baseline` is treated as `top` (one line of
89
+ cells has no baseline distinct from its top).
90
+
91
+ ## Positioning
92
+
93
+ `position: absolute` and `fixed` take elements out of flow and place them
94
+ by `top`/`right`/`bottom`/`left` with `z-index` stacking. `position:
95
+ relative` shifts the element (and its descendants) visually while the
96
+ flow behaves as if it hadn't moved, per spec. `position: sticky` pins an
97
+ element to the top of its scroll container once scrolled past
98
+ (`top`-edge only; it doesn't yet push out at the end of its containing
99
+ block, and hit-testing targets the flow position rather than the stuck
100
+ one). A sticky element with a transparent background shows scrolled
101
+ content through it — give it a background. Sub-cell geometry
102
+ (`transform`, floats) is out of scope; see
103
+ [compatibility](./compatibility.md).
104
+
105
+ ## Sizing behaviours worth knowing
106
+
107
+ - Blocks fill their container's width; inline-blocks shrink-wrap.
108
+ - `input`/`textarea`/`select` have a minimum height of one row;
109
+ `<select>` sizes to its longest option plus the `▾` indicator;
110
+ checkboxes/radios are 3×1; `progress`/`meter` default to 20×1.
111
+ - `@container` size queries evaluate against the nearest laid-out
112
+ ancestor, in cells.
package/docs/motion.md ADDED
@@ -0,0 +1,91 @@
1
+ # Animations and transitions
2
+
3
+ CSS motion works on the grid with one honest constraint: a cell either
4
+ has a glyph and colours or it doesn't. Colours interpolate smoothly;
5
+ geometry steps cell by cell; everything else switches discretely.
6
+
7
+ ## Animations
8
+
9
+ [`@keyframes`](https://developer.mozilla.org/en-US/docs/Web/CSS/@keyframes)
10
+ with `from`/`to` or percentage stops, driven by the `animation` shorthand
11
+ or `animation-name` / `animation-duration` / `animation-iteration-count`
12
+ (including `infinite`) / `animation-timing-function`:
13
+
14
+ ```css
15
+ @keyframes pulse {
16
+ 0% { color: #ef4444; }
17
+ 50% { color: #7f1d1d; }
18
+ }
19
+ .recording { animation: pulse 1s ease-in-out infinite; }
20
+ ```
21
+
22
+ Keyframe values resolve
23
+ [`var()`](https://developer.mozilla.org/en-US/docs/Web/CSS/var) and
24
+ [`light-dark()`](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/light-dark)
25
+ against the animated element when the animation starts.
26
+
27
+ ## Easing
28
+
29
+ [`animation-timing-function`](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timing-function)
30
+ and
31
+ [`transition-timing-function`](https://developer.mozilla.org/en-US/docs/Web/CSS/transition-timing-function)
32
+ support `linear`, `ease` (the default, as in browsers), `ease-in`,
33
+ `ease-out`, `ease-in-out`, `cubic-bezier(x1, y1, x2, y2)`,
34
+ `steps(n[, start | end])`, `step-start` and `step-end`. As in CSS, the
35
+ easing shapes progress within each keyframe segment, and non-interpolable
36
+ values switch when *eased* progress crosses the midpoint.
37
+
38
+ ## What interpolates
39
+
40
+ - **Colours** (`color`, `background`) mix in RGB at ~30fps between the
41
+ surrounding stops. ANSI palette names interpolate through nominal
42
+ values and stay exact at the endpoints.
43
+ - **Single cell/ch lengths** (`width`, `height`, `padding-*`, `margin-*`,
44
+ insets, `gap`) interpolate and round to whole cells — a 10-cell slide
45
+ is ten visible steps, so give movement enough distance to read.
46
+ - **Everything else** supported by the style system (`display`,
47
+ `font-weight`, `border-*`, `visibility`, …) applies discretely,
48
+ switching at the midpoint of the segment — the CSS rule for
49
+ non-interpolable values.
50
+
51
+ Layout-affecting animations re-flow every frame; colour-only animations
52
+ just repaint.
53
+
54
+ ## Transitions
55
+
56
+ [`transition`](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_transitions)
57
+ runs when a tracked property's target changes — a class toggles, an
58
+ inline style updates, a `:checked` rule starts applying:
59
+
60
+ ```css
61
+ .preview { transition: background-color 400ms; }
62
+ .tab { transition: color 150ms, padding-left 150ms; }
63
+ ```
64
+
65
+ `transition-property` takes a list or `all`; `transition-duration` takes
66
+ `ms`/`s`. The same interpolation rules as animations apply. The initial
67
+ style never transitions.
68
+
69
+ ## Deviations from browsers
70
+
71
+ - One duration and one timing function apply to all listed transition
72
+ properties (per-property lists aren't split).
73
+ - An interrupted transition restarts from its previous target value, not
74
+ the current blended value.
75
+ - `opacity` doesn't interpolate (it applies discretely mid-animation) —
76
+ animate colour toward the background for a smooth fade.
77
+ - Keyframe `var()`/`light-dark()` resolution happens once when the
78
+ animation starts; changing a custom property doesn't retarget a
79
+ running animation.
80
+ - Per-keyframe `animation-timing-function` overrides are ignored — the
81
+ element's timing function applies to every segment.
82
+
83
+ ## Reduced motion
84
+
85
+ Media queries work, so honour user preference the standard way:
86
+
87
+ ```css
88
+ @media (prefers-reduced-motion: reduce) {
89
+ .recording { animation: none; }
90
+ }
91
+ ```
@@ -0,0 +1,65 @@
1
+ # Svelterm Reference
2
+
3
+ Authoritative reference for svelterm's authoring surface and runtime APIs.
4
+
5
+ ## Layout
6
+
7
+ ```
8
+ docs/reference/
9
+ ├── css/
10
+ │ ├── properties/ # Per-property pages (border-style, padding, …)
11
+ │ ├── units/ # Unit pages (cell, %, …)
12
+ │ ├── at-rules/ # @media, @keyframes, …
13
+ │ └── selectors.md # Selector reference
14
+ ├── runtime/ # run(), IO, Terminal, …
15
+ └── integration/ # Svelte integration, custom renderer, …
16
+ ```
17
+
18
+ ## Page conventions
19
+
20
+ Each page is a standalone Markdown file with the following structure:
21
+
22
+ ```markdown
23
+ ---
24
+ name: border-style
25
+ category: css/properties
26
+ summary: Controls the visual appearance of the element's border.
27
+ related:
28
+ - css/properties/border-corner
29
+ - css/properties/border-color
30
+ ---
31
+
32
+ # `border-style`
33
+
34
+ One-line description.
35
+
36
+ ## Syntax
37
+
38
+ (Or `## Values` for properties with a fixed value list.)
39
+
40
+ ## Examples
41
+
42
+ Each example shows the CSS, then the resulting terminal output (ASCII frame inline; later: actual rendered image).
43
+
44
+ ## Notes
45
+
46
+ Caveats, terminal-vs-browser differences, edge cases.
47
+
48
+ ## See also
49
+
50
+ Cross-links to related pages.
51
+ ```
52
+
53
+ ## Visual examples
54
+
55
+ Examples should include a small terminal "frame" rendered as Markdown so the page is readable as plain text. The site build can later replace these with real rendered images.
56
+
57
+ ```
58
+ ┌─────────────┐
59
+ │ hello │
60
+ └─────────────┘
61
+ ```
62
+
63
+ ## Stable surface
64
+
65
+ Documented properties, values, and APIs are part of svelterm's stable surface. Behaviour changes need a corresponding doc update.
@@ -0,0 +1,82 @@
1
+ ---
2
+ name: border-corner
3
+ category: css/properties
4
+ summary: Controls which axis owns the corner cells of a block-character border.
5
+ related:
6
+ - css/properties/border-style
7
+ ---
8
+
9
+ # `border-corner`
10
+
11
+ Block-character border styles (`eighth-cell-*`, `half-cell-*`) have no Unicode glyph that combines two perpendicular strokes in a single cell. `border-corner` selects which axis "owns" the corner cells.
12
+
13
+ ## Values
14
+
15
+ | Value | Effect |
16
+ |-------|--------|
17
+ | `none` | Default. Corner cells blank — produces a soft-rounded look. |
18
+ | `h` | Top/bottom strokes extend through corner cells. Side strokes stop one cell short. |
19
+ | `v` | Left/right strokes extend through corner cells. Top/bottom stop one cell short. |
20
+
21
+ Only meaningful for [`border-style`](border-style.md) values in the `*-cell-*` family. Box-drawing styles (`single`, `double`, `rounded`, `heavy`) have proper corner glyphs and ignore this property.
22
+
23
+ ## Examples
24
+
25
+ ### `border-corner: none` (default)
26
+
27
+ ```css
28
+ .box {
29
+ border-style: eighth-cell-inner;
30
+ }
31
+ ```
32
+
33
+ ```
34
+ ▁▁▁▁▁▁▁▁▁▁▁▁
35
+ ▕ ▏
36
+ ▔▔▔▔▔▔▔▔▔▔▔▔
37
+ ```
38
+
39
+ Corners blank; the box reads as soft-rounded.
40
+
41
+ ### `border-corner: h`
42
+
43
+ ```css
44
+ .box {
45
+ border-style: eighth-cell-inner;
46
+ border-corner: h;
47
+ }
48
+ ```
49
+
50
+ ```
51
+ ▁▁▁▁▁▁▁▁▁▁▁▁▁▁
52
+
53
+ ▔▔▔▔▔▔▔▔▔▔▔▔
54
+ ```
55
+
56
+ Top/bottom strokes own the corners; sides indent by one.
57
+
58
+ ### `border-corner: v`
59
+
60
+ ```css
61
+ .box {
62
+ border-style: eighth-cell-inner;
63
+ border-corner: v;
64
+ }
65
+ ```
66
+
67
+ ```
68
+ ▁▁▁▁▁▁▁▁▁▁▁▁
69
+ ▕ ▏
70
+ ▕ ▏
71
+ ▔▔▔▔▔▔▔▔▔▔▔▔
72
+ ```
73
+
74
+ Side strokes own the corners; top/bottom indent by one.
75
+
76
+ ## Notes
77
+
78
+ - `border-corner` is per-element; mixing per-side `border-style` values does not change which axis owns the corner.
79
+
80
+ ## See also
81
+
82
+ - [`border-style`](border-style.md)