@silvery/term 0.3.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/package.json +54 -0
- package/src/adapters/canvas-adapter.ts +356 -0
- package/src/adapters/dom-adapter.ts +452 -0
- package/src/adapters/flexily-zero-adapter.ts +368 -0
- package/src/adapters/terminal-adapter.ts +305 -0
- package/src/adapters/yoga-adapter.ts +370 -0
- package/src/ansi/ansi.ts +251 -0
- package/src/ansi/constants.ts +76 -0
- package/src/ansi/detection.ts +441 -0
- package/src/ansi/hyperlink.ts +38 -0
- package/src/ansi/index.ts +201 -0
- package/src/ansi/patch-console.ts +159 -0
- package/src/ansi/sgr-codes.ts +34 -0
- package/src/ansi/storybook.ts +209 -0
- package/src/ansi/term.ts +724 -0
- package/src/ansi/types.ts +202 -0
- package/src/ansi/underline.ts +156 -0
- package/src/ansi/utils.ts +65 -0
- package/src/ansi-sanitize.ts +509 -0
- package/src/app.ts +571 -0
- package/src/bound-term.ts +94 -0
- package/src/bracketed-paste.ts +75 -0
- package/src/browser-renderer.ts +174 -0
- package/src/buffer.ts +1984 -0
- package/src/clipboard.ts +74 -0
- package/src/cursor-query.ts +85 -0
- package/src/device-attrs.ts +228 -0
- package/src/devtools.ts +123 -0
- package/src/dom/index.ts +194 -0
- package/src/errors.ts +39 -0
- package/src/focus-reporting.ts +48 -0
- package/src/hit-registry-core.ts +228 -0
- package/src/hit-registry.ts +176 -0
- package/src/index.ts +458 -0
- package/src/input.ts +119 -0
- package/src/inspector.ts +155 -0
- package/src/kitty-detect.ts +95 -0
- package/src/kitty-manager.ts +160 -0
- package/src/layout-engine.ts +296 -0
- package/src/layout.ts +26 -0
- package/src/measurer.ts +74 -0
- package/src/mode-query.ts +106 -0
- package/src/mouse-events.ts +419 -0
- package/src/mouse.ts +83 -0
- package/src/non-tty.ts +223 -0
- package/src/osc-markers.ts +32 -0
- package/src/osc-palette.ts +169 -0
- package/src/output.ts +406 -0
- package/src/pane-manager.ts +248 -0
- package/src/pipeline/CLAUDE.md +587 -0
- package/src/pipeline/content-phase-adapter.ts +976 -0
- package/src/pipeline/content-phase.ts +1765 -0
- package/src/pipeline/helpers.ts +42 -0
- package/src/pipeline/index.ts +416 -0
- package/src/pipeline/layout-phase.ts +686 -0
- package/src/pipeline/measure-phase.ts +198 -0
- package/src/pipeline/measure-stats.ts +21 -0
- package/src/pipeline/output-phase.ts +2593 -0
- package/src/pipeline/render-box.ts +343 -0
- package/src/pipeline/render-helpers.ts +243 -0
- package/src/pipeline/render-text.ts +1255 -0
- package/src/pipeline/types.ts +161 -0
- package/src/pipeline.ts +29 -0
- package/src/pixel-size.ts +119 -0
- package/src/render-adapter.ts +179 -0
- package/src/renderer.ts +1330 -0
- package/src/runtime/create-app.tsx +1845 -0
- package/src/runtime/create-buffer.ts +18 -0
- package/src/runtime/create-runtime.ts +325 -0
- package/src/runtime/diff.ts +56 -0
- package/src/runtime/event-handlers.ts +254 -0
- package/src/runtime/index.ts +119 -0
- package/src/runtime/keys.ts +8 -0
- package/src/runtime/layout.ts +164 -0
- package/src/runtime/run.tsx +318 -0
- package/src/runtime/term-provider.ts +399 -0
- package/src/runtime/terminal-lifecycle.ts +246 -0
- package/src/runtime/tick.ts +219 -0
- package/src/runtime/types.ts +210 -0
- package/src/scheduler.ts +723 -0
- package/src/screenshot.ts +57 -0
- package/src/scroll-region.ts +69 -0
- package/src/scroll-utils.ts +97 -0
- package/src/term-def.ts +267 -0
- package/src/terminal-caps.ts +5 -0
- package/src/terminal-colors.ts +216 -0
- package/src/termtest.ts +224 -0
- package/src/text-sizing.ts +109 -0
- package/src/toolbelt/index.ts +72 -0
- package/src/unicode.ts +1763 -0
- package/src/xterm/index.ts +491 -0
- package/src/xterm/xterm-provider.ts +204 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bracketed Paste Mode
|
|
3
|
+
*
|
|
4
|
+
* Enables bracketed paste so the terminal wraps pasted text with markers.
|
|
5
|
+
* This lets the app distinguish pasted text from typed input and receive
|
|
6
|
+
* it as a single event rather than individual keystrokes.
|
|
7
|
+
*
|
|
8
|
+
* Protocol: DEC private mode 2004
|
|
9
|
+
* - Enable: CSI ? 2004 h
|
|
10
|
+
* - Disable: CSI ? 2004 l
|
|
11
|
+
* - Paste start marker: CSI 200 ~
|
|
12
|
+
* - Paste end marker: CSI 201 ~
|
|
13
|
+
*
|
|
14
|
+
* Supported by: Ghostty, Kitty, WezTerm, iTerm2, Alacritty, xterm, tmux, foot
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Constants
|
|
19
|
+
// ============================================================================
|
|
20
|
+
|
|
21
|
+
/** Escape sequence that marks the beginning of pasted text */
|
|
22
|
+
export const PASTE_START = "\x1b[200~"
|
|
23
|
+
|
|
24
|
+
/** Escape sequence that marks the end of pasted text */
|
|
25
|
+
export const PASTE_END = "\x1b[201~"
|
|
26
|
+
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// Protocol Control
|
|
29
|
+
// ============================================================================
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Enable bracketed paste mode.
|
|
33
|
+
* Writes CSI ? 2004 h to the output stream.
|
|
34
|
+
*/
|
|
35
|
+
export function enableBracketedPaste(stdout: NodeJS.WriteStream): void {
|
|
36
|
+
stdout.write("\x1b[?2004h")
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Disable bracketed paste mode.
|
|
41
|
+
* Writes CSI ? 2004 l to the output stream.
|
|
42
|
+
*/
|
|
43
|
+
export function disableBracketedPaste(stdout: NodeJS.WriteStream): void {
|
|
44
|
+
stdout.write("\x1b[?2004l")
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ============================================================================
|
|
48
|
+
// Parsing
|
|
49
|
+
// ============================================================================
|
|
50
|
+
|
|
51
|
+
/** Result of parsing a bracketed paste sequence */
|
|
52
|
+
export interface BracketedPasteResult {
|
|
53
|
+
type: "paste"
|
|
54
|
+
content: string
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Detect and extract bracketed paste content from raw terminal input.
|
|
59
|
+
*
|
|
60
|
+
* Returns the paste content if the input contains a complete bracketed paste
|
|
61
|
+
* sequence (PASTE_START ... PASTE_END), or null if no paste markers are found.
|
|
62
|
+
*/
|
|
63
|
+
export function parseBracketedPaste(input: string): BracketedPasteResult | null {
|
|
64
|
+
const startIdx = input.indexOf(PASTE_START)
|
|
65
|
+
if (startIdx === -1) return null
|
|
66
|
+
|
|
67
|
+
const contentStart = startIdx + PASTE_START.length
|
|
68
|
+
const endIdx = input.indexOf(PASTE_END, contentStart)
|
|
69
|
+
if (endIdx === -1) return null
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
type: "paste",
|
|
73
|
+
content: input.slice(contentStart, endIdx),
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Browser Renderer
|
|
3
|
+
*
|
|
4
|
+
* Common lifecycle logic for browser-based renderers (Canvas, DOM).
|
|
5
|
+
* Both canvas/index.ts and dom/index.ts were ~70-80% identical — this module
|
|
6
|
+
* extracts the shared reconciler setup, scheduling, and render loop.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ReactElement } from "react"
|
|
10
|
+
import { createFlexilyZeroEngine } from "./adapters/flexily-zero-adapter"
|
|
11
|
+
import { setLayoutEngine } from "./layout-engine"
|
|
12
|
+
import { executeRenderAdapter } from "./pipeline"
|
|
13
|
+
import { createContainer, createFiberRoot, getContainerRoot, reconciler } from "@silvery/react/reconciler"
|
|
14
|
+
import type { RenderAdapter, RenderBuffer } from "./render-adapter"
|
|
15
|
+
import { setRenderAdapter } from "./render-adapter"
|
|
16
|
+
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Types
|
|
19
|
+
// ============================================================================
|
|
20
|
+
|
|
21
|
+
/** Config for creating a browser adapter */
|
|
22
|
+
export interface BrowserAdapterFactory<TConfig> {
|
|
23
|
+
createAdapter(config: TConfig): RenderAdapter
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Callback invoked after each render with the new buffer */
|
|
27
|
+
export type OnRender<TBuffer extends RenderBuffer> = (buffer: TBuffer) => void
|
|
28
|
+
|
|
29
|
+
/** Base instance returned by createBrowserRenderer */
|
|
30
|
+
export interface BrowserInstance {
|
|
31
|
+
/** Re-render with a new element */
|
|
32
|
+
rerender: (element: ReactElement) => void
|
|
33
|
+
/** Unmount and clean up */
|
|
34
|
+
unmount: () => void
|
|
35
|
+
/** Dispose (alias for unmount) — enables `using` */
|
|
36
|
+
[Symbol.dispose](): void
|
|
37
|
+
/** Get the current buffer */
|
|
38
|
+
getBuffer: () => RenderBuffer | null
|
|
39
|
+
/** Force a re-render */
|
|
40
|
+
refresh: () => void
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// Initialization
|
|
45
|
+
// ============================================================================
|
|
46
|
+
|
|
47
|
+
let initialized = false
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Initialize the browser rendering system with a specific adapter.
|
|
51
|
+
* Called automatically by render functions, but can be called manually.
|
|
52
|
+
* Idempotent — only the first call takes effect.
|
|
53
|
+
*/
|
|
54
|
+
export function initBrowserRenderer<TConfig>(factory: BrowserAdapterFactory<TConfig>, config: TConfig): void {
|
|
55
|
+
if (initialized) return
|
|
56
|
+
|
|
57
|
+
setLayoutEngine(createFlexilyZeroEngine())
|
|
58
|
+
setRenderAdapter(factory.createAdapter(config))
|
|
59
|
+
|
|
60
|
+
initialized = true
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ============================================================================
|
|
64
|
+
// Render Loop
|
|
65
|
+
// ============================================================================
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Create a browser renderer instance with reconciler lifecycle and scheduling.
|
|
69
|
+
*
|
|
70
|
+
* @param element - React element to render
|
|
71
|
+
* @param width - Render width
|
|
72
|
+
* @param height - Render height
|
|
73
|
+
* @param onRender - Called after each render with the new buffer
|
|
74
|
+
* @param onUnmount - Optional cleanup callback after reconciler unmount
|
|
75
|
+
*/
|
|
76
|
+
export function createBrowserRenderer<TBuffer extends RenderBuffer>(
|
|
77
|
+
element: ReactElement,
|
|
78
|
+
width: number,
|
|
79
|
+
height: number,
|
|
80
|
+
onRender: OnRender<TBuffer>,
|
|
81
|
+
onUnmount?: () => void,
|
|
82
|
+
): BrowserInstance {
|
|
83
|
+
const container = createContainer(() => {
|
|
84
|
+
scheduleRender()
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
const root = getContainerRoot(container)
|
|
88
|
+
const fiberRoot = createFiberRoot(container)
|
|
89
|
+
|
|
90
|
+
let currentBuffer: RenderBuffer | null = null
|
|
91
|
+
let currentElement: ReactElement = element
|
|
92
|
+
let renderScheduled = false
|
|
93
|
+
|
|
94
|
+
function scheduleRender(): void {
|
|
95
|
+
if (renderScheduled) return
|
|
96
|
+
renderScheduled = true
|
|
97
|
+
|
|
98
|
+
if (typeof requestAnimationFrame !== "undefined") {
|
|
99
|
+
requestAnimationFrame(() => {
|
|
100
|
+
renderScheduled = false
|
|
101
|
+
doRender()
|
|
102
|
+
})
|
|
103
|
+
} else {
|
|
104
|
+
setTimeout(() => {
|
|
105
|
+
renderScheduled = false
|
|
106
|
+
doRender()
|
|
107
|
+
}, 0)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function doRender(): void {
|
|
112
|
+
reconciler.updateContainerSync(currentElement, fiberRoot, null, null)
|
|
113
|
+
reconciler.flushSyncWork()
|
|
114
|
+
|
|
115
|
+
const prevBuffer = currentBuffer
|
|
116
|
+
const result = executeRenderAdapter(root, width, height, prevBuffer)
|
|
117
|
+
currentBuffer = result.buffer
|
|
118
|
+
|
|
119
|
+
onRender(currentBuffer as TBuffer)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Initial render
|
|
123
|
+
doRender()
|
|
124
|
+
|
|
125
|
+
const unmount = (): void => {
|
|
126
|
+
reconciler.updateContainer(null, fiberRoot, null, () => {})
|
|
127
|
+
onUnmount?.()
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
rerender(newElement: ReactElement): void {
|
|
132
|
+
currentElement = newElement
|
|
133
|
+
scheduleRender()
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
unmount,
|
|
137
|
+
[Symbol.dispose]: unmount,
|
|
138
|
+
|
|
139
|
+
getBuffer(): RenderBuffer | null {
|
|
140
|
+
return currentBuffer
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
refresh(): void {
|
|
144
|
+
scheduleRender()
|
|
145
|
+
},
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ============================================================================
|
|
150
|
+
// One-Shot Render
|
|
151
|
+
// ============================================================================
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Render a React element once and return the buffer.
|
|
155
|
+
* No ongoing updates — useful for static rendering or server-side generation.
|
|
156
|
+
*/
|
|
157
|
+
export function renderOnce<TBuffer extends RenderBuffer>(
|
|
158
|
+
element: ReactElement,
|
|
159
|
+
width: number,
|
|
160
|
+
height: number,
|
|
161
|
+
): TBuffer {
|
|
162
|
+
const container = createContainer(() => {})
|
|
163
|
+
const root = getContainerRoot(container)
|
|
164
|
+
const fiberRoot = createFiberRoot(container)
|
|
165
|
+
|
|
166
|
+
reconciler.updateContainerSync(element, fiberRoot, null, null)
|
|
167
|
+
reconciler.flushSyncWork()
|
|
168
|
+
|
|
169
|
+
const { buffer } = executeRenderAdapter(root, width, height, null)
|
|
170
|
+
|
|
171
|
+
reconciler.updateContainer(null, fiberRoot, null, () => {})
|
|
172
|
+
|
|
173
|
+
return buffer as TBuffer
|
|
174
|
+
}
|