@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,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.
|