@aurigax-ai/ghostty-web 0.0.0-stage → 0.2.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/GHOSTTY_COMMIT +1 -0
- package/LICENSE +22 -0
- package/README.md +94 -2
- package/ghostty-vt.wasm +0 -0
- package/lib/abi.ts +227 -0
- package/lib/addons/fit.ts +242 -0
- package/lib/box-glyphs.ts +278 -0
- package/lib/buffer.ts +402 -0
- package/lib/event-emitter.ts +27 -0
- package/lib/ghostty.ts +1253 -0
- package/lib/index.ts +111 -0
- package/lib/input-handler.ts +1116 -0
- package/lib/interfaces.ts +224 -0
- package/lib/link-detector.ts +215 -0
- package/lib/marker.ts +55 -0
- package/lib/providers/osc8-link-provider.ts +144 -0
- package/lib/providers/url-regex-provider.ts +150 -0
- package/lib/renderer.ts +820 -0
- package/lib/selection-manager.ts +1057 -0
- package/lib/terminal.ts +2054 -0
- package/lib/types.ts +648 -0
- package/lib/webgl-renderer.ts +870 -0
- package/package.json +70 -4
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL Regex Link Provider
|
|
3
|
+
*
|
|
4
|
+
* Detects plain text URLs using regex pattern matching.
|
|
5
|
+
* Supports common protocols but excludes file paths.
|
|
6
|
+
*
|
|
7
|
+
* This provider runs after OSC8LinkProvider, so explicit hyperlinks
|
|
8
|
+
* take precedence over regex-detected URLs.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { ILink, ILinkProvider } from '../types';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* URL Regex Provider
|
|
15
|
+
*
|
|
16
|
+
* Detects plain text URLs on a single line using regex.
|
|
17
|
+
* Does not support multi-line URLs or file paths.
|
|
18
|
+
*
|
|
19
|
+
* Supported protocols:
|
|
20
|
+
* - https://, http://
|
|
21
|
+
* - mailto:
|
|
22
|
+
* - ftp://, ssh://, git://
|
|
23
|
+
* - tel:, magnet:
|
|
24
|
+
* - gemini://, gopher://, news:
|
|
25
|
+
*/
|
|
26
|
+
export class UrlRegexProvider implements ILinkProvider {
|
|
27
|
+
/**
|
|
28
|
+
* URL regex pattern
|
|
29
|
+
* Matches common protocols followed by valid URL characters
|
|
30
|
+
* Excludes file paths (no ./ or ../ or bare /)
|
|
31
|
+
*/
|
|
32
|
+
private static readonly URL_REGEX =
|
|
33
|
+
/(?:https?:\/\/|mailto:|ftp:\/\/|ssh:\/\/|git:\/\/|tel:|magnet:|gemini:\/\/|gopher:\/\/|news:)[\w\-.~:\/?#@!$&*+,;=%]+/gi;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Characters to strip from end of URLs
|
|
37
|
+
* Common punctuation that's unlikely to be part of the URL
|
|
38
|
+
*/
|
|
39
|
+
private static readonly TRAILING_PUNCTUATION = /[.,;!?)\]]+$/;
|
|
40
|
+
|
|
41
|
+
constructor(private terminal: ITerminalForUrlProvider) {}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Provide all regex-detected URLs on the given row
|
|
45
|
+
*/
|
|
46
|
+
provideLinks(y: number, callback: (links: ILink[] | undefined) => void): void {
|
|
47
|
+
const links: ILink[] = [];
|
|
48
|
+
|
|
49
|
+
const line = this.terminal.buffer.active.getLine(y);
|
|
50
|
+
if (!line) {
|
|
51
|
+
callback(undefined);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Convert line cells to text
|
|
56
|
+
const lineText = this.lineToText(line);
|
|
57
|
+
|
|
58
|
+
// Reset regex state (global flag maintains state)
|
|
59
|
+
UrlRegexProvider.URL_REGEX.lastIndex = 0;
|
|
60
|
+
|
|
61
|
+
// Find all URL matches in the line
|
|
62
|
+
let match: RegExpExecArray | null = UrlRegexProvider.URL_REGEX.exec(lineText);
|
|
63
|
+
while (match !== null) {
|
|
64
|
+
let url = match[0];
|
|
65
|
+
const startX = match.index;
|
|
66
|
+
let endX = match.index + url.length - 1; // Inclusive end
|
|
67
|
+
|
|
68
|
+
// Strip trailing punctuation
|
|
69
|
+
const stripped = url.replace(UrlRegexProvider.TRAILING_PUNCTUATION, '');
|
|
70
|
+
if (stripped.length < url.length) {
|
|
71
|
+
url = stripped;
|
|
72
|
+
endX = startX + url.length - 1;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Skip if URL is too short (e.g., just "http://")
|
|
76
|
+
if (url.length > 8) {
|
|
77
|
+
links.push({
|
|
78
|
+
text: url,
|
|
79
|
+
range: {
|
|
80
|
+
start: { x: startX, y },
|
|
81
|
+
end: { x: endX, y },
|
|
82
|
+
},
|
|
83
|
+
activate: (event) => {
|
|
84
|
+
// Open link if Ctrl/Cmd is pressed
|
|
85
|
+
if (event.ctrlKey || event.metaKey) {
|
|
86
|
+
window.open(url, '_blank', 'noopener,noreferrer');
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Get next match
|
|
93
|
+
match = UrlRegexProvider.URL_REGEX.exec(lineText);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
callback(links.length > 0 ? links : undefined);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Convert a buffer line to plain text string
|
|
101
|
+
*/
|
|
102
|
+
private lineToText(line: IBufferLineForUrlProvider): string {
|
|
103
|
+
const chars: string[] = [];
|
|
104
|
+
|
|
105
|
+
for (let x = 0; x < line.length; x++) {
|
|
106
|
+
const cell = line.getCell(x);
|
|
107
|
+
if (!cell) {
|
|
108
|
+
chars.push(' ');
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const codepoint = cell.getCodepoint();
|
|
113
|
+
// Skip null characters and control characters
|
|
114
|
+
if (codepoint === 0 || codepoint < 32) {
|
|
115
|
+
chars.push(' ');
|
|
116
|
+
} else {
|
|
117
|
+
chars.push(String.fromCodePoint(codepoint));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return chars.join('');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
dispose(): void {
|
|
125
|
+
// No resources to clean up
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Minimal terminal interface required by UrlRegexProvider
|
|
131
|
+
*/
|
|
132
|
+
export interface ITerminalForUrlProvider {
|
|
133
|
+
buffer: {
|
|
134
|
+
active: {
|
|
135
|
+
getLine(y: number): IBufferLineForUrlProvider | undefined;
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Minimal buffer line interface for URL detection
|
|
142
|
+
*/
|
|
143
|
+
interface IBufferLineForUrlProvider {
|
|
144
|
+
length: number;
|
|
145
|
+
getCell(x: number):
|
|
146
|
+
| {
|
|
147
|
+
getCodepoint(): number;
|
|
148
|
+
}
|
|
149
|
+
| undefined;
|
|
150
|
+
}
|