@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.
- package/CHANGELOG.md +465 -0
- package/README.md +42 -29
- package/dist/src/cli/build.d.ts +13 -0
- package/dist/src/cli/build.js +119 -0
- package/dist/src/cli/bundle.d.ts +25 -0
- package/dist/src/cli/bundle.js +61 -0
- package/dist/src/cli/dev.d.ts +10 -0
- package/dist/src/cli/dev.js +152 -0
- package/dist/src/cli/devtools.d.ts +9 -0
- package/dist/src/cli/devtools.js +47 -0
- package/dist/src/cli/init.d.ts +8 -0
- package/dist/src/cli/init.js +153 -0
- package/dist/src/cli/main.d.ts +9 -0
- package/dist/src/cli/main.js +52 -0
- package/dist/src/cli/svt-bin.d.ts +2 -0
- package/dist/src/cli/svt-bin.js +6 -0
- package/dist/src/cli/svt.d.ts +14 -0
- package/dist/src/cli/svt.js +76 -0
- package/dist/src/components/text-buffer.js +8 -5
- package/dist/src/css/animation-runner.d.ts +15 -6
- package/dist/src/css/animation-runner.js +80 -29
- package/dist/src/css/animation.d.ts +12 -0
- package/dist/src/css/animation.js +21 -0
- package/dist/src/css/calc.js +4 -3
- package/dist/src/css/color.d.ts +19 -0
- package/dist/src/css/color.js +371 -62
- package/dist/src/css/compute.d.ts +31 -4
- package/dist/src/css/compute.js +273 -34
- package/dist/src/css/defaults.d.ts +1 -1
- package/dist/src/css/defaults.js +9 -0
- package/dist/src/css/easing.d.ts +9 -0
- package/dist/src/css/easing.js +95 -0
- package/dist/src/css/incremental.d.ts +1 -1
- package/dist/src/css/incremental.js +2 -2
- package/dist/src/css/interpolate.d.ts +13 -0
- package/dist/src/css/interpolate.js +41 -0
- package/dist/src/css/parser.js +59 -3
- package/dist/src/css/pseudo-elements.d.ts +9 -0
- package/dist/src/css/pseudo-elements.js +97 -0
- package/dist/src/css/selector.d.ts +17 -2
- package/dist/src/css/selector.js +128 -13
- package/dist/src/css/specificity.js +17 -6
- package/dist/src/css/values.d.ts +6 -1
- package/dist/src/css/values.js +13 -6
- package/dist/src/debug/context.d.ts +13 -0
- package/dist/src/debug/context.js +11 -0
- package/dist/src/debug/css.d.ts +12 -0
- package/dist/src/debug/css.js +28 -0
- package/dist/src/debug/dom.d.ts +17 -0
- package/dist/src/debug/dom.js +92 -0
- package/dist/src/devtools/DevTools.compiled.js +327 -0
- package/dist/src/devtools/DevTools.css.js +1 -0
- package/dist/src/devtools/client.d.ts +36 -0
- package/dist/src/devtools/client.js +76 -0
- package/dist/src/framelog.d.ts +54 -0
- package/dist/src/framelog.js +99 -0
- package/dist/src/headless.js +12 -4
- package/dist/src/index.d.ts +66 -3
- package/dist/src/index.js +610 -81
- package/dist/src/input/checkable.d.ts +8 -0
- package/dist/src/input/checkable.js +66 -0
- package/dist/src/input/details.d.ts +6 -0
- package/dist/src/input/details.js +34 -0
- package/dist/src/input/focus.d.ts +6 -0
- package/dist/src/input/focus.js +27 -9
- package/dist/src/input/keyboard.d.ts +2 -2
- package/dist/src/input/keyboard.js +32 -5
- package/dist/src/input/label.d.ts +8 -0
- package/dist/src/input/label.js +53 -0
- package/dist/src/input/modal.d.ts +9 -0
- package/dist/src/input/modal.js +28 -0
- package/dist/src/input/mouse.d.ts +2 -2
- package/dist/src/input/mouse.js +15 -2
- package/dist/src/input/select.d.ts +12 -0
- package/dist/src/input/select.js +63 -0
- package/dist/src/input/selection.d.ts +48 -0
- package/dist/src/input/selection.js +150 -0
- package/dist/src/layout/engine.d.ts +2 -0
- package/dist/src/layout/engine.js +1092 -142
- package/dist/src/layout/flex.js +4 -4
- package/dist/src/layout/size.js +3 -2
- package/dist/src/layout/text.d.ts +3 -2
- package/dist/src/layout/text.js +96 -17
- package/dist/src/layout/unicode.d.ts +20 -0
- package/dist/src/layout/unicode.js +121 -0
- package/dist/src/render/animation-clock.d.ts +57 -0
- package/dist/src/render/animation-clock.js +221 -0
- package/dist/src/render/ansi-text.d.ts +26 -0
- package/dist/src/render/ansi-text.js +131 -0
- package/dist/src/render/ansi.d.ts +18 -0
- package/dist/src/render/ansi.js +64 -19
- package/dist/src/render/border.js +166 -17
- package/dist/src/render/buffer.d.ts +1 -0
- package/dist/src/render/buffer.js +5 -2
- package/dist/src/render/clock.d.ts +35 -0
- package/dist/src/render/clock.js +67 -0
- package/dist/src/render/color-depth.d.ts +8 -0
- package/dist/src/render/color-depth.js +59 -0
- package/dist/src/render/context.d.ts +1 -0
- package/dist/src/render/context.js +17 -21
- package/dist/src/render/cursor-emit.d.ts +18 -0
- package/dist/src/render/cursor-emit.js +50 -0
- package/dist/src/render/diff.d.ts +12 -0
- package/dist/src/render/diff.js +120 -0
- package/dist/src/render/generation.d.ts +9 -0
- package/dist/src/render/generation.js +14 -0
- package/dist/src/render/graphics-layer.d.ts +27 -0
- package/dist/src/render/graphics-layer.js +86 -0
- package/dist/src/render/image.d.ts +27 -0
- package/dist/src/render/image.js +113 -0
- package/dist/src/render/incremental-paint.d.ts +7 -3
- package/dist/src/render/incremental-paint.js +52 -79
- package/dist/src/render/inline.d.ts +59 -0
- package/dist/src/render/inline.js +219 -0
- package/dist/src/render/kitty-graphics.d.ts +24 -0
- package/dist/src/render/kitty-graphics.js +58 -0
- package/dist/src/render/paint-text.js +68 -22
- package/dist/src/render/paint.d.ts +8 -1
- package/dist/src/render/paint.js +358 -31
- package/dist/src/render/png.d.ts +13 -0
- package/dist/src/render/png.js +145 -0
- package/dist/src/render/scrollbar.d.ts +8 -2
- package/dist/src/render/scrollbar.js +71 -14
- package/dist/src/render/snapshot.js +3 -1
- package/dist/src/renderer/default.d.ts +7 -0
- package/dist/src/renderer/default.js +11 -0
- package/dist/src/renderer/index.d.ts +8 -2
- package/dist/src/renderer/index.js +4 -2
- package/dist/src/renderer/node.d.ts +109 -0
- package/dist/src/renderer/node.js +165 -1
- package/dist/src/terminal/capabilities.d.ts +33 -0
- package/dist/src/terminal/capabilities.js +66 -0
- package/dist/src/terminal/clipboard.d.ts +9 -0
- package/dist/src/terminal/clipboard.js +39 -0
- package/dist/src/terminal/io.d.ts +82 -0
- package/dist/src/terminal/io.js +155 -0
- package/dist/src/terminal/screen.d.ts +3 -10
- package/dist/src/terminal/screen.js +5 -28
- package/dist/src/terminal/stdin-router.d.ts +8 -5
- package/dist/src/terminal/stdin-router.js +22 -11
- package/dist/src/utils/node-map.d.ts +24 -0
- package/dist/src/utils/node-map.js +75 -0
- package/dist/src/vite/config.d.ts +62 -0
- package/dist/src/vite/config.js +191 -0
- package/docs/compatibility.md +67 -0
- package/docs/debug/devtools.md +40 -0
- package/docs/debug/svt.md +50 -0
- package/docs/distribution.md +106 -0
- package/docs/elements.md +120 -0
- package/docs/getting-started.md +177 -0
- package/docs/guide/css.md +187 -0
- package/docs/guide/input.md +143 -0
- package/docs/guide/layout.md +171 -0
- package/docs/guide/theming.md +94 -0
- package/docs/how-it-works.md +115 -0
- package/docs/inline-mode.md +77 -0
- package/docs/layout.md +112 -0
- package/docs/motion.md +91 -0
- package/docs/reference/README.md +65 -0
- package/docs/reference/css/properties/border-corner.md +82 -0
- package/docs/reference/css/properties/border-style.md +168 -0
- package/docs/reference.md +227 -0
- package/docs/selectors.md +80 -0
- package/docs/terminal-css.md +149 -0
- package/docs/terminals.md +83 -0
- 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)
|