@neuroplastio/xterm-addon-hotty 0.1.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/LICENSE +73 -0
- package/README.md +358 -0
- package/dist/hotty-xterm.js +2906 -0
- package/dist/hotty-xterm.js.map +7 -0
- package/dist/types/addon.d.ts +170 -0
- package/dist/types/delta.d.ts +19 -0
- package/dist/types/hostcss.d.ts +48 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/network.d.ts +18 -0
- package/dist/types/resolver.d.ts +41 -0
- package/dist/types/resources.d.ts +32 -0
- package/dist/types/surface.d.ts +405 -0
- package/dist/types/touch.d.ts +37 -0
- package/dist/types/version.d.ts +10 -0
- package/dist/types/wire.d.ts +59 -0
- package/package.json +54 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import type { ITerminalAddon, Terminal } from "@xterm/xterm";
|
|
2
|
+
import { type Policy } from "./network.ts";
|
|
3
|
+
export interface HottyOptions {
|
|
4
|
+
/** Most surfaces at once (reported under `limits`). Default 64. */
|
|
5
|
+
maxSurfaces?: number;
|
|
6
|
+
/** Bytes of resources (reported under `limits`). Default 64 MB. */
|
|
7
|
+
resourceQuota?: number;
|
|
8
|
+
/** Called once per applied command batch, with timings, for measurements. */
|
|
9
|
+
onFrame?: (f: FrameStats) => void;
|
|
10
|
+
/** Called for commands that could not be decoded. */
|
|
11
|
+
onInvalid?: (reason: string) => void;
|
|
12
|
+
/**
|
|
13
|
+
* What surfaces may fetch from the network: the host's half of the network
|
|
14
|
+
* policy (SPEC §7.2), directive to sources, e.g.
|
|
15
|
+
* `{ "img-src": ["https://example.com"] }`. A document gets what it asks
|
|
16
|
+
* for (`<meta name="hotty-network">`) and this allows. Default: nothing.
|
|
17
|
+
* The embedding page's own CSP must allow these sources too.
|
|
18
|
+
*/
|
|
19
|
+
network?: Policy;
|
|
20
|
+
/**
|
|
21
|
+
* Touch on the whole terminal (SPEC §9): a drag scrolls, over the cells
|
|
22
|
+
* and the surfaces alike, and a tap on the cells is a click for the
|
|
23
|
+
* program. This replaces xterm.js's own touch handling, which in 6.1 sends
|
|
24
|
+
* wheel reports with no position (NaN) and turns taps into nothing.
|
|
25
|
+
* Default true.
|
|
26
|
+
*/
|
|
27
|
+
touch?: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* What a wheel or a touch drag scrolls, over the cells and the surfaces
|
|
30
|
+
* alike (SPEC §9). `"terminal"` (the default): the terminal's scrollback,
|
|
31
|
+
* or on the alternate screen the program's wheel input. `"page"`: the page
|
|
32
|
+
* the terminal is in, for a page that shows a program's output whole, with
|
|
33
|
+
* the terminal as tall as what it shows. Neither the addon nor xterm.js
|
|
34
|
+
* takes a wheel or a touch then: the browser scrolls the page, natively,
|
|
35
|
+
* and the program hears no wheel. `touch` does nothing in this mode.
|
|
36
|
+
*/
|
|
37
|
+
scroll?: "terminal" | "page";
|
|
38
|
+
/**
|
|
39
|
+
* Keys the browser keeps: they never reach the program, from the terminal
|
|
40
|
+
* or from a surface holding the keyboard, and the browser acts on them.
|
|
41
|
+
* A terminal cannot know which keys a program binds, so these are the
|
|
42
|
+
* browser's own. Default: `browserKeys`, which covers reload, zoom, full
|
|
43
|
+
* screen, the developer tools, and on a Mac everything with Cmd.
|
|
44
|
+
*/
|
|
45
|
+
browserKeys?: (e: KeyboardEvent) => boolean;
|
|
46
|
+
}
|
|
47
|
+
/** The browser's own keys (the default for `browserKeys`): reload (F5, Ctrl
|
|
48
|
+
* or Cmd with R), zoom (Ctrl or Cmd with +, - or 0), full screen (F11), the
|
|
49
|
+
* developer tools (F12, Ctrl+Shift with I, J or C), and on a Mac everything
|
|
50
|
+
* with Cmd. */
|
|
51
|
+
export declare function browserKeys(e: KeyboardEvent): boolean;
|
|
52
|
+
export interface FrameStats {
|
|
53
|
+
commands: number;
|
|
54
|
+
/** Milliseconds spent applying them. */
|
|
55
|
+
applyMs: number;
|
|
56
|
+
}
|
|
57
|
+
export interface Inspected {
|
|
58
|
+
tag: string;
|
|
59
|
+
attrs: Record<string, string>;
|
|
60
|
+
text: string;
|
|
61
|
+
children: [string, string | null, string][];
|
|
62
|
+
}
|
|
63
|
+
/** `drag` stands for `dragstart`, `drag` and `dragend` (SPEC §4, §9.1). */
|
|
64
|
+
export declare const EVENTS: string[];
|
|
65
|
+
export declare class HottyAddon implements ITerminalAddon {
|
|
66
|
+
private term;
|
|
67
|
+
private readonly opts;
|
|
68
|
+
private readonly store;
|
|
69
|
+
private readonly surfaces;
|
|
70
|
+
private readonly placements;
|
|
71
|
+
private readonly assembler;
|
|
72
|
+
private readonly disposables;
|
|
73
|
+
private layer;
|
|
74
|
+
/** Commands held while synchronized output is on (PROTOCOL §5). */
|
|
75
|
+
private held;
|
|
76
|
+
private heldTimer;
|
|
77
|
+
private css;
|
|
78
|
+
private readonly policy;
|
|
79
|
+
private metrics;
|
|
80
|
+
private resizeTimer;
|
|
81
|
+
/** A surface's key is being forwarded to the terminal (`key`), and the
|
|
82
|
+
* terminal's own focus is held off meanwhile. A program in the page may
|
|
83
|
+
* answer the key inside its dispatch, since xterm.js parses what follows
|
|
84
|
+
* user input at once: a surface that gives the keyboard back then has
|
|
85
|
+
* its focus carried out once the key is through. */
|
|
86
|
+
private forwarding;
|
|
87
|
+
private focusAfter;
|
|
88
|
+
/** The wheel gesture under way: the surface its last wheel was over ("" for
|
|
89
|
+
* the cells), where it goes, and when that wheel came. */
|
|
90
|
+
private gesture;
|
|
91
|
+
constructor(options?: HottyOptions);
|
|
92
|
+
activate(term: Terminal): void;
|
|
93
|
+
dispose(): void;
|
|
94
|
+
/**
|
|
95
|
+
* An element of a surface as the program wrote it (tag, attributes, text,
|
|
96
|
+
* child elements): the shape the shared conformance vectors check
|
|
97
|
+
* (conformance/README.md). `null` if there is no such surface or element.
|
|
98
|
+
*/
|
|
99
|
+
inspect(surface: string, id: string): Inspected | null;
|
|
100
|
+
private onOsc;
|
|
101
|
+
private run;
|
|
102
|
+
private submit;
|
|
103
|
+
private release;
|
|
104
|
+
private apply;
|
|
105
|
+
private handle;
|
|
106
|
+
private reply;
|
|
107
|
+
private send;
|
|
108
|
+
private dispatch;
|
|
109
|
+
private capabilities;
|
|
110
|
+
private surfaceName;
|
|
111
|
+
private existing;
|
|
112
|
+
private place;
|
|
113
|
+
/** As the native host: index `rows` times, then carriage return. */
|
|
114
|
+
private moveCursorBelow;
|
|
115
|
+
/** Puts every placed surface where its cells are now. */
|
|
116
|
+
private reposition;
|
|
117
|
+
private position;
|
|
118
|
+
private unplace;
|
|
119
|
+
private remove;
|
|
120
|
+
private reset;
|
|
121
|
+
private onBufferChange;
|
|
122
|
+
private host;
|
|
123
|
+
private cellTouch;
|
|
124
|
+
private pageScrollBound;
|
|
125
|
+
/** Whether the page scrolls, not the terminal (the `scroll` option). */
|
|
126
|
+
private get pageScrolls();
|
|
127
|
+
/**
|
|
128
|
+
* Wheels and touches on the cells left to the browser, which scrolls the
|
|
129
|
+
* page (`scroll: "page"`): they stop at the terminal's element, before
|
|
130
|
+
* xterm.js, which would take them for its scrollback, or turn a drag into
|
|
131
|
+
* arrow keys, and cancel the browser's scroll. Nothing here cancels it.
|
|
132
|
+
*/
|
|
133
|
+
private bindPageScroll;
|
|
134
|
+
/** Scrolls what scrolls the terminal's page: its nearest ancestor that
|
|
135
|
+
* scrolls, or the document. */
|
|
136
|
+
private scrollPage;
|
|
137
|
+
private cellWheelBound;
|
|
138
|
+
/** A wheel over the cells begins or goes on with a gesture of the
|
|
139
|
+
* terminal's: a document that scrolls under the pointer as the terminal
|
|
140
|
+
* scrolls takes none of it (`wheelGesture`). */
|
|
141
|
+
private bindCellWheel;
|
|
142
|
+
/** Touch on the cells (the `touch` option), once the terminal is open. */
|
|
143
|
+
private bindTouch;
|
|
144
|
+
/** A wheel, at a point in the page, replayed on the terminal's screen,
|
|
145
|
+
* where xterm.js handles it as its own: scrollback, or mouse reports and
|
|
146
|
+
* arrow keys on the alternate screen. */
|
|
147
|
+
private forwardWheel;
|
|
148
|
+
/**
|
|
149
|
+
* A press with Alt held on a surface, a move of its gesture, or its
|
|
150
|
+
* release (SPEC §9.2), replayed on the cells beneath, where xterm.js
|
|
151
|
+
* handles them as its own mouse: a report to the program, with Alt, or
|
|
152
|
+
* its selection (rectangular, with Alt). The press goes to the screen,
|
|
153
|
+
* where xterm.js listens for presses; the moves and the release to its
|
|
154
|
+
* document, where it listens while a button is held. The keyboard goes
|
|
155
|
+
* back to the terminal first, as on a click on the cells (§10.1): a
|
|
156
|
+
* surface that had it sends `blur`.
|
|
157
|
+
*/
|
|
158
|
+
private pressCells;
|
|
159
|
+
private ensureLayer;
|
|
160
|
+
private browserKey;
|
|
161
|
+
/** A box in the page as the buffer cells it covers (1-based, as xterm.js
|
|
162
|
+
* gives an OSC 8 link's range). */
|
|
163
|
+
private cellRange;
|
|
164
|
+
/** Cell size in CSS pixels (private in xterm.js), with a measured fallback. */
|
|
165
|
+
private cell;
|
|
166
|
+
private hostCss;
|
|
167
|
+
private scheme;
|
|
168
|
+
/** On every render: did the cell size, font or theme change? */
|
|
169
|
+
private checkMetrics;
|
|
170
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Resolver } from "./resolver.ts";
|
|
2
|
+
export declare class DeltaError extends Error {
|
|
3
|
+
readonly code: string;
|
|
4
|
+
constructor(code: string, detail: string);
|
|
5
|
+
}
|
|
6
|
+
export declare const OPS: string[];
|
|
7
|
+
export declare class Deltas {
|
|
8
|
+
private readonly doc;
|
|
9
|
+
private readonly resolver;
|
|
10
|
+
constructor(doc: Document, resolver: Resolver);
|
|
11
|
+
/** Parses a fragment inert, cleaned and resolved. */
|
|
12
|
+
parse(html: string): Node[];
|
|
13
|
+
apply(op: string, target: string | undefined, key: string | undefined, payload: string): void;
|
|
14
|
+
/** Makes `old` (live) look like `neu` (parsed), keeping `old` where possible. Returns the node now in place. */
|
|
15
|
+
morphNode(old: Node, neu: Node): Node;
|
|
16
|
+
private syncAttributes;
|
|
17
|
+
/** Morphs the children of `parent` (live) into `kids` (parsed). */
|
|
18
|
+
morphChildren(parent: Node, kids: Node[]): void;
|
|
19
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { ITheme } from "@xterm/xterm";
|
|
2
|
+
export interface Metrics {
|
|
3
|
+
/** Cell size in CSS pixels. */
|
|
4
|
+
cellW: number;
|
|
5
|
+
cellH: number;
|
|
6
|
+
fontFamily: string;
|
|
7
|
+
fontSize: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function palette(theme: ITheme | undefined): {
|
|
10
|
+
fg: string;
|
|
11
|
+
bg: string;
|
|
12
|
+
ansi: string[];
|
|
13
|
+
dark: boolean;
|
|
14
|
+
};
|
|
15
|
+
/** The host stylesheet, the same for every surface. What scrolls is each
|
|
16
|
+
* document's own (`scrollCss`). */
|
|
17
|
+
export declare function hostCss(theme: ITheme | undefined, m: Metrics): string;
|
|
18
|
+
/** The host's own attribute on the root while the rows a document needs are
|
|
19
|
+
* measured. Under the addon's vendor prefix: `data-hotty-*` is the spec's
|
|
20
|
+
* (§15). */
|
|
21
|
+
export declare const MEASURE = "data-xterm-hotty-measure";
|
|
22
|
+
/** The host's own attribute on an element of a document that scrolls along
|
|
23
|
+
* one axis, whose `overflow` along the other is `auto` or `scroll`: that
|
|
24
|
+
* axis is clipped as `overflow: hidden` clips it (SPEC §5.3). */
|
|
25
|
+
export declare const CLIP = "data-xterm-hotty-clip";
|
|
26
|
+
/** Set on the root while the addon reads which elements need `CLIP`: the
|
|
27
|
+
* values the document gives them, without the host's. */
|
|
28
|
+
export declare const UNCLIPPED = "data-xterm-hotty-unclipped";
|
|
29
|
+
/**
|
|
30
|
+
* What scrolls in a document (SPEC §5.1, §5.3): the axes it asked for, a
|
|
31
|
+
* bitmask (1 vertically, 2 horizontally). `pageScrolls`: the page scrolls,
|
|
32
|
+
* not the terminal (the addon's `scroll` option), and the browser pans it
|
|
33
|
+
* from a surface.
|
|
34
|
+
*
|
|
35
|
+
* - **None:** a surface is a fixed rectangle of cells, and nothing in it
|
|
36
|
+
* scrolls: what does not fit is clipped. The browser pans nothing on a
|
|
37
|
+
* touch (a drag is the terminal's, SPEC §9; the surface forwards it),
|
|
38
|
+
* unless the page scrolls, which the browser pans as from the cells.
|
|
39
|
+
* Nothing shows a scrollbar, even with `overflow: auto`.
|
|
40
|
+
* - **Along the axes asked for,** the document scrolls as a page does, with
|
|
41
|
+
* the browser's scrollbars, inside the frame: they take pixels, never
|
|
42
|
+
* cells. The browser pans those axes on a touch.
|
|
43
|
+
* - **Along an axis not asked for,** the root and every element `CLIP`
|
|
44
|
+
* marks are `overflow: hidden` there, important in the host's layer, so
|
|
45
|
+
* that the document's CSS cannot undo it: no scrollbar, and nothing the
|
|
46
|
+
* user does moves it.
|
|
47
|
+
*/
|
|
48
|
+
export declare function scrollCss(axes: number, pageScrolls: boolean): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type Directive = "img-src" | "media-src" | "font-src" | "style-src";
|
|
2
|
+
export declare const DIRECTIVES: readonly Directive[];
|
|
3
|
+
/** Directive → sources: origins (`https://example.com`) or `https:`. */
|
|
4
|
+
export type Policy = Partial<Record<Directive, string[]>>;
|
|
5
|
+
/** A source in canonical form, or null if it is not one HOTTY allows. */
|
|
6
|
+
export declare function source(s: string): string | null;
|
|
7
|
+
/** A policy with only valid directives and sources. */
|
|
8
|
+
export declare function clean(p: Policy | undefined): Policy;
|
|
9
|
+
/** A document's request, from its `<meta name="hotty-network">` content (CSP syntax). */
|
|
10
|
+
export declare function parse(content: string): Policy;
|
|
11
|
+
/** What both halves allow: sources of the document that the host's cover. */
|
|
12
|
+
export declare function intersect(host: Policy, doc: Policy): Policy;
|
|
13
|
+
/** Whether a policy allows fetching an absolute URL for a directive. */
|
|
14
|
+
export declare function allows(p: Policy, d: Directive, url: string): boolean;
|
|
15
|
+
/** A directive's sources as CSP source expressions. */
|
|
16
|
+
export declare function cspSources(p: Policy, d: Directive): string;
|
|
17
|
+
/** The directive that covers an attribute of an element, if it fetches at all. */
|
|
18
|
+
export declare function directiveFor(tag: string, attr: string, rel?: string): Directive | null;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { INVALID, type Store, type UrlContext } from "./resources.ts";
|
|
2
|
+
export declare class Resolver {
|
|
3
|
+
/** The program's value of each attribute the resolver rewrote. */
|
|
4
|
+
private orig;
|
|
5
|
+
/** The program's text of each `<style>` whose CSS names a resource. */
|
|
6
|
+
private css;
|
|
7
|
+
/** Elements that name resources, and which. */
|
|
8
|
+
private refs;
|
|
9
|
+
/** Attributes the host set and the program did not write (a detached
|
|
10
|
+
* surface's `disabled`, SPEC §5.5): live, never reported. */
|
|
11
|
+
private own;
|
|
12
|
+
private readonly store;
|
|
13
|
+
/** The document's base URL and effective network policy (set per a=doc). */
|
|
14
|
+
ctx: UrlContext | undefined;
|
|
15
|
+
constructor(store: Store);
|
|
16
|
+
/** Cleans and resolves an inert subtree, in place. */
|
|
17
|
+
adopt(root: Node): void;
|
|
18
|
+
private allowed;
|
|
19
|
+
private cleanAttributes;
|
|
20
|
+
/** An attribute as the program wrote it. */
|
|
21
|
+
get(el: Element, name: string): string | null;
|
|
22
|
+
/** Every attribute as the program wrote it, in order. */
|
|
23
|
+
attributes(el: Element): [string, string][];
|
|
24
|
+
/** Sets an attribute of the host's own, which the program does not see. */
|
|
25
|
+
setOwn(el: Element, name: string, value: string): void;
|
|
26
|
+
/** Removes an attribute, if it is the host's own. */
|
|
27
|
+
removeOwn(el: Element, name: string): void;
|
|
28
|
+
/** Sets an attribute to the program's value, resolving URLs and CSS. */
|
|
29
|
+
set(el: Element, name: string, value: string): void;
|
|
30
|
+
remove(el: Element, name: string): void;
|
|
31
|
+
/** A `<style>`'s CSS as the program wrote it. */
|
|
32
|
+
cssOf(el: Element): string;
|
|
33
|
+
setCss(el: Element, text: string): void;
|
|
34
|
+
/** Re-resolves everything that names one of `names` (a resource changed);
|
|
35
|
+
* whether anything did. */
|
|
36
|
+
refresh(names: Set<string>): boolean;
|
|
37
|
+
private srcset;
|
|
38
|
+
private remember;
|
|
39
|
+
private track;
|
|
40
|
+
}
|
|
41
|
+
export { INVALID };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type Directive, type Policy } from "./network.ts";
|
|
2
|
+
export declare const INVALID = "about:invalid";
|
|
3
|
+
/** The base URL no document declared (SPEC §7.3): nothing under it loads. */
|
|
4
|
+
export declare const NO_BASE = "https://hotty.invalid/";
|
|
5
|
+
/** What a document's references resolve against (SPEC §7.2, §7.3). */
|
|
6
|
+
export interface UrlContext {
|
|
7
|
+
/** The document's base URL. */
|
|
8
|
+
base: string;
|
|
9
|
+
/** The effective network policy: the host's half and the document's. */
|
|
10
|
+
policy: Policy;
|
|
11
|
+
}
|
|
12
|
+
export declare function isUrlAttr(name: string): boolean;
|
|
13
|
+
export declare class Store {
|
|
14
|
+
private entries;
|
|
15
|
+
private used;
|
|
16
|
+
readonly quota: number;
|
|
17
|
+
/** Called with the names whose URL changed, so surfaces can re-resolve. */
|
|
18
|
+
onChange: (names: Set<string>) => void;
|
|
19
|
+
constructor(quota?: number);
|
|
20
|
+
put(name: string, mime: string, bytes: Uint8Array): void;
|
|
21
|
+
remove(name: string): boolean;
|
|
22
|
+
/** The `blob:` URL for a resource, or `null` if there is none (yet). */
|
|
23
|
+
url(name: string): string | null;
|
|
24
|
+
/** CSS text with every reference resolved; `names` collects the resources.
|
|
25
|
+
* Without a context (a stylesheet resource, shared by surfaces with
|
|
26
|
+
* different policies) nothing from the network is allowed. */
|
|
27
|
+
rewriteCss(css: string, names: Set<string>, ctx?: UrlContext): string;
|
|
28
|
+
/** Resolves one attribute value; `names` collects the resources it names.
|
|
29
|
+
* `d` is the directive that covers the attribute, if it fetches. */
|
|
30
|
+
resolveUrl(value: string, names: Set<string>, ctx?: UrlContext, d?: Directive | null): string;
|
|
31
|
+
private changed;
|
|
32
|
+
}
|