@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,177 @@
1
+ # Getting started
2
+
3
+ svelterm runs Svelte components in a terminal. You write ordinary Svelte —
4
+ markup, scoped `<style>`, runes, event handlers — and the renderer lays it
5
+ out on a character-cell grid instead of a browser DOM.
6
+
7
+ ## Prerequisites
8
+
9
+ svelterm builds on Svelte's experimental custom renderer API
10
+ ([sveltejs/svelte#18042](https://github.com/sveltejs/svelte/pull/18042)).
11
+ Until [#18505](https://github.com/sveltejs/svelte/pull/18505) lands, use
12
+ the svelterm fork of that branch (upstream plus the `svelte/renderer`
13
+ mount export svelterm needs on Node):
14
+
15
+ ```bash
16
+ git clone -b svelte-custom-renderer https://github.com/tomyan/svelte.git svelte-fork
17
+ cd svelte-fork
18
+ pnpm install
19
+ pnpm -C packages/svelte build
20
+ ```
21
+
22
+ ## Scaffold a project
23
+
24
+ ```bash
25
+ npx @svelterm/core init my-app # or: svelterm init my-app
26
+ cd my-app
27
+ npm install
28
+ ```
29
+
30
+ The scaffold expects the Svelte fork as a sibling directory
31
+ (`../svelte-fork`) — adjust the `svelte` path in `package.json` if yours
32
+ lives elsewhere. It contains a counter component, a vite config, and
33
+ three scripts:
34
+
35
+ ```bash
36
+ npm run dev # vite dev server (terminal 1)
37
+ npm run app # the app itself, hot-reloading (terminal 2)
38
+ npm run build # one self-contained dist/app.mjs
39
+ ```
40
+
41
+ ## Dev mode
42
+
43
+ `svelterm dev <url>` connects to the vite dev server over WebSocket and
44
+ renders the app in *its own* terminal — the server terminal keeps logs
45
+ and errors readable:
46
+
47
+ - Edits to any imported module restart the app in place.
48
+ - `console.log` from the app appears in the **vite terminal**, prefixed
49
+ `[svelterm]` — the app's terminal is its screen.
50
+ - `Ctrl+C` in the app terminal exits and restores the screen.
51
+
52
+ Terminal-only projects need no `vite-plugin-svelte`: svelterm's
53
+ `terminalServer()` plugin compiles `.svelte` files for the terminal
54
+ environment itself.
55
+
56
+ ## Shipping
57
+
58
+ `svelterm build [entry.svelte]` bundles the component graph, the Svelte
59
+ runtime, and svelterm into one `.mjs` (default `dist/app.mjs`) that runs
60
+ with plain `node` — no `node_modules` at the destination:
61
+
62
+ ```bash
63
+ npx svelterm build src/App.svelte -o dist/app.mjs
64
+ node dist/app.mjs
65
+ ```
66
+
67
+ Global CSS is picked up from `src/main.css`/`main.css` (or the
68
+ `--css` flag); each component's scoped styles travel inside the bundle.
69
+ Component libraries installed as symlinks (e.g. a local `@svelterm/ui`
70
+ via `file:`) resolve `@svelterm/core` and `svelte` from your project, so
71
+ they bundle correctly. See
72
+ [distribution](./distribution.md) for platform packaging and the
73
+ curl-pipe pattern.
74
+
75
+ ## Manual setup
76
+
77
+ An existing project needs the compiler pointed at svelterm's renderer
78
+ with CSS kept external. With the environment-aware `vite-plugin-svelte`
79
+ fork (needed for dual-target; terminal-only projects can rely on
80
+ `terminalServer()` instead):
81
+
82
+ ```typescript
83
+ // vite.config.ts
84
+ import { defineConfig } from 'vite'
85
+ import { svelterm } from '@svelterm/core/vite'
86
+
87
+ export default defineConfig({
88
+ plugins: [
89
+ ...svelterm.terminalServer({ entry: './src/App.svelte' }),
90
+ ],
91
+ environments: svelterm.environments(),
92
+ optimizeDeps: { exclude: ['svelte'] },
93
+ ssr: { noExternal: ['svelte'] },
94
+ })
95
+ ```
96
+
97
+ ## A first component
98
+
99
+ ```svelte
100
+ <script>
101
+ let count = $state(0)
102
+ </script>
103
+
104
+ <div class="counter">
105
+ <button onclick={() => count++}>count is {count}</button>
106
+ </div>
107
+
108
+ <style>
109
+ .counter {
110
+ padding: 1cell 2cell;
111
+ }
112
+ button {
113
+ border: single;
114
+ border-color: cyan;
115
+ padding: 0 1cell;
116
+ }
117
+ button:focus {
118
+ border-color: yellow;
119
+ }
120
+ </style>
121
+ ```
122
+
123
+ Run it programmatically (the CLI does this for you):
124
+
125
+ ```typescript
126
+ import { run } from '@svelterm/core/app'
127
+ import App from './App.svelte'
128
+ import css from './App.css?raw' // the extracted stylesheet
129
+
130
+ const handle = run(App, { css })
131
+ // handle.cleanup() restores the terminal
132
+ ```
133
+
134
+ Press `Tab` to focus the button, `Enter` to click it, `Ctrl+C` to exit.
135
+
136
+ ## `run(component, options?)`
137
+
138
+ | Option | Default | Meaning |
139
+ |---|---|---|
140
+ | `css` | registered CSS | Extracted CSS; defaults to styles components registered via `registerComponentCss` (bundles do this automatically) |
141
+ | `fullscreen` | `true` | Use the alternate screen buffer |
142
+ | `mouse` | `true` | Enable mouse click/scroll/hover |
143
+ | `props` | — | Props passed to the component |
144
+ | `colorScheme` | auto | Force `'dark'`/`'light'` instead of OSC 11 detection |
145
+ | `io` | process stdio | A custom `TerminalIO` (embedding, tests) |
146
+ | `onConsole` | — | Receive `console.*` output; without it, `console.log` throws rather than corrupt the screen |
147
+ | `mode` | `'fullscreen'` | `'inline'` renders at the shell cursor — see [inline mode](./inline-mode.md) |
148
+ | `exitOn` | `['ctrl+c']` | Add `'ctrl+d'` for EOF-style exit |
149
+ | `colorDepth` | detected | Force `'truecolor' \| '256' \| '16' \| 'mono'` |
150
+ | `debug` / `debugPort` | off | WebSocket debug server |
151
+
152
+ Returns `{ cleanup, setColorScheme }`. `cleanup()` unmounts, restores the
153
+ screen and input modes; `setColorScheme('light')` re-resolves
154
+ `light-dark()` and scheme media queries on a live app.
155
+
156
+ ## Writing dual-target components
157
+
158
+ The same source can render in the browser. Standard CSS behaves
159
+ identically; anything mode-specific goes in a
160
+ [`display-mode`](./terminal-css.md) block:
161
+
162
+ ```css
163
+ .card {
164
+ display: flex;
165
+ gap: 1ch; /* 1ch = 1 cell, valid both sides */
166
+ @media (display-mode: terminal) { border: single; }
167
+ @media (display-mode: browser) { border: 1px solid #ccc; border-radius: 6px; }
168
+ }
169
+ ```
170
+
171
+ Dual-target builds compile per environment, which needs the
172
+ environment-aware [`vite-plugin-svelte` fork](https://github.com/sveltejs/vite-plugin-svelte/pull/1318)
173
+ and the `svelterm.svelteOptions()` helper.
174
+
175
+ Event payloads differ slightly: terminal events carry data on
176
+ `event.data`, browsers on the event itself — handlers that run in both
177
+ read `event.data?.value ?? event.target.value`.
@@ -0,0 +1,187 @@
1
+ # CSS
2
+
3
+ Svelterm uses real CSS — the same scoped `<style>` blocks that Svelte compiles for the browser. The CSS engine parses selectors, computes specificity, cascades rules, resolves variables, and evaluates media queries. The difference is how property values are interpreted: `border: rounded` draws box-drawing characters, `1cell` maps to terminal cells, colors map to ANSI codes or truecolor sequences.
4
+
5
+ ## Terminal-specific values
6
+
7
+ These values work in the terminal and are ignored by browsers:
8
+
9
+ ### Border styles
10
+
11
+ ```css
12
+ .box {
13
+ border: single; /* ┌──┐ │ │ └──┘ */
14
+ border: double; /* ╔══╗ ║ ║ ╚══╝ */
15
+ border: rounded; /* ╭──╮ │ │ ╰──╯ */
16
+ border: heavy; /* ┏━━┓ ┃ ┃ ┗━━┛ */
17
+ }
18
+ ```
19
+
20
+ Browsers don't recognise these values and fall back to `border: none`.
21
+
22
+ ### The `cell` unit
23
+
24
+ Terminal cells are the fundamental unit — one character position. Use `cell` anywhere you'd use `px` or `rem`:
25
+
26
+ ```css
27
+ .panel {
28
+ width: 30cell;
29
+ padding: 1cell 2cell;
30
+ gap: 1cell;
31
+ }
32
+ ```
33
+
34
+ Browsers ignore `cell` values since it's not a recognised CSS unit. Use `@media` blocks for browser-specific sizing:
35
+
36
+ ```css
37
+ .panel { padding: 1cell; }
38
+
39
+ @media (display-mode: screen) {
40
+ .panel { padding: 0.5rem; }
41
+ }
42
+ ```
43
+
44
+ ### Opacity
45
+
46
+ `opacity: dim` renders text with the terminal dim attribute (SGR code 2). Values less than 1 also trigger dim:
47
+
48
+ ```css
49
+ .muted { opacity: dim; }
50
+ .faint { opacity: 0.5; } /* also dim */
51
+ ```
52
+
53
+ ## Selectors
54
+
55
+ Full CSS selector support:
56
+
57
+ | Selector | Example | Description |
58
+ |---|---|---|
59
+ | Tag | `div` | Element type |
60
+ | Class | `.active` | Class attribute |
61
+ | ID | `#main` | ID attribute |
62
+ | Universal | `*` | Any element |
63
+ | Attribute | `[href]`, `[data-type="x"]` | Attribute presence/value |
64
+ | Descendant | `.app span` | Any descendant |
65
+ | Child | `.app > div` | Direct child |
66
+ | Adjacent sibling | `h1 + p` | Immediately after |
67
+ | General sibling | `h1 ~ p` | Any sibling after |
68
+ | `:first-child` | `li:first-child` | First child of parent |
69
+ | `:last-child` | `li:last-child` | Last child of parent |
70
+ | `:not()` | `div:not(.hidden)` | Negation |
71
+ | `:focus` | `button:focus` | Has keyboard focus |
72
+ | `:hover` | `.item:hover` | Mouse is over element |
73
+ | `:root` | `:root` | Root element |
74
+
75
+ Svelte's scoped class hashes (e.g. `.svelte-abc123`) work naturally — they're just class selectors.
76
+
77
+ ## Colors
78
+
79
+ ### ANSI named colors
80
+
81
+ ```css
82
+ .error { color: red; }
83
+ .info { color: cyan; }
84
+ .warning { color: yellow; background-color: black; }
85
+ ```
86
+
87
+ The 8 base ANSI colors: `black`, `red`, `green`, `yellow`, `blue`, `magenta`, `cyan`, `white`.
88
+
89
+ ### Truecolor (24-bit)
90
+
91
+ ```css
92
+ .brand { color: #ff6b6b; }
93
+ .accent { color: rgb(81, 207, 102); }
94
+ .highlight { color: hsl(210, 100%, 60%); }
95
+ ```
96
+
97
+ Hex (3 and 6 digit), `rgb()`, `rgba()`, `hsl()`, `hsla()` all produce 24-bit truecolor output.
98
+
99
+ ### CSS named colors
100
+
101
+ All 148 CSS Level 4 named colors are supported: `coral`, `teal`, `rebeccapurple`, `goldenrod`, etc.
102
+
103
+ ## CSS Variables
104
+
105
+ ```css
106
+ :root {
107
+ --primary: cyan;
108
+ --accent: yellow;
109
+ --muted: gray;
110
+ }
111
+
112
+ .title { color: var(--primary); }
113
+ .value { color: var(--accent); }
114
+ .hint { color: var(--muted, gray); } /* with fallback */
115
+ ```
116
+
117
+ Variables cascade through the tree. Changing a class on an ancestor updates all descendants that reference its variables.
118
+
119
+ ## Media queries
120
+
121
+ ```css
122
+ /* Terminal-only rules */
123
+ @media (display-mode: terminal) {
124
+ .panel { border: rounded; padding: 1cell; }
125
+ }
126
+
127
+ /* Browser-only rules */
128
+ @media (display-mode: screen) {
129
+ .panel { border-radius: 8px; padding: 0.5rem; }
130
+ }
131
+
132
+ /* Light/dark adaptation — detected automatically via OSC 11 */
133
+ @media (prefers-color-scheme: dark) {
134
+ :root { --bg: black; --fg: white; }
135
+ }
136
+ @media (prefers-color-scheme: light) {
137
+ :root { --bg: #eeeeee; --fg: #111111; }
138
+ }
139
+
140
+ /* Responsive to terminal size */
141
+ @media (min-width: 80) {
142
+ .sidebar { display: flex; }
143
+ }
144
+ ```
145
+
146
+ ## Container queries
147
+
148
+ ```css
149
+ @container (min-width: 40) {
150
+ .card { flex-direction: row; }
151
+ }
152
+ ```
153
+
154
+ Container queries evaluate after the first layout pass, then re-resolve styles for matching containers.
155
+
156
+ ## Math functions
157
+
158
+ ```css
159
+ .panel {
160
+ width: calc(100% - 4cell);
161
+ height: min(20cell, 50%);
162
+ padding: clamp(1cell, 5%, 3cell);
163
+ }
164
+ ```
165
+
166
+ `calc()`, `min()`, `max()`, `clamp()` work in sizing properties.
167
+
168
+ ## Specificity and cascade
169
+
170
+ Standard CSS specificity rules apply:
171
+
172
+ 1. ID selectors (`#main`) — highest
173
+ 2. Class, attribute, pseudo-class (`.active`, `[href]`, `:focus`) — medium
174
+ 3. Tag selectors (`div`, `span`) — lowest
175
+
176
+ At equal specificity, later rules win. `inherit`, `initial`, and `unset` keywords are supported.
177
+
178
+ ## What's not supported
179
+
180
+ Properties that don't map to terminal capabilities:
181
+
182
+ - `font-family`, `font-size` — terminal is monospace, fixed size
183
+ - `border-radius` — use `border: rounded` instead
184
+ - `box-shadow`, `transform`, `transition` — no sub-cell rendering
185
+ - `float` — not implemented
186
+ - `!important` — not implemented
187
+ - `opacity` with blending — terminal cells are opaque; opacity maps to dim
@@ -0,0 +1,143 @@
1
+ # Input
2
+
3
+ Svelterm handles keyboard, mouse, and focus input using the same event model as the browser — events dispatch to a target element, bubble up through ancestors, and can be stopped or prevented.
4
+
5
+ ## Keyboard events
6
+
7
+ ```svelte
8
+ <div onkeydown={(e) => handleKey(e.data)}>
9
+ Press a key
10
+ </div>
11
+ ```
12
+
13
+ The event's `data` property contains a `KeyEvent`:
14
+
15
+ ```ts
16
+ interface KeyEvent {
17
+ key: string // 'a', 'Enter', 'ArrowUp', 'Tab', etc.
18
+ ctrl: boolean
19
+ shift: boolean
20
+ meta: boolean
21
+ }
22
+ ```
23
+
24
+ ### Special keys
25
+
26
+ `Enter`, `Escape`, `Backspace`, `Delete`, `Tab`, `ArrowUp`, `ArrowDown`, `ArrowLeft`, `ArrowRight`, `Home`, `End`, `PageUp`, `PageDown`, `Insert`
27
+
28
+ ### Built-in key handling
29
+
30
+ | Key | Action |
31
+ |---|---|
32
+ | `Ctrl+C` | Exit the application |
33
+ | `Ctrl+Z` | Suspend (SIGTSTP) |
34
+ | `Tab` | Focus next focusable element |
35
+ | `Shift+Tab` | Focus previous element |
36
+ | `Enter` | Click the focused element |
37
+
38
+ These fire before your component's `onkeydown`. All other keys are dispatched to the focused element, or to the component root if nothing is focused.
39
+
40
+ ## Mouse events
41
+
42
+ Mouse input is enabled by default. Click, scroll, and hover events are supported.
43
+
44
+ ```svelte
45
+ <button onclick={(e) => console.log('clicked at', e.data.col, e.data.row)}>
46
+ Click me
47
+ </button>
48
+ ```
49
+
50
+ Mouse events include position data:
51
+
52
+ ```ts
53
+ interface MouseEvent {
54
+ button: 'left' | 'right' | 'middle' | 'scrollUp' | 'scrollDown'
55
+ type: 'press' | 'release' | 'motion' | 'scroll'
56
+ col: number
57
+ row: number
58
+ }
59
+ ```
60
+
61
+ ### Click
62
+
63
+ Clicking a focusable element (`<button>`, `<input>`, `<a>`) focuses it and dispatches a `click` event. Clicking an `<a>` element opens its `href` in the default browser.
64
+
65
+ ### Scroll
66
+
67
+ Mouse wheel events scroll `overflow: auto/scroll` containers. The nearest scrollable ancestor of the element under the cursor receives the scroll.
68
+
69
+ ### Hover
70
+
71
+ Mouse motion sets the `:hover` pseudo-class on the element under the cursor:
72
+
73
+ ```css
74
+ .item:hover {
75
+ color: cyan;
76
+ font-weight: bold;
77
+ }
78
+ ```
79
+
80
+ ## Focus management
81
+
82
+ Focusable elements: `<button>`, `<input>`, `<textarea>`, `<a>`, `<select>`
83
+
84
+ ```css
85
+ button:focus {
86
+ border-color: yellow;
87
+ color: yellow;
88
+ }
89
+ ```
90
+
91
+ Focus events fire when focus changes:
92
+
93
+ ```svelte
94
+ <button
95
+ onfocus={() => console.log('gained focus')}
96
+ onblur={() => console.log('lost focus')}
97
+ >
98
+ Tab to me
99
+ </button>
100
+ ```
101
+
102
+ When Tab moves focus to an element outside the visible scroll region, the container scrolls to reveal it.
103
+
104
+ ## Event dispatch
105
+
106
+ Events follow the W3C model with capture and bubble phases:
107
+
108
+ 1. **Capture**: root → target (listeners registered on `type__capture`)
109
+ 2. **Target**: fire listeners on the target
110
+ 3. **Bubble**: target → root (normal listeners)
111
+
112
+ ```svelte
113
+ <div onclick={(e) => {
114
+ e.stopPropagation() // prevent bubbling
115
+ e.preventDefault() // prevent default action (e.g. link navigation)
116
+ }}>
117
+ Handled here
118
+ </div>
119
+ ```
120
+
121
+ ## Text input
122
+
123
+ `<input>` elements accept keyboard input automatically when focused:
124
+
125
+ ```svelte
126
+ <input value="" oninput={(e) => value = e.data.value} />
127
+ ```
128
+
129
+ Features:
130
+ - Character insertion at cursor position
131
+ - Backspace, Delete
132
+ - Arrow keys move cursor
133
+ - Home / End
134
+ - Ctrl+A (home), Ctrl+E (end), Ctrl+U (clear to start), Ctrl+K (clear to end)
135
+ - Horizontal scroll with overflow indicators (`…`) when text exceeds width
136
+ - Cursor rendered as inverted cell
137
+ - Bracketed paste support (paste from clipboard inserts as a single operation)
138
+
139
+ ## Disabling mouse
140
+
141
+ ```ts
142
+ mount(App, { css, mouse: false })
143
+ ```
@@ -0,0 +1,171 @@
1
+ # Layout
2
+
3
+ Svelterm implements CSS layout on a cell grid. Integer coordinates — every element occupies whole terminal cells. The layout engine supports flexbox, grid, block flow, table, and absolute positioning.
4
+
5
+ ## Block flow
6
+
7
+ The default. Block elements stack vertically, filling available width. Inline elements flow horizontally within a line.
8
+
9
+ ```svelte
10
+ <div>
11
+ <div>First block</div>
12
+ <div>Second block</div>
13
+ <span>inline </span><span>text flows</span>
14
+ </div>
15
+ ```
16
+
17
+ Block elements (`<div>`, `<p>`, `<h1>`, etc.) fill their parent's width by default. Inline elements (`<span>`, `<a>`, `<strong>`, etc.) shrink-wrap to content.
18
+
19
+ ## Flexbox
20
+
21
+ ```css
22
+ .row { display: flex; flex-direction: row; gap: 2cell; }
23
+ .col { display: flex; flex-direction: column; }
24
+ ```
25
+
26
+ ### Properties
27
+
28
+ | Property | Values | Default |
29
+ |---|---|---|
30
+ | `flex-direction` | `row`, `column`, `row-reverse`, `column-reverse` | `row` |
31
+ | `justify-content` | `start`, `end`, `center`, `space-between`, `space-around`, `space-evenly` | `start` |
32
+ | `align-items` | `start`, `end`, `center`, `stretch` | `start` |
33
+ | `align-self` | `auto`, `start`, `end`, `center`, `stretch` | `auto` |
34
+ | `flex-grow` | number | `0` |
35
+ | `flex-shrink` | number | `1` |
36
+ | `flex-basis` | `auto`, size value | `auto` |
37
+ | `flex-wrap` | `nowrap`, `wrap` | `nowrap` |
38
+ | `gap` | size value | `0` |
39
+ | `order` | number | `0` |
40
+ | `flex` | shorthand: `<grow> [<shrink> [<basis>]]` | — |
41
+
42
+ ### Example: sidebar layout
43
+
44
+ ```svelte
45
+ <style>
46
+ .app {
47
+ display: flex;
48
+ flex-direction: row;
49
+ height: 100%;
50
+ }
51
+ .sidebar {
52
+ width: 20cell;
53
+ border: single;
54
+ border-color: gray;
55
+ }
56
+ .main {
57
+ flex-grow: 1;
58
+ }
59
+ </style>
60
+
61
+ <div class="app">
62
+ <div class="sidebar">Navigation</div>
63
+ <div class="main">Content</div>
64
+ </div>
65
+ ```
66
+
67
+ ## Grid
68
+
69
+ ```css
70
+ .grid {
71
+ display: grid;
72
+ grid-template-columns: 1fr 2fr;
73
+ gap: 1cell;
74
+ }
75
+ ```
76
+
77
+ Grid supports `cell`, `%`, and `fr` (fractional) units for column and row templates. Children auto-flow into cells, wrapping to the next row when columns are filled.
78
+
79
+ ## Table
80
+
81
+ ```svelte
82
+ <table>
83
+ <tr>
84
+ <td>Name</td>
85
+ <td>Status</td>
86
+ </tr>
87
+ <tr>
88
+ <td>Server 1</td>
89
+ <td>Online</td>
90
+ </tr>
91
+ </table>
92
+ ```
93
+
94
+ Column widths are determined by the widest cell across all rows. Cells fill their column width.
95
+
96
+ ## Sizing
97
+
98
+ | Property | Values |
99
+ |---|---|
100
+ | `width`, `height` | `Ncell`, `N%`, `auto`, `calc()` |
101
+ | `min-width`, `min-height` | `Ncell` |
102
+ | `max-width`, `max-height` | `Ncell` |
103
+
104
+ Percentage widths resolve against the parent's width. Percentage heights resolve against the parent's height (parent must have an explicit height).
105
+
106
+ ## Spacing
107
+
108
+ ```css
109
+ .box {
110
+ padding: 1cell 2cell; /* top/bottom, left/right */
111
+ margin: 1cell; /* all sides */
112
+ margin: 1cell 2cell 1cell 2cell; /* top, right, bottom, left */
113
+ margin: 0 auto; /* auto horizontal centering */
114
+ }
115
+ ```
116
+
117
+ Vertical margins collapse between adjacent block siblings (the larger margin wins).
118
+
119
+ ## Positioning
120
+
121
+ ```css
122
+ .overlay {
123
+ position: absolute;
124
+ top: 2cell;
125
+ left: 5cell;
126
+ }
127
+ ```
128
+
129
+ `position: absolute` and `position: fixed` remove the element from flow and position it relative to its parent.
130
+
131
+ ## Overflow and scrolling
132
+
133
+ ```css
134
+ .scrollable {
135
+ overflow: auto;
136
+ height: 20cell;
137
+ }
138
+ ```
139
+
140
+ | Value | Behaviour |
141
+ |---|---|
142
+ | `visible` | Content overflows (default) |
143
+ | `hidden` | Content clipped |
144
+ | `scroll` | Content clipped, scrollbar shown |
145
+ | `auto` | Content clipped, scrollbar when needed |
146
+
147
+ Mouse wheel scrolls `overflow: auto/scroll` containers. Tab navigation scrolls focused elements into view.
148
+
149
+ ## display: contents
150
+
151
+ ```css
152
+ .wrapper { display: contents; }
153
+ ```
154
+
155
+ The element is invisible to layout — its children are promoted to the parent's layout context. Useful for Svelte wrapper components that shouldn't add a layout box.
156
+
157
+ ## display: none
158
+
159
+ ```css
160
+ .hidden { display: none; }
161
+ ```
162
+
163
+ Element and all descendants are invisible and take no space.
164
+
165
+ ## Element defaults
166
+
167
+ Block elements: `<div>`, `<p>`, `<h1>`–`<h6>`, `<ul>`, `<ol>`, `<li>`, `<pre>`, `<blockquote>`, `<hr>`, `<table>`
168
+
169
+ Inline elements: `<span>`, `<a>`, `<strong>`, `<em>`, `<b>`, `<i>`, `<u>`, `<code>`, `<small>`
170
+
171
+ `<input>` and `<textarea>` have an intrinsic minimum height of 1 row.