@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.
Files changed (92) hide show
  1. package/package.json +54 -0
  2. package/src/adapters/canvas-adapter.ts +356 -0
  3. package/src/adapters/dom-adapter.ts +452 -0
  4. package/src/adapters/flexily-zero-adapter.ts +368 -0
  5. package/src/adapters/terminal-adapter.ts +305 -0
  6. package/src/adapters/yoga-adapter.ts +370 -0
  7. package/src/ansi/ansi.ts +251 -0
  8. package/src/ansi/constants.ts +76 -0
  9. package/src/ansi/detection.ts +441 -0
  10. package/src/ansi/hyperlink.ts +38 -0
  11. package/src/ansi/index.ts +201 -0
  12. package/src/ansi/patch-console.ts +159 -0
  13. package/src/ansi/sgr-codes.ts +34 -0
  14. package/src/ansi/storybook.ts +209 -0
  15. package/src/ansi/term.ts +724 -0
  16. package/src/ansi/types.ts +202 -0
  17. package/src/ansi/underline.ts +156 -0
  18. package/src/ansi/utils.ts +65 -0
  19. package/src/ansi-sanitize.ts +509 -0
  20. package/src/app.ts +571 -0
  21. package/src/bound-term.ts +94 -0
  22. package/src/bracketed-paste.ts +75 -0
  23. package/src/browser-renderer.ts +174 -0
  24. package/src/buffer.ts +1984 -0
  25. package/src/clipboard.ts +74 -0
  26. package/src/cursor-query.ts +85 -0
  27. package/src/device-attrs.ts +228 -0
  28. package/src/devtools.ts +123 -0
  29. package/src/dom/index.ts +194 -0
  30. package/src/errors.ts +39 -0
  31. package/src/focus-reporting.ts +48 -0
  32. package/src/hit-registry-core.ts +228 -0
  33. package/src/hit-registry.ts +176 -0
  34. package/src/index.ts +458 -0
  35. package/src/input.ts +119 -0
  36. package/src/inspector.ts +155 -0
  37. package/src/kitty-detect.ts +95 -0
  38. package/src/kitty-manager.ts +160 -0
  39. package/src/layout-engine.ts +296 -0
  40. package/src/layout.ts +26 -0
  41. package/src/measurer.ts +74 -0
  42. package/src/mode-query.ts +106 -0
  43. package/src/mouse-events.ts +419 -0
  44. package/src/mouse.ts +83 -0
  45. package/src/non-tty.ts +223 -0
  46. package/src/osc-markers.ts +32 -0
  47. package/src/osc-palette.ts +169 -0
  48. package/src/output.ts +406 -0
  49. package/src/pane-manager.ts +248 -0
  50. package/src/pipeline/CLAUDE.md +587 -0
  51. package/src/pipeline/content-phase-adapter.ts +976 -0
  52. package/src/pipeline/content-phase.ts +1765 -0
  53. package/src/pipeline/helpers.ts +42 -0
  54. package/src/pipeline/index.ts +416 -0
  55. package/src/pipeline/layout-phase.ts +686 -0
  56. package/src/pipeline/measure-phase.ts +198 -0
  57. package/src/pipeline/measure-stats.ts +21 -0
  58. package/src/pipeline/output-phase.ts +2593 -0
  59. package/src/pipeline/render-box.ts +343 -0
  60. package/src/pipeline/render-helpers.ts +243 -0
  61. package/src/pipeline/render-text.ts +1255 -0
  62. package/src/pipeline/types.ts +161 -0
  63. package/src/pipeline.ts +29 -0
  64. package/src/pixel-size.ts +119 -0
  65. package/src/render-adapter.ts +179 -0
  66. package/src/renderer.ts +1330 -0
  67. package/src/runtime/create-app.tsx +1845 -0
  68. package/src/runtime/create-buffer.ts +18 -0
  69. package/src/runtime/create-runtime.ts +325 -0
  70. package/src/runtime/diff.ts +56 -0
  71. package/src/runtime/event-handlers.ts +254 -0
  72. package/src/runtime/index.ts +119 -0
  73. package/src/runtime/keys.ts +8 -0
  74. package/src/runtime/layout.ts +164 -0
  75. package/src/runtime/run.tsx +318 -0
  76. package/src/runtime/term-provider.ts +399 -0
  77. package/src/runtime/terminal-lifecycle.ts +246 -0
  78. package/src/runtime/tick.ts +219 -0
  79. package/src/runtime/types.ts +210 -0
  80. package/src/scheduler.ts +723 -0
  81. package/src/screenshot.ts +57 -0
  82. package/src/scroll-region.ts +69 -0
  83. package/src/scroll-utils.ts +97 -0
  84. package/src/term-def.ts +267 -0
  85. package/src/terminal-caps.ts +5 -0
  86. package/src/terminal-colors.ts +216 -0
  87. package/src/termtest.ts +224 -0
  88. package/src/text-sizing.ts +109 -0
  89. package/src/toolbelt/index.ts +72 -0
  90. package/src/unicode.ts +1763 -0
  91. package/src/xterm/index.ts +491 -0
  92. package/src/xterm/xterm-provider.ts +204 -0
@@ -0,0 +1,202 @@
1
+ /**
2
+ * Type definitions for @silvery/ansi
3
+ */
4
+
5
+ import type { TerminalCaps } from "./detection"
6
+
7
+ // =============================================================================
8
+ // Color Types
9
+ // =============================================================================
10
+
11
+ /**
12
+ * Color level supported by terminal.
13
+ * - 'basic': 16 colors (SGR 30-37, 40-47)
14
+ * - '256': 256 colors (SGR 38;5;n)
15
+ * - 'truecolor': 16M colors (SGR 38;2;r;g;b)
16
+ */
17
+ export type ColorLevel = "basic" | "256" | "truecolor"
18
+
19
+ /**
20
+ * RGB color tuple for underline color.
21
+ * Each component is 0-255.
22
+ */
23
+ export type RGB = [r: number, g: number, b: number]
24
+
25
+ /**
26
+ * Standard ANSI color names (the 16 base colors).
27
+ * These map to SGR 30-37 (foreground) and 40-47 (background),
28
+ * plus their bright variants (SGR 90-97, 100-107).
29
+ */
30
+ export type AnsiColorName =
31
+ | "black"
32
+ | "red"
33
+ | "green"
34
+ | "yellow"
35
+ | "blue"
36
+ | "magenta"
37
+ | "cyan"
38
+ | "white"
39
+ | "gray"
40
+ | "grey"
41
+ | "blackBright"
42
+ | "redBright"
43
+ | "greenBright"
44
+ | "yellowBright"
45
+ | "blueBright"
46
+ | "magentaBright"
47
+ | "cyanBright"
48
+ | "whiteBright"
49
+
50
+ /**
51
+ * Hex color string pattern.
52
+ * Accepts 3-digit (#rgb) and 6-digit (#rrggbb) hex colors.
53
+ */
54
+ type HexColor = `#${string}`
55
+
56
+ /**
57
+ * RGB function-style color string pattern.
58
+ * Format: rgb(r,g,b) where r, g, b are 0-255.
59
+ */
60
+ type RgbColor = `rgb(${string})`
61
+
62
+ /**
63
+ * Theme token color string pattern.
64
+ * Format: $name — resolved against the active theme at render time.
65
+ * Examples: $primary, $surface, $error, $bg, $fg, $muted
66
+ */
67
+ type ThemeToken = `$${string}`
68
+
69
+ /**
70
+ * Type-safe color value accepted by ansi APIs.
71
+ *
72
+ * Accepts:
73
+ * - ANSI color names: `"red"`, `"cyan"`, `"whiteBright"`, etc.
74
+ * - Hex colors: `"#ff0000"`, `"#f00"`
75
+ * - RGB function: `"rgb(255, 0, 0)"`
76
+ * - Theme tokens: `"$primary"`, `"$error"`, `"$surface"`
77
+ * - Any other string (for forward compatibility with custom color schemes)
78
+ *
79
+ * The union of known literals provides autocompletion in editors while
80
+ * the `string & {}` fallback allows arbitrary color strings.
81
+ */
82
+ export type Color = AnsiColorName | HexColor | RgbColor | ThemeToken | (string & {})
83
+
84
+ // =============================================================================
85
+ // Underline Types
86
+ // =============================================================================
87
+
88
+ /**
89
+ * Extended underline styles supported by modern terminals.
90
+ *
91
+ * - `single`: Standard underline (SGR 4:1)
92
+ * - `double`: Two parallel lines (SGR 4:2)
93
+ * - `curly`: Wavy/squiggly line (SGR 4:3) - commonly used for spell check
94
+ * - `dotted`: Dotted line (SGR 4:4)
95
+ * - `dashed`: Dashed line (SGR 4:5)
96
+ */
97
+ export type UnderlineStyle = "single" | "double" | "curly" | "dotted" | "dashed"
98
+
99
+ // =============================================================================
100
+ // Style Types
101
+ // =============================================================================
102
+
103
+ /**
104
+ * Style options for term.style() method.
105
+ */
106
+ export interface StyleOptions {
107
+ color?: Color
108
+ bgColor?: Color
109
+ bold?: boolean
110
+ dim?: boolean
111
+ italic?: boolean
112
+ underline?: boolean
113
+ strikethrough?: boolean
114
+ inverse?: boolean
115
+ }
116
+
117
+ // =============================================================================
118
+ // Console Types
119
+ // =============================================================================
120
+
121
+ /**
122
+ * Console method names that can be intercepted.
123
+ */
124
+ export type ConsoleMethod = "log" | "info" | "warn" | "error" | "debug"
125
+
126
+ /**
127
+ * Entry captured from console.
128
+ */
129
+ export interface ConsoleEntry {
130
+ method: ConsoleMethod
131
+ args: unknown[]
132
+ stream: "stdout" | "stderr"
133
+ }
134
+
135
+ // =============================================================================
136
+ // Term Types
137
+ // =============================================================================
138
+
139
+ /**
140
+ * Options for createTerm().
141
+ */
142
+ export interface CreateTermOptions {
143
+ stdout?: NodeJS.WriteStream
144
+ stdin?: NodeJS.ReadStream
145
+
146
+ // Override auto-detection (for testing or forcing)
147
+ color?: ColorLevel | null // override hasColor()
148
+ unicode?: boolean // override hasUnicode()
149
+ cursor?: boolean // override hasCursor()
150
+
151
+ // Terminal capabilities override
152
+ caps?: Partial<TerminalCaps>
153
+ }
154
+
155
+ // Re-export TerminalCaps from detection for convenience
156
+ export type { TerminalCaps } from "./detection"
157
+
158
+ // =============================================================================
159
+ // Terminal Emulator Types (duck-types for termless integration)
160
+ // =============================================================================
161
+
162
+ /**
163
+ * A screen region — duck-type matching termless RegionView.
164
+ * Provides text content and line access for assertions.
165
+ */
166
+ export interface TermScreen {
167
+ getText(): string
168
+ getLines(): string[]
169
+ containsText?(text: string): boolean
170
+ }
171
+
172
+ /**
173
+ * A terminal emulator — duck-type matching termless Terminal.
174
+ * Accepts ANSI output, provides screen/scrollback for inspection.
175
+ */
176
+ export interface TermEmulator {
177
+ readonly cols: number
178
+ readonly rows: number
179
+ readonly screen: TermScreen
180
+ readonly scrollback: TermScreen
181
+ feed(data: Uint8Array | string): void
182
+ resize(cols: number, rows: number): void
183
+ close(): Promise<void>
184
+ }
185
+
186
+ /**
187
+ * A terminal emulator backend — duck-type matching termless TerminalBackend.
188
+ * Raw backend that needs initialization. Pass to createTerm(backend, { cols, rows }).
189
+ *
190
+ * @example
191
+ * ```ts
192
+ * import { createXtermBackend } from "@termless/xtermjs"
193
+ * using term = createTerm(createXtermBackend(), { cols: 80, rows: 24 })
194
+ * ```
195
+ */
196
+ export interface TermEmulatorBackend {
197
+ readonly name: string
198
+ init(opts: { cols: number; rows: number; scrollbackLimit?: number }): void
199
+ destroy(): void
200
+ feed(data: Uint8Array): void
201
+ resize(cols: number, rows: number): void
202
+ }
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Extended underline style functions.
3
+ *
4
+ * Provides curly, dotted, dashed, and double underline styles
5
+ * with graceful fallback to standard underline on unsupported terminals.
6
+ */
7
+
8
+ import chalk from "chalk"
9
+ import {
10
+ UNDERLINE_CODES,
11
+ UNDERLINE_COLOR_RESET,
12
+ UNDERLINE_STANDARD,
13
+ UNDERLINE_RESET_STANDARD,
14
+ buildUnderlineColorCode,
15
+ } from "./constants"
16
+ import { detectExtendedUnderline } from "./detection"
17
+ import type { UnderlineStyle, RGB } from "./types"
18
+
19
+ // =============================================================================
20
+ // Extended Underline Functions
21
+ // =============================================================================
22
+
23
+ /**
24
+ * Apply an extended underline style to text.
25
+ * Falls back to regular underline on unsupported terminals.
26
+ *
27
+ * @param text - Text to underline
28
+ * @param style - Underline style (default: "single")
29
+ * @returns Styled text with ANSI codes
30
+ */
31
+ export function underline(text: string, style: UnderlineStyle = "single"): string {
32
+ if (!detectExtendedUnderline() || style === "single") {
33
+ return chalk.underline(text)
34
+ }
35
+
36
+ return `${UNDERLINE_CODES[style]}${text}${UNDERLINE_CODES.reset}`
37
+ }
38
+
39
+ /**
40
+ * Apply curly/wavy underline to text.
41
+ * Commonly used for spell check errors in IDEs.
42
+ * Falls back to regular underline on unsupported terminals.
43
+ *
44
+ * @param text - Text to underline
45
+ * @returns Styled text with curly underline
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * import { curlyUnderline, chalk } from '@silvery/ansi';
50
+ *
51
+ * console.log(curlyUnderline('misspelled'));
52
+ * console.log(chalk.red(curlyUnderline('error')));
53
+ * ```
54
+ */
55
+ export function curlyUnderline(text: string): string {
56
+ return underline(text, "curly")
57
+ }
58
+
59
+ /**
60
+ * Apply dotted underline to text.
61
+ * Falls back to regular underline on unsupported terminals.
62
+ *
63
+ * @param text - Text to underline
64
+ * @returns Styled text with dotted underline
65
+ */
66
+ export function dottedUnderline(text: string): string {
67
+ return underline(text, "dotted")
68
+ }
69
+
70
+ /**
71
+ * Apply dashed underline to text.
72
+ * Falls back to regular underline on unsupported terminals.
73
+ *
74
+ * @param text - Text to underline
75
+ * @returns Styled text with dashed underline
76
+ */
77
+ export function dashedUnderline(text: string): string {
78
+ return underline(text, "dashed")
79
+ }
80
+
81
+ /**
82
+ * Apply double underline to text.
83
+ * Falls back to regular underline on unsupported terminals.
84
+ *
85
+ * @param text - Text to underline
86
+ * @returns Styled text with double underline
87
+ */
88
+ export function doubleUnderline(text: string): string {
89
+ return underline(text, "double")
90
+ }
91
+
92
+ // =============================================================================
93
+ // Underline Color Functions
94
+ // =============================================================================
95
+
96
+ /**
97
+ * Set underline color independently of text color.
98
+ * On unsupported terminals, the color is ignored but underline still applies.
99
+ *
100
+ * @param r - Red component (0-255)
101
+ * @param g - Green component (0-255)
102
+ * @param b - Blue component (0-255)
103
+ * @param text - Text to style
104
+ * @returns Styled text with colored underline
105
+ *
106
+ * @example
107
+ * ```ts
108
+ * import { underlineColor, chalk } from '@silvery/ansi';
109
+ *
110
+ * // Red underline (text color unchanged)
111
+ * console.log(underlineColor(255, 0, 0, 'warning'));
112
+ *
113
+ * // Red underline with blue text
114
+ * console.log(chalk.blue(underlineColor(255, 0, 0, 'blue text, red underline')));
115
+ * ```
116
+ */
117
+ export function underlineColor(r: number, g: number, b: number, text: string): string {
118
+ if (!detectExtendedUnderline()) {
119
+ // Fallback: just apply regular underline, ignore color
120
+ return chalk.underline(text)
121
+ }
122
+
123
+ const colorCode = buildUnderlineColorCode(r, g, b)
124
+ return `${UNDERLINE_STANDARD}${colorCode}${text}${UNDERLINE_COLOR_RESET}${UNDERLINE_RESET_STANDARD}`
125
+ }
126
+
127
+ /**
128
+ * Combine underline style with underline color.
129
+ *
130
+ * @param style - Underline style ('curly', 'dotted', 'dashed', 'double', 'single')
131
+ * @param rgb - Color as [r, g, b] tuple (0-255 each)
132
+ * @param text - Text to style
133
+ * @returns Styled text with colored underline in specified style
134
+ *
135
+ * @example
136
+ * ```ts
137
+ * import { styledUnderline, chalk } from '@silvery/ansi';
138
+ *
139
+ * // Red curly underline (spell-check style)
140
+ * console.log(styledUnderline('curly', [255, 0, 0], 'misspelled'));
141
+ *
142
+ * // Orange dashed underline with yellow text
143
+ * console.log(chalk.yellow(styledUnderline('dashed', [255, 165, 0], 'warning')));
144
+ * ```
145
+ */
146
+ export function styledUnderline(style: UnderlineStyle, rgb: RGB, text: string): string {
147
+ if (!detectExtendedUnderline()) {
148
+ return chalk.underline(text)
149
+ }
150
+
151
+ const [r, g, b] = rgb
152
+ const styleCode = UNDERLINE_CODES[style]
153
+ const colorCode = buildUnderlineColorCode(r, g, b)
154
+
155
+ return `${styleCode}${colorCode}${text}${UNDERLINE_CODES.reset}${UNDERLINE_COLOR_RESET}`
156
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * ANSI string utilities.
3
+ *
4
+ * This module can be imported separately via `@silvery/ansi/utils`
5
+ * for projects that only need ANSI stripping without chalk.
6
+ */
7
+
8
+ import stringWidth from "string-width"
9
+
10
+ // =============================================================================
11
+ // ANSI Regex Pattern
12
+ // =============================================================================
13
+
14
+ /**
15
+ * ANSI escape code pattern for stripping.
16
+ *
17
+ * Matches:
18
+ * - ESC CSI SGR sequences: \x1b[31m, \x1b[4:3m, \x1b[38:2::255:100:0m
19
+ * - C1 CSI SGR sequences: \x9b31m, \x9b4:3m
20
+ * - ESC OSC 8 hyperlinks (BEL-terminated): \x1b]8;;<url>\x07
21
+ * - ESC OSC 8 hyperlinks (ST-terminated): \x1b]8;;<url>\x1b\\
22
+ * - C1 OSC 8 hyperlinks (BEL-terminated): \x9d8;;<url>\x07
23
+ * - C1 OSC 8 hyperlinks (ST-terminated): \x9d8;;<url>\x1b\\
24
+ * - C1 OSC 8 hyperlinks (C1 ST-terminated): \x9d8;;<url>\x9c
25
+ */
26
+ export const ANSI_REGEX =
27
+ /\x1b\[[0-9;:]*m|\x9b[0-9;:]*m|\x1b\]8;;[^\x07\x1b]*(?:\x07|\x1b\\)|\x9d8;;[^\x07\x1b\x9c]*(?:\x07|\x1b\\|\x9c)/g
28
+
29
+ // =============================================================================
30
+ // String Utilities
31
+ // =============================================================================
32
+
33
+ /**
34
+ * Strip all ANSI escape codes from a string.
35
+ *
36
+ * @param text - String potentially containing ANSI codes
37
+ * @returns Clean string with all ANSI codes removed
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * stripAnsi('\x1b[31mred\x1b[0m') // 'red'
42
+ * stripAnsi('\x1b[4:3mwavy\x1b[4:0m') // 'wavy'
43
+ * ```
44
+ */
45
+ export function stripAnsi(text: string): string {
46
+ return text.replace(ANSI_REGEX, "")
47
+ }
48
+
49
+ /**
50
+ * Get the display width of a string, excluding ANSI escape codes.
51
+ * Correctly handles CJK characters, emoji, and other wide characters.
52
+ *
53
+ * @param text - String potentially containing ANSI codes
54
+ * @returns Number of terminal columns the text will occupy
55
+ *
56
+ * @example
57
+ * ```ts
58
+ * displayLength('\x1b[31mhello\x1b[0m') // 5
59
+ * displayLength('hello') // 5
60
+ * displayLength('한글') // 4 (2 chars × 2 cells each)
61
+ * ```
62
+ */
63
+ export function displayLength(text: string): number {
64
+ return stringWidth(stripAnsi(text))
65
+ }