anvilsign in

collin/browser-terminal-extension · b9b5b120

Initial commit: terminal sidebar extension and termbridge daemon

Collin Richards · 2026-08-12 09:23 UTC · b9b5b120efbd61817fb954ea0574156d8e39a302 · browse files

added.gitignore+2 −0
1+node_modules/
2+dist/
addedREADME.md+495 −0
1+# terminal
2+
3+A tmux sidebar for Chrome and Firefox.
4+
5+The extension is called **terminal**; the daemon it talks to is `termbridge`.
6+They are deliberately separate names — the daemon is a browser-neutral CLI with
7+its own config directory, and keeping it stable means renaming the extension
8+never touches `~/.config/termbridge/`, the token, or the certificate.
9+
10+```
11+sidebar (xterm.js) daemon (Rust)
12+ │ │
13+ │ ws://127.0.0.1:7681 │
14+ │ ─── {"type":"auth","token":"…"} ──────▶ │ origin + host + token
15+ │ ◀── {"type":"ok"} ───────────────────── │
16+ │ ─── {"type":"open","cols":80,…} ──────▶ │ spawn pty
17+ │ ═══ binary frames (raw bytes) ════════▶ │ ──▶ tmux new-session -A -s browser
18+ │ ◀══ binary frames (raw bytes) ═════════ │
19+ │ ─── {"type":"resize","cols":…} ───────▶ │ TIOCSWINSZ
20+```
21+
22+The daemon runs a PTY. tmux inside it does all multiplexing and, crucially, all
23+persistence — close the sidebar, restart the browser, reattach and everything is
24+where you left it.
25+
26+## Setup
27+
28+```sh
29+cd daemon && cargo build --release
30+./build.sh # produces dist/chrome and dist/firefox
31+```
32+
33+Load the extension:
34+
35+| | |
36+|---|---|
37+| Chrome | `chrome://extensions` → Developer mode → Load unpacked → `dist/chrome` |
38+| Firefox | `about:debugging#/runtime/this-firefox` → Load Temporary Add-on → `dist/firefox/manifest.json` |
39+
40+Open the sidebar, hit ⚙, and it shows you the command to run. Then:
41+
42+```sh
43+termbridge pair chrome-extension://<the id it showed you>
44+termbridge token # paste this into the sidebar
45+termbridge serve
46+```
47+
48+**Firefox needs one extra step.** HTTPS-Only Mode silently rewrites `ws://` to
49+`wss://`, so the daemon serves TLS and plaintext on the same port, choosing per
50+connection by sniffing the first byte. The certificate is self-signed, so trust
51+it once: open `https://127.0.0.1:7681/` and accept the warning (the sidebar has a
52+button for this). Chrome uses plaintext and skips the step entirely.
53+
54+Verify you're trusting the right certificate — `termbridge cert` prints the
55+SHA-256 the browser will show you.
56+
57+Pairing is per-browser. Firefox's `moz-extension://` origin is a random UUID
58+regenerated on each temporary install, so you'll re-pair each time until the
59+add-on is signed.
60+
61+## Theme
62+
63+The **◐** button in the header cycles *follow system → light → dark*, and the ⚙
64+panel has the same setting. `auto` tracks `prefers-color-scheme` live, so it
65+follows the OS without a reconnect.
66+
67+Both palettes live in `extension/lib/theme.js` as the single source of truth:
68+the `ui` block becomes CSS custom properties on `:root`, the `xterm` block goes
69+to `term.options.theme`. Chrome and terminal cannot drift apart.
70+
71+## Header size
72+
73+⚙ → Appearance has a header density control:
74+
75+| | |
76+|---|---|
77+| Normal | Status text plus icons |
78+| Compact | Icons only, tighter padding — about one extra terminal row |
79+| Hide header | No header; hover the top edge of the panel to bring it back |
80+
81+Changing it refits the terminal and sends the new size to the pty, so tmux
82+reflows immediately.
83+
84+Note this only covers *our* header. The bar above it — extension name, close ✕,
85+panel switcher — is browser chrome. Chrome's side panel and Firefox's sidebar
86+both render it and neither exposes any way for an extension to remove or restyle
87+it.
88+
89+## Choosing a tmux session
90+
91+By default the sidebar joins the tmux session you already have running, when
92+there is exactly one — no point starting a second session beside the only one
93+you are using. With none, or with several, it attaches to a session called
94+`browser` and creates it if needed. The check happens per connection, so it
95+reflects what tmux holds when the sidebar connects, not when the daemon
96+started.
97+
98+To pin a specific session and skip that guessing entirely:
99+
100+```sh
101+termbridge serve --session my-existing-work
102+```
103+
104+Or pick it live: the dropdown at the left of the header lists every session on
105+the server. Choosing one runs `switch-client` on the sidebar's own tmux client
106+— the WebSocket stays up, no second pty is spawned, and whatever is running in
107+the session you left keeps running. The ⚙ panel's **tmux session** field is the
108+way to reach a session that doesn't exist yet: it creates-or-attaches and
109+switches to it.
110+
111+The dropdown shows the session you are *actually* on, not the one you asked
112+for, so a `switch-client`, `choose-tree` or prefix-`(`/`)` typed in the terminal
113+updates it too.
114+
115+Both names are validated server-side — see the security notes below.
116+
117+## Window tabs
118+
119+The rest of the header is a browser-style tab bar, one tab per **window** in the
120+attached session — the same windows `prefix 2` selects and the tmux status line
121+lists. Clicking one runs `select-window`, and **+** runs `new-window`. Neither
122+touches the connection: the pty, the session and everything running in it stay
123+exactly as they were.
124+
125+Selecting a window deliberately moves *every* client watching that session, not
126+just the sidebar — a window belongs to the session, so this behaves the same as
127+pressing prefix-2 in your terminal, and the tab bar tracks what you do there.
128+
129+The active tab is drawn in the terminal's own background so the two read as one
130+surface. The dot in its favicon slot is what Claude Code is doing in that
131+window — amber and pulsing for working, blue for waiting on you — and stays
132+empty for a window that is just a shell, rather than lighting up a status
133+indicator with no status to report. A background window that has produced
134+output since you last looked wears tmux's activity flag as a bolder name.
135+
136+A new window needs no name (tmux names it after what it runs), so **+** is one
137+click with nothing to fill in.
138+
139+The **✕** closes a window (`kill-window`) on the first click, like a browser
140+tab. Unlike a browser tab there is no undo — it kills whatever was running in
141+that window — so it goes red under the pointer, and the log records what went.
142+
143+It only appears on the window you are on and the one you are pointing at, and
144+never on a session's *last* window: that would take the session and the
145+sidebar's own client with it, which is not a tab close.
146+
147+Right-clicking a tab offers **Pin**, **Select** and **Close window**.
148+
149+Pinning works like a browser's: the tab moves to the head of the strip and
150+shrinks to its dot and index, and loses its ✕ so it can't be closed by a
151+mis-aimed click. What it does *not* do is touch tmux. Nothing is renumbered, no
152+`move-window` is sent, and your terminal's status line doesn't change — the
153+window keeps its real index, which is why the index is the thing a pinned tab
154+keeps showing: `prefix 3` still selects it, pinned or not. The gain is purely
155+that a window you care about stays visible when the strip overflows, at about a
156+quarter of the width.
157+
158+Pins live in extension storage, keyed by session name, and are dropped when the
159+window they point at closes. They are per-browser-profile, not shared with
160+anyone else attached to the session.
161+
162+## How the daemon talks to tmux
163+
164+The interactive client in the pty is busy being a terminal, so the daemon
165+attaches a *second* client in [control
166+mode](https://github.com/tmux/tmux/wiki/Control-Mode) to use as a query and
167+event channel:
168+
169+```
170+tmux -C attach -t <session> -f read-only,ignore-size,no-output
171+```
172+
173+Each flag is load-bearing. `read-only` means the channel can never send
174+keystrokes to a pane. `ignore-size` stops an 80x24 control client from shrinking
175+your windows to fit itself. `no-output` stops tmux streaming every byte every
176+pane produces to a client with no use for it.
177+
178+tmux pushes `%client-session-changed`, `%sessions-changed`, `%window-renamed`
179+and friends, so the sidebar updates when something happens rather than on a
180+timer, and no `tmux` process is spawned per refresh.
181+
182+`read-only` governs keys, not commands, so the same channel carries the
183+sidebar's requests. Those are a closed allowlist — switch to a session,
184+create-and-switch, focus a pane, select a window, open a window, close a window
185+— expressed as an enum, not a command string. The wire protocol cannot name a
186+tmux command, and every argument is validated (`valid_session_name`,
187+`valid_pane_id`, `valid_window_id`) before it is quoted into a command line.
188+
189+Exactly one of them destroys anything, `kill-window`, and it can only ever name
190+one window: a window id is `@` plus digits, so `-a` (which would kill every
191+window *but* the target) and `session:` targets do not parse. There is no
192+kill-session and no kill-pane.
193+
194+## Claude Code status
195+
196+The glyph on each window tab is what Claude Code is doing in that window. It is
197+Claude Code's own asterisk spinner, so a window that is thinking looks in the
198+tab strip the way it looks in the pane:
199+
200+| Glyph | Meaning |
201+|---|---|
202+| amber, cycling `· ✢ ✳ ∗ ✻ ✽` | working |
203+| blue `✳` | waiting on you |
204+| grey `✻` | idle at the prompt |
205+| faded `·` | Claude is there, but no hooks are installed for it |
206+| nothing | no Claude in this window |
207+
208+One timer drives the whole strip and only runs while something is working, so
209+an idle panel is not repainting forever. `prefers-reduced-motion` parks the
210+glyph on a single frame rather than dropping the indicator.
211+
212+Joined on the tmux window id, so a window shows the loudest state in it —
213+waiting beats working. The tab's tooltip carries the detail the dot can't: the
214+tool in flight, or what Claude is blocked on.
215+
216+There used to be a second row of per-pane chips under the header saying the
217+same thing at more length. It was costing a terminal line to repeat what the
218+tabs already show, so it's gone; the trade is that a window whose tab is
219+scrolled out of a narrow panel no longer announces itself.
220+
221+This comes from [Claude Code's hook
222+interface](https://code.claude.com/docs/en/hooks), not from reading the screen:
223+Claude runs `termbridge hook` on `UserPromptSubmit`, `PreToolUse`,
224+`PostToolUse`, `Notification`, `Stop`, `SessionStart` and `SessionEnd`, and each
225+event's JSON tells us the state directly. `Notification` even distinguishes
226+`permission_prompt` (blocked on you) from `idle_prompt` (just quiet).
227+
228+Install it once:
229+
230+```sh
231+termbridge hooks # prints the block to merge into ~/.claude/settings.json
232+```
233+
234+Each session's state lands in `~/.config/termbridge/agents/<session_id>.json`.
235+The pane a session belongs to comes from `$TMUX_PANE`, which Claude's process
236+inherits and passes to the hook — that is the join between a Claude session and
237+a tmux pane. `permission_mode` is carried forward across the events that omit it
238+(`Notification` is the one that matters: the mode must not blink out exactly
239+when you are being asked to approve something).
240+
241+### The same glyph in tmux's own status line
242+
243+The sidebar reads its state over the daemon's control channel, which exists
244+only while a browser is attached — exactly the case where you are not looking
245+at the browser. So the terminal gets it from the other end: the hook already
246+runs inside the pane it is reporting on, with `$TMUX` naming the right server,
247+and it sets a window user option on the way out.
248+
249+```sh
250+termbridge tmux # prints the ~/.tmux.conf lines
251+```
252+
253+```tmux
254+set -g window-status-format "#{@tb_claude}#I:#W#F"
255+set -g window-status-current-format "#{@tb_claude}#I:#W#F"
256+```
257+
258+The option holds a styled glyph and a space, and is unset — expanding to
259+nothing — for a window with no Claude in it, so windows that never see one look
260+exactly as they do now. A window with several Claudes in it shows the loudest,
261+the same rule the tabs use: `list-panes` on the hook's own pane is the join.
262+
263+It advances one frame per hook event rather than on a timer. tmux only redraws
264+its status when an option changes or `status-interval` elapses, so a true
265+spinner would mean forcing a full status repaint on every attached client
266+several times a second; stepping on events costs nothing and moves the glyph
267+exactly when Claude crosses a tool boundary.
268+
269+The only command this issues is `set-option -w @tb_claude`. It cannot rename a
270+window, change a layout, or send a key.
271+
272+## Picking elements off the page
273+
274+Two ways to start it, and the difference matters:
275+
276+| | |
277+|---|---|
278+| **Alt+Shift+P** | Always works. Rebindable at `chrome://extensions/shortcuts` or `about:addons` → gear → Manage Extension Shortcuts |
279+| Crosshair button in the header | Convenient, but may fail — see below |
280+
281+Then: hover highlights the element under the cursor with its tag and size, click
282+selects. Clicks are swallowed, so picking a link or a submit button doesn't
283+navigate or submit.
284+
285+To get out: **Esc** (from either the page or the sidebar), or press the
286+crosshair again — it toggles.
287+
288+Picking sends the result straight to the terminal, screenshot first:
289+
290+1. The element's box is cropped out of a screenshot of the tab and put on the
291+ system clipboard as a PNG.
292+2. A literal **^V** goes down the wire. That byte is not a paste in the panel —
293+ xterm.js never sees it — it reaches the program in the pty, and an agent that
294+ handles image paste (Claude Code does) reads the clipboard and attaches the
295+ PNG itself.
296+3. The selector follows, control-character stripped and single-quoted, with no
297+ trailing newline. You press Enter yourself.
298+
299+This works because the daemon runs on the same machine as the browser, so the
300+clipboard the extension writes is the clipboard the agent reads. In a bare shell
301+^V is literal-next-character instead, so it is only sent when a screenshot was
302+actually captured; if the capture fails, only the selector is inserted and the
303+reason is logged.
304+
305+The panel still shows the element afterwards: CSS selector, XPath, `id`, test
306+id, text, or `href`. **copy** puts the selected one on the clipboard, and
307+**insert** re-types it (text only — the screenshot is already on the clipboard
308+if you want it again).
309+
310+**Why the button can fail.** Touching a page needs `activeTab`, which the
311+browser grants only on certain user gestures — a toolbar click, a context menu,
312+or a keyboard command. A click inside the side panel is not one of them, so the
313+button falls back to standing host permissions. Those are:
314+
315+- **localhost is granted up front** — `localhost`, `*.localhost` and `127.0.0.1`
316+ on both schemes. Match patterns carry no port, so `:5173`, `:3000` and
317+ everything else are covered.
318+- **Every other site is opt-in, one origin at a time.** When the button hits a
319+ site it can't reach, it offers an *Allow https://example.com/\** button that
320+ triggers the browser's own permission prompt. Nothing is granted until you say
321+ so.
322+
323+The shortcut needs none of this — it runs in the background worker, which is a
324+gesture the browser does honour, and works on any page.
325+
326+**Why the button can pick but not screenshot.** `tabs.captureVisibleTab` accepts
327+exactly two things: the `activeTab` grant a keyboard command mints, or a host
328+permission set containing the literal `<all_urls>` pattern. A per-origin grant is
329+enough to read the element but not to capture it, so the button hands back a
330+selector and no image even on a site you approved. When that happens the panel
331+offers an *Allow screenshots on all sites* button, which requests `<all_urls>`;
332+it is optional and revocable in the extension's settings, and Alt+Shift+P keeps
333+capturing without it.
334+
335+Picks made while the sidebar is closed are parked in `storage.local` and appear
336+when you next open it.
337+
338+Nothing is inserted automatically — see below for why that matters.
339+
340+## Security model
341+
342+This daemon hands out shell access. It is `sshd` with a smaller feature set, and
343+is treated that way.
344+
345+**Threats it stops**
346+
347+| Attacker | Defense |
348+|---|---|
349+| Any webpage you visit (WebSocket has no CORS — every page can reach 127.0.0.1) | Origin allowlist; `Origin` is browser-set and unforgeable from page JS |
350+| `evil.com` rebound to 127.0.0.1 | `Host` header must be loopback |
351+| Another user on the machine | Token in a `0600` file, `0700` dir; refuses to load if the mode loosens |
352+| The network | Binds `127.0.0.1` only, not configurable |
353+| Token brute-force | Constant-time compare, lockout after repeated failures |
354+| Firefox HTTPS-Only Mode breaking the connection | Serves TLS and plaintext on one port; no browser setting has to be weakened |
355+| A page injecting shell commands via the element picker | Picked text is control-character stripped and single-quoted before it can reach the pty |
356+| A page reading the clipboard after the picker writes a screenshot to it | The write happens in the extension's isolated world; the page could already read its own clipboard, and the image is a picture of the page itself |
357+
358+**Threats it does not stop**
359+
360+A process running as *you* can already read `~/.ssh`, patch `~/.bashrc`, and
361+ptrace your browser. Same-uid isolation is not a thing, and pretending otherwise
362+would be theatre.
363+
364+**Design rules**
365+
366+- Token travels in a post-upgrade frame, never the URL — query strings leak into
367+ logs, crash dumps, and devtools history.
368+- Pairing is explicit. No trust-on-first-use, no blanket `chrome-extension://`
369+ prefix match (that would admit every other extension you have installed).
370+- The extension requests `storage` and `sidePanel`. No host permissions, no
371+ content scripts, no `web_accessible_resources`, no `externally_connectable` —
372+ so there is no bridge from page content to the socket.
373+- The sidebar owns the WebSocket directly. Terminal data never passes through
374+ `runtime.sendMessage`, which content scripts can reach.
375+- The client cannot choose what runs. The protocol has no argv field, so an auth
376+ bypass yields the configured profile rather than arbitrary exec.
377+- TLS changes no policy: origin, host and token are enforced identically over
378+ `wss://`. The private key is `0600` and refused if the mode loosens, and the
379+ certificate is a leaf with `CA:FALSE` — trusting it cannot be leveraged to
380+ vouch for any other host.
381+- **The element picker treats the page as hostile.** A selector is built from
382+ attributes the page chose, so an `id` of `x'; rm -rf ~; '` or an `aria-label`
383+ containing a newline is arbitrary code execution — a newline typed at a
384+ terminal is a pressed Enter. Two independent defenses, both required: strip
385+ every C0 control character, then POSIX single-quote. `insert` never appends a
386+ newline; you press Enter yourself. The UI says so when a value had to be
387+ modified.
388+- The picker's standing page access is limited to loopback hosts, which are your
389+ own machine. Everything else is `optional_host_permissions`, granted per-origin
390+ through the browser's prompt and revocable in the extension's settings — never
391+ a blanket `<all_urls>` at install time. `<all_urls>` is offered as an optional
392+ permission too, because the screenshot API takes nothing narrower, but only
393+ when a capture has already failed and only behind an explicit button. Still no
394+ declared content scripts and no `web_accessible_resources`.
395+- The picker returns its result as the `executeScript` **return value**, not via
396+ `runtime.sendMessage` — so the sidebar still has no inbound message listener
397+ that a content script could reach.
398+- tmux session names are client-selectable but validated: no leading dash (tmux
399+ would read it as a flag), and `[A-Za-z0-9_-]` only. They reach `execvp` as a
400+ separate argv element, never a shell. An invalid name falls back to the
401+ default rather than erroring.
402+- The HTTPS landing page exists only so the certificate-trust visit is
403+ comprehensible. It serves no data and, unlike the implementation we looked at,
404+ there is no endpoint anywhere that hands out the auth token.
405+
406+Every one of those is covered by a test, and the suite is mutation-checked:
407+reverting the origin check to "allow any origin" turns 5 tests red.
408+
409+```sh
410+cd daemon && cargo test # 46
411+npm test # 25
412+npm run check # types, see below
413+```
414+
415+The sanitizer tests don't just assert on strings — they hand the quoted output
416+to a real `/bin/sh` and check it comes back as one literal argument, including
417+for `x'; rm -rf ~; echo '` and `harmless\nid`.
418+
419+The theme tests compute WCAG contrast ratios for all 16 ANSI slots against their
420+own background, plus body text, muted text, buttons and selection. They already
421+earned their keep: `brightBlack` landed at exactly 3.00:1 on the dark background
422+— the slot most tools use for comments — and was corrected.
423+
424+## Types without a build step
425+
426+The extension is plain `.js` that the browser loads exactly as it sits on disk
427+— no bundler, no transpile, `build.sh` is a `cp`. That stays true. What was
428+added is a type *checker* over it: JSDoc annotations plus `npm run check`,
429+which runs `tsc --noEmit` and emits nothing. Editors pick the same config up
430+automatically and give completion on the daemon's frames.
431+
432+```sh
433+npm install # one dev dependency: typescript
434+npm run check
435+```
436+
437+Two projects, because the manifest creates two global scopes and both declare
438+`const api` at the top level:
439+
440+| Config | Realm | Files |
441+|---|---|---|
442+| `extension/jsconfig.json` | sidebar document | `sidebar.js`, `picker.js`, `lib/` |
443+| `extension/jsconfig.sw.json` | service worker | `sw.js`, `picker.js`, `lib/shot.js` |
444+
445+`extension/types/globals.d.ts` is hand-written rather than pulled from
446+`@types/chrome`, so it doubles as the inventory of extension API surface this
447+extension touches — adding a call means adding it there first, which is the
448+same conversation as growing the manifest's permission list. It also carries
449+the daemon's wire frames (`TbOkFrame`, `TbStatusFrame`, `TbSessionInfo`,
450+`TbAgent`); the other half of those shapes is `daemon/src/status.rs`, and the
451+two have to move together.
452+
453+Nothing in `node_modules/` reaches `dist/`.
454+
455+## Known gotchas
456+
457+**tmux resizes for everyone.** Default `window-size` is `latest`, so attaching a
458+narrow sidebar shrinks the same session in your real terminal. Verified: a
459+100x30 window became 40x20 on sidebar attach. Either give the sidebar its own
460+session, or:
461+
462+```sh
463+setw -g window-size manual
464+```
465+
466+**Firefox temporary add-ons** get a new UUID per install, so re-pair after each
467+reload.
468+
469+## Layout
470+
471+```
472+daemon/src/paths.rs token + paired-origin files, permission enforcement
473+daemon/src/auth.rs origin / host / token checks
474+daemon/src/server.rs listener, handshake, session pump
475+daemon/src/pty.rs portable-pty backend
476+daemon/src/tls.rs self-signed cert generation, rustls config
477+daemon/src/rewind.rs replayable stream, so we can inspect the request head
478+daemon/tests/ security.rs (28), tls.rs (10), pty_e2e.rs (8)
479+extension/picker.js injected element picker (no privileges, runs in page)
480+extension/lib/ sanitize.js (page-text-to-shell boundary), theme.js
481+ (light/dark palettes) — both with tests; shot.js
482+ (element screenshot crop, clipboard write)
483+extension/types/ hand-written ambients: extension API surface, the
484+ daemon's wire frames, the vendored xterm build
485+extension/ sidebar, two manifests, vendored xterm.js
486+```
487+
488+## Not done yet
489+
490+- Firefox reaches the daemon (confirmed: HTTPS-Only Mode was rewriting the
491+ scheme, which is why TLS exists). Not yet confirmed end-to-end after trusting
492+ the certificate.
493+- Chrome has not been loaded at all; whether MV3 needs anything in
494+ `host_permissions` or CSP `connect-src` is still unverified.
495+- No packaging: the daemon is started by hand, and there's no systemd unit.
addedTODO.md+1 −0
1+- when opening and disconnected in the browser extension, the extension should display to the user the command they need to run to start the service in their terminal
addedassets/make-icons.py+98 −0
1+#!/usr/bin/env python3
2+"""Generate the extension icons.
3+
4+Design: a dark terminal tile with a green `>_` prompt, and a thin accent bar
5+down the right side standing for the sidebar the terminal lives in. The prompt
6+is the part that has to survive 16px, so at small sizes the accent bar and the
7+corner rounding are simplified rather than shrunk into mush.
8+
9+Colours come from the dark theme in extension/lib/theme.js.
10+
11+ python3 assets/make-icons.py
12+"""
13+
14+from PIL import Image, ImageDraw
15+
16+BG = (20, 22, 26, 255) # --bg #14161a
17+EDGE = (45, 49, 56, 255) # --border #2d3138
18+GREEN = (110, 193, 119, 255) # ok #6ec177
19+FG = (215, 218, 224, 255) # --fg #d7dae0
20+ACCENT = (59, 110, 165, 255) # --accent #3b6ea5
21+
22+# Draw everything on a 128-unit grid at 8x, then downsample.
23+GRID = 128
24+SS = 8
25+
26+SIZES = [16, 32, 48, 96, 128]
27+
28+
29+def draw_icon(size: int) -> Image.Image:
30+ small = size <= 20
31+ w = GRID * SS
32+ img = Image.new("RGBA", (w, w), (0, 0, 0, 0))
33+ d = ImageDraw.Draw(img)
34+
35+ def u(v: float) -> float:
36+ """Grid units -> supersampled pixels."""
37+ return v * SS
38+
39+ # --- tile ---------------------------------------------------------------
40+ # Less rounding at 16px: a large radius eats the glyph's breathing room.
41+ radius = 20 if small else 27
42+ d.rounded_rectangle(
43+ [u(2), u(2), u(GRID - 2), u(GRID - 2)],
44+ radius=u(radius),
45+ fill=BG,
46+ outline=EDGE,
47+ width=int(u(2)),
48+ )
49+
50+ # --- sidebar accent bar -------------------------------------------------
51+ # Dropped at 16px, where it would be a single muddy pixel column.
52+ if not small:
53+ d.rounded_rectangle(
54+ [u(102), u(34), u(109), u(94)],
55+ radius=u(3.5),
56+ fill=ACCENT,
57+ )
58+
59+ # --- prompt: >_ ---------------------------------------------------------
60+ # Shifted right slightly when there's no accent bar, to stay centred.
61+ dx = 6 if small else 0
62+ stroke = int(u(11 if small else 10))
63+
64+ d.line(
65+ [(u(30 + dx), u(44)), (u(53 + dx), u(65)), (u(30 + dx), u(86))],
66+ fill=GREEN,
67+ width=stroke,
68+ joint="curve",
69+ )
70+ # Round the ends by hand; PIL only rounds joints, not caps.
71+ r = stroke / 2
72+ for px, py in [(u(30 + dx), u(44)), (u(30 + dx), u(86))]:
73+ d.ellipse([px - r, py - r, px + r, py + r], fill=GREEN)
74+
75+ # Underscore / cursor.
76+ bar_left, bar_right = u(62 + dx), u(92 + dx if not small else 96)
77+ d.rounded_rectangle(
78+ [bar_left, u(78), bar_right, u(88)],
79+ radius=u(5),
80+ fill=FG,
81+ )
82+
83+ return img.resize((size, size), Image.LANCZOS)
84+
85+
86+def main() -> None:
87+ import pathlib
88+
89+ out = pathlib.Path(__file__).resolve().parent.parent / "extension" / "icons"
90+ out.mkdir(parents=True, exist_ok=True)
91+ for size in SIZES:
92+ path = out / f"icon{size}.png"
93+ draw_icon(size).save(path)
94+ print(f"wrote {path.relative_to(out.parent.parent)}")
95+
96+
97+if __name__ == "__main__":
98+ main()
addedbuild.sh+24 −0
1+#!/usr/bin/env bash
2+# Assemble unpacked extensions for both browsers.
3+set -euo pipefail
4+cd "$(dirname "$0")"
5+
6+for browser in chrome firefox; do
7+ out="dist/$browser"
8+ rm -rf "$out"; mkdir -p "$out"
9+ cp extension/sidebar.html extension/sidebar.js extension/sidebar.css extension/picker.js "$out/"
10+ mkdir -p "$out/lib" && cp extension/lib/sanitize.js extension/lib/theme.js extension/lib/shot.js "$out/lib/"
11+ cp -r extension/vendor extension/icons "$out/"
12+ cp "extension/manifest.$browser.json" "$out/manifest.json"
13+ cp extension/sw.js "$out/" # both browsers now run a background script
14+ echo "built $out"
15+done
16+
17+cat <<'EOF'
18+
19+Load them:
20+ Chrome chrome://extensions -> Developer mode -> Load unpacked -> dist/chrome
21+ Firefox about:debugging#/runtime/this-firefox -> Load Temporary Add-on -> dist/firefox/manifest.json
22+
23+Then open the sidebar and follow the three steps in it.
24+EOF
addeddaemon/.gitignore+1 −0
1+/target
addeddaemon/Cargo.lock+1124 −0
1+# This file is automatically @generated by Cargo.
2+# It is not intended for manual editing.
3+version = 4
4+
5+[[package]]
6+name = "anyhow"
7+version = "1.0.104"
8+source = "registry+https://github.com/rust-lang/crates.io-index"
9+checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
10+
11+[[package]]
12+name = "asn1-rs"
13+version = "0.7.2"
14+source = "registry+https://github.com/rust-lang/crates.io-index"
15+checksum = "b7f43a50ac4fdca5df8e885c21b835997f0a1cdee65494a6847694a98652d9d8"
16+dependencies = [
17+ "asn1-rs-derive",
18+ "asn1-rs-impl",
19+ "displaydoc",
20+ "nom",
21+ "num-traits",
22+ "rusticata-macros",
23+ "thiserror 2.0.19",
24+ "time",
25+]
26+
27+[[package]]
28+name = "asn1-rs-derive"
29+version = "0.6.0"
30+source = "registry+https://github.com/rust-lang/crates.io-index"
31+checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c"
32+dependencies = [
33+ "proc-macro2",
34+ "quote",
35+ "syn 2.0.119",
36+ "synstructure",
37+]
38+
39+[[package]]
40+name = "asn1-rs-impl"
41+version = "0.2.0"
42+source = "registry+https://github.com/rust-lang/crates.io-index"
43+checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7"
44+dependencies = [
45+ "proc-macro2",
46+ "quote",
47+ "syn 2.0.119",
48+]
49+
50+[[package]]
51+name = "autocfg"
52+version = "1.5.1"
53+source = "registry+https://github.com/rust-lang/crates.io-index"
54+checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
55+
56+[[package]]
57+name = "base64"
58+version = "0.22.1"
59+source = "registry+https://github.com/rust-lang/crates.io-index"
60+checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
61+
62+[[package]]
63+name = "bit-vec"
64+version = "0.9.1"
65+source = "registry+https://github.com/rust-lang/crates.io-index"
66+checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51"
67+dependencies = [
68+ "serde",
69+]
70+
71+[[package]]
72+name = "bitflags"
73+version = "1.3.2"
74+source = "registry+https://github.com/rust-lang/crates.io-index"
75+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
76+
77+[[package]]
78+name = "bitflags"
79+version = "2.13.1"
80+source = "registry+https://github.com/rust-lang/crates.io-index"
81+checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
82+
83+[[package]]
84+name = "block-buffer"
85+version = "0.12.1"
86+source = "registry+https://github.com/rust-lang/crates.io-index"
87+checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa"
88+dependencies = [
89+ "hybrid-array",
90+]
91+
92+[[package]]
93+name = "bytes"
94+version = "1.12.1"
95+source = "registry+https://github.com/rust-lang/crates.io-index"
96+checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
97+
98+[[package]]
99+name = "cc"
100+version = "1.4.0"
101+source = "registry+https://github.com/rust-lang/crates.io-index"
102+checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
103+dependencies = [
104+ "find-msvc-tools",
105+ "shlex",
106+]
107+
108+[[package]]
109+name = "cfg-if"
110+version = "1.0.4"
111+source = "registry+https://github.com/rust-lang/crates.io-index"
112+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
113+
114+[[package]]
115+name = "cfg_aliases"
116+version = "0.1.1"
117+source = "registry+https://github.com/rust-lang/crates.io-index"
118+checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
119+
120+[[package]]
121+name = "chacha20"
122+version = "0.10.1"
123+source = "registry+https://github.com/rust-lang/crates.io-index"
124+checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
125+dependencies = [
126+ "cfg-if",
127+ "cpufeatures",
128+ "rand_core",
129+]
130+
131+[[package]]
132+name = "const-oid"
133+version = "0.10.2"
134+source = "registry+https://github.com/rust-lang/crates.io-index"
135+checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
136+
137+[[package]]
138+name = "cpufeatures"
139+version = "0.3.0"
140+source = "registry+https://github.com/rust-lang/crates.io-index"
141+checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
142+dependencies = [
143+ "libc",
144+]
145+
146+[[package]]
147+name = "crypto-common"
148+version = "0.2.2"
149+source = "registry+https://github.com/rust-lang/crates.io-index"
150+checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
151+dependencies = [
152+ "hybrid-array",
153+]
154+
155+[[package]]
156+name = "data-encoding"
157+version = "2.11.0"
158+source = "registry+https://github.com/rust-lang/crates.io-index"
159+checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8"
160+
161+[[package]]
162+name = "der-parser"
163+version = "10.0.0"
164+source = "registry+https://github.com/rust-lang/crates.io-index"
165+checksum = "07da5016415d5a3c4dd39b11ed26f915f52fc4e0dc197d87908bc916e51bc1a6"
166+dependencies = [
167+ "asn1-rs",
168+ "displaydoc",
169+ "nom",
170+ "num-bigint",
171+ "num-traits",
172+ "rusticata-macros",
173+]
174+
175+[[package]]
176+name = "deranged"
177+version = "0.5.8"
178+source = "registry+https://github.com/rust-lang/crates.io-index"
179+checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
180+
181+[[package]]
182+name = "digest"
183+version = "0.11.3"
184+source = "registry+https://github.com/rust-lang/crates.io-index"
185+checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
186+dependencies = [
187+ "block-buffer",
188+ "const-oid",
189+ "crypto-common",
190+]
191+
192+[[package]]
193+name = "displaydoc"
194+version = "0.2.6"
195+source = "registry+https://github.com/rust-lang/crates.io-index"
196+checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f"
197+dependencies = [
198+ "proc-macro2",
199+ "quote",
200+ "syn 2.0.119",
201+]
202+
203+[[package]]
204+name = "downcast-rs"
205+version = "1.2.1"
206+source = "registry+https://github.com/rust-lang/crates.io-index"
207+checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
208+
209+[[package]]
210+name = "errno"
211+version = "0.3.14"
212+source = "registry+https://github.com/rust-lang/crates.io-index"
213+checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
214+dependencies = [
215+ "libc",
216+ "windows-sys 0.61.2",
217+]
218+
219+[[package]]
220+name = "fastrand"
221+version = "2.5.0"
222+source = "registry+https://github.com/rust-lang/crates.io-index"
223+checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
224+
225+[[package]]
226+name = "filedescriptor"
227+version = "0.8.3"
228+source = "registry+https://github.com/rust-lang/crates.io-index"
229+checksum = "e40758ed24c9b2eeb76c35fb0aebc66c626084edd827e07e1552279814c6682d"
230+dependencies = [
231+ "libc",
232+ "thiserror 1.0.69",
233+ "winapi",
234+]
235+
236+[[package]]
237+name = "find-msvc-tools"
238+version = "0.1.9"
239+source = "registry+https://github.com/rust-lang/crates.io-index"
240+checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
241+
242+[[package]]
243+name = "futures-core"
244+version = "0.3.33"
245+source = "registry+https://github.com/rust-lang/crates.io-index"
246+checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"
247+
248+[[package]]
249+name = "futures-sink"
250+version = "0.3.33"
251+source = "registry+https://github.com/rust-lang/crates.io-index"
252+checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307"
253+
254+[[package]]
255+name = "futures-task"
256+version = "0.3.33"
257+source = "registry+https://github.com/rust-lang/crates.io-index"
258+checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109"
259+
260+[[package]]
261+name = "futures-util"
262+version = "0.3.33"
263+source = "registry+https://github.com/rust-lang/crates.io-index"
264+checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa"
265+dependencies = [
266+ "futures-core",
267+ "futures-sink",
268+ "futures-task",
269+ "pin-project-lite",
270+ "slab",
271+]
272+
273+[[package]]
274+name = "getrandom"
275+version = "0.2.17"
276+source = "registry+https://github.com/rust-lang/crates.io-index"
277+checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
278+dependencies = [
279+ "cfg-if",
280+ "libc",
281+ "wasi",
282+]
283+
284+[[package]]
285+name = "getrandom"
286+version = "0.4.3"
287+source = "registry+https://github.com/rust-lang/crates.io-index"
288+checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
289+dependencies = [
290+ "cfg-if",
291+ "libc",
292+ "r-efi",
293+ "rand_core",
294+]
295+
296+[[package]]
297+name = "http"
298+version = "1.4.2"
299+source = "registry+https://github.com/rust-lang/crates.io-index"
300+checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425"
301+dependencies = [
302+ "bytes",
303+ "itoa",
304+]
305+
306+[[package]]
307+name = "httparse"
308+version = "1.10.1"
309+source = "registry+https://github.com/rust-lang/crates.io-index"
310+checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
311+
312+[[package]]
313+name = "hybrid-array"
314+version = "0.4.13"
315+source = "registry+https://github.com/rust-lang/crates.io-index"
316+checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c"
317+dependencies = [
318+ "typenum",
319+]
320+
321+[[package]]
322+name = "itoa"
323+version = "1.0.18"
324+source = "registry+https://github.com/rust-lang/crates.io-index"
325+checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
326+
327+[[package]]
328+name = "lazy_static"
329+version = "1.5.0"
330+source = "registry+https://github.com/rust-lang/crates.io-index"
331+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
332+
333+[[package]]
334+name = "libc"
335+version = "0.2.189"
336+source = "registry+https://github.com/rust-lang/crates.io-index"
337+checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
338+
339+[[package]]
340+name = "linux-raw-sys"
341+version = "0.12.1"
342+source = "registry+https://github.com/rust-lang/crates.io-index"
343+checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
344+
345+[[package]]
346+name = "log"
347+version = "0.4.33"
348+source = "registry+https://github.com/rust-lang/crates.io-index"
349+checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
350+
351+[[package]]
352+name = "memchr"
353+version = "2.8.3"
354+source = "registry+https://github.com/rust-lang/crates.io-index"
355+checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98"
356+
357+[[package]]
358+name = "minimal-lexical"
359+version = "0.2.1"
360+source = "registry+https://github.com/rust-lang/crates.io-index"
361+checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
362+
363+[[package]]
364+name = "mio"
365+version = "1.2.2"
366+source = "registry+https://github.com/rust-lang/crates.io-index"
367+checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427"
368+dependencies = [
369+ "libc",
370+ "wasi",
371+ "windows-sys 0.61.2",
372+]
373+
374+[[package]]
375+name = "nix"
376+version = "0.28.0"
377+source = "registry+https://github.com/rust-lang/crates.io-index"
378+checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
379+dependencies = [
380+ "bitflags 2.13.1",
381+ "cfg-if",
382+ "cfg_aliases",
383+ "libc",
384+]
385+
386+[[package]]
387+name = "nom"
388+version = "7.1.3"
389+source = "registry+https://github.com/rust-lang/crates.io-index"
390+checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
391+dependencies = [
392+ "memchr",
393+ "minimal-lexical",
394+]
395+
396+[[package]]
397+name = "num-bigint"
398+version = "0.4.8"
399+source = "registry+https://github.com/rust-lang/crates.io-index"
400+checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367"
401+dependencies = [
402+ "num-integer",
403+ "num-traits",
404+]
405+
406+[[package]]
407+name = "num-conv"
408+version = "0.2.2"
409+source = "registry+https://github.com/rust-lang/crates.io-index"
410+checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
411+
412+[[package]]
413+name = "num-integer"
414+version = "0.1.46"
415+source = "registry+https://github.com/rust-lang/crates.io-index"
416+checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
417+dependencies = [
418+ "num-traits",
419+]
420+
421+[[package]]
422+name = "num-traits"
423+version = "0.2.19"
424+source = "registry+https://github.com/rust-lang/crates.io-index"
425+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
426+dependencies = [
427+ "autocfg",
428+]
429+
430+[[package]]
431+name = "oid-registry"
432+version = "0.8.1"
433+source = "registry+https://github.com/rust-lang/crates.io-index"
434+checksum = "12f40cff3dde1b6087cc5d5f5d4d65712f34016a03ed60e9c08dcc392736b5b7"
435+dependencies = [
436+ "asn1-rs",
437+]
438+
439+[[package]]
440+name = "once_cell"
441+version = "1.21.4"
442+source = "registry+https://github.com/rust-lang/crates.io-index"
443+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
444+
445+[[package]]
446+name = "pem"
447+version = "3.0.6"
448+source = "registry+https://github.com/rust-lang/crates.io-index"
449+checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be"
450+dependencies = [
451+ "base64",
452+ "serde_core",
453+]
454+
455+[[package]]
456+name = "pin-project-lite"
457+version = "0.2.17"
458+source = "registry+https://github.com/rust-lang/crates.io-index"
459+checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
460+
461+[[package]]
462+name = "portable-pty"
463+version = "0.9.0"
464+source = "registry+https://github.com/rust-lang/crates.io-index"
465+checksum = "b4a596a2b3d2752d94f51fac2d4a96737b8705dddd311a32b9af47211f08671e"
466+dependencies = [
467+ "anyhow",
468+ "bitflags 1.3.2",
469+ "downcast-rs",
470+ "filedescriptor",
471+ "lazy_static",
472+ "libc",
473+ "log",
474+ "nix",
475+ "serial2",
476+ "shared_library",
477+ "shell-words",
478+ "winapi",
479+ "winreg",
480+]
481+
482+[[package]]
483+name = "powerfmt"
484+version = "0.2.0"
485+source = "registry+https://github.com/rust-lang/crates.io-index"
486+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
487+
488+[[package]]
489+name = "proc-macro2"
490+version = "1.0.107"
491+source = "registry+https://github.com/rust-lang/crates.io-index"
492+checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
493+dependencies = [
494+ "unicode-ident",
495+]
496+
497+[[package]]
498+name = "quote"
499+version = "1.0.47"
500+source = "registry+https://github.com/rust-lang/crates.io-index"
501+checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
502+dependencies = [
503+ "proc-macro2",
504+]
505+
506+[[package]]
507+name = "r-efi"
508+version = "6.0.0"
509+source = "registry+https://github.com/rust-lang/crates.io-index"
510+checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
511+
512+[[package]]
513+name = "rand"
514+version = "0.10.2"
515+source = "registry+https://github.com/rust-lang/crates.io-index"
516+checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
517+dependencies = [
518+ "chacha20",
519+ "getrandom 0.4.3",
520+ "rand_core",
521+]
522+
523+[[package]]
524+name = "rand_core"
525+version = "0.10.1"
526+source = "registry+https://github.com/rust-lang/crates.io-index"
527+checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
528+
529+[[package]]
530+name = "rcgen"
531+version = "0.14.8"
532+source = "registry+https://github.com/rust-lang/crates.io-index"
533+checksum = "57f6d249aad744e274e682777a50283a225a32705394ee6d5fcc01efa25e4055"
534+dependencies = [
535+ "pem",
536+ "ring",
537+ "rustls-pki-types",
538+ "time",
539+ "x509-parser",
540+ "yasna",
541+]
542+
543+[[package]]
544+name = "ring"
545+version = "0.17.14"
546+source = "registry+https://github.com/rust-lang/crates.io-index"
547+checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
548+dependencies = [
549+ "cc",
550+ "cfg-if",
551+ "getrandom 0.2.17",
552+ "libc",
553+ "untrusted",
554+ "windows-sys 0.52.0",
555+]
556+
557+[[package]]
558+name = "rusticata-macros"
559+version = "4.1.0"
560+source = "registry+https://github.com/rust-lang/crates.io-index"
561+checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632"
562+dependencies = [
563+ "nom",
564+]
565+
566+[[package]]
567+name = "rustix"
568+version = "1.1.4"
569+source = "registry+https://github.com/rust-lang/crates.io-index"
570+checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
571+dependencies = [
572+ "bitflags 2.13.1",
573+ "errno",
574+ "libc",
575+ "linux-raw-sys",
576+ "windows-sys 0.61.2",
577+]
578+
579+[[package]]
580+name = "rustls"
581+version = "0.23.42"
582+source = "registry+https://github.com/rust-lang/crates.io-index"
583+checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138"
584+dependencies = [
585+ "log",
586+ "once_cell",
587+ "ring",
588+ "rustls-pki-types",
589+ "rustls-webpki",
590+ "subtle",
591+ "zeroize",
592+]
593+
594+[[package]]
595+name = "rustls-pki-types"
596+version = "1.15.1"
597+source = "registry+https://github.com/rust-lang/crates.io-index"
598+checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96"
599+dependencies = [
600+ "zeroize",
601+]
602+
603+[[package]]
604+name = "rustls-webpki"
605+version = "0.103.13"
606+source = "registry+https://github.com/rust-lang/crates.io-index"
607+checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
608+dependencies = [
609+ "ring",
610+ "rustls-pki-types",
611+ "untrusted",
612+]
613+
614+[[package]]
615+name = "serde"
616+version = "1.0.229"
617+source = "registry+https://github.com/rust-lang/crates.io-index"
618+checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
619+dependencies = [
620+ "serde_core",
621+ "serde_derive",
622+]
623+
624+[[package]]
625+name = "serde_core"
626+version = "1.0.229"
627+source = "registry+https://github.com/rust-lang/crates.io-index"
628+checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
629+dependencies = [
630+ "serde_derive",
631+]
632+
633+[[package]]
634+name = "serde_derive"
635+version = "1.0.229"
636+source = "registry+https://github.com/rust-lang/crates.io-index"
637+checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
638+dependencies = [
639+ "proc-macro2",
640+ "quote",
641+ "syn 3.0.3",
642+]
643+
644+[[package]]
645+name = "serde_json"
646+version = "1.0.151"
647+source = "registry+https://github.com/rust-lang/crates.io-index"
648+checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
649+dependencies = [
650+ "itoa",
651+ "memchr",
652+ "serde",
653+ "serde_core",
654+ "zmij",
655+]
656+
657+[[package]]
658+name = "serial2"
659+version = "0.2.37"
660+source = "registry+https://github.com/rust-lang/crates.io-index"
661+checksum = "9eb6ea5562eeaed6936b8b54e086aa0f88b9e5b1bef45beb038e2519fa1185b1"
662+dependencies = [
663+ "cfg-if",
664+ "libc",
665+ "windows-sys 0.61.2",
666+]
667+
668+[[package]]
669+name = "sha1"
670+version = "0.11.0"
671+source = "registry+https://github.com/rust-lang/crates.io-index"
672+checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214"
673+dependencies = [
674+ "cfg-if",
675+ "cpufeatures",
676+ "digest",
677+]
678+
679+[[package]]
680+name = "sha2"
681+version = "0.11.0"
682+source = "registry+https://github.com/rust-lang/crates.io-index"
683+checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4"
684+dependencies = [
685+ "cfg-if",
686+ "cpufeatures",
687+ "digest",
688+]
689+
690+[[package]]
691+name = "shared_library"
692+version = "0.1.9"
693+source = "registry+https://github.com/rust-lang/crates.io-index"
694+checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11"
695+dependencies = [
696+ "lazy_static",
697+ "libc",
698+]
699+
700+[[package]]
701+name = "shell-words"
702+version = "1.1.1"
703+source = "registry+https://github.com/rust-lang/crates.io-index"
704+checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77"
705+
706+[[package]]
707+name = "shlex"
708+version = "2.0.1"
709+source = "registry+https://github.com/rust-lang/crates.io-index"
710+checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
711+
712+[[package]]
713+name = "signal-hook-registry"
714+version = "1.4.8"
715+source = "registry+https://github.com/rust-lang/crates.io-index"
716+checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b"
717+dependencies = [
718+ "errno",
719+ "libc",
720+]
721+
722+[[package]]
723+name = "slab"
724+version = "0.4.12"
725+source = "registry+https://github.com/rust-lang/crates.io-index"
726+checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
727+
728+[[package]]
729+name = "socket2"
730+version = "0.6.5"
731+source = "registry+https://github.com/rust-lang/crates.io-index"
732+checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
733+dependencies = [
734+ "libc",
735+ "windows-sys 0.61.2",
736+]
737+
738+[[package]]
739+name = "subtle"
740+version = "2.6.1"
741+source = "registry+https://github.com/rust-lang/crates.io-index"
742+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
743+
744+[[package]]
745+name = "syn"
746+version = "2.0.119"
747+source = "registry+https://github.com/rust-lang/crates.io-index"
748+checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
749+dependencies = [
750+ "proc-macro2",
751+ "quote",
752+ "unicode-ident",
753+]
754+
755+[[package]]
756+name = "syn"
757+version = "3.0.3"
758+source = "registry+https://github.com/rust-lang/crates.io-index"
759+checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
760+dependencies = [
761+ "proc-macro2",
762+ "quote",
763+ "unicode-ident",
764+]
765+
766+[[package]]
767+name = "synstructure"
768+version = "0.13.2"
769+source = "registry+https://github.com/rust-lang/crates.io-index"
770+checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
771+dependencies = [
772+ "proc-macro2",
773+ "quote",
774+ "syn 2.0.119",
775+]
776+
777+[[package]]
778+name = "tempfile"
779+version = "3.27.0"
780+source = "registry+https://github.com/rust-lang/crates.io-index"
781+checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
782+dependencies = [
783+ "fastrand",
784+ "getrandom 0.4.3",
785+ "once_cell",
786+ "rustix",
787+ "windows-sys 0.61.2",
788+]
789+
790+[[package]]
791+name = "termbridge"
792+version = "0.1.0"
793+dependencies = [
794+ "futures-util",
795+ "getrandom 0.4.3",
796+ "portable-pty",
797+ "rcgen",
798+ "serde",
799+ "serde_json",
800+ "sha2",
801+ "subtle",
802+ "tempfile",
803+ "tokio",
804+ "tokio-rustls",
805+ "tokio-tungstenite",
806+]
807+
808+[[package]]
809+name = "thiserror"
810+version = "1.0.69"
811+source = "registry+https://github.com/rust-lang/crates.io-index"
812+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
813+dependencies = [
814+ "thiserror-impl 1.0.69",
815+]
816+
817+[[package]]
818+name = "thiserror"
819+version = "2.0.19"
820+source = "registry+https://github.com/rust-lang/crates.io-index"
821+checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9"
822+dependencies = [
823+ "thiserror-impl 2.0.19",
824+]
825+
826+[[package]]
827+name = "thiserror-impl"
828+version = "1.0.69"
829+source = "registry+https://github.com/rust-lang/crates.io-index"
830+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
831+dependencies = [
832+ "proc-macro2",
833+ "quote",
834+ "syn 2.0.119",
835+]
836+
837+[[package]]
838+name = "thiserror-impl"
839+version = "2.0.19"
840+source = "registry+https://github.com/rust-lang/crates.io-index"
841+checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd"
842+dependencies = [
843+ "proc-macro2",
844+ "quote",
845+ "syn 3.0.3",
846+]
847+
848+[[package]]
849+name = "time"
850+version = "0.3.54"
851+source = "registry+https://github.com/rust-lang/crates.io-index"
852+checksum = "3e1d5e639ff6bab73cb6885cc7e7b1de96c3f32c68ec55f3952614bec1092244"
853+dependencies = [
854+ "deranged",
855+ "num-conv",
856+ "powerfmt",
857+ "serde_core",
858+ "time-core",
859+ "time-macros",
860+]
861+
862+[[package]]
863+name = "time-core"
864+version = "0.1.9"
865+source = "registry+https://github.com/rust-lang/crates.io-index"
866+checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109"
867+
868+[[package]]
869+name = "time-macros"
870+version = "0.2.32"
871+source = "registry+https://github.com/rust-lang/crates.io-index"
872+checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85"
873+dependencies = [
874+ "num-conv",
875+ "time-core",
876+]
877+
878+[[package]]
879+name = "tokio"
880+version = "1.53.1"
881+source = "registry+https://github.com/rust-lang/crates.io-index"
882+checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed"
883+dependencies = [
884+ "bytes",
885+ "libc",
886+ "mio",
887+ "pin-project-lite",
888+ "signal-hook-registry",
889+ "socket2",
890+ "tokio-macros",
891+ "windows-sys 0.61.2",
892+]
893+
894+[[package]]
895+name = "tokio-macros"
896+version = "2.7.1"
897+source = "registry+https://github.com/rust-lang/crates.io-index"
898+checksum = "6328af13490e73a9b4694030fafd93f8c8c6a9dede33e821c3fc63eddf8042ba"
899+dependencies = [
900+ "proc-macro2",
901+ "quote",
902+ "syn 2.0.119",
903+]
904+
905+[[package]]
906+name = "tokio-rustls"
907+version = "0.26.4"
908+source = "registry+https://github.com/rust-lang/crates.io-index"
909+checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
910+dependencies = [
911+ "rustls",
912+ "tokio",
913+]
914+
915+[[package]]
916+name = "tokio-tungstenite"
917+version = "0.30.0"
918+source = "registry+https://github.com/rust-lang/crates.io-index"
919+checksum = "17a073bfed563fa236697a068031408a93cd9522e08abf9933ead3e73411bd71"
920+dependencies = [
921+ "futures-util",
922+ "log",
923+ "tokio",
924+ "tungstenite",
925+]
926+
927+[[package]]
928+name = "tungstenite"
929+version = "0.30.0"
930+source = "registry+https://github.com/rust-lang/crates.io-index"
931+checksum = "e48ac77174b19c110a50ab2128b24215ac9cb40e0e12e093fb602d175c569d22"
932+dependencies = [
933+ "bytes",
934+ "data-encoding",
935+ "http",
936+ "httparse",
937+ "log",
938+ "rand",
939+ "sha1",
940+ "thiserror 2.0.19",
941+]
942+
943+[[package]]
944+name = "typenum"
945+version = "1.20.1"
946+source = "registry+https://github.com/rust-lang/crates.io-index"
947+checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
948+
949+[[package]]
950+name = "unicode-ident"
951+version = "1.0.24"
952+source = "registry+https://github.com/rust-lang/crates.io-index"
953+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
954+
955+[[package]]
956+name = "untrusted"
957+version = "0.9.0"
958+source = "registry+https://github.com/rust-lang/crates.io-index"
959+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
960+
961+[[package]]
962+name = "wasi"
963+version = "0.11.1+wasi-snapshot-preview1"
964+source = "registry+https://github.com/rust-lang/crates.io-index"
965+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
966+
967+[[package]]
968+name = "winapi"
969+version = "0.3.9"
970+source = "registry+https://github.com/rust-lang/crates.io-index"
971+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
972+dependencies = [
973+ "winapi-i686-pc-windows-gnu",
974+ "winapi-x86_64-pc-windows-gnu",
975+]
976+
977+[[package]]
978+name = "winapi-i686-pc-windows-gnu"
979+version = "0.4.0"
980+source = "registry+https://github.com/rust-lang/crates.io-index"
981+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
982+
983+[[package]]
984+name = "winapi-x86_64-pc-windows-gnu"
985+version = "0.4.0"
986+source = "registry+https://github.com/rust-lang/crates.io-index"
987+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
988+
989+[[package]]
990+name = "windows-link"
991+version = "0.2.1"
992+source = "registry+https://github.com/rust-lang/crates.io-index"
993+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
994+
995+[[package]]
996+name = "windows-sys"
997+version = "0.52.0"
998+source = "registry+https://github.com/rust-lang/crates.io-index"
999+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
1000+dependencies = [
1001+ "windows-targets",
1002+]
1003+
1004+[[package]]
1005+name = "windows-sys"
1006+version = "0.61.2"
1007+source = "registry+https://github.com/rust-lang/crates.io-index"
1008+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
1009+dependencies = [
1010+ "windows-link",
1011+]
1012+
1013+[[package]]
1014+name = "windows-targets"
1015+version = "0.52.6"
1016+source = "registry+https://github.com/rust-lang/crates.io-index"
1017+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
1018+dependencies = [
1019+ "windows_aarch64_gnullvm",
1020+ "windows_aarch64_msvc",
1021+ "windows_i686_gnu",
1022+ "windows_i686_gnullvm",
1023+ "windows_i686_msvc",
1024+ "windows_x86_64_gnu",
1025+ "windows_x86_64_gnullvm",
1026+ "windows_x86_64_msvc",
1027+]
1028+
1029+[[package]]
1030+name = "windows_aarch64_gnullvm"
1031+version = "0.52.6"
1032+source = "registry+https://github.com/rust-lang/crates.io-index"
1033+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
1034+
1035+[[package]]
1036+name = "windows_aarch64_msvc"
1037+version = "0.52.6"
1038+source = "registry+https://github.com/rust-lang/crates.io-index"
1039+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
1040+
1041+[[package]]
1042+name = "windows_i686_gnu"
1043+version = "0.52.6"
1044+source = "registry+https://github.com/rust-lang/crates.io-index"
1045+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
1046+
1047+[[package]]
1048+name = "windows_i686_gnullvm"
1049+version = "0.52.6"
1050+source = "registry+https://github.com/rust-lang/crates.io-index"
1051+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
1052+
1053+[[package]]
1054+name = "windows_i686_msvc"
1055+version = "0.52.6"
1056+source = "registry+https://github.com/rust-lang/crates.io-index"
1057+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
1058+
1059+[[package]]
1060+name = "windows_x86_64_gnu"
1061+version = "0.52.6"
1062+source = "registry+https://github.com/rust-lang/crates.io-index"
1063+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
1064+
1065+[[package]]
1066+name = "windows_x86_64_gnullvm"
1067+version = "0.52.6"
1068+source = "registry+https://github.com/rust-lang/crates.io-index"
1069+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
1070+
1071+[[package]]
1072+name = "windows_x86_64_msvc"
1073+version = "0.52.6"
1074+source = "registry+https://github.com/rust-lang/crates.io-index"
1075+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
1076+
1077+[[package]]
1078+name = "winreg"
1079+version = "0.10.1"
1080+source = "registry+https://github.com/rust-lang/crates.io-index"
1081+checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
1082+dependencies = [
1083+ "winapi",
1084+]
1085+
1086+[[package]]
1087+name = "x509-parser"
1088+version = "0.18.1"
1089+source = "registry+https://github.com/rust-lang/crates.io-index"
1090+checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202"
1091+dependencies = [
1092+ "asn1-rs",
1093+ "data-encoding",
1094+ "der-parser",
1095+ "lazy_static",
1096+ "nom",
1097+ "oid-registry",
1098+ "ring",
1099+ "rusticata-macros",
1100+ "thiserror 2.0.19",
1101+ "time",
1102+]
1103+
1104+[[package]]
1105+name = "yasna"
1106+version = "0.6.0"
1107+source = "registry+https://github.com/rust-lang/crates.io-index"
1108+checksum = "b5f6765e852b9b4dc8e2a76843e4d64d1cea8e79bcde0b6901aea8e7c7f08282"
1109+dependencies = [
1110+ "bit-vec",
1111+ "time",
1112+]
1113+
1114+[[package]]
1115+name = "zeroize"
1116+version = "1.9.0"
1117+source = "registry+https://github.com/rust-lang/crates.io-index"
1118+checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
1119+
1120+[[package]]
1121+name = "zmij"
1122+version = "1.0.23"
1123+source = "registry+https://github.com/rust-lang/crates.io-index"
1124+checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
addeddaemon/Cargo.toml+20 −0
1+[package]
2+name = "termbridge"
3+version = "0.1.0"
4+edition = "2024"
5+
6+[dependencies]
7+futures-util = { version = "0.3.33", default-features = false, features = ["std", "sink"] }
8+getrandom = "0.4.3"
9+portable-pty = "0.9.0"
10+rcgen = { version = "0.14.8", default-features = false, features = ["pem", "ring"] }
11+serde = { version = "1.0.229", features = ["derive"] }
12+serde_json = "1.0.151"
13+sha2 = "0.11.0"
14+subtle = "2.6.1"
15+tokio = { version = "1.53.1", features = ["rt-multi-thread", "macros", "net", "io-util", "time", "sync", "process"] }
16+tokio-rustls = { version = "0.26.4", default-features = false, features = ["ring", "tls12", "logging"] }
17+tokio-tungstenite = "0.30.0"
18+
19+[dev-dependencies]
20+tempfile = "3.27.0"
addeddaemon/src/agents.rs+352 −0
1+//! Claude Code session state, published by Claude Code's own hook interface.
2+//!
3+//! Claude runs `termbridge hook` on the events it documents, each of which
4+//! arrives as JSON on stdin. We keep one record per Claude session under
5+//! `<config>/agents/<session_id>.json` and update it in place. The daemon reads
6+//! those records; nothing here parses a terminal screen, so a UI change in
7+//! Claude cannot silently turn the status wrong.
8+//!
9+//! The link back to tmux is `$TMUX_PANE`, which Claude's process inherits and
10+//! passes on to the hook. It is the pane id (`%3`) and is what lets the sidebar
11+//! say *which* pane is waiting on you.
12+
13+use std::collections::HashMap;
14+use std::fs;
15+use std::io;
16+use std::path::PathBuf;
17+
18+use serde::{Deserialize, Serialize};
19+
20+/// What a Claude session is doing. Derived from the hook event, never guessed.
21+#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
22+#[serde(rename_all = "lowercase")]
23+pub enum State {
24+ /// Mid-turn: thinking, or running a tool.
25+ Working,
26+ /// Blocked on the user — a permission prompt or an elicitation dialog.
27+ Waiting,
28+ /// Turn finished, prompt is yours.
29+ Idle,
30+}
31+
32+impl State {
33+ pub fn as_str(self) -> &'static str {
34+ match self {
35+ State::Working => "working",
36+ State::Waiting => "waiting",
37+ State::Idle => "idle",
38+ }
39+ }
40+}
41+
42+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
43+pub struct Record {
44+ pub session_id: String,
45+ /// tmux pane id (`%3`) from `$TMUX_PANE`. Absent when Claude runs outside
46+ /// tmux, in which case the sidebar has nothing to attach the status to.
47+ #[serde(default, skip_serializing_if = "Option::is_none")]
48+ pub pane: Option<String>,
49+ pub cwd: String,
50+ pub state: State,
51+ /// `permission_mode` as Claude reports it: `default`, `acceptEdits`,
52+ /// `plan`, `bypassPermissions`. Carried forward across events that don't
53+ /// include it (`Notification` is the one that matters — the mode must not
54+ /// blink out exactly when you're being asked to approve something).
55+ #[serde(default, skip_serializing_if = "Option::is_none")]
56+ pub mode: Option<String>,
57+ /// Tool currently running, from `PreToolUse`.
58+ #[serde(default, skip_serializing_if = "Option::is_none")]
59+ pub tool: Option<String>,
60+ /// Why it is waiting, from the `Notification` message.
61+ #[serde(default, skip_serializing_if = "Option::is_none")]
62+ pub message: Option<String>,
63+ /// Session title, when Claude has one.
64+ #[serde(default, skip_serializing_if = "Option::is_none")]
65+ pub name: Option<String>,
66+ /// Which asterisk frame this session's glyph is on. Advanced once per hook
67+ /// event so the tmux status line has something to animate with — see
68+ /// [`crate::window_status`]. The sidebar runs its own timer and ignores it.
69+ #[serde(default)]
70+ pub frame: u8,
71+ /// Unix seconds of the last event. Lets a reader spot a session whose
72+ /// process died without a `SessionEnd`.
73+ pub updated: u64,
74+}
75+
76+pub fn dir() -> PathBuf {
77+ crate::paths::config_dir().join("agents")
78+}
79+
80+/// Every record on disk, newest first. Unreadable or malformed files are
81+/// skipped: a status panel is not worth failing a connection over.
82+pub fn read_all() -> Vec<Record> {
83+ let Ok(entries) = fs::read_dir(dir()) else {
84+ return Vec::new();
85+ };
86+ let mut out: Vec<Record> = entries
87+ .flatten()
88+ .filter(|e| e.path().extension().is_some_and(|x| x == "json"))
89+ .filter_map(|e| fs::read_to_string(e.path()).ok())
90+ .filter_map(|s| serde_json::from_str(&s).ok())
91+ .collect();
92+ out.sort_by_key(|r| std::cmp::Reverse(r.updated));
93+ out
94+}
95+
96+/// Newest record per pane. Panes get reused across sessions, so "newest wins"
97+/// is what keeps a finished session from shadowing the live one.
98+pub fn by_pane() -> HashMap<String, Record> {
99+ let mut map = HashMap::new();
100+ for r in read_all() {
101+ if let Some(pane) = r.pane.clone() {
102+ map.entry(pane).or_insert(r);
103+ }
104+ }
105+ map
106+}
107+
108+fn now() -> u64 {
109+ std::time::SystemTime::now()
110+ .duration_since(std::time::UNIX_EPOCH)
111+ .map(|d| d.as_secs())
112+ .unwrap_or(0)
113+}
114+
115+/// Apply one hook event. Returns the new record, or `None` when the event ends
116+/// the session and the record was removed.
117+///
118+/// Unknown events are ignored rather than rejected — Claude Code gains hook
119+/// events over time and an unrecognised one is not an error.
120+pub fn apply(event: &serde_json::Value) -> io::Result<Option<Record>> {
121+ let Some(session_id) = event.get("session_id").and_then(|v| v.as_str()) else {
122+ return Ok(None);
123+ };
124+ let path = dir().join(format!("{}.json", sanitize_id(session_id)));
125+ let hook = event
126+ .get("hook_event_name")
127+ .and_then(|v| v.as_str())
128+ .unwrap_or("");
129+
130+ if hook == "SessionEnd" {
131+ let _ = fs::remove_file(&path);
132+ return Ok(None);
133+ }
134+
135+ let str_field = |k: &str| event.get(k).and_then(|v| v.as_str()).map(str::to_string);
136+ let notification_type = str_field("notification_type");
137+
138+ let state = match hook {
139+ "UserPromptSubmit" | "PreToolUse" | "PostToolUse" | "PostToolUseFailure" => State::Working,
140+ "Stop" | "SessionStart" | "SubagentStop" => State::Idle,
141+ // Claude fires this both to ask for something and to say it has been
142+ // idle a while; only the first means it is blocked on you.
143+ "Notification" => match notification_type.as_deref() {
144+ Some("idle_prompt") => State::Idle,
145+ _ => State::Waiting,
146+ },
147+ "PermissionRequest" => State::Waiting,
148+ _ => return Ok(None),
149+ };
150+
151+ let previous: Option<Record> = fs::read_to_string(&path)
152+ .ok()
153+ .and_then(|s| serde_json::from_str(&s).ok());
154+ let carry = |field: fn(&Record) -> Option<String>| previous.as_ref().and_then(field);
155+
156+ let record = Record {
157+ session_id: session_id.to_string(),
158+ // TMUX_PANE comes from the environment Claude was launched in, which
159+ // the hook process inherits.
160+ pane: std::env::var("TMUX_PANE")
161+ .ok()
162+ .or_else(|| carry(|r| r.pane.clone())),
163+ cwd: str_field("cwd")
164+ .or_else(|| previous.as_ref().map(|r| r.cwd.clone()))
165+ .unwrap_or_default(),
166+ state,
167+ mode: str_field("permission_mode").or_else(|| carry(|r| r.mode.clone())),
168+ tool: match hook {
169+ "PreToolUse" => str_field("tool_name"),
170+ _ => None,
171+ },
172+ message: match state {
173+ State::Waiting => str_field("message"),
174+ _ => None,
175+ },
176+ name: str_field("session_name").or_else(|| carry(|r| r.name.clone())),
177+ // Wrapping, and the glyph indexes into a six-frame cycle: the absolute
178+ // count is never read, only its position in the loop.
179+ frame: previous.as_ref().map_or(0, |p| p.frame.wrapping_add(1)),
180+ updated: now(),
181+ };
182+
183+ write(&path, &record)?;
184+ Ok(Some(record))
185+}
186+
187+fn write(path: &std::path::Path, record: &Record) -> io::Result<()> {
188+ let dir = path.parent().expect("record path has a parent");
189+ fs::create_dir_all(dir)?;
190+ let json = serde_json::to_string(record).map_err(io::Error::other)?;
191+ // Write-then-rename: the daemon polls this directory and must never read a
192+ // half-written file.
193+ let tmp = path.with_extension("tmp");
194+ fs::write(&tmp, json)?;
195+ fs::rename(&tmp, path)
196+}
197+
198+/// Session ids come from Claude, but they end up in a filename, so treat them
199+/// as untrusted: no separators, no `..`, no surprises.
200+fn sanitize_id(id: &str) -> String {
201+ id.chars()
202+ .filter(|c| c.is_ascii_alphanumeric() || *c == '-' || *c == '_')
203+ .take(128)
204+ .collect()
205+}
206+
207+/// The settings block a user pastes into `~/.claude/settings.json`.
208+pub fn hook_settings(exe: &str) -> String {
209+ let events = [
210+ ("SessionStart", ""),
211+ ("UserPromptSubmit", ""),
212+ ("PreToolUse", "*"),
213+ ("PostToolUse", "*"),
214+ ("Notification", ""),
215+ ("Stop", ""),
216+ ("SessionEnd", ""),
217+ ];
218+ let blocks: Vec<String> = events
219+ .iter()
220+ .map(|(event, matcher)| {
221+ let matcher = if matcher.is_empty() {
222+ String::new()
223+ } else {
224+ format!("\"matcher\": \"{matcher}\", ")
225+ };
226+ format!(
227+ " \"{event}\": [\n {{ {matcher}\"hooks\": [{{ \"type\": \"command\", \"command\": \"{exe} hook\" }}] }}\n ]"
228+ )
229+ })
230+ .collect();
231+ format!("{{\n \"hooks\": {{\n{}\n }}\n}}", blocks.join(",\n"))
232+}
233+
234+#[cfg(test)]
235+mod tests {
236+ use super::*;
237+
238+ /// Each test gets its own config dir; `apply` writes to a real path. The
239+ /// lock is load-bearing: the dir is selected by an env var, which is
240+ /// process-wide, and cargo runs these tests on threads.
241+ static ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
242+
243+ fn with_temp_dir(f: impl FnOnce()) {
244+ let _guard = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
245+ let tmp = tempfile::tempdir().unwrap();
246+ // SAFETY: single-threaded test, and the var is read through
247+ // paths::config_dir() only.
248+ unsafe { std::env::set_var("TERMBRIDGE_CONFIG_DIR", tmp.path()) };
249+ f();
250+ unsafe { std::env::remove_var("TERMBRIDGE_CONFIG_DIR") };
251+ }
252+
253+ fn event(json: serde_json::Value) -> Option<Record> {
254+ apply(&json).unwrap()
255+ }
256+
257+ #[test]
258+ fn tracks_the_documented_lifecycle() {
259+ with_temp_dir(|| {
260+ let r = event(serde_json::json!({
261+ "session_id": "abc", "hook_event_name": "UserPromptSubmit",
262+ "cwd": "/tmp/p", "permission_mode": "acceptEdits",
263+ }))
264+ .unwrap();
265+ assert_eq!(r.state, State::Working);
266+ assert_eq!(r.mode.as_deref(), Some("acceptEdits"));
267+
268+ let r = event(serde_json::json!({
269+ "session_id": "abc", "hook_event_name": "PreToolUse",
270+ "cwd": "/tmp/p", "permission_mode": "acceptEdits", "tool_name": "Bash",
271+ }))
272+ .unwrap();
273+ assert_eq!(r.tool.as_deref(), Some("Bash"));
274+
275+ let r = event(serde_json::json!({
276+ "session_id": "abc", "hook_event_name": "Stop", "cwd": "/tmp/p",
277+ }))
278+ .unwrap();
279+ assert_eq!(r.state, State::Idle);
280+ assert_eq!(r.tool, None);
281+ });
282+ }
283+
284+ #[test]
285+ fn notification_distinguishes_asking_from_idling() {
286+ with_temp_dir(|| {
287+ let asking = event(serde_json::json!({
288+ "session_id": "a", "hook_event_name": "Notification",
289+ "notification_type": "permission_prompt", "message": "Bash needs approval",
290+ }))
291+ .unwrap();
292+ assert_eq!(asking.state, State::Waiting);
293+ assert_eq!(asking.message.as_deref(), Some("Bash needs approval"));
294+
295+ let idling = event(serde_json::json!({
296+ "session_id": "b", "hook_event_name": "Notification",
297+ "notification_type": "idle_prompt",
298+ }))
299+ .unwrap();
300+ assert_eq!(idling.state, State::Idle);
301+ });
302+ }
303+
304+ /// Notification carries no `permission_mode`, and that is exactly when the
305+ /// user wants to see it.
306+ #[test]
307+ fn mode_survives_events_that_omit_it() {
308+ with_temp_dir(|| {
309+ event(serde_json::json!({
310+ "session_id": "a", "hook_event_name": "UserPromptSubmit",
311+ "permission_mode": "plan", "cwd": "/tmp/p",
312+ }));
313+ let r = event(serde_json::json!({
314+ "session_id": "a", "hook_event_name": "Notification",
315+ "notification_type": "permission_prompt",
316+ }))
317+ .unwrap();
318+ assert_eq!(r.mode.as_deref(), Some("plan"));
319+ assert_eq!(r.cwd, "/tmp/p");
320+ });
321+ }
322+
323+ #[test]
324+ fn session_end_removes_the_record() {
325+ with_temp_dir(|| {
326+ event(serde_json::json!({
327+ "session_id": "a", "hook_event_name": "SessionStart", "cwd": "/tmp/p",
328+ }));
329+ assert_eq!(read_all().len(), 1);
330+ assert!(event(serde_json::json!({
331+ "session_id": "a", "hook_event_name": "SessionEnd", "reason": "clear",
332+ }))
333+ .is_none());
334+ assert!(read_all().is_empty());
335+ });
336+ }
337+
338+ #[test]
339+ fn unknown_events_and_ids_are_ignored() {
340+ with_temp_dir(|| {
341+ assert!(event(serde_json::json!({"hook_event_name": "Stop"})).is_none());
342+ assert!(event(serde_json::json!({"session_id": "a", "hook_event_name": "Wat"})).is_none());
343+ assert!(read_all().is_empty());
344+ });
345+ }
346+
347+ #[test]
348+ fn session_ids_cannot_escape_the_directory() {
349+ assert_eq!(sanitize_id("../../etc/passwd"), "etcpasswd");
350+ assert_eq!(sanitize_id("abc-123_XY"), "abc-123_XY");
351+ }
352+}
addeddaemon/src/auth.rs+151 −0
1+//! Handshake authorization: Origin allowlist, Host validation, token compare.
2+//!
3+//! Why both an origin check and a token:
4+//!
5+//! - `Origin` is set by the browser and cannot be forged from page JS, so it is
6+//! a real defense against a hostile *web page* (WebSocket has no CORS — any
7+//! page can open a socket to 127.0.0.1). It is useless against a local
8+//! process, which can set any header it likes.
9+//! - The token defends against local processes belonging to *other* uids, via
10+//! the 0600 file mode. It does nothing against a hostile page, which would
11+//! happily send a token it stole.
12+//!
13+//! Neither is sufficient alone. Both are required.
14+
15+use subtle::ConstantTimeEq;
16+
17+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18+pub enum Denied {
19+ /// No Origin header at all (a non-browser client, or a browser context we
20+ /// don't understand). Rejected: pairing is keyed on origin.
21+ MissingOrigin,
22+ /// Origin is well-formed but the user has not paired it.
23+ UnpairedOrigin,
24+ /// Host header points somewhere other than loopback — the signature of a
25+ /// DNS-rebinding attempt, where `evil.com` resolves to 127.0.0.1.
26+ ForeignHost,
27+ /// Wrong token in the auth frame.
28+ BadToken,
29+ /// No auth frame arrived inside the deadline.
30+ AuthTimeout,
31+ /// Auth frame was not the expected shape.
32+ MalformedAuth,
33+ /// Too many recent failures.
34+ RateLimited,
35+ /// The request never was a WebSocket upgrade — a plain HTTP request, a TLS
36+ /// ClientHello, or a proxy in the way. Nothing to do with auth.
37+ NotAWebSocketUpgrade,
38+ /// The client opened with a TLS ClientHello but we have no TLS identity.
39+ TlsAttempted,
40+ /// TLS negotiation failed — usually the browser refusing our self-signed
41+ /// certificate because no exception has been added yet.
42+ TlsHandshakeFailed,
43+}
44+
45+impl Denied {
46+ /// HTTP status used when we can still reject during the handshake.
47+ pub fn status(self) -> u16 {
48+ match self {
49+ Denied::RateLimited => 429,
50+ Denied::NotAWebSocketUpgrade => 400,
51+ Denied::TlsAttempted => 400,
52+ Denied::TlsHandshakeFailed => 400,
53+ _ => 403,
54+ }
55+ }
56+
57+ pub fn reason(self) -> &'static str {
58+ match self {
59+ Denied::MissingOrigin => "missing Origin header",
60+ Denied::UnpairedOrigin => "origin not paired",
61+ Denied::ForeignHost => "Host header is not loopback",
62+ Denied::BadToken => "invalid token",
63+ Denied::AuthTimeout => "auth timeout",
64+ Denied::MalformedAuth => "malformed auth frame",
65+ Denied::RateLimited => "too many failed attempts",
66+ Denied::NotAWebSocketUpgrade => "not a WebSocket upgrade request",
67+ Denied::TlsAttempted => "client spoke TLS but no certificate is configured",
68+ Denied::TlsHandshakeFailed => "TLS handshake failed (certificate not trusted yet?)",
69+ }
70+ }
71+}
72+
73+/// Hostnames we accept in the `Host` header. Anything else means the client
74+/// reached us under a name that isn't loopback.
75+const LOOPBACK_HOSTS: &[&str] = &["127.0.0.1", "localhost", "[::1]", "::1"];
76+
77+pub fn host_is_loopback(host_header: &str) -> bool {
78+ let h = host_header.trim().to_ascii_lowercase();
79+ // Strip the port. Bracketed IPv6 literals need care: "[::1]:7681".
80+ let hostname = if let Some(rest) = h.strip_prefix('[') {
81+ match rest.split_once(']') {
82+ Some((inner, _)) => format!("[{inner}]"),
83+ None => return false,
84+ }
85+ } else {
86+ h.split(':').next().unwrap_or("").to_string()
87+ };
88+ LOOPBACK_HOSTS.contains(&hostname.as_str())
89+}
90+
91+pub fn origin_is_paired(origin: &str, paired: &[String]) -> bool {
92+ let o = origin.trim().to_ascii_lowercase();
93+ if o.is_empty() || o == "null" {
94+ return false;
95+ }
96+ paired.iter().any(|p| p == &o)
97+}
98+
99+/// Constant-time token comparison. A byte-by-byte `==` leaks the length of the
100+/// matching prefix through timing, which is enough to recover a token given
101+/// enough attempts from a local process.
102+pub fn token_matches(presented: &str, expected: &str) -> bool {
103+ let a = presented.trim().as_bytes();
104+ let b = expected.trim().as_bytes();
105+ if a.len() != b.len() {
106+ // Length is not secret-dependent in a useful way here (the token is a
107+ // fixed 64 hex chars), but still burn a comparison so the fast path
108+ // isn't obviously distinguishable.
109+ let _ = b.ct_eq(b);
110+ return false;
111+ }
112+ a.ct_eq(b).into()
113+}
114+
115+/// Validate the parts of the request we can see before the upgrade completes.
116+pub fn check_handshake(
117+ origin: Option<&str>,
118+ host: Option<&str>,
119+ paired: &[String],
120+) -> Result<(), Denied> {
121+ // Host first: a rebinding attempt should not even reach origin logic.
122+ match host {
123+ Some(h) if host_is_loopback(h) => {}
124+ _ => return Err(Denied::ForeignHost),
125+ }
126+ match origin {
127+ None => Err(Denied::MissingOrigin),
128+ Some(o) if origin_is_paired(o, paired) => Ok(()),
129+ Some(_) => Err(Denied::UnpairedOrigin),
130+ }
131+}
132+
133+/// Parse the first frame, which must be `{"type":"auth","token":"..."}`.
134+///
135+/// Note there is deliberately no way to authenticate via the URL query string.
136+/// Query strings leak into logs, crash dumps and devtools history, and the
137+/// browser WebSocket API makes it tempting to put the token there.
138+pub fn check_auth_frame(text: &str, expected_token: &str) -> Result<(), Denied> {
139+ let v: serde_json::Value = serde_json::from_str(text).map_err(|_| Denied::MalformedAuth)?;
140+ if v.get("type").and_then(|t| t.as_str()) != Some("auth") {
141+ return Err(Denied::MalformedAuth);
142+ }
143+ let Some(tok) = v.get("token").and_then(|t| t.as_str()) else {
144+ return Err(Denied::MalformedAuth);
145+ };
146+ if token_matches(tok, expected_token) {
147+ Ok(())
148+ } else {
149+ Err(Denied::BadToken)
150+ }
151+}
addeddaemon/src/control.rs+219 −0
1+//! A second tmux client, in control mode, used as a query and event channel.
2+//!
3+//! The interactive client in the pty cannot answer questions — it is busy being
4+//! a terminal. Control mode gives us a client that speaks a line protocol
5+//! instead of drawing: we write commands, tmux answers in `%begin`/`%end`
6+//! blocks, and pushes `%`-prefixed notifications whenever the server changes.
7+//! That replaces both halves of the obvious alternative: no `tmux` process
8+//! spawned per poll, and no polling at all for things tmux will tell us about.
9+//!
10+//! The client attaches with `-f read-only,ignore-size,no-output`:
11+//!
12+//! - `read-only` — it can never send keystrokes to a pane.
13+//! - `ignore-size` — an 80x24 control client would otherwise shrink the
14+//! session's windows to fit itself, which the user would see immediately.
15+//! - `no-output` — without it tmux streams every byte every pane produces, to a
16+//! client that has no use for any of it.
17+//!
18+//! `read-only` governs *keys*, not commands, so commands issued here still take
19+//! effect. Nothing in this module decides to run one: the caller passes a fully
20+//! built line, and the only lines built from client input are the allowlisted
21+//! ones in `server.rs`.
22+
23+use std::collections::VecDeque;
24+use std::process::Stdio;
25+use std::sync::Arc;
26+
27+use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
28+use tokio::sync::{mpsc, oneshot, Mutex};
29+
30+/// A `%`-prefixed line tmux sent us unprompted, verbatim.
31+pub type Notification = String;
32+
33+type Reply = oneshot::Sender<Result<Vec<String>, String>>;
34+
35+pub struct Control {
36+ jobs: mpsc::Sender<(String, Reply)>,
37+ /// Held, not used: the client is spawned with `kill_on_drop`, so the handle
38+ /// living exactly as long as this struct is what ties the extra tmux client
39+ /// to the connection that wanted it.
40+ _child: tokio::process::Child,
41+}
42+
43+impl Control {
44+ /// Attach to `session`. The session must already exist — the caller spawns
45+ /// the interactive client first, which creates it.
46+ ///
47+ /// `global_args` must be the profile's server-selection flags, or this
48+ /// attaches to a different tmux server than the terminal is showing.
49+ pub async fn attach(
50+ session: &str,
51+ global_args: &[String],
52+ ) -> std::io::Result<(Control, mpsc::Receiver<Notification>)> {
53+ let mut child = tokio::process::Command::new("tmux")
54+ .args(global_args)
55+ .args([
56+ "-C",
57+ "attach",
58+ "-t",
59+ session,
60+ "-f",
61+ "read-only,ignore-size,no-output",
62+ ])
63+ .stdin(Stdio::piped())
64+ .stdout(Stdio::piped())
65+ .stderr(Stdio::null())
66+ .kill_on_drop(true)
67+ .spawn()?;
68+
69+ let mut stdin = child.stdin.take().expect("stdin piped");
70+ let stdout = child.stdout.take().expect("stdout piped");
71+
72+ let pending: Arc<Mutex<VecDeque<Reply>>> = Arc::new(Mutex::new(VecDeque::new()));
73+ let (notify_tx, notify_rx) = mpsc::channel::<Notification>(64);
74+ let (jobs_tx, mut jobs_rx) = mpsc::channel::<(String, Reply)>(16);
75+
76+ // Writer. Queues the reply slot *before* writing, so a fast answer can
77+ // never arrive with the queue still empty.
78+ let queue = Arc::clone(&pending);
79+ tokio::spawn(async move {
80+ while let Some((line, reply)) = jobs_rx.recv().await {
81+ queue.lock().await.push_back(reply);
82+ if stdin.write_all(format!("{line}\n").as_bytes()).await.is_err()
83+ || stdin.flush().await.is_err()
84+ {
85+ break;
86+ }
87+ }
88+ // Dropping stdin makes tmux print %exit and the child exit, which
89+ // is how the control client goes away when the sidebar does.
90+ });
91+
92+ let mut lines = BufReader::new(stdout).lines();
93+
94+ // tmux answers the attach itself with an empty block before we have
95+ // asked anything. Swallowing it here, while nothing is in flight, is
96+ // what keeps replies lined up with requests: leave it for the reader
97+ // loop and it consumes the first real request's slot, silently shifting
98+ // every answer by one. The timeout is for a future tmux that stops
99+ // sending it, so we hang for half a second rather than forever.
100+ let _ = tokio::time::timeout(std::time::Duration::from_millis(500), async {
101+ while let Ok(Some(line)) = lines.next_line().await {
102+ if line.starts_with("%end") || line.starts_with("%error") {
103+ break;
104+ }
105+ }
106+ })
107+ .await;
108+
109+ // Reader.
110+ let queue = Arc::clone(&pending);
111+ tokio::spawn(async move {
112+ let mut block: Option<Vec<String>> = None;
113+ while let Ok(Some(line)) = lines.next_line().await {
114+ if line.starts_with("%begin") {
115+ block = Some(Vec::new());
116+ } else if line.starts_with("%end") || line.starts_with("%error") {
117+ let body = block.take().unwrap_or_default();
118+ let result = if line.starts_with("%error") {
119+ Err(body.join("\n"))
120+ } else {
121+ Ok(body)
122+ };
123+ // tmux answers the attach itself with an empty block before
124+ // we have asked anything; an unmatched reply is normal and
125+ // is dropped rather than desynchronising the queue.
126+ if let Some(reply) = queue.lock().await.pop_front() {
127+ let _ = reply.send(result);
128+ }
129+ } else if let Some(body) = block.as_mut() {
130+ body.push(line);
131+ } else if line.starts_with('%') && notify_tx.send(line).await.is_err() {
132+ break;
133+ }
134+ }
135+ // tmux is gone: fail every waiter rather than leaving them hanging.
136+ for reply in queue.lock().await.drain(..) {
137+ let _ = reply.send(Err("control client exited".into()));
138+ }
139+ });
140+
141+ let control = Control {
142+ jobs: jobs_tx,
143+ _child: child,
144+ };
145+ Ok((control, notify_rx))
146+ }
147+
148+ /// Run one tmux command line and return its output lines.
149+ ///
150+ /// `line` must be a complete, already-quoted tmux command. Anything derived
151+ /// from client input has to be validated by the caller first — this is a
152+ /// command channel to a live tmux server, not a sandbox.
153+ pub async fn run(&self, line: impl Into<String>) -> Result<Vec<String>, String> {
154+ let (tx, rx) = oneshot::channel();
155+ self.jobs
156+ .send((line.into(), tx))
157+ .await
158+ .map_err(|_| "control client is gone".to_string())?;
159+ rx.await.unwrap_or_else(|_| Err("no reply".into()))
160+ }
161+}
162+
163+/// Notifications that mean our view of the server is stale. Everything else
164+/// (`%output`, `%layout-change`, pane modes) changes nothing we display.
165+pub fn is_interesting(notification: &str) -> bool {
166+ const PREFIXES: [&str; 8] = [
167+ "%sessions-changed",
168+ "%session-changed",
169+ "%session-renamed",
170+ "%session-window-changed",
171+ "%client-session-changed",
172+ "%window-add",
173+ "%window-close",
174+ "%window-renamed",
175+ ];
176+ PREFIXES.iter().any(|p| notification.starts_with(p))
177+}
178+
179+#[cfg(test)]
180+mod tests {
181+ use super::*;
182+
183+ #[test]
184+ fn only_server_shape_changes_are_interesting() {
185+ assert!(is_interesting("%client-session-changed /dev/pts/3 $1 work"));
186+ assert!(is_interesting("%window-renamed @4 editor"));
187+ assert!(!is_interesting("%output %2 hello"));
188+ assert!(!is_interesting("%layout-change @1 abcd,80x24,0,0,1"));
189+ }
190+
191+ /// Needs a tmux on PATH; skipped rather than failed where there isn't one,
192+ /// so the suite still runs in a bare container.
193+ #[tokio::test]
194+ async fn round_trips_a_command_against_real_tmux() {
195+ if !crate::pty::Profile::tmux_available() {
196+ return;
197+ }
198+ let session = "termbridge-control-test";
199+ let _ = std::process::Command::new("tmux")
200+ .args(["new-session", "-d", "-s", session])
201+ .status();
202+
203+ let Ok((control, _notifications)) = Control::attach(session, &[]).await else {
204+ return;
205+ };
206+ let out = control
207+ .run("list-sessions -F '#{session_name}'")
208+ .await
209+ .expect("list-sessions succeeds");
210+ assert!(out.iter().any(|l| l == session));
211+
212+ let err = control.run("definitely-not-a-command").await;
213+ assert!(err.is_err(), "tmux errors come back as Err, not output");
214+
215+ let _ = std::process::Command::new("tmux")
216+ .args(["kill-session", "-t", session])
217+ .status();
218+ }
219+}
addeddaemon/src/lib.rs+12 −0
1+pub mod agents;
2+pub mod auth;
3+pub mod control;
4+pub mod paths;
5+pub mod pty;
6+pub mod rewind;
7+pub mod status;
8+pub mod tls;
9+pub mod window_status;
10+pub mod server;
11+
12+pub use server::{Config, Event, Server};
addeddaemon/src/main.rs+250 −0
1+use std::time::Duration;
2+
3+use termbridge::paths;
4+use termbridge::pty::Profile;
5+use termbridge::{Config, Event, Server};
6+
7+const DEFAULT_PORT: u16 = 7681;
8+
9+fn usage() -> ! {
10+ eprintln!(
11+ "termbridge — loopback WebSocket bridge (security spike)
12+
13+USAGE:
14+ termbridge serve [--port N] [--session NAME]
15+ start the daemon
16+ termbridge token print the auth token (paste into extension)
17+ termbridge token --rotate generate a new token
18+ termbridge pair <origin> approve an extension origin
19+ termbridge unpair <origin> revoke an origin
20+ termbridge list show paired origins
21+ termbridge cert show the TLS certificate path + fingerprint
22+ termbridge cert --regenerate make a new certificate
23+ termbridge hooks print the Claude Code hook settings to install
24+ termbridge hook record one hook event (reads JSON on stdin)
25+ termbridge tmux print the tmux.conf line for the status glyph
26+
27+Origins look like:
28+ chrome-extension://abcdefghijklmnopabcdefghijklmnop
29+ moz-extension://11111111-2222-3333-4444-555555555555
30+"
31+ );
32+ std::process::exit(2)
33+}
34+
35+#[tokio::main]
36+async fn main() -> Result<(), Box<dyn std::error::Error>> {
37+ let args: Vec<String> = std::env::args().skip(1).collect();
38+ let dir = paths::config_dir();
39+
40+ match args.first().map(String::as_str) {
41+ Some("serve") => {
42+ let port = match args.iter().position(|a| a == "--port") {
43+ Some(i) => args
44+ .get(i + 1)
45+ .and_then(|p| p.parse().ok())
46+ .unwrap_or(DEFAULT_PORT),
47+ None => DEFAULT_PORT,
48+ };
49+ let session = args
50+ .iter()
51+ .position(|a| a == "--session")
52+ .and_then(|i| args.get(i + 1))
53+ .cloned();
54+ serve(port, session).await
55+ }
56+ Some("token") => {
57+ let token = if args.iter().any(|a| a == "--rotate") {
58+ paths::generate_token(&dir)?
59+ } else {
60+ paths::load_or_create_token(&dir)?
61+ };
62+ println!("{token}");
63+ println!("\n(from {})", paths::token_path().display());
64+ Ok(())
65+ }
66+ Some("pair") => {
67+ let origin = args.get(1).unwrap_or_else(|| usage());
68+ if paths::pair_origin(&dir, origin)? {
69+ println!("paired: {origin}");
70+ } else {
71+ println!("already paired: {origin}");
72+ }
73+ Ok(())
74+ }
75+ Some("unpair") => {
76+ let origin = args.get(1).unwrap_or_else(|| usage());
77+ if paths::unpair_origin(&dir, origin)? {
78+ println!("unpaired: {origin}");
79+ } else {
80+ println!("not paired: {origin}");
81+ }
82+ Ok(())
83+ }
84+ Some("cert") => {
85+ let id = if args.iter().any(|a| a == "--regenerate") {
86+ termbridge::tls::generate(&dir)?
87+ } else {
88+ termbridge::tls::load_or_create(&dir)?
89+ };
90+ println!("certificate: {}", dir.join(termbridge::tls::CERT_FILE).display());
91+ println!("private key: {}", dir.join(termbridge::tls::KEY_FILE).display());
92+ println!("SHA-256: {}", id.fingerprint);
93+ Ok(())
94+ }
95+ // Runs on every hook event, so it must be quiet and must never fail in
96+ // a way Claude would surface. Any error here is a status panel that is
97+ // briefly stale, which is not worth interrupting the user's session.
98+ Some("hook") => {
99+ let mut input = String::new();
100+ use std::io::Read;
101+ let _ = std::io::stdin().read_to_string(&mut input);
102+ if let Ok(event) = serde_json::from_str::<serde_json::Value>(&input) {
103+ let _ = termbridge::agents::apply(&event);
104+ // After the record is on disk, so the status line is computed
105+ // from the same state the sidebar will read. Runs even when
106+ // the event removed the record: that is what clears the glyph
107+ // from a window whose Claude has gone.
108+ termbridge::window_status::publish();
109+ }
110+ Ok(())
111+ }
112+ Some("hooks") => {
113+ let exe = std::env::current_exe()
114+ .map(|p| p.display().to_string())
115+ .unwrap_or_else(|_| "termbridge".into());
116+ println!(
117+ "Add this to ~/.claude/settings.json (merge with any hooks you already have):\n"
118+ );
119+ println!("{}", termbridge::agents::hook_settings(&exe));
120+ println!(
121+ "\nRecords are written to {}\n\
122+ Claude sessions then show up in the sidebar with their state and permission mode.",
123+ termbridge::agents::dir().display()
124+ );
125+ Ok(())
126+ }
127+ Some("tmux") => {
128+ println!(
129+ "Add this to ~/.tmux.conf, then `tmux source-file ~/.tmux.conf`:\n"
130+ );
131+ print!("{}", termbridge::window_status::tmux_conf());
132+ println!(
133+ "\nThe hooks are what set it, so install those first if you haven't:\n\
134+ \x20 termbridge hooks"
135+ );
136+ Ok(())
137+ }
138+ Some("list") => {
139+ let origins = paths::load_paired_origins(&dir);
140+ if origins.is_empty() {
141+ println!(
142+ "no paired origins ({})",
143+ paths::paired_origins_path().display()
144+ );
145+ } else {
146+ for o in origins {
147+ println!("{o}");
148+ }
149+ }
150+ Ok(())
151+ }
152+ _ => usage(),
153+ }
154+}
155+
156+async fn serve(port: u16, session: Option<String>) -> Result<(), Box<dyn std::error::Error>> {
157+ let dir = paths::config_dir();
158+ let token = paths::load_or_create_token(&dir)?;
159+ let paired = paths::load_paired_origins(&dir);
160+
161+ let identity = termbridge::tls::load_or_create(&dir)?;
162+ let mut config = Config::new(token, paired.clone());
163+ config.auth_timeout = Duration::from_secs(3);
164+ config.tls = Some(termbridge::tls::acceptor(&identity)?);
165+ let pinned = session
166+ .as_deref()
167+ .and_then(termbridge::pty::valid_session_name);
168+ let default_session = pinned
169+ .clone()
170+ .unwrap_or_else(|| termbridge::pty::DEFAULT_SESSION.to_string());
171+ config.default_session = default_session.clone();
172+ // A pinned --session is a choice; without one, a lone existing session is
173+ // a better guess than a fresh "browser" beside it.
174+ config.adopt_sole_session = pinned.is_none();
175+ config.profile = if Profile::tmux_available() {
176+ Profile::tmux(&default_session)
177+ } else {
178+ eprintln!("warning: tmux not found on PATH — falling back to a plain shell.");
179+ eprintln!(" Sessions will NOT survive closing the sidebar.");
180+ Profile::shell_fallback()
181+ };
182+ let profile = config.profile.clone();
183+ let config_adopts_sole = config.adopt_sole_session && profile.program == "tmux";
184+
185+ let mut server = Server::start(config, port).await?;
186+ println!("termbridge listening on wss://{a} (ws:// also accepted on the same port)", a = server.addr());
187+ println!("running: {} {}", profile.program, profile.args.join(" "));
188+ let existing = termbridge::pty::list_sessions(&profile.tmux_global_args());
189+ if !existing.is_empty() {
190+ println!("tmux sessions available: {}", existing.join(", "));
191+ }
192+ if config_adopts_sole {
193+ match existing.as_slice() {
194+ [only] => println!("joining the only existing session: {only}"),
195+ _ => println!("new clients start in session: {default_session}"),
196+ }
197+ }
198+ println!("token: {}", paths::token_path().display());
199+ println!("cert: SHA-256 {}", identity.fingerprint);
200+ println!(
201+ "\nFirst time only — the certificate is self-signed, so trust it once:\n\
202+ \n open https://{a}/ and accept the warning\n",
203+ a = server.addr()
204+ );
205+ if paired.is_empty() {
206+ println!(
207+ "\nNo origins paired yet — every connection will be refused.\n\
208+ Load the extension, copy its origin from the sidebar, then run:\n\
209+ \n termbridge pair <origin>\n"
210+ );
211+ } else {
212+ println!("paired origins: {}", paired.join(", "));
213+ }
214+
215+ while let Some(event) = server.next_event().await {
216+ match event {
217+ Event::Accepted { origin } => println!("[accept] {origin}"),
218+ Event::Rejected { origin, why, detail } => {
219+ let o = origin.unwrap_or_else(|| "<none>".into());
220+ println!("[reject] {o}: {}", why.reason());
221+ if let Some(d) = detail {
222+ println!(" cause: {d}");
223+ }
224+ match why {
225+ termbridge::auth::Denied::UnpairedOrigin => {
226+ println!(" to approve: termbridge pair {o}");
227+ }
228+ // By far the most common first-run mistake: the extension
229+ // was loaded but nobody ever pasted a token into it. The
230+ // wire-level reason ("invalid token") does not say where
231+ // the right token comes from, so spell it out.
232+ termbridge::auth::Denied::BadToken
233+ | termbridge::auth::Denied::MalformedAuth
234+ | termbridge::auth::Denied::AuthTimeout => {
235+ println!(
236+ " the extension did not present the token in {}",
237+ paths::token_path().display()
238+ );
239+ println!(
240+ " to fix: run termbridge token and paste the 64 hex chars\n\
241+ \x20 into the sidebar's settings → Token field"
242+ );
243+ }
244+ _ => {}
245+ }
246+ }
247+ }
248+ }
249+ Ok(())
250+}
addeddaemon/src/paths.rs+189 −0
1+//! On-disk state: the auth token and the explicitly-paired origin list.
2+//!
3+//! Both live in a 0700 config dir. The token file is 0600 and we *refuse to use
4+//! it* if the mode ever loosens — on a multi-user box, 127.0.0.1 is reachable by
5+//! every local uid, so the file mode is the only thing keeping other users out.
6+
7+use std::fs;
8+use std::io::{self, Write};
9+use std::os::unix::fs::{OpenOptionsExt, PermissionsExt};
10+use std::path::{Path, PathBuf};
11+
12+pub const TOKEN_BYTES: usize = 32;
13+
14+#[derive(Debug)]
15+pub enum TokenError {
16+ Io(io::Error),
17+ /// The token file is readable by group or other. Refuse rather than
18+ /// silently authenticate against a world-readable secret.
19+ TooPermissive { path: PathBuf, mode: u32 },
20+ Malformed,
21+}
22+
23+impl std::fmt::Display for TokenError {
24+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25+ match self {
26+ TokenError::Io(e) => write!(f, "{e}"),
27+ TokenError::TooPermissive { path, mode } => write!(
28+ f,
29+ "token file {} has mode {:04o}; expected 0600. \
30+ Fix with: chmod 600 {}",
31+ path.display(),
32+ mode,
33+ path.display()
34+ ),
35+ TokenError::Malformed => write!(f, "token file is empty or malformed"),
36+ }
37+ }
38+}
39+
40+impl std::error::Error for TokenError {}
41+
42+impl From<io::Error> for TokenError {
43+ fn from(e: io::Error) -> Self {
44+ TokenError::Io(e)
45+ }
46+}
47+
48+pub fn config_dir() -> PathBuf {
49+ if let Some(x) = std::env::var_os("TERMBRIDGE_CONFIG_DIR") {
50+ return PathBuf::from(x);
51+ }
52+ let base = std::env::var_os("XDG_CONFIG_HOME")
53+ .map(PathBuf::from)
54+ .unwrap_or_else(|| {
55+ let home = std::env::var_os("HOME").map(PathBuf::from).unwrap_or_default();
56+ home.join(".config")
57+ });
58+ base.join("termbridge")
59+}
60+
61+pub fn token_path() -> PathBuf {
62+ config_dir().join("token")
63+}
64+
65+pub fn paired_origins_path() -> PathBuf {
66+ config_dir().join("paired-origins")
67+}
68+
69+fn ensure_config_dir(dir: &Path) -> io::Result<()> {
70+ fs::create_dir_all(dir)?;
71+ // 0700: the dir itself should not be traversable by other users.
72+ fs::set_permissions(dir, fs::Permissions::from_mode(0o700))
73+}
74+
75+/// Generate a fresh CSPRNG token and write it 0600, replacing any existing one.
76+pub fn generate_token(dir: &Path) -> Result<String, TokenError> {
77+ ensure_config_dir(dir)?;
78+ let token = random_hex(TOKEN_BYTES);
79+ let path = dir.join("token");
80+
81+ // Create with 0600 *at open time* — never create-then-chmod, which leaves a
82+ // window where the secret is world-readable.
83+ let mut f = fs::OpenOptions::new()
84+ .write(true)
85+ .create(true)
86+ .truncate(true)
87+ .mode(0o600)
88+ .open(&path)?;
89+ f.write_all(token.as_bytes())?;
90+ f.write_all(b"\n")?;
91+ f.sync_all()?;
92+
93+ // An existing file keeps its old mode through O_CREAT, so enforce it too.
94+ fs::set_permissions(&path, fs::Permissions::from_mode(0o600))?;
95+ Ok(token)
96+}
97+
98+/// Load the token, refusing if the file is group/other accessible.
99+pub fn load_token(dir: &Path) -> Result<String, TokenError> {
100+ let path = dir.join("token");
101+ let meta = fs::metadata(&path)?;
102+ let mode = meta.permissions().mode() & 0o777;
103+ if mode & 0o077 != 0 {
104+ return Err(TokenError::TooPermissive { path, mode });
105+ }
106+ let token = fs::read_to_string(&path)?.trim().to_string();
107+ if token.is_empty() {
108+ return Err(TokenError::Malformed);
109+ }
110+ Ok(token)
111+}
112+
113+pub fn load_or_create_token(dir: &Path) -> Result<String, TokenError> {
114+ match load_token(dir) {
115+ Ok(t) => Ok(t),
116+ Err(TokenError::Io(e)) if e.kind() == io::ErrorKind::NotFound => generate_token(dir),
117+ Err(e) => Err(e),
118+ }
119+}
120+
121+/// Origins the user has *explicitly* approved. Absence of this file means no
122+/// client can connect — pairing is never implicit.
123+pub fn load_paired_origins(dir: &Path) -> Vec<String> {
124+ let path = dir.join("paired-origins");
125+ let Ok(contents) = fs::read_to_string(path) else {
126+ return Vec::new();
127+ };
128+ contents
129+ .lines()
130+ .map(str::trim)
131+ .filter(|l| !l.is_empty() && !l.starts_with('#'))
132+ .map(|l| l.to_ascii_lowercase())
133+ .collect()
134+}
135+
136+pub fn pair_origin(dir: &Path, origin: &str) -> io::Result<bool> {
137+ ensure_config_dir(dir)?;
138+ let origin = origin.trim().to_ascii_lowercase();
139+ let mut existing = load_paired_origins(dir);
140+ if existing.iter().any(|o| o == &origin) {
141+ return Ok(false);
142+ }
143+ existing.push(origin);
144+ let path = dir.join("paired-origins");
145+ let mut f = fs::OpenOptions::new()
146+ .write(true)
147+ .create(true)
148+ .truncate(true)
149+ .mode(0o600)
150+ .open(&path)?;
151+ writeln!(f, "# Origins approved to connect to termbridge. One per line.")?;
152+ for o in &existing {
153+ writeln!(f, "{o}")?;
154+ }
155+ Ok(true)
156+}
157+
158+pub fn unpair_origin(dir: &Path, origin: &str) -> io::Result<bool> {
159+ let origin = origin.trim().to_ascii_lowercase();
160+ let existing = load_paired_origins(dir);
161+ if !existing.iter().any(|o| o == &origin) {
162+ return Ok(false);
163+ }
164+ let path = dir.join("paired-origins");
165+ let mut f = fs::OpenOptions::new()
166+ .write(true)
167+ .create(true)
168+ .truncate(true)
169+ .mode(0o600)
170+ .open(&path)?;
171+ writeln!(f, "# Origins approved to connect to termbridge. One per line.")?;
172+ for o in existing.iter().filter(|o| *o != &origin) {
173+ writeln!(f, "{o}")?;
174+ }
175+ Ok(true)
176+}
177+
178+fn random_hex(n: usize) -> String {
179+ let mut buf = vec![0u8; n];
180+ // Straight from the OS CSPRNG. Deliberately not a userspace PRNG — this is
181+ // the only thing standing between another local uid and a shell.
182+ getrandom::fill(&mut buf).expect("OS CSPRNG unavailable");
183+ let mut s = String::with_capacity(n * 2);
184+ for b in buf {
185+ use std::fmt::Write as _;
186+ let _ = write!(s, "{b:02x}");
187+ }
188+ s
189+}
addeddaemon/src/pty.rs+359 −0
1+//! PTY session backend.
2+//!
3+//! One PTY per authenticated connection, running tmux. tmux does the
4+//! multiplexing, so the daemon deliberately has no concept of tabs, panes or
5+//! sessions — that would be reimplementing tmux badly.
6+//!
7+//! `portable-pty` is blocking, so reads and writes each get a dedicated thread
8+//! and talk to the async side over channels.
9+
10+use std::io::{Read, Write};
11+use std::sync::{Arc, Mutex};
12+
13+use portable_pty::{Child, CommandBuilder, MasterPty, PtySize};
14+use tokio::sync::mpsc;
15+
16+/// What the daemon is allowed to run.
17+///
18+/// Deliberately *not* client-supplied. An authenticated client can type
19+/// anything into the resulting shell anyway, so this isn't a privilege
20+/// boundary — but it means the WebSocket API is not literally "exec arbitrary
21+/// argv", which keeps the blast radius of any future auth bug much smaller.
22+#[derive(Debug, Clone)]
23+pub struct Profile {
24+ pub program: String,
25+ pub args: Vec<String>,
26+}
27+
28+impl Default for Profile {
29+ fn default() -> Self {
30+ Profile::tmux(DEFAULT_SESSION)
31+ }
32+}
33+
34+pub const DEFAULT_SESSION: &str = "browser";
35+
36+/// Session names reach `execvp` as a separate argv element, never a shell, so
37+/// metacharacters cannot inject a command. The one real hazard is a leading
38+/// dash, which tmux would parse as a flag. The charset restriction on top of
39+/// that is belt-and-braces, and keeps names to something tmux is happy with.
40+pub fn valid_session_name(raw: &str) -> Option<String> {
41+ let s = raw.trim();
42+ if s.is_empty() || s.len() > 64 || s.starts_with('-') {
43+ return None;
44+ }
45+ // tmux itself rejects '.' and ':' in session names.
46+ if s.contains('.') || s.contains(':') {
47+ return None;
48+ }
49+ if !s
50+ .chars()
51+ .all(|c| c.is_ascii_alphanumeric() || c == '-' || c == '_')
52+ {
53+ return None;
54+ }
55+ Some(s.to_string())
56+}
57+
58+/// Existing tmux sessions, so the sidebar can offer them instead of guessing.
59+///
60+/// `global_args` carries the server-selection flags from the profile; without
61+/// them this asks the *default* tmux server, which is a different set of
62+/// sessions than the one the sidebar is attached to.
63+pub fn list_sessions(global_args: &[String]) -> Vec<String> {
64+ let Ok(out) = std::process::Command::new("tmux")
65+ .args(global_args)
66+ .args(["list-sessions", "-F", "#{session_name}"])
67+ .output()
68+ else {
69+ return Vec::new();
70+ };
71+ if !out.status.success() {
72+ return Vec::new(); // no server running yet
73+ }
74+ String::from_utf8_lossy(&out.stdout)
75+ .lines()
76+ .map(str::trim)
77+ .filter(|l| !l.is_empty())
78+ .map(str::to_string)
79+ .collect()
80+}
81+
82+/// The one existing tmux session, when there is exactly one.
83+///
84+/// With a single session running there is no ambiguity about which one the
85+/// user meant, so joining it beats standing up a second session beside it.
86+/// Zero sessions, or more than one, and the caller falls back to its default.
87+pub fn sole_session(global_args: &[String]) -> Option<String> {
88+ let mut sessions = list_sessions(global_args);
89+ (sessions.len() == 1).then(|| sessions.remove(0))
90+}
91+
92+/// tmux pane ids are `%` followed by digits. Client-supplied ones end up in a
93+/// command line sent to a live tmux server, so nothing else is allowed through.
94+pub fn valid_pane_id(raw: &str) -> Option<String> {
95+ let s = raw.trim();
96+ let Some(digits) = s.strip_prefix('%') else {
97+ return None;
98+ };
99+ if digits.is_empty() || digits.len() > 12 || !digits.chars().all(|c| c.is_ascii_digit()) {
100+ return None;
101+ }
102+ Some(s.to_string())
103+}
104+
105+/// A tmux window id (`@3`), same treatment as a pane id.
106+///
107+/// The sidebar names windows by id rather than index precisely because it goes
108+/// into a command line: an id is `@` plus digits and nothing else, while an
109+/// index is part of a `session:index.pane` target syntax with its own escapes.
110+pub fn valid_window_id(raw: &str) -> Option<String> {
111+ let s = raw.trim();
112+ let Some(digits) = s.strip_prefix('@') else {
113+ return None;
114+ };
115+ if digits.is_empty() || digits.len() > 12 || !digits.chars().all(|c| c.is_ascii_digit()) {
116+ return None;
117+ }
118+ Some(s.to_string())
119+}
120+
121+/// Our own pty's slave path, on its way into `switch-client -c`. It comes from
122+/// the kernel rather than a client, but it is still quoted into a command line,
123+/// so it gets the same treatment.
124+pub fn valid_tty(raw: &str) -> Option<&str> {
125+ let s = raw.trim();
126+ (s.starts_with("/dev/")
127+ && s.len() < 64
128+ && s.chars()
129+ .all(|c| c.is_ascii_alphanumeric() || "/-_.".contains(c)))
130+ .then_some(s)
131+}
132+
133+impl Profile {
134+ /// Fall back to a bare shell when tmux isn't installed, so the daemon is
135+ /// still useful rather than failing to start.
136+ pub fn shell_fallback() -> Self {
137+ Self {
138+ program: std::env::var("SHELL").unwrap_or_else(|_| "/bin/sh".into()),
139+ args: vec!["-l".into()],
140+ }
141+ }
142+
143+ /// Build a tmux profile for a specific session.
144+ ///
145+ /// `-A` attaches to `session` if it exists and creates it otherwise, which
146+ /// is what lets the sidebar rejoin your existing work rather than starting
147+ /// something new every time.
148+ pub fn tmux(session: &str) -> Self {
149+ Self {
150+ program: "tmux".into(),
151+ args: vec![
152+ "new-session".into(),
153+ "-A".into(),
154+ "-s".into(),
155+ session.to_string(),
156+ ],
157+ }
158+ }
159+
160+ /// The same profile pointed at a different session.
161+ ///
162+ /// Only the `-s` value changes, so the server-selection flags and any
163+ /// trailing command survive — rebuilding with [`Profile::tmux`] would drop
164+ /// both and silently address the default tmux server.
165+ pub fn with_session(&self, session: &str) -> Self {
166+ let mut args = self.args.clone();
167+ match args.iter().position(|a| a == "-s") {
168+ Some(i) if i + 1 < args.len() => {
169+ args[i + 1] = session.to_string();
170+ Self {
171+ program: self.program.clone(),
172+ args,
173+ }
174+ }
175+ _ => Profile::tmux(session),
176+ }
177+ }
178+
179+ /// Flags that select which tmux *server* to talk to (`-L`, `-S`, `-f`),
180+ /// taken from the front of the profile's argv. Every other tmux invocation
181+ /// — the control client, session listings — has to repeat them or it
182+ /// silently addresses a different server.
183+ pub fn tmux_global_args(&self) -> Vec<String> {
184+ let mut out = Vec::new();
185+ let mut args = self.args.iter();
186+ while let Some(a) = args.next() {
187+ if !a.starts_with('-') {
188+ break; // First command word; globals are all before it.
189+ }
190+ out.push(a.clone());
191+ if matches!(a.as_str(), "-L" | "-S" | "-f") {
192+ if let Some(value) = args.next() {
193+ out.push(value.clone());
194+ }
195+ }
196+ }
197+ out
198+ }
199+
200+ pub fn tmux_available() -> bool {
201+ std::process::Command::new("tmux")
202+ .arg("-V")
203+ .stdout(std::process::Stdio::null())
204+ .stderr(std::process::Stdio::null())
205+ .status()
206+ .map(|s| s.success())
207+ .unwrap_or(false)
208+ }
209+}
210+
211+pub struct PtySession {
212+ master: Box<dyn MasterPty + Send>,
213+ writer_tx: mpsc::UnboundedSender<Vec<u8>>,
214+ child: Arc<Mutex<Box<dyn Child + Send + Sync>>>,
215+}
216+
217+pub struct Spawned {
218+ pub session: PtySession,
219+ /// Raw bytes from the pty. Chunked as they arrive; no framing, no encoding.
220+ pub output: mpsc::Receiver<Vec<u8>>,
221+ /// Fires once with the child's exit status.
222+ pub exit: tokio::sync::oneshot::Receiver<i32>,
223+}
224+
225+impl PtySession {
226+ pub fn spawn(profile: &Profile, cols: u16, rows: u16) -> std::io::Result<Spawned> {
227+ let pty = portable_pty::native_pty_system();
228+ let pair = pty
229+ .openpty(PtySize {
230+ rows: rows.max(1),
231+ cols: cols.max(1),
232+ pixel_width: 0,
233+ pixel_height: 0,
234+ })
235+ .map_err(|e| std::io::Error::other(e.to_string()))?;
236+
237+ let mut cmd = CommandBuilder::new(&profile.program);
238+ for a in &profile.args {
239+ cmd.arg(a);
240+ }
241+ // xterm-256color is what xterm.js actually implements. Advertising
242+ // anything richer makes programs emit sequences the renderer drops.
243+ cmd.env("TERM", "xterm-256color");
244+ cmd.env("COLORTERM", "truecolor");
245+ if let Some(home) = std::env::var_os("HOME") {
246+ cmd.cwd(home);
247+ }
248+
249+ let child = pair
250+ .slave
251+ .spawn_command(cmd)
252+ .map_err(|e| std::io::Error::other(e.to_string()))?;
253+ // Drop the slave in the parent, or the pty never reports EOF.
254+ drop(pair.slave);
255+
256+ let mut reader = pair
257+ .master
258+ .try_clone_reader()
259+ .map_err(|e| std::io::Error::other(e.to_string()))?;
260+ let mut writer = pair
261+ .master
262+ .take_writer()
263+ .map_err(|e| std::io::Error::other(e.to_string()))?;
264+
265+ // Bounded: if the browser can't keep up with a `yes` flood, block the
266+ // reader thread rather than growing a queue until the daemon OOMs.
267+ let (out_tx, output) = mpsc::channel::<Vec<u8>>(256);
268+ std::thread::spawn(move || {
269+ let mut buf = [0u8; 8192];
270+ loop {
271+ match reader.read(&mut buf) {
272+ Ok(0) | Err(_) => break,
273+ Ok(n) => {
274+ if out_tx.blocking_send(buf[..n].to_vec()).is_err() {
275+ break;
276+ }
277+ }
278+ }
279+ }
280+ });
281+
282+ let (writer_tx, mut writer_rx) = mpsc::unbounded_channel::<Vec<u8>>();
283+ std::thread::spawn(move || {
284+ while let Some(chunk) = writer_rx.blocking_recv() {
285+ if writer.write_all(&chunk).is_err() || writer.flush().is_err() {
286+ break;
287+ }
288+ }
289+ });
290+
291+ let child = Arc::new(Mutex::new(child));
292+ let (exit_tx, exit) = tokio::sync::oneshot::channel();
293+ let waiter = Arc::clone(&child);
294+ std::thread::spawn(move || {
295+ let code = loop {
296+ let status = { waiter.lock().unwrap().try_wait() };
297+ match status {
298+ Ok(Some(s)) => break s.exit_code() as i32,
299+ Ok(None) => std::thread::sleep(std::time::Duration::from_millis(100)),
300+ Err(_) => break -1,
301+ }
302+ };
303+ let _ = exit_tx.send(code);
304+ });
305+
306+ Ok(Spawned {
307+ session: PtySession {
308+ master: pair.master,
309+ writer_tx,
310+ child,
311+ },
312+ output,
313+ exit,
314+ })
315+ }
316+
317+ /// Path of the slave side, e.g. `/dev/pts/7`. This is how tmux names our
318+ /// client, so it is the key for looking the client's session back up.
319+ #[cfg(unix)]
320+ pub fn tty_name(&self) -> Option<String> {
321+ self.master
322+ .tty_name()
323+ .map(|p| p.to_string_lossy().into_owned())
324+ }
325+
326+ #[cfg(not(unix))]
327+ pub fn tty_name(&self) -> Option<String> {
328+ None
329+ }
330+
331+ /// Keystrokes from the browser, straight through. No interpretation.
332+ pub fn write(&self, bytes: Vec<u8>) -> bool {
333+ self.writer_tx.send(bytes).is_ok()
334+ }
335+
336+ /// TIOCSWINSZ on the master; the child gets SIGWINCH and tmux reflows.
337+ pub fn resize(&self, cols: u16, rows: u16) -> std::io::Result<()> {
338+ self.master
339+ .resize(PtySize {
340+ rows: rows.max(1),
341+ cols: cols.max(1),
342+ pixel_width: 0,
343+ pixel_height: 0,
344+ })
345+ .map_err(|e| std::io::Error::other(e.to_string()))
346+ }
347+
348+ pub fn kill(&self) {
349+ let _ = self.child.lock().unwrap().kill();
350+ }
351+}
352+
353+impl Drop for PtySession {
354+ fn drop(&mut self) {
355+ // Killing the tmux *client* on disconnect is correct and is the whole
356+ // point: the tmux server keeps the session alive for the next attach.
357+ self.kill();
358+ }
359+}
addeddaemon/src/rewind.rs+87 −0
1+//! A stream wrapper that replays bytes we already consumed.
2+//!
3+//! We need to look at the HTTP request head *before* handing the stream to the
4+//! WebSocket handshake, so we can serve a human-readable page to someone who
5+//! navigated to https://127.0.0.1:7681 to accept the certificate. TLS streams
6+//! can't be peeked like a TcpStream, so we read, decide, and rewind.
7+
8+use std::pin::Pin;
9+use std::task::{Context, Poll};
10+
11+use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
12+
13+pub struct Rewind<S> {
14+ prefix: Vec<u8>,
15+ pos: usize,
16+ inner: S,
17+}
18+
19+impl<S> Rewind<S> {
20+ pub fn new(prefix: Vec<u8>, inner: S) -> Self {
21+ Self {
22+ prefix,
23+ pos: 0,
24+ inner,
25+ }
26+ }
27+}
28+
29+impl<S: AsyncRead + Unpin> AsyncRead for Rewind<S> {
30+ fn poll_read(
31+ mut self: Pin<&mut Self>,
32+ cx: &mut Context<'_>,
33+ buf: &mut ReadBuf<'_>,
34+ ) -> Poll<std::io::Result<()>> {
35+ if self.pos < self.prefix.len() {
36+ let remaining = &self.prefix[self.pos..];
37+ let n = remaining.len().min(buf.remaining());
38+ buf.put_slice(&remaining[..n]);
39+ self.pos += n;
40+ return Poll::Ready(Ok(()));
41+ }
42+ Pin::new(&mut self.inner).poll_read(cx, buf)
43+ }
44+}
45+
46+impl<S: AsyncWrite + Unpin> AsyncWrite for Rewind<S> {
47+ fn poll_write(
48+ mut self: Pin<&mut Self>,
49+ cx: &mut Context<'_>,
50+ buf: &[u8],
51+ ) -> Poll<std::io::Result<usize>> {
52+ Pin::new(&mut self.inner).poll_write(cx, buf)
53+ }
54+ fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
55+ Pin::new(&mut self.inner).poll_flush(cx)
56+ }
57+ fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
58+ Pin::new(&mut self.inner).poll_shutdown(cx)
59+ }
60+}
61+
62+/// Read the request head (up to the blank line) without consuming the body.
63+/// Returns the bytes read so they can be replayed.
64+pub async fn read_head<S: AsyncRead + Unpin>(
65+ stream: &mut S,
66+ limit: usize,
67+) -> std::io::Result<Vec<u8>> {
68+ use tokio::io::AsyncReadExt;
69+ let mut buf = Vec::with_capacity(1024);
70+ let mut byte = [0u8; 1];
71+ while buf.len() < limit {
72+ let n = stream.read(&mut byte).await?;
73+ if n == 0 {
74+ break;
75+ }
76+ buf.push(byte[0]);
77+ if buf.ends_with(b"\r\n\r\n") {
78+ break;
79+ }
80+ }
81+ Ok(buf)
82+}
83+
84+pub fn is_websocket_upgrade(head: &[u8]) -> bool {
85+ let text = String::from_utf8_lossy(head).to_ascii_lowercase();
86+ text.contains("upgrade: websocket") || text.contains("upgrade:websocket")
87+}
addeddaemon/src/server.rs+917 −0
1+//! The WebSocket listener.
2+//!
3+//! Spike scope: prove the *security-relevant* path end to end (bind, Origin,
4+//! Host, token, pairing) and echo afterwards. Attaching `tmux -C` replaces the
5+//! echo loop later and changes none of the code above it.
6+
7+use std::net::{IpAddr, Ipv4Addr, SocketAddr};
8+use std::sync::Arc;
9+use std::time::Duration;
10+
11+use futures_util::{SinkExt, StreamExt};
12+use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt};
13+use tokio::net::{TcpListener, TcpStream};
14+use tokio::sync::{mpsc, Mutex};
15+use tokio_tungstenite::tungstenite::handshake::server::{ErrorResponse, Request, Response};
16+use tokio_tungstenite::tungstenite::http;
17+use tokio_tungstenite::tungstenite::Message;
18+use tokio_tungstenite::WebSocketStream;
19+
20+use crate::auth::{self, Denied};
21+
22+#[derive(Clone)]
23+pub struct Config {
24+ pub token: String,
25+ pub paired_origins: Vec<String>,
26+ /// How long a client has to send its auth frame after the upgrade.
27+ pub auth_timeout: Duration,
28+ pub max_failures: u32,
29+ pub lockout: Duration,
30+ /// What we run in the pty. Fixed by the daemon, never client-supplied.
31+ pub profile: crate::pty::Profile,
32+ /// Session used when the client doesn't name one.
33+ pub default_session: String,
34+ /// When the client names no session and exactly one tmux session already
35+ /// exists, attach to that one instead of `default_session`. Off when the
36+ /// user pinned a session on the command line — an explicit `--session` is
37+ /// a choice, not a default to be second-guessed.
38+ pub adopt_sole_session: bool,
39+ /// Skip the pty and echo frames back. Used by the security tests so they
40+ /// exercise the auth path without spawning shells.
41+ pub echo_only: bool,
42+ /// TLS identity. When present the listener serves wss:// and ws:// on the
43+ /// same port, chosen per-connection by sniffing the first byte.
44+ pub tls: Option<tokio_rustls::TlsAcceptor>,
45+}
46+
47+impl Config {
48+ pub fn new(token: impl Into<String>, paired_origins: Vec<String>) -> Self {
49+ Self {
50+ token: token.into(),
51+ paired_origins,
52+ auth_timeout: Duration::from_secs(3),
53+ max_failures: 10,
54+ lockout: Duration::from_secs(30),
55+ profile: crate::pty::Profile::default(),
56+ default_session: crate::pty::DEFAULT_SESSION.to_string(),
57+ // Opt-in: a caller that hands us a profile has picked its session,
58+ // and only the CLI knows whether the user picked it or we did.
59+ adopt_sole_session: false,
60+ echo_only: false,
61+ tls: None,
62+ }
63+ }
64+
65+ /// The lone existing session to join instead of the profile's own, if
66+ /// adoption is on and tmux is holding exactly one.
67+ ///
68+ /// Resolved per connection rather than at startup: sessions come and go
69+ /// while the daemon runs, and the answer should reflect what tmux holds at
70+ /// the moment the sidebar connects. `None` means "use the profile as
71+ /// configured".
72+ pub fn adopted_session(&self) -> Option<String> {
73+ if !self.adopt_sole_session || self.profile.program != "tmux" {
74+ return None;
75+ }
76+ crate::pty::sole_session(&self.profile.tmux_global_args())
77+ }
78+}
79+
80+#[derive(Debug, Clone, PartialEq, Eq)]
81+pub enum Event {
82+ Accepted { origin: String },
83+ Rejected {
84+ origin: Option<String>,
85+ why: Denied,
86+ /// Underlying cause, when the failure happened below our own checks
87+ /// (e.g. the request never was a WebSocket upgrade).
88+ detail: Option<String>,
89+ },
90+}
91+
92+struct Failures {
93+ count: u32,
94+ locked_until: Option<tokio::time::Instant>,
95+}
96+
97+struct State {
98+ config: Config,
99+ failures: Mutex<Failures>,
100+ events: mpsc::UnboundedSender<Event>,
101+}
102+
103+impl State {
104+ async fn locked_out(&self) -> bool {
105+ let f = self.failures.lock().await;
106+ match f.locked_until {
107+ Some(t) => tokio::time::Instant::now() < t,
108+ None => false,
109+ }
110+ }
111+
112+ async fn record_failure(&self) {
113+ let mut f = self.failures.lock().await;
114+ f.count += 1;
115+ if f.count >= self.config.max_failures {
116+ f.locked_until = Some(tokio::time::Instant::now() + self.config.lockout);
117+ }
118+ }
119+
120+ async fn record_success(&self) {
121+ let mut f = self.failures.lock().await;
122+ f.count = 0;
123+ f.locked_until = None;
124+ }
125+
126+ fn emit(&self, e: Event) {
127+ let _ = self.events.send(e);
128+ }
129+}
130+
131+pub struct Server {
132+ addr: SocketAddr,
133+ events: mpsc::UnboundedReceiver<Event>,
134+ _task: tokio::task::JoinHandle<()>,
135+}
136+
137+impl Server {
138+ /// Bind and start accepting.
139+ ///
140+ /// The bind address is hardcoded to loopback and is not configurable. This
141+ /// daemon hands out shell access; a `--bind` flag would be a footgun whose
142+ /// only purpose is to create a remotely exploitable configuration.
143+ pub async fn start(config: Config, port: u16) -> std::io::Result<Self> {
144+ let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), port);
145+ let listener = TcpListener::bind(addr).await?;
146+ let addr = listener.local_addr()?;
147+
148+ // Belt and braces: if this ever regresses, fail loudly at startup
149+ // rather than quietly listening on the network.
150+ assert!(
151+ addr.ip().is_loopback(),
152+ "refusing to serve on non-loopback address {addr}"
153+ );
154+
155+ let (tx, rx) = mpsc::unbounded_channel();
156+ let state = Arc::new(State {
157+ config,
158+ failures: Mutex::new(Failures {
159+ count: 0,
160+ locked_until: None,
161+ }),
162+ events: tx,
163+ });
164+
165+ let task = tokio::spawn(async move {
166+ loop {
167+ let Ok((stream, peer)) = listener.accept().await else {
168+ continue;
169+ };
170+ let state = Arc::clone(&state);
171+ tokio::spawn(async move {
172+ let _ = handle_conn(stream, peer, state).await;
173+ });
174+ }
175+ });
176+
177+ Ok(Server {
178+ addr,
179+ events: rx,
180+ _task: task,
181+ })
182+ }
183+
184+ pub fn addr(&self) -> SocketAddr {
185+ self.addr
186+ }
187+
188+ pub fn url(&self) -> String {
189+ format!("ws://{}", self.addr)
190+ }
191+
192+ pub async fn next_event(&mut self) -> Option<Event> {
193+ self.events.recv().await
194+ }
195+}
196+
197+fn deny(why: Denied) -> ErrorResponse {
198+ http::Response::builder()
199+ .status(why.status())
200+ .body(Some(why.reason().to_string()))
201+ .expect("static response builds")
202+}
203+
204+async fn handle_conn(
205+ stream: TcpStream,
206+ _peer: SocketAddr,
207+ state: Arc<State>,
208+) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
209+ if state.locked_out().await {
210+ state.emit(Event::Rejected {
211+ origin: None,
212+ why: Denied::RateLimited,
213+ detail: None,
214+ });
215+ // Still perform the reject through the handshake so the client sees 429.
216+ let _ = tokio_tungstenite::accept_hdr_async(stream, |_: &Request, _| {
217+ Err(deny(Denied::RateLimited))
218+ })
219+ .await;
220+ return Ok(());
221+ }
222+
223+ // A TLS ClientHello starts with 0x16 (handshake record). Sniffing it lets
224+ // one port serve both wss:// and ws://, which matters because Firefox's
225+ // HTTPS-Only Mode rewrites ws:// to wss:// and the user should not have to
226+ // weaken a browser security setting to run this.
227+ let mut first = [0u8; 1];
228+ let is_tls = matches!(stream.peek(&mut first).await, Ok(1) if first[0] == 0x16);
229+
230+ if is_tls {
231+ let Some(acceptor) = state.config.tls.clone() else {
232+ state.record_failure().await;
233+ state.emit(Event::Rejected {
234+ origin: None,
235+ why: Denied::TlsAttempted,
236+ detail: Some("this daemon was started without a TLS identity".into()),
237+ });
238+ return Ok(());
239+ };
240+ let tls_stream = match acceptor.accept(stream).await {
241+ Ok(s) => s,
242+ Err(e) => {
243+ // Almost always the browser refusing our self-signed cert.
244+ state.emit(Event::Rejected {
245+ origin: None,
246+ why: Denied::TlsHandshakeFailed,
247+ detail: Some(e.to_string()),
248+ });
249+ return Ok(());
250+ }
251+ };
252+ return dispatch(tls_stream, state).await;
253+ }
254+
255+ dispatch(stream, state).await
256+}
257+
258+/// Serve one already-negotiated stream: either the certificate-trust landing
259+/// page, or the WebSocket handshake.
260+async fn dispatch<S>(mut stream: S, state: Arc<State>) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
261+where
262+ S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
263+{
264+ // Look at the request head so a browser that navigated here to accept the
265+ // certificate gets an explanation instead of a protocol error.
266+ let head = crate::rewind::read_head(&mut stream, 8192).await.unwrap_or_default();
267+ if !crate::rewind::is_websocket_upgrade(&head) {
268+ state.emit(Event::Rejected {
269+ origin: None,
270+ why: Denied::NotAWebSocketUpgrade,
271+ detail: Some("served the certificate-trust page".into()),
272+ });
273+ let _ = stream.write_all(LANDING_PAGE.as_bytes()).await;
274+ let _ = stream.flush().await;
275+ return Ok(());
276+ }
277+ let stream = crate::rewind::Rewind::new(head, stream);
278+ handshake(stream, state).await
279+}
280+
281+const LANDING_PAGE: &str = concat!(
282+ "HTTP/1.1 200 OK\r\n",
283+ "Content-Type: text/html; charset=utf-8\r\n",
284+ "Connection: close\r\n",
285+ "Cache-Control: no-store\r\n",
286+ "\r\n",
287+ "<!doctype html><meta charset=utf-8><title>termbridge</title>",
288+ "<style>body{font:15px/1.6 system-ui,sans-serif;max-width:34rem;margin:12vh auto;",
289+ "padding:0 1.5rem;background:#14161a;color:#d7dae0}h1{font-size:1.1rem}",
290+ "code{background:#1c1f25;padding:.15em .4em;border-radius:3px;font-size:.9em}",
291+ "p{color:#9aa3b2}</style>",
292+ "<h1>termbridge is running</h1>",
293+ "<p>If you reached this page to accept the certificate, you're done \u{2014} ",
294+ "close this tab and reconnect the sidebar.</p>",
295+ "<p>This endpoint serves the terminal over WebSocket only. It exposes no ",
296+ "other data, and in particular it never hands out the auth token.</p>"
297+);
298+
299+async fn handshake<S>(stream: S, state: Arc<State>) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
300+where
301+ S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
302+{
303+ let seen_origin: Arc<std::sync::Mutex<Option<String>>> = Arc::new(std::sync::Mutex::new(None));
304+ let handshake_err: Arc<std::sync::Mutex<Option<Denied>>> =
305+ Arc::new(std::sync::Mutex::new(None));
306+
307+ let paired = state.config.paired_origins.clone();
308+ let so = Arc::clone(&seen_origin);
309+ let he = Arc::clone(&handshake_err);
310+
311+ let ws = tokio_tungstenite::accept_hdr_async(stream, move |req: &Request, res: Response| {
312+ let header = |name: &str| {
313+ req.headers()
314+ .get(name)
315+ .and_then(|v| v.to_str().ok())
316+ .map(str::to_string)
317+ };
318+ let origin = header("origin");
319+ let host = header("host");
320+ *so.lock().unwrap() = origin.clone();
321+
322+ match auth::check_handshake(origin.as_deref(), host.as_deref(), &paired) {
323+ Ok(()) => Ok(res),
324+ Err(why) => {
325+ *he.lock().unwrap() = Some(why);
326+ Err(deny(why))
327+ }
328+ }
329+ })
330+ .await;
331+
332+ let origin = seen_origin.lock().unwrap().clone();
333+ let hs_err = *handshake_err.lock().unwrap();
334+
335+ let mut ws = match ws {
336+ Ok(ws) => ws,
337+ Err(e) => {
338+ // If hs_err is set, we rejected it deliberately. If it is not, the
339+ // handshake failed before our callback ever ran — which means the
340+ // request was not a valid WebSocket upgrade at all. Reporting that
341+ // as an auth problem sends people hunting for the wrong bug.
342+ let (why, detail) = match hs_err {
343+ Some(why) => (why, None),
344+ None => (Denied::NotAWebSocketUpgrade, Some(e.to_string())),
345+ };
346+ state.record_failure().await;
347+ state.emit(Event::Rejected { origin, why, detail });
348+ return Ok(());
349+ }
350+ };
351+
352+ // Upgrade succeeded; now the auth frame, on a deadline.
353+ let first = tokio::time::timeout(state.config.auth_timeout, ws.next()).await;
354+
355+ let result = match first {
356+ Err(_) => Err(Denied::AuthTimeout),
357+ Ok(None) => Err(Denied::AuthTimeout),
358+ Ok(Some(Err(_))) => Err(Denied::MalformedAuth),
359+ Ok(Some(Ok(Message::Text(t)))) => auth::check_auth_frame(&t, &state.config.token),
360+ Ok(Some(Ok(_))) => Err(Denied::MalformedAuth),
361+ };
362+
363+ if let Err(why) = result {
364+ state.record_failure().await;
365+ state.emit(Event::Rejected {
366+ origin,
367+ why,
368+ detail: None,
369+ });
370+ let _ = ws
371+ .send(Message::Text(
372+ serde_json::json!({"type": "error", "reason": why.reason()})
373+ .to_string()
374+ .into(),
375+ ))
376+ .await;
377+ let _ = ws.close(None).await;
378+ return Ok(());
379+ }
380+
381+ state.record_success().await;
382+ state.emit(Event::Accepted {
383+ origin: origin.clone().unwrap_or_default(),
384+ });
385+
386+ let is_tmux = state.config.profile.program == "tmux";
387+ ws.send(Message::Text(
388+ serde_json::json!({
389+ "type": "ok",
390+ "profile": state.config.profile.program,
391+ "tmux": is_tmux,
392+ "defaultSession": state.config.adopted_session()
393+ .unwrap_or_else(|| state.config.default_session.clone()),
394+ // Enumerated server-side; the client picks from reality rather
395+ // than inventing names.
396+ "sessions": if is_tmux { crate::pty::list_sessions(&state.config.profile.tmux_global_args()) } else { Vec::new() },
397+ })
398+ .to_string()
399+ .into(),
400+ ))
401+ .await?;
402+
403+ if state.config.echo_only {
404+ return echo_loop(ws).await;
405+ }
406+ pty_loop(ws, &state.config).await
407+}
408+
409+/// Post-auth session: binary frames are raw terminal bytes in both directions,
410+/// text frames are JSON control messages.
411+async fn pty_loop<S>(
412+ mut ws: WebSocketStream<S>,
413+ config: &Config,
414+) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
415+where
416+ S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
417+{
418+ // Wait for the client to tell us its size before spawning, so the shell's
419+ // first prompt is drawn at the right width.
420+ let (cols, rows, requested) = match tokio::time::timeout(Duration::from_secs(5), ws.next()).await
421+ {
422+ Ok(Some(Ok(Message::Text(t)))) => parse_open(&t).unwrap_or((80, 24, None)),
423+ _ => (80, 24, None),
424+ };
425+
426+ // The client may name a tmux session, but only a name that survives
427+ // validation, and only when we are actually running tmux. Anything else
428+ // silently falls back to the configured default rather than erroring —
429+ // there is no path here from client input to an arbitrary program.
430+ let profile = if config.profile.program == "tmux" {
431+ match requested {
432+ Some(name) => config.profile.with_session(&name),
433+ None => match config.adopted_session() {
434+ Some(only) => config.profile.with_session(&only),
435+ None => config.profile.clone(),
436+ },
437+ }
438+ } else {
439+ config.profile.clone()
440+ };
441+ let profile = &profile;
442+
443+ let spawned = match crate::pty::PtySession::spawn(profile, cols, rows) {
444+ Ok(s) => s,
445+ Err(e) => {
446+ let _ = ws
447+ .send(Message::Text(
448+ serde_json::json!({"type": "error", "reason": e.to_string()})
449+ .to_string()
450+ .into(),
451+ ))
452+ .await;
453+ return Ok(());
454+ }
455+ };
456+
457+ let crate::pty::Spawned {
458+ session,
459+ mut output,
460+ mut exit,
461+ } = spawned;
462+
463+ // tmux is the source of truth for which session we're on, what else exists,
464+ // and what Claude Code is doing in it — and all of that changes without
465+ // anything crossing this socket. A control-mode client in its own task both
466+ // watches for those changes and carries the sidebar's requests back.
467+ let (status_tx, mut status_rx) = mpsc::channel::<String>(8);
468+ let (tmux_tx, tmux_rx) = mpsc::channel::<TmuxRequest>(8);
469+ if profile.program == "tmux" {
470+ // The session the interactive client was pointed at. Only used to give
471+ // the control client something to attach to; from then on tmux tells us
472+ // where that client actually is.
473+ let started_on = profile
474+ .args
475+ .iter()
476+ .position(|a| a == "-s")
477+ .and_then(|i| profile.args.get(i + 1))
478+ .cloned()
479+ .unwrap_or_else(|| config.default_session.clone());
480+ tokio::spawn(control_loop(
481+ started_on,
482+ profile.tmux_global_args(),
483+ session.tty_name(),
484+ status_tx,
485+ tmux_rx,
486+ ));
487+ }
488+
489+ loop {
490+ tokio::select! {
491+ // pty -> browser
492+ chunk = output.recv() => {
493+ match chunk {
494+ Some(bytes) => ws.send(Message::Binary(bytes.into())).await?,
495+ None => {
496+ // EOF on the pty beats the child being reaped, so wait
497+ // briefly for the status rather than dropping the
498+ // sidebar with no explanation.
499+ let code = tokio::time::timeout(Duration::from_secs(2), &mut exit)
500+ .await
501+ .ok()
502+ .and_then(|r| r.ok())
503+ .unwrap_or(-1);
504+ let _ = ws.send(Message::Text(
505+ serde_json::json!({"type": "exit", "code": code}).to_string().into()
506+ )).await;
507+ break;
508+ }
509+ }
510+ }
511+ // browser -> pty
512+ msg = ws.next() => {
513+ match msg {
514+ Some(Ok(Message::Binary(b))) => {
515+ if !session.write(b.to_vec()) { break; }
516+ }
517+ // Text is control only. Keystrokes must arrive as binary so
518+ // that non-UTF-8 input is never mangled.
519+ Some(Ok(Message::Text(t))) => {
520+ if let Some((c, r)) = parse_resize(&t) {
521+ let _ = session.resize(c, r);
522+ }
523+ if let Some(req) = parse_tmux_request(&t) {
524+ // Full queue means the control client is wedged;
525+ // dropping the request beats stalling the terminal.
526+ let _ = tmux_tx.try_send(req);
527+ }
528+ }
529+ Some(Ok(Message::Close(_))) | None => break,
530+ Some(Err(_)) => break,
531+ _ => {}
532+ }
533+ }
534+ Some(json) = status_rx.recv() => {
535+ ws.send(Message::Text(json.into())).await?;
536+ }
537+ code = &mut exit => {
538+ let code = code.unwrap_or(-1);
539+ let _ = ws.send(Message::Text(
540+ serde_json::json!({"type": "exit", "code": code}).to_string().into()
541+ )).await;
542+ break;
543+ }
544+ }
545+ }
546+
547+ // Dropping the session kills the tmux *client*. The tmux server, and the
548+ // session itself, keep running for the next attach.
549+ drop(session);
550+ let _ = ws.close(None).await;
551+ Ok(())
552+}
553+
554+/// The complete set of things the sidebar may ask tmux to do.
555+///
556+/// An enum rather than a command string, so the wire protocol cannot express
557+/// anything outside this list. Each variant's payload is validated at parse
558+/// time, and the command lines built from them are the only ones in the daemon
559+/// that contain client-supplied text.
560+#[derive(Debug, Clone, PartialEq, Eq)]
561+pub enum TmuxRequest {
562+ /// Move the live client to an existing session — no reconnect, no new pty.
563+ Switch(String),
564+ /// Create a detached session, then switch to it.
565+ Create(String),
566+ /// Bring a pane into view: select it, its window, and its session.
567+ Focus(String),
568+ /// Make a window of the attached session the current one — the sidebar's
569+ /// tab click. Windows belong to the session, not to a client, so this
570+ /// deliberately moves every client watching that session, exactly as
571+ /// pressing `prefix 2` in the terminal would.
572+ SelectWindow(String),
573+ /// Open a window in a session and select it — the sidebar's "+".
574+ NewWindow(String),
575+ /// Close a window and everything running in it — the sidebar's tab ✕.
576+ ///
577+ /// The one entry here that destroys anything, and tmux has no undo for it.
578+ /// The window id keeps the blast radius to exactly one window; the sidebar
579+ /// additionally refuses to offer it for a session's last window, which
580+ /// would take the session and our own client with it.
581+ KillWindow(String),
582+}
583+
584+fn parse_tmux_request(text: &str) -> Option<TmuxRequest> {
585+ let v: serde_json::Value = serde_json::from_str(text).ok()?;
586+ if v.get("type")?.as_str()? != "tmux" {
587+ return None;
588+ }
589+ let arg = |k: &str| v.get(k).and_then(|x| x.as_str());
590+ match v.get("cmd")?.as_str()? {
591+ "switch" => crate::pty::valid_session_name(arg("session")?).map(TmuxRequest::Switch),
592+ "create" => crate::pty::valid_session_name(arg("session")?).map(TmuxRequest::Create),
593+ "focus" => crate::pty::valid_pane_id(arg("pane")?).map(TmuxRequest::Focus),
594+ "select-window" => {
595+ crate::pty::valid_window_id(arg("window")?).map(TmuxRequest::SelectWindow)
596+ }
597+ "new-window" => crate::pty::valid_session_name(arg("session")?).map(TmuxRequest::NewWindow),
598+ "kill-window" => crate::pty::valid_window_id(arg("window")?).map(TmuxRequest::KillWindow),
599+ _ => None,
600+ }
601+}
602+
603+/// Command lines for a request. `tty` identifies our interactive client, so the
604+/// switch moves *it* rather than whichever client tmux would otherwise pick.
605+fn command_lines(req: &TmuxRequest, tty: Option<&str>) -> Vec<String> {
606+ let client = tty
607+ .and_then(crate::pty::valid_tty)
608+ .map(|t| format!(" -c '{t}'"))
609+ .unwrap_or_default();
610+ match req {
611+ TmuxRequest::Switch(name) => vec![format!("switch-client{client} -t '{name}'")],
612+ TmuxRequest::Create(name) => vec![
613+ // -A so a name that already exists attaches instead of failing,
614+ // matching what the sidebar's session field has always done.
615+ format!("new-session -d -A -s '{name}'"),
616+ format!("switch-client{client} -t '{name}'"),
617+ ],
618+ TmuxRequest::Focus(pane) => vec![format!(
619+ "select-pane -t '{pane}' ; select-window -t '{pane}' ; switch-client{client} -t '{pane}'"
620+ )],
621+ // No `-c`: a window id already identifies its session, and selecting a
622+ // window is a property of the session rather than of our client.
623+ TmuxRequest::SelectWindow(id) => vec![format!("select-window -t '{id}'")],
624+ // `-a` inserts after the current window instead of claiming an index
625+ // that may already be taken, which is an error rather than a shuffle.
626+ // The trailing colon targets the session's current window.
627+ TmuxRequest::NewWindow(name) => vec![format!("new-window -a -t '{name}:'")],
628+ TmuxRequest::KillWindow(id) => vec![format!("kill-window -t '{id}'")],
629+ }
630+}
631+
632+/// Owns the control-mode client: pushes a status frame whenever tmux says
633+/// something changed, and runs the sidebar's requests.
634+async fn control_loop(
635+ session: String,
636+ global_args: Vec<String>,
637+ tty: Option<String>,
638+ status_tx: mpsc::Sender<String>,
639+ mut requests: mpsc::Receiver<TmuxRequest>,
640+) {
641+ let Some((control, mut notifications)) = attach_with_retry(&session, &global_args).await else {
642+ return;
643+ };
644+ // Claude Code's hook records live on disk and change without tmux noticing,
645+ // so a slow tick backs up the push notifications. It reads a handful of
646+ // small files; there is no process spawn on this path at all.
647+ let mut ticker = tokio::time::interval(Duration::from_secs(1));
648+ let mut last: Option<crate::status::Snapshot> = None;
649+
650+ loop {
651+ let snap = crate::status::snapshot(&control, tty.as_deref()).await;
652+ if last.as_ref() != Some(&snap) {
653+ let json = serde_json::json!({
654+ "type": "status",
655+ "session": snap.session,
656+ "sessions": snap.sessions,
657+ "windows": snap.windows,
658+ "agents": snap.agents,
659+ })
660+ .to_string();
661+ if status_tx.send(json).await.is_err() {
662+ return; // Connection gone.
663+ }
664+ last = Some(snap);
665+ }
666+
667+ tokio::select! {
668+ _ = ticker.tick() => {}
669+ note = notifications.recv() => {
670+ match note {
671+ // Uninteresting notifications are the common case; skip the
672+ // round trip rather than rebuilding the snapshot for a
673+ // layout change nobody displays.
674+ Some(n) if !crate::control::is_interesting(&n) => continue,
675+ Some(_) => {}
676+ None => return,
677+ }
678+ }
679+ req = requests.recv() => {
680+ let Some(req) = req else { return };
681+ for line in command_lines(&req, tty.as_deref()) {
682+ if let Err(reason) = control.run(line).await {
683+ let json = serde_json::json!({
684+ "type": "tmux-error", "reason": reason,
685+ }).to_string();
686+ let _ = status_tx.send(json).await;
687+ break;
688+ }
689+ }
690+ }
691+ }
692+ }
693+}
694+
695+/// The interactive client creates the session, and we may get here first.
696+async fn attach_with_retry(
697+ session: &str,
698+ global_args: &[String],
699+) -> Option<(
700+ crate::control::Control,
701+ mpsc::Receiver<crate::control::Notification>,
702+)> {
703+ for _ in 0..10 {
704+ if let Ok(pair) = crate::control::Control::attach(session, global_args).await {
705+ // A failed attach still spawns: confirm the client is actually
706+ // talking before handing it out.
707+ if pair.0.run("display-message -p ok").await.is_ok() {
708+ return Some(pair);
709+ }
710+ }
711+ tokio::time::sleep(Duration::from_millis(200)).await;
712+ }
713+ None
714+}
715+
716+fn parse_open(text: &str) -> Option<(u16, u16, Option<String>)> {
717+ let v: serde_json::Value = serde_json::from_str(text).ok()?;
718+ if v.get("type")?.as_str()? != "open" {
719+ return None;
720+ }
721+ let (cols, rows) = dims(&v);
722+ let session = v
723+ .get("session")
724+ .and_then(|x| x.as_str())
725+ .and_then(crate::pty::valid_session_name);
726+ Some((cols, rows, session))
727+}
728+
729+fn parse_resize(text: &str) -> Option<(u16, u16)> {
730+ let v: serde_json::Value = serde_json::from_str(text).ok()?;
731+ match v.get("type")?.as_str()? {
732+ "resize" | "open" => Some(dims(&v)),
733+ _ => None,
734+ }
735+}
736+
737+fn dims(v: &serde_json::Value) -> (u16, u16) {
738+ let get = |k: &str, d: u64| v.get(k).and_then(|x| x.as_u64()).unwrap_or(d).clamp(1, 1000) as u16;
739+ (get("cols", 80), get("rows", 24))
740+}
741+
742+async fn echo_loop<S>(
743+ mut ws: WebSocketStream<S>,
744+) -> Result<(), Box<dyn std::error::Error + Send + Sync>>
745+where
746+ S: AsyncRead + AsyncWrite + Unpin + Send + 'static,
747+{
748+ while let Some(Ok(msg)) = ws.next().await {
749+ match msg {
750+ Message::Text(t) => ws.send(Message::Text(t)).await?,
751+ Message::Binary(b) => ws.send(Message::Binary(b)).await?,
752+ Message::Close(_) => break,
753+ _ => {}
754+ }
755+ }
756+ Ok(())
757+}
758+
759+#[cfg(test)]
760+mod tests {
761+ use super::*;
762+
763+ /// A lone session is what the user is already working in, so a client that
764+ /// names none should land there rather than in a fresh `browser` session.
765+ /// Two sessions is ambiguous, and the configured default wins again.
766+ ///
767+ /// Runs against a real tmux on a private socket; skipped where there is no
768+ /// tmux to talk to.
769+ #[test]
770+ fn a_single_existing_session_is_adopted() {
771+ if !crate::pty::Profile::tmux_available() {
772+ eprintln!("skipping: no tmux on PATH");
773+ return;
774+ }
775+ let socket = "termbridge-sole-session-test";
776+ let tmux = |args: &[&str]| {
777+ std::process::Command::new("tmux")
778+ .args(["-L", socket])
779+ .args(args)
780+ .stdout(std::process::Stdio::null())
781+ .stderr(std::process::Stdio::null())
782+ .status()
783+ };
784+ let _ = tmux(&["kill-server"]);
785+
786+ let mut config = Config::new("t", vec![]);
787+ config.profile = crate::pty::Profile {
788+ program: "tmux".into(),
789+ args: vec![
790+ "-L".into(),
791+ socket.into(),
792+ "new-session".into(),
793+ "-A".into(),
794+ "-s".into(),
795+ "browser".into(),
796+ ],
797+ };
798+ config.default_session = "browser".into();
799+ config.adopt_sole_session = true;
800+
801+ // No server running: nothing to adopt.
802+ assert_eq!(config.adopted_session(), None);
803+
804+ let _ = tmux(&["new-session", "-d", "-s", "work"]);
805+ assert_eq!(config.adopted_session().as_deref(), Some("work"));
806+
807+ // Pinned by the command line: the user's choice is not overridden.
808+ config.adopt_sole_session = false;
809+ assert_eq!(config.adopted_session(), None);
810+ config.adopt_sole_session = true;
811+
812+ // Two sessions is ambiguous, so the profile's own session stands.
813+ let _ = tmux(&["new-session", "-d", "-s", "other"]);
814+ assert_eq!(config.adopted_session(), None);
815+
816+ let _ = tmux(&["kill-server"]);
817+ }
818+
819+ /// The wire protocol must not be able to name a tmux command. Anything the
820+ /// sidebar sends either maps to one of the allowlisted variants or is
821+ /// dropped.
822+ #[test]
823+ fn only_the_allowlisted_requests_parse() {
824+ let req = |s: &str| parse_tmux_request(s);
825+ assert_eq!(
826+ req(r#"{"type":"tmux","cmd":"switch","session":"work"}"#),
827+ Some(TmuxRequest::Switch("work".into()))
828+ );
829+ assert_eq!(
830+ req(r#"{"type":"tmux","cmd":"focus","pane":"%12"}"#),
831+ Some(TmuxRequest::Focus("%12".into()))
832+ );
833+ assert_eq!(
834+ req(r#"{"type":"tmux","cmd":"select-window","window":"@3"}"#),
835+ Some(TmuxRequest::SelectWindow("@3".into()))
836+ );
837+ assert_eq!(
838+ req(r#"{"type":"tmux","cmd":"new-window","session":"work"}"#),
839+ Some(TmuxRequest::NewWindow("work".into()))
840+ );
841+ assert_eq!(
842+ req(r#"{"type":"tmux","cmd":"kill-window","window":"@3"}"#),
843+ Some(TmuxRequest::KillWindow("@3".into()))
844+ );
845+ // Killing anything larger than a window is still not expressible.
846+ assert_eq!(req(r#"{"type":"tmux","cmd":"kill-session","session":"work"}"#), None);
847+ assert_eq!(req(r#"{"type":"tmux","cmd":"kill-pane","pane":"%3"}"#), None);
848+ assert_eq!(req(r#"{"type":"tmux","cmd":"kill-server"}"#), None);
849+ assert_eq!(req(r#"{"type":"tmux","cmd":"run","command":"rm -rf /"}"#), None);
850+ assert_eq!(req(r#"{"type":"resize","cols":80,"rows":24}"#), None);
851+ }
852+
853+ /// Names and pane ids are quoted into a command line, so the validators are
854+ /// the boundary. Quotes, semicolons and spaces must not survive parsing.
855+ #[test]
856+ fn hostile_arguments_are_rejected_not_escaped() {
857+ let req = |s: &str| parse_tmux_request(s);
858+ for hostile in [
859+ r#"{"type":"tmux","cmd":"switch","session":"a' ; kill-server ; '"}"#,
860+ r#"{"type":"tmux","cmd":"switch","session":"-C"}"#,
861+ r#"{"type":"tmux","cmd":"switch","session":"a b"}"#,
862+ r#"{"type":"tmux","cmd":"focus","pane":"%1 ; kill-server"}"#,
863+ r#"{"type":"tmux","cmd":"focus","pane":"$1"}"#,
864+ r#"{"type":"tmux","cmd":"focus","pane":"%"}"#,
865+ r#"{"type":"tmux","cmd":"select-window","window":"@1 ; kill-server"}"#,
866+ r#"{"type":"tmux","cmd":"select-window","window":"%1"}"#,
867+ r#"{"type":"tmux","cmd":"select-window","window":"@"}"#,
868+ // An index is not an id: `2` would be a bare target, and
869+ // `session:2.0` carries syntax of its own.
870+ r#"{"type":"tmux","cmd":"select-window","window":"2"}"#,
871+ r#"{"type":"tmux","cmd":"new-window","session":"a' ; kill-server ; '"}"#,
872+ r#"{"type":"tmux","cmd":"new-window","session":"work:1"}"#,
873+ // The destructive one gets the same validator, and `-a` is the
874+ // difference between one window and every window.
875+ r#"{"type":"tmux","cmd":"kill-window","window":"@1 ; kill-server"}"#,
876+ r#"{"type":"tmux","cmd":"kill-window","window":"-a"}"#,
877+ r#"{"type":"tmux","cmd":"kill-window","window":""}"#,
878+ ] {
879+ assert_eq!(req(hostile), None, "accepted {hostile}");
880+ }
881+ }
882+
883+ #[test]
884+ fn window_commands_stay_inside_their_session() {
885+ // No -c: windows belong to the session, so this moves whoever is
886+ // watching it — the same thing `prefix 2` in the terminal does.
887+ let lines = command_lines(&TmuxRequest::SelectWindow("@3".into()), Some("/dev/pts/7"));
888+ assert_eq!(lines, vec!["select-window -t '@3'"]);
889+
890+ // -a rather than an index, which could collide with an existing window.
891+ let lines = command_lines(&TmuxRequest::NewWindow("work".into()), None);
892+ assert_eq!(lines, vec!["new-window -a -t 'work:'"]);
893+
894+ // One window, named by id. No -a, which would kill all *but* it.
895+ let lines = command_lines(&TmuxRequest::KillWindow("@3".into()), Some("/dev/pts/7"));
896+ assert_eq!(lines, vec!["kill-window -t '@3'"]);
897+ }
898+
899+ #[test]
900+ fn switch_targets_our_own_client() {
901+ let lines = command_lines(&TmuxRequest::Switch("work".into()), Some("/dev/pts/7"));
902+ assert_eq!(lines, vec!["switch-client -c '/dev/pts/7' -t 'work'"]);
903+ // No tty is a degraded but safe case: tmux picks a client itself.
904+ let lines = command_lines(&TmuxRequest::Switch("work".into()), None);
905+ assert_eq!(lines, vec!["switch-client -t 'work'"]);
906+ // A tty that doesn't look like one never reaches the command line.
907+ let lines = command_lines(&TmuxRequest::Switch("work".into()), Some("nope'; x"));
908+ assert_eq!(lines, vec!["switch-client -t 'work'"]);
909+ }
910+
911+ #[test]
912+ fn create_attaches_rather_than_failing_on_an_existing_name() {
913+ let lines = command_lines(&TmuxRequest::Create("scratch".into()), None);
914+ assert_eq!(lines[0], "new-session -d -A -s 'scratch'");
915+ assert_eq!(lines[1], "switch-client -t 'scratch'");
916+ }
917+}
addeddaemon/src/status.rs+205 −0
1+//! What the sidebar shows above the terminal: which tmux session we're on, what
2+//! else is available, and what each Claude Code in the session is doing.
3+//!
4+//! Two sources, both authoritative, neither guessed:
5+//!
6+//! - tmux, asked over the [control channel](crate::control), for the session
7+//! our client is attached to and the panes in it.
8+//! - Claude Code's hook interface, via the records in [`crate::agents`].
9+//!
10+//! The join between them is the tmux pane id.
11+
12+use serde::Serialize;
13+
14+use crate::control::Control;
15+
16+/// Tab character, as tmux sees it. Single quotes make tmux take the byte
17+/// literally, so the separator survives into the output and window names
18+/// containing spaces or `|` stay intact.
19+const T: char = '\t';
20+
21+/// One pane running Claude Code.
22+#[derive(Serialize, Debug, Clone, PartialEq, Eq)]
23+pub struct Agent {
24+ /// tmux pane id (`%12`), stable for the pane's lifetime.
25+ pub pane: String,
26+ /// tmux window id (`@3`) — the join to [`WindowInfo`], so the sidebar can
27+ /// mark the tab an agent is in without matching on a shifting index.
28+ pub window_id: String,
29+ pub window: String,
30+ /// Window name — what the user sees in the tmux status line.
31+ pub name: String,
32+ /// `working` | `waiting` | `idle`, or `unknown` when the pane is running
33+ /// Claude but no hook has ever reported for it.
34+ pub state: &'static str,
35+ /// Claude's `permission_mode`: `default`, `acceptEdits`, `plan`,
36+ /// `bypassPermissions`.
37+ pub mode: Option<String>,
38+ /// Tool in flight, while working.
39+ pub tool: Option<String>,
40+ /// Why it is waiting on you.
41+ pub message: Option<String>,
42+ /// Claude's session title, when it has one.
43+ pub title: Option<String>,
44+}
45+
46+#[derive(Serialize, Debug, Clone, PartialEq, Eq)]
47+pub struct SessionInfo {
48+ pub name: String,
49+ pub windows: u32,
50+ pub attached: bool,
51+}
52+
53+/// One window in the attached session — one tab in the sidebar.
54+#[derive(Serialize, Debug, Clone, PartialEq, Eq)]
55+pub struct WindowInfo {
56+ /// tmux window id (`@3`). Stable for the window's lifetime, unlike the
57+ /// index, which shifts when a window before it closes — so this, not the
58+ /// index, is what the sidebar sends back to select one.
59+ pub id: String,
60+ /// What the user sees in the status line and types as `prefix 2`.
61+ pub index: u32,
62+ pub name: String,
63+ pub active: bool,
64+ pub panes: u32,
65+ /// Output arrived here since it was last looked at.
66+ pub activity: bool,
67+}
68+
69+#[derive(Serialize, Debug, Clone, PartialEq, Eq)]
70+pub struct Snapshot {
71+ /// Session our client is attached to *right now*, not the one we asked for.
72+ pub session: Option<String>,
73+ /// Every session on the server, for the sidebar's switcher.
74+ pub sessions: Vec<SessionInfo>,
75+ /// Windows of the attached session, in index order — the sidebar's tabs.
76+ pub windows: Vec<WindowInfo>,
77+ pub agents: Vec<Agent>,
78+}
79+
80+/// Ask tmux, then fold in the hook records.
81+pub async fn snapshot(control: &Control, tty: Option<&str>) -> Snapshot {
82+ let session = current_session(control, tty).await;
83+ let sessions = list_sessions(control).await;
84+ let (windows, agents) = match &session {
85+ Some(s) => (list_windows(control, s).await, agents_in(control, s).await),
86+ None => (Vec::new(), Vec::new()),
87+ };
88+ Snapshot {
89+ session,
90+ sessions,
91+ windows,
92+ agents,
93+ }
94+}
95+
96+/// Which session the interactive client on `tty` is on.
97+///
98+/// tmux is the authority: the client can leave the session it was spawned with
99+/// (`switch-client`, `choose-tree`, prefix-`(`/`)`), and the name we passed to
100+/// `new-session` goes stale the moment it does.
101+pub async fn current_session(control: &Control, tty: Option<&str>) -> Option<String> {
102+ let tty = tty?;
103+ let out = control
104+ .run(format!("list-clients -F '#{{client_tty}}{T}#{{session_name}}'"))
105+ .await
106+ .ok()?;
107+ out.iter()
108+ .filter_map(|l| l.split_once(T))
109+ .find(|(client_tty, _)| *client_tty == tty)
110+ .map(|(_, name)| name.trim().to_string())
111+ .filter(|name| !name.is_empty())
112+}
113+
114+async fn list_sessions(control: &Control) -> Vec<SessionInfo> {
115+ let fmt = format!("#{{session_name}}{T}#{{session_windows}}{T}#{{session_attached}}");
116+ let Ok(out) = control.run(format!("list-sessions -F '{fmt}'")).await else {
117+ return Vec::new();
118+ };
119+ out.iter()
120+ .filter_map(|line| {
121+ let mut f = line.split(T);
122+ Some(SessionInfo {
123+ name: f.next()?.to_string(),
124+ windows: f.next()?.parse().unwrap_or(0),
125+ attached: f.next().unwrap_or("0") != "0",
126+ })
127+ })
128+ .collect()
129+}
130+
131+/// Windows of `session`, in the order tmux lists them (by index).
132+///
133+/// `session` reaches here from tmux itself or from a name validated in
134+/// [`crate::pty::valid_session_name`], and is single-quoted besides.
135+async fn list_windows(control: &Control, session: &str) -> Vec<WindowInfo> {
136+ let fmt = format!(
137+ "#{{window_id}}{T}#{{window_index}}{T}#{{window_name}}{T}\
138+ #{{window_active}}{T}#{{window_panes}}{T}#{{window_activity_flag}}"
139+ );
140+ let Ok(out) = control
141+ .run(format!("list-windows -t '{session}' -F '{fmt}'"))
142+ .await
143+ else {
144+ return Vec::new();
145+ };
146+ out.iter()
147+ .filter_map(|line| {
148+ let mut f = line.split(T);
149+ Some(WindowInfo {
150+ id: f.next()?.to_string(),
151+ index: f.next()?.parse().ok()?,
152+ name: f.next()?.to_string(),
153+ active: f.next().unwrap_or("0") != "0",
154+ panes: f.next().and_then(|p| p.parse().ok()).unwrap_or(1),
155+ activity: f.next().unwrap_or("0") != "0",
156+ })
157+ })
158+ .collect()
159+}
160+
161+async fn agents_in(control: &Control, session: &str) -> Vec<Agent> {
162+ let fmt = format!(
163+ "#{{pane_id}}{T}#{{window_id}}{T}#{{window_index}}{T}\
164+ #{{window_name}}{T}#{{pane_current_command}}"
165+ );
166+ // `session` reaches here from tmux itself or from a validated name, and is
167+ // single-quoted besides.
168+ let Ok(out) = control
169+ .run(format!("list-panes -s -t '{session}' -F '{fmt}'"))
170+ .await
171+ else {
172+ return Vec::new();
173+ };
174+ let records = crate::agents::by_pane();
175+
176+ out.iter()
177+ .filter_map(|line| {
178+ let mut f = line.split(T);
179+ let pane = f.next()?.to_string();
180+ let window_id = f.next()?.to_string();
181+ let window = f.next()?.to_string();
182+ let name = f.next()?.to_string();
183+ let cmd = f.next().unwrap_or("");
184+ let record = records.get(&pane);
185+ // A pane qualifies if Claude is the foreground process, or if a
186+ // hook has reported for it — Claude that shelled out to a
187+ // long-running command shows that command as pane_current_command
188+ // and would otherwise vanish from the sidebar mid-run.
189+ if cmd != "claude" && record.is_none() {
190+ return None;
191+ }
192+ Some(Agent {
193+ pane,
194+ window_id,
195+ window,
196+ name,
197+ state: record.map(|r| r.state.as_str()).unwrap_or("unknown"),
198+ mode: record.and_then(|r| r.mode.clone()),
199+ tool: record.and_then(|r| r.tool.clone()),
200+ message: record.and_then(|r| r.message.clone()),
201+ title: record.and_then(|r| r.name.clone()),
202+ })
203+ })
204+ .collect()
205+}
addeddaemon/src/tls.rs+219 −0
1+//! Self-signed TLS for loopback.
2+//!
3+//! Firefox's HTTPS-Only Mode rewrites `ws://` to `wss://`, including for
4+//! loopback. Rather than asking people to weaken a browser security setting so
5+//! our daemon can run, we just speak TLS.
6+//!
7+//! The certificate is generated once, stored in the config dir with the key at
8+//! 0600, and covers `127.0.0.1`, `::1` and `localhost`. It is self-signed, so
9+//! the browser needs a one-time exception — see `serve` output.
10+
11+use std::fs;
12+use std::io::Write;
13+use std::os::unix::fs::{OpenOptionsExt, PermissionsExt};
14+use std::path::Path;
15+use std::sync::Arc;
16+
17+use tokio_rustls::rustls::pki_types::{CertificateDer, PrivateKeyDer};
18+use tokio_rustls::rustls::ServerConfig;
19+use tokio_rustls::TlsAcceptor;
20+
21+pub const CERT_FILE: &str = "cert.pem";
22+pub const KEY_FILE: &str = "key.pem";
23+
24+/// Regenerate when the cert is within this of expiry, so a long-lived install
25+/// doesn't silently start failing.
26+const VALIDITY_DAYS: i64 = 3650;
27+
28+pub struct Identity {
29+ pub cert_pem: String,
30+ pub key_pem: String,
31+ /// SHA-256 of the DER certificate, colon-separated — the same fingerprint
32+ /// the browser shows, so a user can verify they're trusting our cert.
33+ pub fingerprint: String,
34+}
35+
36+fn fingerprint(der: &[u8]) -> String {
37+ // Avoid pulling in a hash crate for one call; rcgen already depends on ring.
38+ let digest = ring_sha256(der);
39+ digest
40+ .iter()
41+ .map(|b| format!("{b:02X}"))
42+ .collect::<Vec<_>>()
43+ .join(":")
44+}
45+
46+fn ring_sha256(data: &[u8]) -> Vec<u8> {
47+ use sha2::{Digest, Sha256};
48+ Sha256::digest(data).to_vec()
49+}
50+
51+/// Load the existing identity, or generate one on first run.
52+pub fn load_or_create(dir: &Path) -> std::io::Result<Identity> {
53+ let cert_path = dir.join(CERT_FILE);
54+ let key_path = dir.join(KEY_FILE);
55+
56+ if cert_path.exists() && key_path.exists() {
57+ let mode = fs::metadata(&key_path)?.permissions().mode() & 0o777;
58+ if mode & 0o077 != 0 {
59+ return Err(std::io::Error::other(format!(
60+ "{} has mode {mode:04o}; expected 0600. Fix with: chmod 600 {}",
61+ key_path.display(),
62+ key_path.display()
63+ )));
64+ }
65+ let cert_pem = fs::read_to_string(&cert_path)?;
66+ let key_pem = fs::read_to_string(&key_path)?;
67+ let der = first_cert_der(&cert_pem)?;
68+ return Ok(Identity {
69+ fingerprint: fingerprint(&der),
70+ cert_pem,
71+ key_pem,
72+ });
73+ }
74+
75+ generate(dir)
76+}
77+
78+pub fn generate(dir: &Path) -> std::io::Result<Identity> {
79+ fs::create_dir_all(dir)?;
80+ fs::set_permissions(dir, fs::Permissions::from_mode(0o700))?;
81+
82+ let mut params = rcgen::CertificateParams::new(vec![
83+ "localhost".to_string(),
84+ "127.0.0.1".to_string(),
85+ "::1".to_string(),
86+ ])
87+ .map_err(|e| std::io::Error::other(e.to_string()))?;
88+
89+ params.distinguished_name = {
90+ let mut dn = rcgen::DistinguishedName::new();
91+ dn.push(rcgen::DnType::CommonName, "termbridge (local)");
92+ dn.push(rcgen::DnType::OrganizationName, "termbridge");
93+ dn
94+ };
95+ params.not_before = rcgen::date_time_ymd(2020, 1, 1);
96+ params.not_after = rcgen::date_time_ymd(2025 + (VALIDITY_DAYS / 365) as i32, 1, 1);
97+ // A leaf, not a CA: this cert can only ever vouch for this host, so
98+ // trusting it cannot be leveraged to impersonate anything else.
99+ params.is_ca = rcgen::IsCa::ExplicitNoCa;
100+
101+ let key = rcgen::KeyPair::generate().map_err(|e| std::io::Error::other(e.to_string()))?;
102+ let cert = params
103+ .self_signed(&key)
104+ .map_err(|e| std::io::Error::other(e.to_string()))?;
105+
106+ let cert_pem = cert.pem();
107+ let key_pem = key.serialize_pem();
108+
109+ fs::write(dir.join(CERT_FILE), &cert_pem)?;
110+ fs::set_permissions(dir.join(CERT_FILE), fs::Permissions::from_mode(0o644))?;
111+
112+ // The private key gets the same treatment as the auth token.
113+ let key_path = dir.join(KEY_FILE);
114+ let mut f = fs::OpenOptions::new()
115+ .write(true)
116+ .create(true)
117+ .truncate(true)
118+ .mode(0o600)
119+ .open(&key_path)?;
120+ f.write_all(key_pem.as_bytes())?;
121+ f.sync_all()?;
122+ fs::set_permissions(&key_path, fs::Permissions::from_mode(0o600))?;
123+
124+ let der = cert.der().to_vec();
125+ Ok(Identity {
126+ fingerprint: fingerprint(&der),
127+ cert_pem,
128+ key_pem,
129+ })
130+}
131+
132+fn first_cert_der(pem: &str) -> std::io::Result<Vec<u8>> {
133+ let mut cursor = std::io::Cursor::new(pem.as_bytes());
134+ let certs: Vec<CertificateDer> = rustls_pemfile_certs(&mut cursor)?;
135+ certs
136+ .first()
137+ .map(|c| c.as_ref().to_vec())
138+ .ok_or_else(|| std::io::Error::other("no certificate in cert.pem"))
139+}
140+
141+/// Minimal PEM decoder so we don't add rustls-pemfile for two call sites.
142+fn rustls_pemfile_certs(
143+ r: &mut std::io::Cursor<&[u8]>,
144+) -> std::io::Result<Vec<CertificateDer<'static>>> {
145+ let text = String::from_utf8_lossy(*r.get_ref()).to_string();
146+ let mut out = Vec::new();
147+ for block in text.split("-----BEGIN CERTIFICATE-----").skip(1) {
148+ let Some(body) = block.split("-----END CERTIFICATE-----").next() else {
149+ continue;
150+ };
151+ let b64: String = body.chars().filter(|c| !c.is_whitespace()).collect();
152+ out.push(CertificateDer::from(base64_decode(&b64)?));
153+ }
154+ Ok(out)
155+}
156+
157+fn base64_decode(s: &str) -> std::io::Result<Vec<u8>> {
158+ const TABLE: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
159+ let mut rev = [255u8; 256];
160+ for (i, c) in TABLE.iter().enumerate() {
161+ rev[*c as usize] = i as u8;
162+ }
163+ let mut out = Vec::new();
164+ let mut acc: u32 = 0;
165+ let mut bits = 0;
166+ for c in s.bytes() {
167+ if c == b'=' {
168+ break;
169+ }
170+ let v = rev[c as usize];
171+ if v == 255 {
172+ return Err(std::io::Error::other("invalid base64 in PEM"));
173+ }
174+ acc = (acc << 6) | v as u32;
175+ bits += 6;
176+ if bits >= 8 {
177+ bits -= 8;
178+ out.push((acc >> bits) as u8);
179+ }
180+ }
181+ Ok(out)
182+}
183+
184+pub fn acceptor(identity: &Identity) -> std::io::Result<TlsAcceptor> {
185+ let mut cert_cursor = std::io::Cursor::new(identity.cert_pem.as_bytes());
186+ let certs = rustls_pemfile_certs(&mut cert_cursor)?;
187+
188+ let key = parse_private_key(&identity.key_pem)?;
189+
190+ let config = ServerConfig::builder()
191+ .with_no_client_auth()
192+ .with_single_cert(certs, key)
193+ .map_err(|e| std::io::Error::other(e.to_string()))?;
194+
195+ Ok(TlsAcceptor::from(Arc::new(config)))
196+}
197+
198+fn parse_private_key(pem: &str) -> std::io::Result<PrivateKeyDer<'static>> {
199+ for (begin, end) in [
200+ ("-----BEGIN PRIVATE KEY-----", "-----END PRIVATE KEY-----"),
201+ ("-----BEGIN EC PRIVATE KEY-----", "-----END EC PRIVATE KEY-----"),
202+ ("-----BEGIN RSA PRIVATE KEY-----", "-----END RSA PRIVATE KEY-----"),
203+ ] {
204+ if let Some(rest) = pem.split(begin).nth(1) {
205+ if let Some(body) = rest.split(end).next() {
206+ let b64: String = body.chars().filter(|c| !c.is_whitespace()).collect();
207+ let der = base64_decode(&b64)?;
208+ return Ok(match begin {
209+ "-----BEGIN EC PRIVATE KEY-----" => {
210+ PrivateKeyDer::Sec1(der.into())
211+ }
212+ "-----BEGIN RSA PRIVATE KEY-----" => PrivateKeyDer::Pkcs1(der.into()),
213+ _ => PrivateKeyDer::Pkcs8(der.into()),
214+ });
215+ }
216+ }
217+ }
218+ Err(std::io::Error::other("no private key found in key.pem"))
219+}
addeddaemon/src/window_status.rs+167 −0
1+//! The same Claude glyph the sidebar's tabs wear, published into tmux's own
2+//! status line.
3+//!
4+//! The sidebar gets its state over the daemon's control channel, which only
5+//! exists while a browser is attached — exactly the case where you are not
6+//! looking at the terminal. So this takes the other route: `termbridge hook`
7+//! already runs on every Claude Code event, inside the pane it is reporting on,
8+//! with `$TMUX` pointing at the right server. It sets a window user option, and
9+//! tmux redraws on the option change.
10+//!
11+//! The user opts in with one line in `~/.tmux.conf` (`termbridge tmux` prints
12+//! it). Nothing here touches a window that has never had a Claude in it, and
13+//! nothing here can change a window's name, layout or contents: the only
14+//! command it issues is `set-option -w @tb_claude`.
15+
16+use std::process::{Command, Stdio};
17+
18+use crate::agents::{self, Record, State};
19+
20+/// The option the format reads. `@`-prefixed, so tmux treats it as a user
21+/// option and no future tmux release can collide with it.
22+const OPTION: &str = "@tb_claude";
23+
24+/// Claude Code's asterisk cycle. A working window steps one frame per hook
25+/// event rather than on a timer: tmux only redraws its status when an option
26+/// changes or `status-interval` elapses, so a real animation would mean
27+/// forcing a full status repaint on every attached client several times a
28+/// second. Stepping on events costs nothing and still reads as alive — the
29+/// glyph moves exactly when Claude crosses a tool boundary.
30+const FRAMES: [&str; 6] = ["·", "✢", "✳", "∗", "✻", "✽"];
31+
32+/// Recompute the glyph for the window the hook fired in, from every Claude
33+/// record in that window.
34+///
35+/// Errors are swallowed all the way down. This runs inside a Claude Code hook,
36+/// where the cost of failing loudly is an interrupted session and the cost of
37+/// failing quietly is a stale symbol until the next event.
38+pub fn publish() {
39+ // Not in tmux, or not under a tmux that would show it: nothing to say.
40+ if std::env::var_os("TMUX").is_none() {
41+ return;
42+ }
43+ let Ok(pane) = std::env::var("TMUX_PANE") else {
44+ return;
45+ };
46+ let panes = window_panes(&pane);
47+ let by_pane = agents::by_pane();
48+ // A window is one tab in the sidebar and one entry in the status line, so
49+ // it reports the loudest thing in it — the same rule, and the same reason:
50+ // a window with something waiting on you outranks one that is merely busy.
51+ let winner = panes
52+ .iter()
53+ .filter_map(|p| by_pane.get(p))
54+ .min_by_key(|r| rank(r.state));
55+
56+ match winner.map(glyph) {
57+ Some(value) => set_option(&pane, &value),
58+ None => unset_option(&pane),
59+ }
60+}
61+
62+fn rank(state: State) -> u8 {
63+ match state {
64+ State::Waiting => 0,
65+ State::Working => 1,
66+ State::Idle => 2,
67+ }
68+}
69+
70+/// Styled inline, because the option's value is expanded into the status format
71+/// and tmux reads `#[...]` in the result. `#[default]` hands the style back to
72+/// whatever the user's own format was using, so this cannot leak colour into
73+/// the window name that follows it.
74+fn glyph(r: &Record) -> String {
75+ let (colour, sym) = match r.state {
76+ State::Working => ("yellow", FRAMES[r.frame as usize % FRAMES.len()]),
77+ State::Waiting => ("magenta", "✳"),
78+ State::Idle => ("brightblack", "✻"),
79+ };
80+ format!("#[fg={colour}]{sym}#[default] ")
81+}
82+
83+/// The panes of the window `pane` is in — one tmux call, and the only thing we
84+/// need tmux itself to tell us. The hook records key on pane id, so this is the
85+/// whole join.
86+fn window_panes(pane: &str) -> Vec<String> {
87+ let out = tmux(&["list-panes", "-t", pane, "-F", "#{pane_id}"]);
88+ out.lines().map(str::to_string).collect()
89+}
90+
91+/// `-w` with a pane target sets the option on that pane's window, so the pane
92+/// id is the only handle needed.
93+fn set_option(pane: &str, value: &str) {
94+ let _ = tmux(&["set-option", "-w", "-t", pane, OPTION, value]);
95+}
96+
97+fn unset_option(pane: &str) {
98+ let _ = tmux(&["set-option", "-wu", "-t", pane, OPTION]);
99+}
100+
101+/// No `-L`/`-S`: the hook runs inside the pane, so `$TMUX` already names the
102+/// server the user is looking at, and inheriting it is what makes this correct
103+/// under multiple tmux servers.
104+fn tmux(args: &[&str]) -> String {
105+ Command::new("tmux")
106+ .args(args)
107+ .stdin(Stdio::null())
108+ .stderr(Stdio::null())
109+ .output()
110+ .ok()
111+ .and_then(|o| String::from_utf8(o.stdout).ok())
112+ .unwrap_or_default()
113+}
114+
115+/// What `termbridge tmux` prints. Two formats, because tmux styles the current
116+/// window with a separate one and a glyph that vanished on the window you were
117+/// on would be the wrong half to lose.
118+pub fn tmux_conf() -> String {
119+ format!(
120+ "# Claude Code status in the tmux status line, from termbridge's hooks.\n\
121+ # The option is empty for a window with no Claude in it, so windows\n\
122+ # that never see one look exactly as they do now.\n\
123+ set -g window-status-format \"#{{{OPTION}}}#I:#W#F\"\n\
124+ set -g window-status-current-format \"#{{{OPTION}}}#I:#W#F\"\n"
125+ )
126+}
127+
128+#[cfg(test)]
129+mod tests {
130+ use super::*;
131+
132+ fn record(state: State, frame: u8) -> Record {
133+ Record {
134+ session_id: "s".into(),
135+ pane: Some("%1".into()),
136+ cwd: "/tmp".into(),
137+ state,
138+ mode: None,
139+ tool: None,
140+ message: None,
141+ name: None,
142+ frame,
143+ updated: 0,
144+ }
145+ }
146+
147+ #[test]
148+ fn working_steps_through_the_cycle() {
149+ let frames: Vec<String> = (0..7).map(|f| glyph(&record(State::Working, f))).collect();
150+ assert!(frames[0].contains('·'));
151+ assert!(frames[4].contains('✻'));
152+ // Wraps rather than running off the end of the array.
153+ assert_eq!(frames[0], frames[6]);
154+ assert!(frames.iter().all(|f| f.ends_with("#[default] ")));
155+ }
156+
157+ #[test]
158+ fn waiting_outranks_working_outranks_idle() {
159+ assert!(rank(State::Waiting) < rank(State::Working));
160+ assert!(rank(State::Working) < rank(State::Idle));
161+ }
162+
163+ #[test]
164+ fn the_conf_names_the_option_the_hook_sets() {
165+ assert!(tmux_conf().contains(&format!("#{{{OPTION}}}")));
166+ }
167+}
addeddaemon/tests/pty_e2e.rs+559 −0
1+//! End-to-end: browser-shaped client -> WebSocket -> pty -> shell -> back.
2+//!
3+//! Uses `sh` rather than tmux so the test doesn't depend on a tmux server or
4+//! leave sessions behind. The pty path is identical either way.
5+
6+use std::time::Duration;
7+
8+use futures_util::{SinkExt, StreamExt};
9+use tokio_tungstenite::tungstenite::client::IntoClientRequest;
10+use tokio_tungstenite::tungstenite::Message;
11+
12+use termbridge::pty::Profile;
13+use termbridge::{Config, Server};
14+
15+const TOKEN: &str = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
16+const ORIGIN: &str = "chrome-extension://abcdefghijklmnopabcdefghijklmnop";
17+
18+fn sh_profile() -> Profile {
19+ Profile {
20+ program: "/bin/sh".into(),
21+ args: vec![],
22+ }
23+}
24+
25+async fn connect_authed(
26+ server: &Server,
27+) -> tokio_tungstenite::WebSocketStream<
28+ tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>,
29+> {
30+ let mut req = server.url().into_client_request().unwrap();
31+ req.headers_mut().insert("origin", ORIGIN.parse().unwrap());
32+ let (mut ws, _) = tokio_tungstenite::connect_async(req).await.unwrap();
33+
34+ ws.send(Message::Text(
35+ serde_json::json!({"type": "auth", "token": TOKEN})
36+ .to_string()
37+ .into(),
38+ ))
39+ .await
40+ .unwrap();
41+ let ok = ws.next().await.unwrap().unwrap();
42+ assert!(ok.to_text().unwrap().contains("\"ok\""), "{ok:?}");
43+ ws
44+}
45+
46+/// Read binary frames until `needle` shows up in the accumulated output.
47+async fn wait_for(
48+ ws: &mut tokio_tungstenite::WebSocketStream<
49+ tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>,
50+ >,
51+ needle: &str,
52+) -> String {
53+ let mut acc = Vec::new();
54+ let found = tokio::time::timeout(Duration::from_secs(10), async {
55+ while let Some(Ok(msg)) = ws.next().await {
56+ if let Message::Binary(b) = msg {
57+ acc.extend_from_slice(&b);
58+ if String::from_utf8_lossy(&acc).contains(needle) {
59+ return true;
60+ }
61+ }
62+ }
63+ false
64+ })
65+ .await
66+ .unwrap_or(false);
67+
68+ let text = String::from_utf8_lossy(&acc).to_string();
69+ assert!(found, "never saw {needle:?}; got:\n{}", printable(&text));
70+ text
71+}
72+
73+/// Escape control bytes so a failing assertion can't repaint the terminal that
74+/// is printing it.
75+fn printable(s: &str) -> String {
76+ s.chars()
77+ .map(|c| match c {
78+ '\n' | '\t' => c.to_string(),
79+ c if (c as u32) < 0x20 || c as u32 == 0x7f => format!("\\x{:02x}", c as u32),
80+ c => c.to_string(),
81+ })
82+ .collect()
83+}
84+
85+/// Wait until the shell inside tmux is actually consuming input.
86+///
87+/// tmux drops keystrokes that arrive before its client has finished attaching,
88+/// so anything typed immediately after `open` can vanish. Probe until the echo
89+/// comes back rather than assuming a fixed delay is enough.
90+async fn wait_until_ready(
91+ ws: &mut tokio_tungstenite::WebSocketStream<
92+ tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>,
93+ >,
94+ probe: &str,
95+) {
96+ let deadline = tokio::time::Instant::now() + Duration::from_secs(20);
97+ let mut acc = Vec::new();
98+ loop {
99+ assert!(
100+ tokio::time::Instant::now() < deadline,
101+ "shell never became ready; saw:\n{}",
102+ printable(&String::from_utf8_lossy(&acc))
103+ );
104+ ws.send(Message::Binary(format!("echo {probe}\n").into_bytes().into()))
105+ .await
106+ .unwrap();
107+
108+ let until = tokio::time::Instant::now() + Duration::from_millis(700);
109+ loop {
110+ match tokio::time::timeout_at(until, ws.next()).await {
111+ Ok(Some(Ok(Message::Binary(b)))) => {
112+ acc.extend_from_slice(&b);
113+ // Twice: once as terminal echo of what we typed, once as
114+ // the command's own output. One occurrence only proves the
115+ // characters were displayed, not that a shell ran them.
116+ if String::from_utf8_lossy(&acc).matches(probe).count() >= 2 {
117+ return;
118+ }
119+ }
120+ Ok(Some(Ok(_))) => {}
121+ Ok(Some(Err(e))) => panic!("connection error while waiting: {e}"),
122+ Ok(None) => panic!("connection closed while waiting for the shell"),
123+ Err(_) => break, // no progress this round; probe again
124+ }
125+ }
126+ }
127+}
128+
129+async fn start(profile: Profile) -> Server {
130+ let mut cfg = Config::new(TOKEN, vec![ORIGIN.to_string()]);
131+ cfg.profile = profile;
132+ Server::start(cfg, 0).await.unwrap()
133+}
134+
135+#[tokio::test]
136+async fn shell_runs_and_output_comes_back_as_binary() {
137+ let server = start(sh_profile()).await;
138+ let mut ws = connect_authed(&server).await;
139+
140+ ws.send(Message::Text(
141+ serde_json::json!({"type": "open", "cols": 80, "rows": 24})
142+ .to_string()
143+ .into(),
144+ ))
145+ .await
146+ .unwrap();
147+
148+ // Keystrokes go as binary, exactly as the sidebar will send them.
149+ ws.send(Message::Binary(b"echo hello-from-pty\n".to_vec().into()))
150+ .await
151+ .unwrap();
152+
153+ wait_for(&mut ws, "hello-from-pty").await;
154+}
155+
156+/// The pty must report the size we asked for — this is what makes tmux and vim
157+/// lay out correctly in a narrow sidebar.
158+#[tokio::test]
159+async fn winsize_is_applied_and_resizable() {
160+ let server = start(sh_profile()).await;
161+ let mut ws = connect_authed(&server).await;
162+
163+ ws.send(Message::Text(
164+ serde_json::json!({"type": "open", "cols": 40, "rows": 20})
165+ .to_string()
166+ .into(),
167+ ))
168+ .await
169+ .unwrap();
170+ ws.send(Message::Binary(b"stty size\n".to_vec().into()))
171+ .await
172+ .unwrap();
173+ wait_for(&mut ws, "20 40").await;
174+
175+ // Now resize, as the sidebar does when the user drags its edge.
176+ ws.send(Message::Text(
177+ serde_json::json!({"type": "resize", "cols": 100, "rows": 30})
178+ .to_string()
179+ .into(),
180+ ))
181+ .await
182+ .unwrap();
183+ tokio::time::sleep(Duration::from_millis(200)).await;
184+ ws.send(Message::Binary(b"stty size\n".to_vec().into()))
185+ .await
186+ .unwrap();
187+ wait_for(&mut ws, "30 100").await;
188+}
189+
190+/// The pty is a byte pipe. Anything that isn't valid UTF-8 must survive, which
191+/// is the property a JSON transport could not have given us.
192+#[tokio::test]
193+async fn non_utf8_output_survives_intact() {
194+ let server = start(sh_profile()).await;
195+ let mut ws = connect_authed(&server).await;
196+ ws.send(Message::Text(
197+ serde_json::json!({"type": "open", "cols": 80, "rows": 24})
198+ .to_string()
199+ .into(),
200+ ))
201+ .await
202+ .unwrap();
203+
204+ // 0xff is not valid UTF-8 anywhere. Bracket it so we can find it.
205+ ws.send(Message::Binary(
206+ b"printf 'S\\377\\376E\\n'\n".to_vec().into(),
207+ ))
208+ .await
209+ .unwrap();
210+
211+ let mut acc = Vec::new();
212+ let found = tokio::time::timeout(Duration::from_secs(10), async {
213+ while let Some(Ok(Message::Binary(b))) = ws.next().await {
214+ acc.extend_from_slice(&b);
215+ if acc.windows(4).any(|w| w == [b'S', 0xff, 0xfe, b'E']) {
216+ return true;
217+ }
218+ }
219+ false
220+ })
221+ .await
222+ .unwrap_or(false);
223+
224+ assert!(
225+ found,
226+ "raw bytes were mangled in transit; got {:x?}",
227+ &acc[..acc.len().min(400)]
228+ );
229+}
230+
231+/// Exiting the shell must be reported, not silently hang the sidebar.
232+#[tokio::test]
233+async fn shell_exit_is_reported() {
234+ let server = start(sh_profile()).await;
235+ let mut ws = connect_authed(&server).await;
236+ ws.send(Message::Text(
237+ serde_json::json!({"type": "open", "cols": 80, "rows": 24})
238+ .to_string()
239+ .into(),
240+ ))
241+ .await
242+ .unwrap();
243+ ws.send(Message::Binary(b"exit 7\n".to_vec().into()))
244+ .await
245+ .unwrap();
246+
247+ let got = tokio::time::timeout(Duration::from_secs(10), async {
248+ while let Some(Ok(msg)) = ws.next().await {
249+ if let Message::Text(t) = msg {
250+ if t.contains("\"exit\"") {
251+ return Some(t.to_string());
252+ }
253+ }
254+ }
255+ None
256+ })
257+ .await
258+ .unwrap_or(None);
259+
260+ assert!(got.is_some(), "expected an exit message");
261+}
262+
263+/// The client cannot choose what runs. Even authenticated, the protocol has no
264+/// field for argv — an auth bypass gets you the configured profile, not `exec`.
265+#[tokio::test]
266+async fn client_cannot_choose_the_program() {
267+ let server = start(sh_profile()).await;
268+ let mut ws = connect_authed(&server).await;
269+
270+ // Every field an attacker might hope is honoured.
271+ ws.send(Message::Text(
272+ serde_json::json!({
273+ "type": "open", "cols": 80, "rows": 24,
274+ "cmd": ["/bin/sh", "-c", "echo PWNED"],
275+ "command": "echo PWNED",
276+ "program": "/usr/bin/id",
277+ "args": ["-a"],
278+ "env": {"LD_PRELOAD": "/tmp/evil.so"}
279+ })
280+ .to_string()
281+ .into(),
282+ ))
283+ .await
284+ .unwrap();
285+
286+ // Prove the session is the configured shell and that nothing else ran.
287+ ws.send(Message::Binary(b"echo marker-$((6*7))\n".to_vec().into()))
288+ .await
289+ .unwrap();
290+ let out = wait_for(&mut ws, "marker-42").await;
291+ assert!(
292+ !out.contains("PWNED"),
293+ "client-supplied command was executed:\n{out}"
294+ );
295+}
296+
297+/// The whole reason tmux is the persistence layer: state must survive the
298+/// sidebar closing. Disconnect, reconnect, and the shell is still there.
299+#[tokio::test]
300+async fn tmux_session_survives_disconnect() {
301+ if !Profile::tmux_available() {
302+ eprintln!("skipping: tmux not installed");
303+ return;
304+ }
305+ // Private socket + session so we never touch the user's real tmux.
306+ let sock = "termbridge-test";
307+ let sess = "termbridge-e2e";
308+ let tmux = |args: &[&str]| {
309+ std::process::Command::new("tmux")
310+ .args(["-L", sock])
311+ .args(args)
312+ .output()
313+ };
314+ let _ = tmux(&["kill-server"]);
315+
316+ let profile = Profile {
317+ program: "tmux".into(),
318+ // Force /bin/sh: tmux's default-shell may be fish, whose assignment
319+ // syntax differs. The test is about persistence, not shell dialects.
320+ args: ["-L", sock, "new-session", "-A", "-s", sess, "/bin/sh"]
321+ .iter()
322+ .map(|s| s.to_string())
323+ .collect(),
324+ };
325+
326+ let server = start(profile).await;
327+
328+ // First attach: leave a marker in the shell's state.
329+ {
330+ let mut ws = connect_authed(&server).await;
331+ ws.send(Message::Text(
332+ serde_json::json!({"type": "open", "cols": 80, "rows": 24})
333+ .to_string()
334+ .into(),
335+ ))
336+ .await
337+ .unwrap();
338+ wait_until_ready(&mut ws, "boot-one").await;
339+ ws.send(Message::Binary(
340+ b"MARKER=survived-the-disconnect\n".to_vec().into(),
341+ ))
342+ .await
343+ .unwrap();
344+ ws.send(Message::Binary(b"echo first-attach-ok\n".to_vec().into()))
345+ .await
346+ .unwrap();
347+ wait_for(&mut ws, "first-attach-ok").await;
348+ ws.close(None).await.unwrap();
349+ } // socket dropped == sidebar closed
350+
351+ tokio::time::sleep(Duration::from_millis(500)).await;
352+
353+ // Second attach: same session, shell variable still set.
354+ {
355+ let mut ws = connect_authed(&server).await;
356+ ws.send(Message::Text(
357+ serde_json::json!({"type": "open", "cols": 80, "rows": 24})
358+ .to_string()
359+ .into(),
360+ ))
361+ .await
362+ .unwrap();
363+ wait_until_ready(&mut ws, "boot-two").await;
364+ ws.send(Message::Binary(b"echo \"[$MARKER]\"\n".to_vec().into()))
365+ .await
366+ .unwrap();
367+ wait_for(&mut ws, "[survived-the-disconnect]").await;
368+ ws.close(None).await.unwrap();
369+ }
370+
371+ let _ = tmux(&["kill-server"]);
372+}
373+
374+// --- tmux session selection -------------------------------------------------
375+
376+/// Session names reach `execvp` as a separate argv element, so shell
377+/// metacharacters cannot inject a command. A leading dash is the real hazard —
378+/// tmux would read it as a flag — and the charset check backs that up.
379+#[test]
380+fn session_name_validation() {
381+ use termbridge::pty::valid_session_name as v;
382+
383+ for good in ["browser", "work", "my-session", "proj_2", "A1"] {
384+ assert_eq!(v(good).as_deref(), Some(good), "{good} should be accepted");
385+ }
386+
387+ for bad in [
388+ "",
389+ " ",
390+ "-C", // tmux would read this as a flag
391+ "--help",
392+ "a b", // tmux disallows
393+ "a.b", // tmux disallows '.'
394+ "a:b", // tmux disallows ':'
395+ "a/b",
396+ "a;id",
397+ "a$(id)",
398+ "a`id`",
399+ "a|b",
400+ "a&b",
401+ "a\nb",
402+ "a\0b",
403+ "a'b",
404+ "a\"b",
405+ "../../etc/passwd",
406+ "sess$IFS",
407+ ] {
408+ assert_eq!(v(bad), None, "{bad:?} must be rejected");
409+ }
410+
411+ assert_eq!(v(&"x".repeat(65)), None, "absurd length must be rejected");
412+ assert_eq!(v(" padded ").as_deref(), Some("padded"), "trims");
413+}
414+
415+/// A rejected session name must fall back to the default, never reach tmux, and
416+/// never change what program runs.
417+#[tokio::test]
418+async fn hostile_session_name_falls_back_and_cannot_inject() {
419+ if !Profile::tmux_available() {
420+ eprintln!("skipping: tmux not installed");
421+ return;
422+ }
423+ let sock = "termbridge-inject";
424+ let _ = std::process::Command::new("tmux")
425+ .args(["-L", sock, "kill-server"])
426+ .output();
427+
428+ // A tmux profile pinned to a private socket so the test is self-contained.
429+ let profile = Profile {
430+ program: "tmux".into(),
431+ args: ["-L", sock, "new-session", "-A", "-s", "safe", "/bin/sh"]
432+ .iter()
433+ .map(|s| s.to_string())
434+ .collect(),
435+ };
436+ let server = start(profile).await;
437+ let mut ws = connect_authed(&server).await;
438+
439+ ws.send(Message::Text(
440+ serde_json::json!({
441+ "type": "open", "cols": 80, "rows": 24,
442+ "session": "evil; touch /tmp/termbridge-pwned; #"
443+ })
444+ .to_string()
445+ .into(),
446+ ))
447+ .await
448+ .unwrap();
449+
450+ wait_until_ready(&mut ws, "boot-inject").await;
451+ ws.send(Message::Binary(b"echo marker-$((6*7))\n".to_vec().into()))
452+ .await
453+ .unwrap();
454+ wait_for(&mut ws, "marker-42").await;
455+
456+ assert!(
457+ !std::path::Path::new("/tmp/termbridge-pwned").exists(),
458+ "session name was interpreted by a shell"
459+ );
460+
461+ let _ = std::process::Command::new("tmux")
462+ .args(["-L", sock, "kill-server"])
463+ .output();
464+}
465+
466+/// The status channel, end to end: the daemon's control-mode client reports
467+/// which session we're on, lists the others, and moves the live client when the
468+/// sidebar asks — all on a private tmux server so the user's own is untouched.
469+#[tokio::test]
470+async fn status_frames_track_the_session_and_switching_moves_the_client() {
471+ if !Profile::tmux_available() {
472+ eprintln!("skipping: tmux not installed");
473+ return;
474+ }
475+ let sock = "termbridge-status-test";
476+ let (first, second) = ("tb-status-one", "tb-status-two");
477+ let tmux = |args: &[&str]| {
478+ std::process::Command::new("tmux")
479+ .args(["-L", sock])
480+ .args(args)
481+ .output()
482+ };
483+ let _ = tmux(&["kill-server"]);
484+ let _ = tmux(&["new-session", "-d", "-s", second, "/bin/sh"]);
485+
486+ let profile = Profile {
487+ program: "tmux".into(),
488+ args: ["-L", sock, "new-session", "-A", "-s", first, "/bin/sh"]
489+ .iter()
490+ .map(|s| s.to_string())
491+ .collect(),
492+ };
493+ let server = start(profile).await;
494+ let mut ws = connect_authed(&server).await;
495+ ws.send(Message::Text(
496+ serde_json::json!({"type": "open", "cols": 80, "rows": 24})
497+ .to_string()
498+ .into(),
499+ ))
500+ .await
501+ .unwrap();
502+
503+ let on_first = next_status(&mut ws, first).await;
504+ let names: Vec<&str> = on_first["sessions"]
505+ .as_array()
506+ .unwrap()
507+ .iter()
508+ .map(|s| s["name"].as_str().unwrap())
509+ .collect();
510+ assert!(
511+ names.contains(&first) && names.contains(&second),
512+ "switcher should see both sessions, got {names:?}"
513+ );
514+
515+ // The whole point of doing this over control mode: no reconnect, no second
516+ // pty, the same WebSocket keeps streaming.
517+ ws.send(Message::Text(
518+ serde_json::json!({"type": "tmux", "cmd": "switch", "session": second})
519+ .to_string()
520+ .into(),
521+ ))
522+ .await
523+ .unwrap();
524+ next_status(&mut ws, second).await;
525+
526+ let _ = tmux(&["kill-server"]);
527+}
528+
529+/// Read frames until a `status` frame reports `session`, ignoring terminal
530+/// output and the intermediate states tmux passes through.
531+async fn next_status(
532+ ws: &mut tokio_tungstenite::WebSocketStream<
533+ tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>,
534+ >,
535+ session: &str,
536+) -> serde_json::Value {
537+ let mut seen = Vec::new();
538+ let found = tokio::time::timeout(Duration::from_secs(20), async {
539+ while let Some(Ok(msg)) = ws.next().await {
540+ let Message::Text(t) = msg else { continue };
541+ let Ok(v) = serde_json::from_str::<serde_json::Value>(&t) else {
542+ continue;
543+ };
544+ if v["type"] != "status" {
545+ continue;
546+ }
547+ seen.push(v["session"].as_str().unwrap_or("").to_string());
548+ if v["session"] == session {
549+ return Some(v);
550+ }
551+ }
552+ None
553+ })
554+ .await
555+ .ok()
556+ .flatten();
557+
558+ found.unwrap_or_else(|| panic!("never saw a status frame for {session:?}; saw {seen:?}"))
559+}
addeddaemon/tests/security.rs+548 −0
1+//! Regression tests for the four properties we committed to:
2+//!
3+//! 1. bind to loopback only
4+//! 2. Origin allowlist (blocks hostile web pages)
5+//! 3. 0600 token file (blocks other local uids)
6+//! 4. explicit pairing (never trust-on-first-use silently)
7+//!
8+//! Each is something that is easy to get right once and then quietly regress,
9+//! which is exactly what tests are for. The reference implementation we looked
10+//! at got #1 and #3 right and #2 and #4 wrong, and the result was that any
11+//! website could open a shell.
12+
13+use std::net::{IpAddr, SocketAddr};
14+use std::time::Duration;
15+
16+use futures_util::{SinkExt, StreamExt};
17+use tokio_tungstenite::tungstenite::client::IntoClientRequest;
18+use tokio_tungstenite::tungstenite::http::StatusCode;
19+use tokio_tungstenite::tungstenite::{Error as WsError, Message};
20+use tokio_tungstenite::{connect_async, MaybeTlsStream, WebSocketStream};
21+
22+use termbridge::auth::{self, Denied};
23+use termbridge::{paths, Config, Server};
24+
25+const GOOD_TOKEN: &str = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
26+const PAIRED: &str = "chrome-extension://abcdefghijklmnopabcdefghijklmnop";
27+const UNPAIRED: &str = "https://evil.example";
28+
29+type Client = WebSocketStream<MaybeTlsStream<tokio::net::TcpStream>>;
30+
31+fn test_config() -> Config {
32+ let mut c = Config::new(GOOD_TOKEN, vec![PAIRED.to_string()]);
33+ c.auth_timeout = Duration::from_millis(300);
34+ // Exercise auth without spawning real shells.
35+ c.echo_only = true;
36+ c
37+}
38+
39+async fn start(config: Config) -> Server {
40+ Server::start(config, 0).await.expect("bind ephemeral port")
41+}
42+
43+/// Connect with full control over Origin and Host, the two headers an attacker
44+/// would want to lie about.
45+async fn connect(
46+ server: &Server,
47+ origin: Option<&str>,
48+ host_override: Option<&str>,
49+ query: &str,
50+) -> Result<Client, WsError> {
51+ let url = format!("{}{}", server.url(), query);
52+ let mut req = url.into_client_request().unwrap();
53+ if let Some(o) = origin {
54+ req.headers_mut().insert("origin", o.parse().unwrap());
55+ }
56+ if let Some(h) = host_override {
57+ req.headers_mut().insert("host", h.parse().unwrap());
58+ }
59+ connect_async(req).await.map(|(ws, _)| ws)
60+}
61+
62+fn rejected_with(err: &WsError, expect: StatusCode) -> bool {
63+ matches!(err, WsError::Http(resp) if resp.status() == expect)
64+}
65+
66+/// A client that got through the handshake and sent a valid auth frame.
67+async fn authed(server: &Server) -> Client {
68+ let mut ws = connect(server, Some(PAIRED), None, "").await.expect("handshake");
69+ ws.send(Message::Text(
70+ serde_json::json!({"type": "auth", "token": GOOD_TOKEN})
71+ .to_string()
72+ .into(),
73+ ))
74+ .await
75+ .unwrap();
76+ let reply = ws.next().await.unwrap().unwrap();
77+ assert!(
78+ reply.to_text().unwrap().contains("\"ok\""),
79+ "expected ok, got {reply:?}"
80+ );
81+ ws
82+}
83+
84+// ---------------------------------------------------------------------------
85+// 1. Bind to loopback only
86+// ---------------------------------------------------------------------------
87+
88+#[tokio::test]
89+async fn listener_address_is_loopback() {
90+ let server = start(test_config()).await;
91+ assert!(
92+ server.addr().ip().is_loopback(),
93+ "listening on {} — must be loopback",
94+ server.addr()
95+ );
96+}
97+
98+/// The listener must not be reachable on this machine's LAN address. Without
99+/// this, a laptop on café wifi is serving shells to the network.
100+#[tokio::test]
101+async fn not_reachable_on_lan_interface() {
102+ let server = start(test_config()).await;
103+ let port = server.addr().port();
104+
105+ let Some(lan_ip) = outbound_interface_ip() else {
106+ eprintln!("skipping: no non-loopback interface available");
107+ return;
108+ };
109+
110+ let target = SocketAddr::new(lan_ip, port);
111+ let res = tokio::time::timeout(
112+ Duration::from_millis(500),
113+ tokio::net::TcpStream::connect(target),
114+ )
115+ .await;
116+
117+ match res {
118+ Ok(Ok(_)) => panic!("connected to {target} — daemon is exposed on the network"),
119+ _ => { /* refused or timed out, as required */ }
120+ }
121+}
122+
123+/// Guards against someone adding a `--bind` flag or flipping the constant.
124+/// A config option to listen on 0.0.0.0 has no legitimate use here and only
125+/// creates a remotely exploitable setup.
126+#[test]
127+fn source_never_binds_wildcard() {
128+ let src = std::fs::read_to_string("src/server.rs").unwrap();
129+ for needle in ["0.0.0.0", "UNSPECIFIED", "[::]"] {
130+ assert!(
131+ !src.contains(needle),
132+ "src/server.rs mentions `{needle}` — the bind address must stay loopback-only"
133+ );
134+ }
135+}
136+
137+// ---------------------------------------------------------------------------
138+// 2. Origin check — the defense against hostile web pages
139+// ---------------------------------------------------------------------------
140+
141+/// WebSocket has no CORS preflight, so any page you visit can open a socket to
142+/// 127.0.0.1. `Origin` is browser-set and unforgeable from page JS, so this is
143+/// the check that actually stops that.
144+#[tokio::test]
145+async fn rejects_unpaired_origin() {
146+ let server = start(test_config()).await;
147+ let err = connect(&server, Some(UNPAIRED), None, "")
148+ .await
149+ .expect_err("hostile origin must be refused at handshake");
150+ assert!(rejected_with(&err, StatusCode::FORBIDDEN), "got {err:?}");
151+}
152+
153+/// Even *with* a stolen token. Origin and token are independent gates.
154+#[tokio::test]
155+async fn unpaired_origin_rejected_even_with_valid_token() {
156+ let server = start(test_config()).await;
157+ let err = connect(&server, Some(UNPAIRED), None, "")
158+ .await
159+ .expect_err("must fail before the token is ever considered");
160+ assert!(rejected_with(&err, StatusCode::FORBIDDEN), "got {err:?}");
161+}
162+
163+#[tokio::test]
164+async fn rejects_missing_origin() {
165+ let server = start(test_config()).await;
166+ let err = connect(&server, None, None, "")
167+ .await
168+ .expect_err("no Origin must be refused");
169+ assert!(rejected_with(&err, StatusCode::FORBIDDEN), "got {err:?}");
170+}
171+
172+#[tokio::test]
173+async fn rejects_null_origin() {
174+ let server = start(test_config()).await;
175+ let err = connect(&server, Some("null"), None, "")
176+ .await
177+ .expect_err("sandboxed-iframe `null` origin must be refused");
178+ assert!(rejected_with(&err, StatusCode::FORBIDDEN), "got {err:?}");
179+}
180+
181+/// DNS rebinding: attacker.com resolves to 127.0.0.1, so the page reaches us
182+/// while carrying its own origin. The Host header is what gives it away.
183+#[tokio::test]
184+async fn rejects_rebound_host_header() {
185+ let server = start(test_config()).await;
186+ let err = connect(&server, Some(PAIRED), Some("attacker.example"), "")
187+ .await
188+ .expect_err("non-loopback Host must be refused");
189+ assert!(rejected_with(&err, StatusCode::FORBIDDEN), "got {err:?}");
190+}
191+
192+#[test]
193+fn host_header_parsing() {
194+ for good in ["127.0.0.1", "127.0.0.1:7681", "localhost", "localhost:7681", "[::1]:7681"] {
195+ assert!(auth::host_is_loopback(good), "{good} should be loopback");
196+ }
197+ for bad in [
198+ "attacker.example",
199+ "attacker.example:7681",
200+ "127.0.0.1.attacker.example",
201+ "localhost.attacker.example",
202+ "192.168.1.5:7681",
203+ "",
204+ ] {
205+ assert!(!auth::host_is_loopback(bad), "{bad} must not pass");
206+ }
207+}
208+
209+// ---------------------------------------------------------------------------
210+// 3. Token
211+// ---------------------------------------------------------------------------
212+
213+#[tokio::test]
214+async fn rejects_wrong_token() {
215+ let server = start(test_config()).await;
216+ let mut ws = connect(&server, Some(PAIRED), None, "").await.unwrap();
217+ ws.send(Message::Text(
218+ serde_json::json!({"type": "auth", "token": "wrong"})
219+ .to_string()
220+ .into(),
221+ ))
222+ .await
223+ .unwrap();
224+ let reply = ws.next().await.unwrap().unwrap();
225+ assert!(reply.to_text().unwrap().contains("invalid token"), "{reply:?}");
226+}
227+
228+#[tokio::test]
229+async fn rejects_missing_auth_frame() {
230+ let server = start(test_config()).await;
231+ let mut ws = connect(&server, Some(PAIRED), None, "").await.unwrap();
232+ // Say nothing. The deadline must close us.
233+ let outcome = tokio::time::timeout(Duration::from_secs(2), async {
234+ while let Some(Ok(m)) = ws.next().await {
235+ if let Message::Text(t) = &m {
236+ if t.contains("auth timeout") {
237+ return true;
238+ }
239+ }
240+ }
241+ false
242+ })
243+ .await
244+ .expect("server must not leave an unauthenticated socket open");
245+ assert!(outcome, "expected an auth timeout rejection");
246+}
247+
248+/// Sending data before authenticating must not reach the terminal.
249+#[tokio::test]
250+async fn rejects_data_frame_before_auth() {
251+ let server = start(test_config()).await;
252+ let mut ws = connect(&server, Some(PAIRED), None, "").await.unwrap();
253+ ws.send(Message::Binary(b"rm -rf ~\n".to_vec().into()))
254+ .await
255+ .unwrap();
256+ let reply = ws.next().await.unwrap().unwrap();
257+ assert!(
258+ reply.to_text().unwrap().contains("malformed auth"),
259+ "pre-auth data must be refused, got {reply:?}"
260+ );
261+}
262+
263+/// The token must not be accepted from the query string. Query strings leak
264+/// into logs, crash dumps and devtools history, and the browser WebSocket API
265+/// makes putting it there the path of least resistance.
266+#[tokio::test]
267+async fn query_string_token_does_not_authenticate() {
268+ let server = start(test_config()).await;
269+ let mut ws = connect(&server, Some(PAIRED), None, &format!("/?token={GOOD_TOKEN}"))
270+ .await
271+ .unwrap();
272+ let outcome = tokio::time::timeout(Duration::from_secs(2), async {
273+ while let Some(Ok(m)) = ws.next().await {
274+ if let Message::Text(t) = &m {
275+ if t.contains("\"ok\"") {
276+ return false; // authenticated via URL — bad
277+ }
278+ if t.contains("auth timeout") {
279+ return true;
280+ }
281+ }
282+ }
283+ false
284+ })
285+ .await
286+ .expect("must not hang");
287+ assert!(outcome, "query-string token must not authenticate");
288+}
289+
290+#[test]
291+fn token_compare_rejects_wrong_length_and_content() {
292+ assert!(auth::token_matches(GOOD_TOKEN, GOOD_TOKEN));
293+ assert!(!auth::token_matches("", GOOD_TOKEN));
294+ assert!(!auth::token_matches(&GOOD_TOKEN[..63], GOOD_TOKEN));
295+ assert!(!auth::token_matches(&format!("{GOOD_TOKEN}x"), GOOD_TOKEN));
296+ // Differs only in the final byte — a short-circuiting compare would leak
297+ // this via timing.
298+ let mut near = GOOD_TOKEN.to_string();
299+ near.pop();
300+ near.push('0');
301+ assert!(!auth::token_matches(&near, GOOD_TOKEN));
302+}
303+
304+#[test]
305+fn generated_token_file_is_0600() {
306+ use std::os::unix::fs::PermissionsExt;
307+ let dir = tempfile::tempdir().unwrap();
308+ let token = paths::generate_token(dir.path()).unwrap();
309+
310+ assert_eq!(token.len(), paths::TOKEN_BYTES * 2, "expected 32 random bytes as hex");
311+
312+ let mode = std::fs::metadata(dir.path().join("token"))
313+ .unwrap()
314+ .permissions()
315+ .mode()
316+ & 0o777;
317+ assert_eq!(mode, 0o600, "token file mode is {mode:04o}, must be 0600");
318+
319+ let dir_mode = std::fs::metadata(dir.path()).unwrap().permissions().mode() & 0o777;
320+ assert_eq!(dir_mode, 0o700, "config dir mode is {dir_mode:04o}, must be 0700");
321+}
322+
323+/// If the mode ever loosens, refuse rather than authenticate against a secret
324+/// every user on the box can read.
325+#[test]
326+fn load_token_refuses_group_or_world_readable_file() {
327+ use std::os::unix::fs::PermissionsExt;
328+ for bad_mode in [0o644, 0o640, 0o604, 0o666] {
329+ let dir = tempfile::tempdir().unwrap();
330+ paths::generate_token(dir.path()).unwrap();
331+ std::fs::set_permissions(
332+ dir.path().join("token"),
333+ std::fs::Permissions::from_mode(bad_mode),
334+ )
335+ .unwrap();
336+ assert!(
337+ paths::load_token(dir.path()).is_err(),
338+ "mode {bad_mode:04o} must be refused"
339+ );
340+ }
341+}
342+
343+#[test]
344+fn tokens_are_unique_across_generations() {
345+ let a = paths::generate_token(tempfile::tempdir().unwrap().path()).unwrap();
346+ let b = paths::generate_token(tempfile::tempdir().unwrap().path()).unwrap();
347+ assert_ne!(a, b);
348+}
349+
350+// ---------------------------------------------------------------------------
351+// 4. Explicit pairing
352+// ---------------------------------------------------------------------------
353+
354+/// A fresh install must grant nothing. No implicit trust-on-first-use.
355+#[tokio::test]
356+async fn fresh_install_pairs_nothing() {
357+ let mut cfg = Config::new(GOOD_TOKEN, vec![]);
358+ cfg.echo_only = true;
359+ let server = start(cfg).await;
360+ let err = connect(&server, Some(PAIRED), None, "")
361+ .await
362+ .expect_err("with no paired origins, everything is refused");
363+ assert!(rejected_with(&err, StatusCode::FORBIDDEN), "got {err:?}");
364+}
365+
366+#[test]
367+fn connecting_does_not_pair_itself() {
368+ let dir = tempfile::tempdir().unwrap();
369+ assert!(paths::load_paired_origins(dir.path()).is_empty());
370+ // Pairing only ever happens through the explicit CLI path.
371+ assert!(!auth::origin_is_paired(PAIRED, &paths::load_paired_origins(dir.path())));
372+}
373+
374+#[test]
375+fn pair_and_unpair_round_trip() {
376+ let dir = tempfile::tempdir().unwrap();
377+ assert!(paths::pair_origin(dir.path(), PAIRED).unwrap());
378+ assert!(!paths::pair_origin(dir.path(), PAIRED).unwrap(), "idempotent");
379+ assert_eq!(paths::load_paired_origins(dir.path()), vec![PAIRED.to_string()]);
380+
381+ assert!(paths::unpair_origin(dir.path(), PAIRED).unwrap());
382+ assert!(paths::load_paired_origins(dir.path()).is_empty());
383+}
384+
385+/// Firefox's moz-extension origin is a random per-install UUID, so pairing must
386+/// work with an origin that cannot be known ahead of time.
387+#[test]
388+fn pairs_firefox_random_uuid_origin() {
389+ let dir = tempfile::tempdir().unwrap();
390+ let ff = "moz-extension://11111111-2222-3333-4444-555555555555";
391+ paths::pair_origin(dir.path(), ff).unwrap();
392+ let paired = paths::load_paired_origins(dir.path());
393+ assert!(auth::origin_is_paired(ff, &paired));
394+ assert!(auth::origin_is_paired(&ff.to_uppercase(), &paired), "case-insensitive");
395+ assert!(!auth::origin_is_paired(
396+ "moz-extension://99999999-2222-3333-4444-555555555555",
397+ &paired
398+ ));
399+}
400+
401+/// Pairing one origin must not imply its neighbours — no prefix or substring
402+/// matching. `chrome-extension://` as a blanket allow would let any other
403+/// installed extension through.
404+#[test]
405+fn pairing_is_exact_match_not_prefix() {
406+ let paired = vec![PAIRED.to_string()];
407+ for near_miss in [
408+ "chrome-extension://",
409+ "chrome-extension://abcdefghijklmnopabcdefghijklmnoq",
410+ "chrome-extension://abcdefghijklmnopabcdefghijklmnop.evil.example",
411+ "https://abcdefghijklmnopabcdefghijklmnop",
412+ ] {
413+ assert!(
414+ !auth::origin_is_paired(near_miss, &paired),
415+ "{near_miss} must not match"
416+ );
417+ }
418+}
419+
420+// ---------------------------------------------------------------------------
421+// Happy path + rate limiting
422+// ---------------------------------------------------------------------------
423+
424+#[tokio::test]
425+async fn paired_origin_with_valid_token_is_accepted() {
426+ let server = start(test_config()).await;
427+ let _ws = authed(&server).await;
428+}
429+
430+/// Binary frames survive round-trip untouched — this is what makes the
431+/// WebSocket transport worth it over native messaging's JSON-only channel.
432+/// Includes a lone continuation byte, which is invalid UTF-8 and would have to
433+/// be base64'd or mangled on a JSON transport.
434+#[tokio::test]
435+async fn binary_frames_round_trip_invalid_utf8() {
436+ let server = start(test_config()).await;
437+ let mut ws = authed(&server).await;
438+
439+ let raw: Vec<u8> = vec![0x1b, b'[', b'3', b'1', b'm', 0xff, 0xfe, 0x80, b'o', b'k'];
440+ ws.send(Message::Binary(raw.clone().into())).await.unwrap();
441+ let echoed = ws.next().await.unwrap().unwrap();
442+ match echoed {
443+ Message::Binary(b) => assert_eq!(b.as_ref(), raw.as_slice()),
444+ other => panic!("expected binary echo, got {other:?}"),
445+ }
446+}
447+
448+#[tokio::test]
449+async fn repeated_failures_trigger_lockout() {
450+ let mut config = test_config();
451+ config.max_failures = 3;
452+ config.lockout = Duration::from_secs(60);
453+ let server = start(config).await;
454+
455+ for _ in 0..3 {
456+ let _ = connect(&server, Some(UNPAIRED), None, "").await;
457+ }
458+
459+ // Even a legitimate client is now refused, with 429 rather than 403.
460+ let err = connect(&server, Some(PAIRED), None, "")
461+ .await
462+ .expect_err("must be locked out");
463+ assert!(
464+ rejected_with(&err, StatusCode::TOO_MANY_REQUESTS),
465+ "expected 429, got {err:?}"
466+ );
467+}
468+
469+/// A plain HTTP request is not an auth failure, and must not be reported as
470+/// one — that sends you debugging tokens when the problem is the connection.
471+#[tokio::test]
472+async fn plain_http_request_is_reported_as_not_a_websocket() {
473+ use tokio::io::AsyncWriteExt;
474+ let mut server = start(test_config()).await;
475+
476+ let mut sock = tokio::net::TcpStream::connect(server.addr()).await.unwrap();
477+ sock.write_all(
478+ format!(
479+ "GET / HTTP/1.1\r\nHost: 127.0.0.1:{}\r\nOrigin: {PAIRED}\r\n\r\n",
480+ server.addr().port()
481+ )
482+ .as_bytes(),
483+ )
484+ .await
485+ .unwrap();
486+
487+ let ev = tokio::time::timeout(Duration::from_secs(3), server.next_event())
488+ .await
489+ .expect("expected a rejection event")
490+ .unwrap();
491+
492+ match ev {
493+ termbridge::Event::Rejected { why, detail, .. } => {
494+ assert_eq!(
495+ why,
496+ Denied::NotAWebSocketUpgrade,
497+ "a non-upgrade request must not be blamed on auth"
498+ );
499+ assert!(detail.is_some(), "the real cause must be reported");
500+ }
501+ other => panic!("expected Rejected, got {other:?}"),
502+ }
503+}
504+
505+/// A wss:// attempt against a daemon started *without* a TLS identity must be
506+/// named, not reported as a parse failure. (With TLS configured it is served
507+/// normally — see tests/tls.rs.)
508+#[tokio::test]
509+async fn tls_client_hello_is_named() {
510+ use tokio::io::AsyncWriteExt;
511+ let mut server = start(test_config()).await;
512+
513+ let mut sock = tokio::net::TcpStream::connect(server.addr()).await.unwrap();
514+ // Opening bytes of a TLS 1.x ClientHello record.
515+ sock.write_all(&[0x16, 0x03, 0x01, 0x00, 0x2f, 0x01]).await.unwrap();
516+
517+ let ev = tokio::time::timeout(Duration::from_secs(3), server.next_event())
518+ .await
519+ .expect("expected a rejection event")
520+ .unwrap();
521+
522+ match ev {
523+ termbridge::Event::Rejected { why, detail, .. } => {
524+ assert_eq!(why, Denied::TlsAttempted);
525+ assert!(
526+ detail.unwrap().contains("without a TLS identity"),
527+ "the message should say what to do about it"
528+ );
529+ }
530+ other => panic!("expected Rejected, got {other:?}"),
531+ }
532+}
533+
534+#[test]
535+fn denied_reasons_are_stable() {
536+ assert_eq!(Denied::UnpairedOrigin.status(), 403);
537+ assert_eq!(Denied::RateLimited.status(), 429);
538+}
539+
540+// ---------------------------------------------------------------------------
541+
542+/// Discover this host's outbound interface address without sending packets.
543+fn outbound_interface_ip() -> Option<IpAddr> {
544+ let sock = std::net::UdpSocket::bind("0.0.0.0:0").ok()?;
545+ sock.connect("192.0.2.1:9").ok()?; // TEST-NET-1, never routed
546+ let ip = sock.local_addr().ok()?.ip();
547+ (!ip.is_loopback() && !ip.is_unspecified()).then_some(ip)
548+}
addeddaemon/tests/tls.rs+285 −0
1+//! TLS is a new attack surface, so it gets the same scrutiny as the rest.
2+//!
3+//! The rule under test: enabling wss:// must not weaken anything. Every check
4+//! that applies over ws:// applies identically over wss://, and the private key
5+//! is guarded like the token.
6+
7+use std::sync::Arc;
8+use std::time::Duration;
9+
10+use futures_util::{SinkExt, StreamExt};
11+use tokio_rustls::rustls::pki_types::ServerName;
12+use tokio_tungstenite::tungstenite::client::IntoClientRequest;
13+use tokio_tungstenite::tungstenite::Message;
14+
15+use termbridge::{tls, Config, Server};
16+
17+const TOKEN: &str = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
18+const PAIRED: &str = "chrome-extension://abcdefghijklmnopabcdefghijklmnop";
19+const UNPAIRED: &str = "https://evil.example";
20+
21+fn identity(dir: &std::path::Path) -> tls::Identity {
22+ tls::load_or_create(dir).unwrap()
23+}
24+
25+async fn start_tls(dir: &std::path::Path, paired: Vec<String>) -> Server {
26+ let mut cfg = Config::new(TOKEN, paired);
27+ cfg.echo_only = true;
28+ cfg.auth_timeout = Duration::from_millis(500);
29+ cfg.tls = Some(tls::acceptor(&identity(dir)).unwrap());
30+ Server::start(cfg, 0).await.unwrap()
31+}
32+
33+/// A client that trusts only our generated certificate — the browser's position
34+/// after the user accepts the exception.
35+fn client_config(id: &tls::Identity) -> Arc<tokio_rustls::rustls::ClientConfig> {
36+ let mut roots = tokio_rustls::rustls::RootCertStore::empty();
37+ for block in id.cert_pem.split("-----BEGIN CERTIFICATE-----").skip(1) {
38+ let body = block.split("-----END CERTIFICATE-----").next().unwrap();
39+ let der = pem_body_to_der(body);
40+ roots.add(der.into()).unwrap();
41+ }
42+ Arc::new(
43+ tokio_rustls::rustls::ClientConfig::builder()
44+ .with_root_certificates(roots)
45+ .with_no_client_auth(),
46+ )
47+}
48+
49+fn pem_body_to_der(body: &str) -> Vec<u8> {
50+ const T: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
51+ let mut rev = [255u8; 256];
52+ for (i, c) in T.iter().enumerate() {
53+ rev[*c as usize] = i as u8;
54+ }
55+ let (mut out, mut acc, mut bits) = (Vec::new(), 0u32, 0u32);
56+ for c in body.bytes().filter(|c| !c.is_ascii_whitespace()) {
57+ if c == b'=' {
58+ break;
59+ }
60+ acc = (acc << 6) | rev[c as usize] as u32;
61+ bits += 6;
62+ if bits >= 8 {
63+ bits -= 8;
64+ out.push((acc >> bits) as u8);
65+ }
66+ }
67+ out
68+}
69+
70+async fn tls_connect(
71+ server: &Server,
72+ id: &tls::Identity,
73+ origin: Option<&str>,
74+) -> Result<
75+ tokio_tungstenite::WebSocketStream<
76+ tokio_rustls::client::TlsStream<tokio::net::TcpStream>,
77+ >,
78+ tokio_tungstenite::tungstenite::Error,
79+> {
80+ let connector = tokio_rustls::TlsConnector::from(client_config(id));
81+ let tcp = tokio::net::TcpStream::connect(server.addr()).await.unwrap();
82+ let dnsname = ServerName::try_from("127.0.0.1").unwrap();
83+ let stream = connector
84+ .connect(dnsname, tcp)
85+ .await
86+ .map_err(tokio_tungstenite::tungstenite::Error::Io)?;
87+
88+ let mut req = format!("wss://{}/", server.addr())
89+ .into_client_request()
90+ .unwrap();
91+ if let Some(o) = origin {
92+ req.headers_mut().insert("origin", o.parse().unwrap());
93+ }
94+ tokio_tungstenite::client_async(req, stream)
95+ .await
96+ .map(|(ws, _)| ws)
97+}
98+
99+// ---------------------------------------------------------------------------
100+
101+#[tokio::test]
102+async fn wss_happy_path() {
103+ let dir = tempfile::tempdir().unwrap();
104+ let id = identity(dir.path());
105+ let server = start_tls(dir.path(), vec![PAIRED.to_string()]).await;
106+
107+ let mut ws = tls_connect(&server, &id, Some(PAIRED)).await.unwrap();
108+ ws.send(Message::Text(
109+ serde_json::json!({"type": "auth", "token": TOKEN})
110+ .to_string()
111+ .into(),
112+ ))
113+ .await
114+ .unwrap();
115+ let reply = ws.next().await.unwrap().unwrap();
116+ assert!(reply.to_text().unwrap().contains("\"ok\""), "{reply:?}");
117+}
118+
119+/// The whole point of the change: one port serves both, so Firefox's HTTPS-Only
120+/// rewrite and Chrome's plain ws:// both work without configuration.
121+#[tokio::test]
122+async fn same_port_serves_plaintext_too() {
123+ let dir = tempfile::tempdir().unwrap();
124+ let server = start_tls(dir.path(), vec![PAIRED.to_string()]).await;
125+
126+ let mut req = server.url().into_client_request().unwrap();
127+ req.headers_mut().insert("origin", PAIRED.parse().unwrap());
128+ let (mut ws, _) = tokio_tungstenite::connect_async(req).await.unwrap();
129+ ws.send(Message::Text(
130+ serde_json::json!({"type": "auth", "token": TOKEN})
131+ .to_string()
132+ .into(),
133+ ))
134+ .await
135+ .unwrap();
136+ let reply = ws.next().await.unwrap().unwrap();
137+ assert!(reply.to_text().unwrap().contains("\"ok\""), "{reply:?}");
138+}
139+
140+/// TLS must not become a way around the origin allowlist.
141+#[tokio::test]
142+async fn origin_check_still_applies_over_tls() {
143+ let dir = tempfile::tempdir().unwrap();
144+ let id = identity(dir.path());
145+ let server = start_tls(dir.path(), vec![PAIRED.to_string()]).await;
146+
147+ let err = tls_connect(&server, &id, Some(UNPAIRED))
148+ .await
149+ .expect_err("unpaired origin must be refused over wss too");
150+ assert!(
151+ matches!(&err, tokio_tungstenite::tungstenite::Error::Http(r) if r.status() == 403),
152+ "got {err:?}"
153+ );
154+}
155+
156+#[tokio::test]
157+async fn token_check_still_applies_over_tls() {
158+ let dir = tempfile::tempdir().unwrap();
159+ let id = identity(dir.path());
160+ let server = start_tls(dir.path(), vec![PAIRED.to_string()]).await;
161+
162+ let mut ws = tls_connect(&server, &id, Some(PAIRED)).await.unwrap();
163+ ws.send(Message::Text(
164+ serde_json::json!({"type": "auth", "token": "wrong"})
165+ .to_string()
166+ .into(),
167+ ))
168+ .await
169+ .unwrap();
170+ let reply = ws.next().await.unwrap().unwrap();
171+ assert!(reply.to_text().unwrap().contains("invalid token"), "{reply:?}");
172+}
173+
174+#[tokio::test]
175+async fn missing_origin_still_refused_over_tls() {
176+ let dir = tempfile::tempdir().unwrap();
177+ let id = identity(dir.path());
178+ let server = start_tls(dir.path(), vec![PAIRED.to_string()]).await;
179+
180+ let err = tls_connect(&server, &id, None)
181+ .await
182+ .expect_err("no Origin must be refused over wss too");
183+ assert!(
184+ matches!(&err, tokio_tungstenite::tungstenite::Error::Http(r) if r.status() == 403),
185+ "got {err:?}"
186+ );
187+}
188+
189+// --- key material -----------------------------------------------------------
190+
191+#[test]
192+fn private_key_is_0600_and_cert_is_not_secret() {
193+ use std::os::unix::fs::PermissionsExt;
194+ let dir = tempfile::tempdir().unwrap();
195+ tls::generate(dir.path()).unwrap();
196+
197+ let key_mode = std::fs::metadata(dir.path().join(tls::KEY_FILE))
198+ .unwrap()
199+ .permissions()
200+ .mode()
201+ & 0o777;
202+ assert_eq!(key_mode, 0o600, "private key mode is {key_mode:04o}");
203+
204+ let dir_mode = std::fs::metadata(dir.path()).unwrap().permissions().mode() & 0o777;
205+ assert_eq!(dir_mode, 0o700);
206+}
207+
208+#[test]
209+fn refuses_a_world_readable_private_key() {
210+ use std::os::unix::fs::PermissionsExt;
211+ let dir = tempfile::tempdir().unwrap();
212+ tls::generate(dir.path()).unwrap();
213+ std::fs::set_permissions(
214+ dir.path().join(tls::KEY_FILE),
215+ std::fs::Permissions::from_mode(0o644),
216+ )
217+ .unwrap();
218+ assert!(
219+ tls::load_or_create(dir.path()).is_err(),
220+ "a group/world readable key must be refused, not used"
221+ );
222+}
223+
224+#[test]
225+fn certificate_is_not_a_ca() {
226+ // Trusting our cert must not let it vouch for any other host. A CA cert in
227+ // the browser's trust store would be a far bigger grant than intended.
228+ let dir = tempfile::tempdir().unwrap();
229+ let id = tls::generate(dir.path()).unwrap();
230+ let der = pem_body_to_der(
231+ id.cert_pem
232+ .split("-----BEGIN CERTIFICATE-----")
233+ .nth(1)
234+ .unwrap()
235+ .split("-----END CERTIFICATE-----")
236+ .next()
237+ .unwrap(),
238+ );
239+ // basicConstraints CA:TRUE encodes as 30 03 01 01 FF inside the extension.
240+ let ca_true: &[u8] = &[0x30, 0x03, 0x01, 0x01, 0xff];
241+ assert!(
242+ !der.windows(ca_true.len()).any(|w| w == ca_true),
243+ "certificate asserts CA:TRUE"
244+ );
245+}
246+
247+#[test]
248+fn identity_is_stable_across_loads() {
249+ let dir = tempfile::tempdir().unwrap();
250+ let a = tls::load_or_create(dir.path()).unwrap();
251+ let b = tls::load_or_create(dir.path()).unwrap();
252+ assert_eq!(a.fingerprint, b.fingerprint, "cert must not churn per start");
253+ assert_eq!(a.fingerprint.len(), 32 * 3 - 1, "SHA-256 hex with colons");
254+
255+ let c = tls::generate(dir.path()).unwrap();
256+ assert_ne!(a.fingerprint, c.fingerprint, "--regenerate must make a new one");
257+}
258+
259+/// The landing page exists so the certificate-trust visit is comprehensible.
260+/// It must never leak the token — that is the exact mistake that made the
261+/// reference implementation remotely exploitable.
262+#[tokio::test]
263+async fn landing_page_does_not_leak_the_token() {
264+ use tokio::io::{AsyncReadExt, AsyncWriteExt};
265+ let dir = tempfile::tempdir().unwrap();
266+ let server = start_tls(dir.path(), vec![PAIRED.to_string()]).await;
267+
268+ let mut sock = tokio::net::TcpStream::connect(server.addr()).await.unwrap();
269+ sock.write_all(
270+ format!("GET / HTTP/1.1\r\nHost: 127.0.0.1:{}\r\n\r\n", server.addr().port()).as_bytes(),
271+ )
272+ .await
273+ .unwrap();
274+
275+ let mut body = Vec::new();
276+ let _ = tokio::time::timeout(Duration::from_secs(2), sock.read_to_end(&mut body)).await;
277+ let text = String::from_utf8_lossy(&body);
278+
279+ assert!(text.contains("200 OK"), "expected the landing page, got:\n{text}");
280+ assert!(!text.contains(TOKEN), "landing page leaked the auth token");
281+ assert!(
282+ !text.to_lowercase().contains("\"token\""),
283+ "landing page exposes a token field"
284+ );
285+}
addedextension/icons/icon128.pngbinary
Binary file
addedextension/icons/icon16.pngbinary
Binary file
addedextension/icons/icon32.pngbinary
Binary file
addedextension/icons/icon48.pngbinary
Binary file
addedextension/icons/icon96.pngbinary
Binary file
addedextension/jsconfig.base.json+23 −0
1+{
2+ // Type checking without a build step. Nothing here compiles anything: the
3+ // browser loads these .js files exactly as they are on disk, and
4+ // `npm run check` only reads them.
5+ //
6+ // Shared by the two realms the manifest actually creates — see
7+ // jsconfig.json (sidebar document) and jsconfig.sw.json (service worker).
8+ // They are separate projects because they are separate global scopes: both
9+ // declare `const api` at the top level, and only one of them has a `window`.
10+ "compilerOptions": {
11+ "checkJs": true,
12+ "noEmit": true,
13+ "target": "ES2022",
14+ "strict": true,
15+ "noFallthroughCasesInSwitch": true,
16+
17+ // These files are classic scripts, not modules — one shared global scope,
18+ // which is how sidebar.js reaches Themes, Sanitize and tbPickElement
19+ // without importing anything. Nothing here may introduce import/export
20+ // syntax, or that stops being true.
21+ "types": []
22+ }
23+}
addedextension/jsconfig.json+10 −0
1+{
2+ // The sidebar document: everything sidebar.html loads. This is the config
3+ // editors pick up by default.
4+ "extends": "./jsconfig.base.json",
5+ "compilerOptions": {
6+ "lib": ["ES2022", "DOM", "DOM.Iterable"]
7+ },
8+ "include": ["sidebar.js", "picker.js", "lib/*.js", "types/globals.d.ts"],
9+ "exclude": ["lib/*.test.js"]
10+}
addedextension/jsconfig.sw.json+13 −0
1+{
2+ // The service worker: sw.js plus the one file it importScripts. It keeps the
3+ // DOM lib even though a worker has no DOM — picker.js only *defines*
4+ // tbPickElement here; the body runs in the page, injected by
5+ // scripting.executeScript, and has to be checked against the page's globals.
6+ // (lib.webworker and lib.dom cannot both be loaded — they redeclare each
7+ // other. `importScripts` is declared in types/worker.d.ts instead.)
8+ "extends": "./jsconfig.base.json",
9+ "compilerOptions": {
10+ "lib": ["ES2022", "DOM", "DOM.Iterable"]
11+ },
12+ "include": ["sw.js", "picker.js", "lib/shot.js", "types/globals.d.ts", "types/worker.d.ts"]
13+}
addedextension/lib/sanitize.js+85 −0
1+// Turning page-controlled text into something safe to type at a live shell.
2+//
3+// Everything here treats its input as hostile. A selector is built from
4+// attributes the *page* chose — an id, an aria-label, a class name — so a page
5+// can put anything it likes in there, including a newline. A newline typed into
6+// a terminal is a pressed Enter key, which is arbitrary code execution.
7+//
8+// Two independent defenses, both required:
9+// 1. strip control characters, so nothing can submit a line or drive the
10+// terminal's own escape-sequence parser;
11+// 2. single-quote the result, so shell metacharacters are inert.
12+//
13+// Pure functions, no DOM — so they can be tested under `node --test`.
14+
15+/** Hard cap. Nothing legitimate is this long; a 10MB "selector" is an attack. */
16+const MAX_LEN = 4096;
17+
18+/**
19+ * Remove every C0 control character, DEL, and the Unicode line separators.
20+ *
21+ * \r and \n are the dangerous ones (they execute). ESC matters too: xterm.js
22+ * feeds what we send straight to the pty, and an escape sequence could drive
23+ * an application's own input handling. \t is dropped as well — it triggers
24+ * shell completion, which can execute in some configurations.
25+ *
26+ * @param {unknown} input anything at all — callers pass page-controlled values
27+ */
28+function stripControl(input) {
29+ const text = String(input ?? "");
30+ // C0 controls (covers NUL, TAB, LF, CR, ESC), DEL, and the Unicode line
31+ // separators, which some terminals also treat as line breaks.
32+ return text.replace(/[\u0000-\u001F\u007F\u2028\u2029]/g, "");
33+}
34+
35+/**
36+ * POSIX single-quoting. Inside single quotes the shell interprets nothing, so
37+ * $, `, ;, &, |, newline and friends are all literal. The only character that
38+ * needs handling is the single quote itself, which we close/escape/reopen.
39+ *
40+ * @param {unknown} input
41+ */
42+function shellQuote(input) {
43+ const text = String(input ?? "");
44+ return "'" + text.replace(/'/g, "'\\''") + "'";
45+}
46+
47+/**
48+ * The full pipeline for text that is about to be written to the pty.
49+ *
50+ * Returns the safe string plus what had to be removed, so the UI can tell the
51+ * user rather than silently altering what they picked.
52+ *
53+ * @param {unknown} input
54+ */
55+function forTerminal(input) {
56+ const raw = String(input ?? "");
57+ const truncated = raw.length > MAX_LEN;
58+ const capped = truncated ? raw.slice(0, MAX_LEN) : raw;
59+ const stripped = stripControl(capped);
60+ return {
61+ text: shellQuote(stripped),
62+ removedControl: stripped.length !== capped.length,
63+ truncated,
64+ };
65+}
66+
67+/**
68+ * For the system clipboard. Control characters still come out — a newline in
69+ * the clipboard is not execution — but a paste into a terminal *would* be, and
70+ * some terminals paste without bracketed-paste protection. Same treatment,
71+ * minus the shell quoting, which would be noise in an editor.
72+ *
73+ * @param {unknown} input
74+ */
75+function forClipboard(input) {
76+ const raw = String(input ?? "");
77+ const capped = raw.length > MAX_LEN ? raw.slice(0, MAX_LEN) : raw;
78+ return stripControl(capped);
79+}
80+
81+const Sanitize = { stripControl, shellQuote, forTerminal, forClipboard, MAX_LEN };
82+
83+if (typeof module !== "undefined" && module.exports) {
84+ module.exports = Sanitize;
85+}
addedextension/lib/sanitize.test.js+114 −0
1+// Run with: node --test "extension/lib/*.test.js"
2+//
3+// This is the boundary between page-controlled text and a live shell. Every
4+// case below is something a hostile (or merely careless) page can put in an
5+// id, a class, or an aria-label.
6+
7+const test = require("node:test");
8+const assert = require("node:assert");
9+const { execFileSync } = require("node:child_process");
10+const S = require("./sanitize.js");
11+
12+test("newlines are removed — a newline in a terminal is a pressed Enter", () => {
13+ for (const nl of ["\n", "\r", "\r\n", "\u2028", "\u2029"]) {
14+ const { text } = S.forTerminal(`a${nl}whoami`);
15+ assert.strictEqual(text, "'awhoami'", `newline survived: ${JSON.stringify(text)}`);
16+ }
17+});
18+
19+test("the classic injection payload cannot execute", () => {
20+ const { text } = S.forTerminal("x'; rm -rf ~; echo '");
21+ // Single-quoted, with the embedded quotes neutralised. Handing this to sh
22+ // yields one literal argument.
23+ assert.strictEqual(text, "'x'\\''; rm -rf ~; echo '\\'''");
24+});
25+
26+test("shell metacharacters are inert inside single quotes", () => {
27+ for (const payload of [
28+ "$(whoami)",
29+ "`id`",
30+ "${HOME}",
31+ "a && b",
32+ "a || b",
33+ "a; b",
34+ "a | b",
35+ "a > /etc/passwd",
36+ "~/secrets",
37+ "*",
38+ "$IFS",
39+ ]) {
40+ const { text } = S.forTerminal(payload);
41+ assert.strictEqual(text, `'${payload}'`, `mangled: ${payload}`);
42+ }
43+});
44+
45+test("escape sequences are stripped, not passed to the terminal", () => {
46+ const { text, removedControl } = S.forTerminal("\u001b]0;pwned\u0007ok");
47+ assert.ok(!text.includes("\u001b"), "ESC survived");
48+ assert.ok(!text.includes("\u0007"), "BEL survived");
49+ assert.strictEqual(text, "']0;pwnedok'");
50+ assert.ok(removedControl, "should report that something was removed");
51+});
52+
53+test("tab is stripped — it triggers shell completion", () => {
54+ assert.strictEqual(S.forTerminal("a\tb").text, "'ab'");
55+});
56+
57+test("NUL and other C0 controls are stripped", () => {
58+ assert.strictEqual(S.forTerminal("a\u0000b\u0001c").text, "'abc'");
59+});
60+
61+test("removedControl is false for ordinary input", () => {
62+ const { text, removedControl, truncated } = S.forTerminal("#main > div.card");
63+ assert.strictEqual(text, "'#main > div.card'");
64+ assert.strictEqual(removedControl, false);
65+ assert.strictEqual(truncated, false);
66+});
67+
68+test("absurdly long input is capped and reported", () => {
69+ const { text, truncated } = S.forTerminal("a".repeat(S.MAX_LEN * 3));
70+ assert.ok(truncated);
71+ assert.ok(text.length <= S.MAX_LEN + 2);
72+});
73+
74+// The real proof: hand the quoted string to an actual shell and check it comes
75+// back byte-for-byte as a single argument.
76+test("quoting survives a round trip through sh", () => {
77+ for (const payload of [
78+ "x'; rm -rf ~; echo '",
79+ "$(id)",
80+ "it's a `test`",
81+ 'double "quotes" too',
82+ "back\\slash",
83+ "#main > div.card:nth-of-type(2)",
84+ "//*[@id='thing']",
85+ ]) {
86+ const { text } = S.forTerminal(payload);
87+ const out = execFileSync("/bin/sh", ["-c", `printf %s ${text}`], {
88+ encoding: "utf8",
89+ });
90+ assert.strictEqual(out, payload, `sh did not treat it as one literal: ${payload}`);
91+ }
92+});
93+
94+// A newline must not become a second command even when sh evaluates the line.
95+test("an injected newline cannot start a second command in sh", () => {
96+ const { text } = S.forTerminal("harmless\nid");
97+ const out = execFileSync("/bin/sh", ["-c", `printf %s ${text}`], {
98+ encoding: "utf8",
99+ });
100+ assert.strictEqual(out, "harmlessid");
101+ assert.ok(!/uid=/.test(out), "a second command ran");
102+});
103+
104+test("clipboard output strips controls but does not shell-quote", () => {
105+ assert.strictEqual(S.forClipboard("#main > .card"), "#main > .card");
106+ assert.strictEqual(S.forClipboard("a\nwhoami"), "awhoami");
107+ assert.strictEqual(S.forClipboard("it's"), "it's");
108+});
109+
110+test("null and undefined do not throw", () => {
111+ assert.strictEqual(S.forTerminal(null).text, "''");
112+ assert.strictEqual(S.forTerminal(undefined).text, "''");
113+ assert.strictEqual(S.forClipboard(null), "");
114+});
addedextension/lib/shot.js+175 −0
1+// Cropping a picked element out of a tab screenshot and putting it on the
2+// system clipboard.
3+//
4+// Why the clipboard rather than a file: the daemon runs on the same machine as
5+// the browser, so the PNG the browser copies is the PNG an agent running in the
6+// pty reads back when it sees a ^V. That gives us an image handoff without the
7+// extension ever writing to disk or the daemon growing a file-upload path.
8+//
9+// Loaded by both the sidebar (a document) and the background worker (no DOM),
10+// so nothing here may touch `document` outside a function that only the sidebar
11+// calls. `createImageBitmap`, `OffscreenCanvas` and `btoa` exist in both.
12+
13+/** Padding around the element's box, in CSS pixels. Enough to see its edge. */
14+const SHOT_PAD = 4;
15+
16+/** @param {string} b64 */
17+function base64ToBytes(b64) {
18+ const bin = atob(b64);
19+ const out = new Uint8Array(bin.length);
20+ for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
21+ return out;
22+}
23+
24+/** @param {Uint8Array} bytes */
25+function bytesToBase64(bytes) {
26+ // Chunked: String.fromCharCode blows the argument limit on a whole PNG.
27+ let s = "";
28+ const CHUNK = 0x8000;
29+ for (let i = 0; i < bytes.length; i += CHUNK) {
30+ s += String.fromCharCode(...bytes.subarray(i, i + CHUNK));
31+ }
32+ return btoa(s);
33+}
34+
35+/**
36+ * A `data:` URL is decoded by hand rather than with `fetch`, because a page's
37+ * CSP can forbid fetching one and this code also runs injected into pages.
38+ *
39+ * @param {string} dataUrl
40+ */
41+function dataUrlBytes(dataUrl) {
42+ const comma = dataUrl.indexOf(",");
43+ if (comma < 0) throw new Error("not a data URL");
44+ return base64ToBytes(dataUrl.slice(comma + 1));
45+}
46+
47+/**
48+ * Cut the element's box out of a full-viewport screenshot.
49+ *
50+ * The scale factor comes from the captured image's own width rather than
51+ * devicePixelRatio: browser zoom, HiDPI and Chrome's own capture downscaling
52+ * all move the two apart, and the ratio the image reports is the true one.
53+ *
54+ * @param {string} dataUrl PNG data URL of the visible tab
55+ * @param {TbPickedRect} rect element box in CSS pixels, viewport-relative
56+ * @param {{ w: number; h: number }} viewport CSS pixel size of the viewport
57+ * @returns {Promise<Uint8Array>} PNG bytes
58+ */
59+async function cropShot(dataUrl, rect, viewport) {
60+ const bmp = await createImageBitmap(new Blob([dataUrlBytes(dataUrl)], { type: "image/png" }));
61+ const scale = viewport.w > 0 ? bmp.width / viewport.w : 1;
62+
63+ // Clamp to the viewport: an element can hang off the edge, and drawImage
64+ // silently produces transparent pixels for the part that isn't there.
65+ const left = Math.max(0, rect.x - SHOT_PAD);
66+ const top = Math.max(0, rect.y - SHOT_PAD);
67+ const right = Math.min(viewport.w, rect.x + rect.width + SHOT_PAD);
68+ const bottom = Math.min(viewport.h, rect.y + rect.height + SHOT_PAD);
69+
70+ const sx = Math.round(left * scale);
71+ const sy = Math.round(top * scale);
72+ const sw = Math.round((right - left) * scale);
73+ const sh = Math.round((bottom - top) * scale);
74+ if (sw < 1 || sh < 1) throw new Error("element is not visible in the viewport");
75+
76+ const canvas = new OffscreenCanvas(sw, sh);
77+ const ctx = canvas.getContext("2d");
78+ if (!ctx) throw new Error("no 2d context");
79+ ctx.drawImage(bmp, sx, sy, sw, sh, 0, 0, sw, sh);
80+ bmp.close();
81+
82+ const blob = await canvas.convertToBlob({ type: "image/png" });
83+ return new Uint8Array(await blob.arrayBuffer());
84+}
85+
86+/**
87+ * Put a PNG on the clipboard from inside the page.
88+ *
89+ * Serialised into the tab by executeScript, so it must be self-contained. It
90+ * runs in the isolated world — page script can neither see it nor tamper with
91+ * the `navigator.clipboard` it calls.
92+ *
93+ * The page is where this has to happen: the async clipboard API refuses to
94+ * write from a document that isn't focused, and after you click an element the
95+ * focused document is the page, not the side panel.
96+ *
97+ * @param {string} b64 base64 PNG
98+ * @returns {Promise<true | string>} true, or the failure to report
99+ */
100+async function tbCopyPngInPage(b64) {
101+ try {
102+ const bin = atob(b64);
103+ const bytes = new Uint8Array(bin.length);
104+ for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
105+ const blob = new Blob([bytes], { type: "image/png" });
106+ await navigator.clipboard.write([new ClipboardItem({ "image/png": blob })]);
107+ return true;
108+ } catch (e) {
109+ return e instanceof Error ? e.message : String(e);
110+ }
111+}
112+
113+/**
114+ * Screenshot the picked element and leave it on the system clipboard.
115+ *
116+ * Three routes, in order of how little they can go wrong:
117+ * 1. Firefox's `clipboard.setImageData`, which is an extension API and so
118+ * needs neither a focused document nor a content script;
119+ * 2. the calling document, when it happens to hold focus;
120+ * 3. the page, via injection — the usual case right after a pick.
121+ *
122+ * @param {TbExtensionApi} api
123+ * @param {number | undefined} tabId
124+ * @param {TbPicked} picked
125+ * @returns {Promise<true | string>} true, or why not
126+ */
127+async function copyPickedShot(api, tabId, picked) {
128+ if (!picked.rect || !picked.viewport) return "no element geometry";
129+ let png;
130+ try {
131+ const dataUrl = await api.tabs.captureVisibleTab({ format: "png" });
132+ png = await cropShot(dataUrl, picked.rect, picked.viewport);
133+ } catch (e) {
134+ return e instanceof Error ? e.message : String(e);
135+ }
136+
137+ if (api.clipboard?.setImageData) {
138+ try {
139+ // Firefox wants a plain ArrayBuffer, and a subarray's buffer may be larger.
140+ const buf = /** @type {ArrayBuffer} */ (
141+ png.buffer.slice(png.byteOffset, png.byteOffset + png.byteLength)
142+ );
143+ await api.clipboard.setImageData(buf, "png");
144+ return true;
145+ } catch (e) {
146+ // Fall through — the injected route may still work.
147+ }
148+ }
149+
150+ const b64 = bytesToBase64(png);
151+
152+ if (typeof document !== "undefined" && document.hasFocus()) {
153+ const direct = await tbCopyPngInPage(b64);
154+ if (direct === true) return true;
155+ }
156+
157+ if (tabId == null) return "no tab to copy from";
158+ try {
159+ const results = await api.scripting.executeScript({
160+ target: { tabId },
161+ func: tbCopyPngInPage,
162+ args: [b64],
163+ });
164+ const r = results?.[0]?.result;
165+ return r === true ? true : String(r ?? "clipboard write did not run");
166+ } catch (e) {
167+ return e instanceof Error ? e.message : String(e);
168+ }
169+}
170+
171+const Shot = { cropShot, copyPickedShot, SHOT_PAD };
172+
173+if (typeof module !== "undefined" && module.exports) {
174+ module.exports = Shot;
175+}
addedextension/lib/theme.js+127 −0
1+// Terminal + sidebar palettes.
2+//
3+// One source of truth. `ui` becomes CSS custom properties on :root, `xterm`
4+// goes to term.options.theme — so the chrome and the terminal can never drift
5+// apart, which is the usual failure mode when a theme is half in CSS and half
6+// in JS.
7+//
8+// Pure data plus one function, so `node --test` can check the palettes are
9+// complete and actually readable.
10+
11+const THEMES = {
12+ dark: {
13+ name: "dark",
14+ colorScheme: "dark",
15+ ui: {
16+ "--bg": "#14161a",
17+ "--bg-panel": "#1c1f25",
18+ "--bg-input": "#14161a",
19+ "--border": "#2d3138",
20+ "--fg": "#d7dae0",
21+ "--fg-muted": "#8b93a1",
22+ "--accent": "#3b6ea5",
23+ "--accent-hover": "#4a82bd",
24+ "--accent-fg": "#ffffff",
25+ "--ok": "#6ec177",
26+ "--err": "#e06c75",
27+ "--warn": "#d5a458",
28+ "--warn-bg": "#241f16",
29+ "--warn-border": "#4a3a22",
30+ "--shadow": "rgba(0,0,0,.4)",
31+ },
32+ xterm: {
33+ background: "#14161a",
34+ foreground: "#d7dae0",
35+ cursor: "#d7dae0",
36+ cursorAccent: "#14161a",
37+ selectionBackground: "#3b6ea5",
38+ selectionForeground: "#ffffff",
39+ black: "#2d3138",
40+ red: "#e06c75",
41+ green: "#6ec177",
42+ yellow: "#d5a458",
43+ blue: "#61afef",
44+ magenta: "#c678dd",
45+ cyan: "#56b6c2",
46+ white: "#d7dae0",
47+ brightBlack: "#6b7280",
48+ brightRed: "#ff7b86",
49+ brightGreen: "#86d98f",
50+ brightYellow: "#e8c07d",
51+ brightBlue: "#7fb3e8",
52+ brightMagenta: "#d7a3e8",
53+ brightCyan: "#6fd0dd",
54+ brightWhite: "#ffffff",
55+ },
56+ },
57+
58+ light: {
59+ name: "light",
60+ colorScheme: "light",
61+ ui: {
62+ "--bg": "#ffffff",
63+ "--bg-panel": "#f6f8fa",
64+ "--bg-input": "#ffffff",
65+ "--border": "#d0d7de",
66+ "--fg": "#1f2328",
67+ "--fg-muted": "#656d76",
68+ "--accent": "#0969da",
69+ "--accent-hover": "#0550ae",
70+ "--accent-fg": "#ffffff",
71+ "--ok": "#1a7f37",
72+ "--err": "#cf222e",
73+ "--warn": "#9a6700",
74+ "--warn-bg": "#fff8c5",
75+ "--warn-border": "#d4a72c",
76+ "--shadow": "rgba(31,35,40,.15)",
77+ },
78+ xterm: {
79+ background: "#ffffff",
80+ foreground: "#1f2328",
81+ cursor: "#1f2328",
82+ cursorAccent: "#ffffff",
83+ selectionBackground: "#b6d7ff",
84+ selectionForeground: "#1f2328",
85+ // Deliberately the darker end of each hue: these sit on white, and the
86+ // usual bright terminal colours are unreadable there.
87+ black: "#1f2328",
88+ red: "#cf222e",
89+ green: "#116329",
90+ yellow: "#7d4e00",
91+ blue: "#0550ae",
92+ magenta: "#8250df",
93+ cyan: "#1b7c83",
94+ white: "#6e7781",
95+ brightBlack: "#57606a",
96+ brightRed: "#a40e26",
97+ brightGreen: "#1a7f37",
98+ brightYellow: "#9a6700",
99+ brightBlue: "#0969da",
100+ brightMagenta: "#6639ba",
101+ brightCyan: "#3192aa",
102+ brightWhite: "#7f8894",
103+ },
104+ },
105+};
106+
107+/** Preference values the UI offers, in cycle order. */
108+const PREFERENCES = ["auto", "light", "dark"];
109+
110+/**
111+ * Resolve a stored preference to a concrete theme.
112+ * `auto` follows the browser, which follows the OS.
113+ *
114+ * @param {string} preference one of PREFERENCES; anything else means `auto`
115+ * @param {boolean} prefersDark what the browser says the OS wants
116+ */
117+function resolveTheme(preference, prefersDark) {
118+ if (preference === "light") return THEMES.light;
119+ if (preference === "dark") return THEMES.dark;
120+ return prefersDark ? THEMES.dark : THEMES.light;
121+}
122+
123+const Themes = { THEMES, PREFERENCES, resolveTheme };
124+
125+if (typeof module !== "undefined" && module.exports) {
126+ module.exports = Themes;
127+}
addedextension/lib/theme.test.js+137 −0
1+// Run with: node --test "extension/lib/*.test.js"
2+//
3+// A light terminal theme is easy to get wrong: the standard ANSI palette is
4+// tuned for dark backgrounds, and dropping it onto white produces yellow you
5+// cannot read. These tests check every colour against its own background.
6+
7+const test = require("node:test");
8+const assert = require("node:assert");
9+const { THEMES, PREFERENCES, resolveTheme } = require("./theme.js");
10+
11+// --- WCAG relative luminance / contrast ------------------------------------
12+
13+function srgbToLinear(c) {
14+ const v = c / 255;
15+ return v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
16+}
17+
18+function luminance(hex) {
19+ const m = /^#([0-9a-f]{6})$/i.exec(hex);
20+ assert.ok(m, `not a 6-digit hex colour: ${hex}`);
21+ const n = parseInt(m[1], 16);
22+ const [r, g, b] = [(n >> 16) & 255, (n >> 8) & 255, n & 255];
23+ return 0.2126 * srgbToLinear(r) + 0.7152 * srgbToLinear(g) + 0.0722 * srgbToLinear(b);
24+}
25+
26+function contrast(a, b) {
27+ const [x, y] = [luminance(a), luminance(b)].sort((p, q) => q - p);
28+ return (x + 0.05) / (y + 0.05);
29+}
30+
31+test("contrast helper matches known values", () => {
32+ assert.ok(Math.abs(contrast("#000000", "#ffffff") - 21) < 0.01);
33+ assert.strictEqual(contrast("#123456", "#123456"), 1);
34+});
35+
36+// --- structure --------------------------------------------------------------
37+
38+test("both themes define exactly the same keys", () => {
39+ const { light, dark } = THEMES;
40+ assert.deepStrictEqual(Object.keys(light.ui).sort(), Object.keys(dark.ui).sort());
41+ assert.deepStrictEqual(Object.keys(light.xterm).sort(), Object.keys(dark.xterm).sort());
42+});
43+
44+test("the full 16-colour ANSI palette is present", () => {
45+ const ansi = [
46+ "black", "red", "green", "yellow", "blue", "magenta", "cyan", "white",
47+ "brightBlack", "brightRed", "brightGreen", "brightYellow",
48+ "brightBlue", "brightMagenta", "brightCyan", "brightWhite",
49+ ];
50+ for (const theme of Object.values(THEMES)) {
51+ for (const name of ansi) {
52+ assert.ok(theme.xterm[name], `${theme.name} is missing ${name}`);
53+ }
54+ }
55+});
56+
57+// --- readability ------------------------------------------------------------
58+
59+test("body text meets WCAG AA (4.5:1)", () => {
60+ for (const theme of Object.values(THEMES)) {
61+ const c = contrast(theme.xterm.foreground, theme.xterm.background);
62+ assert.ok(c >= 4.5, `${theme.name}: foreground on background is ${c.toFixed(2)}:1`);
63+ const ui = contrast(theme.ui["--fg"], theme.ui["--bg"]);
64+ assert.ok(ui >= 4.5, `${theme.name}: --fg on --bg is ${ui.toFixed(2)}:1`);
65+ }
66+});
67+
68+test("muted text still meets AA for large text (3:1)", () => {
69+ for (const theme of Object.values(THEMES)) {
70+ const c = contrast(theme.ui["--fg-muted"], theme.ui["--bg-panel"]);
71+ assert.ok(c >= 3, `${theme.name}: --fg-muted on --bg-panel is ${c.toFixed(2)}:1`);
72+ }
73+});
74+
75+test("every ANSI colour is legible on its own background", () => {
76+ // 3:1 is the WCAG threshold for non-text/large text. Terminal output is
77+ // mostly small, but demanding 4.5 across all 16 slots would force a palette
78+ // with no hue separation left.
79+ const MIN = 3;
80+ for (const theme of Object.values(THEMES)) {
81+ const bg = theme.xterm.background;
82+ for (const [name, colour] of Object.entries(theme.xterm)) {
83+ if (["background", "cursorAccent", "selectionBackground"].includes(name)) continue;
84+ // Black is the background-ish slot by definition; it is used as a
85+ // foreground only in inverse video.
86+ if (name === "black" && theme.name === "dark") continue;
87+ const c = contrast(colour, bg);
88+ assert.ok(c >= MIN, `${theme.name}: ${name} (${colour}) on ${bg} is ${c.toFixed(2)}:1`);
89+ }
90+ }
91+});
92+
93+test("accent buttons are readable", () => {
94+ for (const theme of Object.values(THEMES)) {
95+ const c = contrast(theme.ui["--accent-fg"], theme.ui["--accent"]);
96+ assert.ok(c >= 4.5, `${theme.name}: accent text is ${c.toFixed(2)}:1`);
97+ }
98+});
99+
100+test("selected text is readable against the selection highlight", () => {
101+ for (const theme of Object.values(THEMES)) {
102+ const c = contrast(theme.xterm.selectionForeground, theme.xterm.selectionBackground);
103+ assert.ok(c >= 4.5, `${theme.name}: selection is ${c.toFixed(2)}:1`);
104+ }
105+});
106+
107+test("the two themes are actually different", () => {
108+ assert.notStrictEqual(THEMES.light.xterm.background, THEMES.dark.xterm.background);
109+ assert.ok(
110+ luminance(THEMES.light.xterm.background) > luminance(THEMES.dark.xterm.background),
111+ "light should be lighter than dark",
112+ );
113+});
114+
115+// --- preference resolution --------------------------------------------------
116+
117+test("auto follows the system", () => {
118+ assert.strictEqual(resolveTheme("auto", true).name, "dark");
119+ assert.strictEqual(resolveTheme("auto", false).name, "light");
120+});
121+
122+test("explicit choices override the system", () => {
123+ assert.strictEqual(resolveTheme("light", true).name, "light");
124+ assert.strictEqual(resolveTheme("dark", false).name, "dark");
125+});
126+
127+test("an unknown or missing preference degrades to auto", () => {
128+ assert.strictEqual(resolveTheme(undefined, true).name, "dark");
129+ assert.strictEqual(resolveTheme("chartreuse", false).name, "light");
130+});
131+
132+test("every offered preference resolves", () => {
133+ for (const p of PREFERENCES) {
134+ assert.ok(resolveTheme(p, false).name, `${p} did not resolve`);
135+ assert.ok(resolveTheme(p, true).name, `${p} did not resolve`);
136+ }
137+});
addedextension/manifest.chrome.json+55 −0
1+{
2+ "manifest_version": 3,
3+ "name": "terminal",
4+ "version": "0.0.1",
5+ "description": "A tmux terminal in your browser sidebar.",
6+ "permissions": [
7+ "storage",
8+ "sidePanel",
9+ "tabs",
10+ "activeTab",
11+ "scripting",
12+ "clipboardWrite"
13+ ],
14+ "background": {
15+ "service_worker": "sw.js"
16+ },
17+ "action": {
18+ "default_title": "terminal",
19+ "default_icon": {
20+ "16": "icons/icon16.png",
21+ "32": "icons/icon32.png",
22+ "48": "icons/icon48.png",
23+ "128": "icons/icon128.png"
24+ }
25+ },
26+ "side_panel": {
27+ "default_path": "sidebar.html"
28+ },
29+ "icons": {
30+ "16": "icons/icon16.png",
31+ "32": "icons/icon32.png",
32+ "48": "icons/icon48.png",
33+ "128": "icons/icon128.png"
34+ },
35+ "commands": {
36+ "pick-element": {
37+ "suggested_key": {
38+ "default": "Alt+Shift+P"
39+ },
40+ "description": "Pick an element on the page"
41+ }
42+ },
43+ "host_permissions": [
44+ "http://localhost/*",
45+ "https://localhost/*",
46+ "http://*.localhost/*",
47+ "https://*.localhost/*",
48+ "http://127.0.0.1/*",
49+ "https://127.0.0.1/*"
50+ ],
51+ "optional_host_permissions": [
52+ "*://*/*",
53+ "<all_urls>"
54+ ]
55+}
addedextension/manifest.firefox.json+60 −0
1+{
2+ "manifest_version": 3,
3+ "name": "terminal",
4+ "version": "0.0.1",
5+ "description": "A tmux terminal in your browser sidebar.",
6+ "browser_specific_settings": {
7+ "gecko": {
8+ "id": "terminal@termbridge.local",
9+ "strict_min_version": "115.0"
10+ }
11+ },
12+ "permissions": [
13+ "storage",
14+ "tabs",
15+ "activeTab",
16+ "scripting",
17+ "clipboardWrite"
18+ ],
19+ "sidebar_action": {
20+ "default_panel": "sidebar.html",
21+ "default_title": "terminal",
22+ "default_icon": {
23+ "16": "icons/icon16.png",
24+ "32": "icons/icon32.png",
25+ "48": "icons/icon48.png"
26+ }
27+ },
28+ "icons": {
29+ "48": "icons/icon48.png",
30+ "96": "icons/icon96.png",
31+ "128": "icons/icon128.png"
32+ },
33+ "commands": {
34+ "pick-element": {
35+ "suggested_key": {
36+ "default": "Alt+Shift+P"
37+ },
38+ "description": "Pick an element on the page"
39+ }
40+ },
41+ "background": {
42+ "scripts": [
43+ "picker.js",
44+ "lib/shot.js",
45+ "sw.js"
46+ ]
47+ },
48+ "host_permissions": [
49+ "http://localhost/*",
50+ "https://localhost/*",
51+ "http://*.localhost/*",
52+ "https://*.localhost/*",
53+ "http://127.0.0.1/*",
54+ "https://127.0.0.1/*"
55+ ],
56+ "optional_permissions": [
57+ "*://*/*",
58+ "<all_urls>"
59+ ]
60+}
addedextension/picker.js+255 −0
1+// The element picker that gets injected into the page.
2+//
3+// This function is serialised and executed in the page's world via
4+// scripting.executeScript, so it must be entirely self-contained — no imports,
5+// no closures over extension state. It has no extension privileges.
6+//
7+// It returns a Promise, and executeScript resolves to that promise's value.
8+// That means the result comes back as a *return value* rather than through
9+// runtime.sendMessage, so the sidebar needs no inbound message listener that a
10+// content script could reach.
11+
12+function tbPickElement() {
13+ return new Promise((resolve) => {
14+ const PREV = "__tbPickerActive";
15+ if (window[PREV]) {
16+ window[PREV]();
17+ }
18+
19+ const box = document.createElement("div");
20+ Object.assign(box.style, {
21+ position: "fixed",
22+ zIndex: "2147483647",
23+ pointerEvents: "none",
24+ border: "2px solid #3b6ea5",
25+ background: "rgba(59,110,165,0.15)",
26+ borderRadius: "2px",
27+ transition: "all 40ms linear",
28+ });
29+
30+ const label = document.createElement("div");
31+ Object.assign(label.style, {
32+ position: "fixed",
33+ zIndex: "2147483647",
34+ pointerEvents: "none",
35+ font: "12px ui-monospace, monospace",
36+ background: "#14161a",
37+ color: "#d7dae0",
38+ padding: "3px 6px",
39+ borderRadius: "3px",
40+ maxWidth: "60vw",
41+ overflow: "hidden",
42+ textOverflow: "ellipsis",
43+ whiteSpace: "nowrap",
44+ boxShadow: "0 2px 8px rgba(0,0,0,.4)",
45+ });
46+
47+ document.documentElement.append(box, label);
48+
49+ /** Element under the cursor, so a click resolves what the box is drawn on. */
50+ let current = /** @type {Element | null} */ (null);
51+
52+ const cleanup = () => {
53+ window[PREV] = null;
54+ box.remove();
55+ label.remove();
56+ document.removeEventListener("mousemove", onMove, true);
57+ document.removeEventListener("click", onClick, true);
58+ document.removeEventListener("keydown", onKey, true);
59+ window.removeEventListener("scroll", onScroll, true);
60+ };
61+ window[PREV] = () => {
62+ cleanup();
63+ resolve(null);
64+ };
65+
66+ /** @param {Element} el */
67+ function paint(el) {
68+ const r = el.getBoundingClientRect();
69+ Object.assign(box.style, {
70+ left: `${r.left}px`,
71+ top: `${r.top}px`,
72+ width: `${r.width}px`,
73+ height: `${r.height}px`,
74+ });
75+ label.textContent = shortLabel(el);
76+ const below = r.bottom + 22 < window.innerHeight;
77+ label.style.left = `${Math.max(2, r.left)}px`;
78+ label.style.top = below ? `${r.bottom + 3}px` : `${Math.max(2, r.top - 21)}px`;
79+ }
80+
81+ /** @param {MouseEvent} e */
82+ function onMove(e) {
83+ const hit = /** @type {Element | null} */ (e.target);
84+ const el =
85+ hit && hit !== box && hit !== label
86+ ? hit
87+ : document.elementFromPoint(e.clientX ?? 0, e.clientY ?? 0);
88+ if (!el || el.nodeType !== 1 || el === box || el === label) return;
89+ current = el;
90+ paint(el);
91+ }
92+
93+ // Scrolling moves the page under a box drawn in fixed coordinates. The
94+ // element does not change, only where it is, so this repaints rather than
95+ // re-hit-testing — a scroll event's target is the scroller, not what the
96+ // cursor is over.
97+ function onScroll() {
98+ if (current) paint(current);
99+ }
100+
101+ /** @param {KeyboardEvent} e */
102+ function onKey(e) {
103+ if (e.key === "Escape") {
104+ e.preventDefault();
105+ e.stopPropagation();
106+ cleanup();
107+ resolve(null);
108+ }
109+ }
110+
111+ // Capture phase + preventDefault, so picking a link doesn't navigate.
112+ /** @param {MouseEvent} e */
113+ function onClick(e) {
114+ e.preventDefault();
115+ e.stopPropagation();
116+ const el = current || /** @type {Element | null} */ (e.target);
117+ cleanup();
118+ resolve(el && el.nodeType === 1 ? describe(el) : null);
119+ }
120+
121+ document.addEventListener("mousemove", onMove, true);
122+ document.addEventListener("click", onClick, true);
123+ document.addEventListener("keydown", onKey, true);
124+ window.addEventListener("scroll", onScroll, true);
125+
126+ // --- identifier construction ---------------------------------------------
127+
128+ /** @param {string} v */
129+ function cssEscape(v) {
130+ return window.CSS && CSS.escape ? CSS.escape(v) : String(v).replace(/[^\w-]/g, "\\$&");
131+ }
132+
133+ /** @param {string} sel */
134+ function isUnique(sel) {
135+ try {
136+ return document.querySelectorAll(sel).length === 1;
137+ } catch {
138+ return false;
139+ }
140+ }
141+
142+ // Attributes worth preferring: chosen by the app author, so they survive a
143+ // redesign in a way that :nth-of-type chains do not.
144+ const STABLE = [
145+ "data-testid",
146+ "data-test-id",
147+ "data-test",
148+ "data-cy",
149+ "data-qa",
150+ "name",
151+ "aria-label",
152+ "role",
153+ ];
154+
155+ /** @param {Element} el */
156+ function stableAttrSelector(el) {
157+ for (const a of STABLE) {
158+ const v = el.getAttribute && el.getAttribute(a);
159+ if (v && v.length < 100) {
160+ const sel = `${el.tagName.toLowerCase()}[${a}="${v.replace(/"/g, '\\"')}"]`;
161+ if (isUnique(sel)) return sel;
162+ }
163+ }
164+ return null;
165+ }
166+
167+ /** @param {Element} el */
168+ function cssSelector(el) {
169+ if (el.id && isUnique(`#${cssEscape(el.id)}`)) return `#${cssEscape(el.id)}`;
170+ const stable = stableAttrSelector(el);
171+ if (stable) return stable;
172+
173+ /** @type {string[]} */
174+ const parts = [];
175+ let cur = /** @type {Element | null} */ (el);
176+ while (cur && cur.nodeType === 1 && parts.length < 8) {
177+ const tag = cur.tagName;
178+ let part = tag.toLowerCase();
179+ if (cur.id && isUnique(`#${cssEscape(cur.id)}`)) {
180+ parts.unshift(`#${cssEscape(cur.id)}`);
181+ break;
182+ }
183+ let idx = 1;
184+ for (let s = cur.previousElementSibling; s; s = s.previousElementSibling) {
185+ if (s.tagName === tag) idx++;
186+ }
187+ const needsIndex =
188+ cur.parentElement &&
189+ [...cur.parentElement.children].filter((c) => c.tagName === tag).length > 1;
190+ if (needsIndex) part += `:nth-of-type(${idx})`;
191+ parts.unshift(part);
192+ if (isUnique(parts.join(" > "))) break;
193+ cur = cur.parentElement;
194+ }
195+ return parts.join(" > ");
196+ }
197+
198+ /** @param {string} v */
199+ function xpathLiteral(v) {
200+ if (!v.includes("'")) return `'${v}'`;
201+ if (!v.includes('"')) return `"${v}"`;
202+ return `concat('${v.split("'").join(`', "'", '`)}')`;
203+ }
204+
205+ /** @param {Element} el */
206+ function xpath(el) {
207+ if (el.id) return `//*[@id=${xpathLiteral(el.id)}]`;
208+ /** @type {string[]} */
209+ const parts = [];
210+ for (
211+ let cur = /** @type {Element | null} */ (el);
212+ cur && cur.nodeType === 1;
213+ cur = cur.parentElement
214+ ) {
215+ let i = 1;
216+ for (let s = cur.previousElementSibling; s; s = s.previousElementSibling) {
217+ if (s.tagName === cur.tagName) i++;
218+ }
219+ parts.unshift(`${cur.tagName.toLowerCase()}[${i}]`);
220+ }
221+ return `/${parts.join("/")}`;
222+ }
223+
224+ /** @param {Element} el */
225+ function shortLabel(el) {
226+ let s = el.tagName.toLowerCase();
227+ if (el.id) s += `#${el.id}`;
228+ else if (el.classList && el.classList.length) s += `.${[...el.classList].slice(0, 2).join(".")}`;
229+ const r = el.getBoundingClientRect();
230+ return `${s} ${Math.round(r.width)}×${Math.round(r.height)}`;
231+ }
232+
233+ /** @param {Element} el */
234+ function describe(el) {
235+ const text = (el.textContent || "").trim().replace(/\s+/g, " ").slice(0, 120);
236+ // Geometry for the screenshot crop. Read here, at pick time, while the
237+ // overlay is already gone and the page has not had a chance to move.
238+ const r = el.getBoundingClientRect();
239+ return {
240+ rect: { x: r.left, y: r.top, width: r.width, height: r.height },
241+ viewport: { w: window.innerWidth, h: window.innerHeight },
242+ css: cssSelector(el),
243+ xpath: xpath(el),
244+ id: el.id || null,
245+ testid:
246+ (el.getAttribute && (el.getAttribute("data-testid") || el.getAttribute("data-test"))) ||
247+ null,
248+ tag: el.tagName.toLowerCase(),
249+ text: text || null,
250+ href: el.getAttribute && el.getAttribute("href"),
251+ pageUrl: location.href,
252+ };
253+ }
254+ });
255+}
addedextension/sidebar.css+414 −0
1+/* Every colour comes from lib/theme.js, applied to :root as custom properties
2+ at startup. The fallbacks here are the dark theme, so the panel is never
3+ unstyled during the first paint. */
4+:root {
5+ color-scheme: dark;
6+ --bg: #14161a;
7+ --bg-panel: #1c1f25;
8+ --bg-input: #14161a;
9+ --border: #2d3138;
10+ --fg: #d7dae0;
11+ --fg-muted: #8b93a1;
12+ --accent: #3b6ea5;
13+ --accent-hover: #4a82bd;
14+ --accent-fg: #ffffff;
15+ --ok: #6ec177;
16+ --err: #e06c75;
17+ --warn: #d5a458;
18+ --warn-bg: #241f16;
19+ --warn-border: #4a3a22;
20+ --shadow: rgba(0, 0, 0, 0.4);
21+}
22+* { box-sizing: border-box; }
23+/* Author `display` beats the UA rule for [hidden] no matter the specificity, so
24+ every flex/inline-flex element in here would otherwise ignore .hidden = true.
25+ Everything that toggles itself does it through the attribute; this is what
26+ makes that work. */
27+[hidden] { display: none !important; }
28+html, body { height: 100%; margin: 0; }
29+body {
30+ font: 13px/1.5 system-ui, sans-serif;
31+ background: var(--bg); color: var(--fg);
32+ display: flex; flex-direction: column; overflow: hidden;
33+}
34+/* Vertical space is scarce in a sidebar, but not so scarce that controls
35+ should become unclickable. 26px is about the smallest comfortable pointer
36+ target; the header stays short by having only two of them. */
37+/* Stretch, not centre: the active tab has to reach the bottom edge to sit on
38+ the seam. Everything else in the row re-centres itself. */
39+header {
40+ display: flex; align-items: stretch; gap: 4px;
41+ padding: 4px 6px 0; background: var(--bg-panel);
42+ border-bottom: 1px solid var(--border); flex: none;
43+}
44+#status-text {
45+ flex: 1; font-size: 11px; color: var(--fg-muted); min-width: 0;
46+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
47+}
48+.dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
49+
50+/* The tabs are the only thing in this row that reaches the seam; everything
51+ else is centred on the same 22px band, so the dot, the session name and the
52+ icons sit on one line rather than each on its own accidental one. The 4px
53+ bottom margin is the tabs' own gap above the seam, matched here. */
54+header > #session-chip,
55+header > #status-text,
56+header > button.icon {
57+ align-self: center;
58+ margin-bottom: 4px;
59+}
60+header > #status-text,
61+header > #session-chip { height: 22px; }
62+header > #status-text { line-height: 20px; }
63+
64+/* The session chip. Without a session there is nothing to switch to, so it is
65+ just the status dot sitting where it always did; with one it closes up into a
66+ recessed pill. The grey is mixed from the row rather than taken from
67+ --bg-input, which in the light theme is the same white as the terminal — the
68+ chip would then read as a lifted tab, which is the whole thing being avoided.
69+ The extra right margin is the gap that says the tabs are a different group. */
70+#session-chip {
71+ display: inline-flex; align-items: center; gap: 5px;
72+ flex: 0 1 auto; min-width: 0;
73+ margin-left: 2px;
74+ border: 1px solid transparent; border-radius: 999px;
75+}
76+body.has-session #session-chip {
77+ padding: 0 5px 0 7px; margin-right: 9px;
78+ background: rgba(128, 128, 128, 0.13);
79+ border-color: var(--border);
80+}
81+body.has-session #session-chip:hover { background: rgba(128, 128, 128, 0.2); }
82+#session-chip:focus-within { border-color: var(--accent); }
83+
84+/* Shrinkable: in a narrow panel the tabs are what the row is for, and the
85+ session name is already in the select's own tooltip. The select draws no box
86+ of its own — the chip around it is the box. */
87+#session-switch {
88+ flex: 0 1 auto; min-width: 3rem; max-width: 9rem;
89+ font: inherit; font-size: 11px; font-weight: 500; line-height: 20px;
90+ padding: 0; border: 0; border-radius: 0;
91+ background: transparent; color: var(--fg);
92+ text-overflow: ellipsis;
93+}
94+#session-switch:focus-visible { outline: none; }
95+
96+/* --- window tabs -----------------------------------------------------------
97+ A tab is a window of the attached session — what `prefix 2` selects. Clicking
98+ one is a select-window: nothing in this strip spawns or destroys a shell
99+ except "+", which opens a window. */
100+/* Sized to its tabs, not to the row: "+" sits against the last tab the way a
101+ browser's does, and the strip gives up width to scrolling before the row
102+ gives up its buttons. */
103+/* The strip has to reach over the header's bottom border, because the active
104+ tab's whole trick is covering the 1px of it that it sits on. `overflow-x:
105+ auto` computes overflow-y to `auto` as well, so a scroll container clips its
106+ children at the padding box no matter which axis you asked for: without the
107+ margin/padding pair below, the tab's overhang is cut off at the seam and the
108+ line runs straight through the one tab that is meant to break it.
109+ The margin grows the box downwards onto the border, the padding puts the
110+ tabs' own bottom edge back where it was, and the 1px between the two is room
111+ the overhang is allowed to paint in. */
112+#tabs {
113+ display: flex; align-items: stretch; gap: 2px;
114+ flex: 0 1 auto; min-width: 0;
115+ margin-bottom: -1px; padding-bottom: 1px;
116+ overflow-x: auto; scrollbar-width: none;
117+}
118+#tabs::-webkit-scrollbar { display: none; }
119+/* Nothing follows the tabs, so this is just what stops "+" from being stretched
120+ away from the last one. */
121+#tab-new { margin-right: auto; }
122+
123+/* The panel's controls are one group and the session is another; the row's 4px
124+ gap is not enough to say so. */
125+header > #settings-toggle { margin-right: 5px; }
126+
127+/* The tab and its close ✕ are siblings — a button cannot contain a button —
128+ so the slot is what the strip lays out, and the ✕ is positioned over the
129+ tab's right edge.
130+
131+ Past the min width a name is unreadable, so the strip scrolls instead of
132+ shrinking further: the same trade browsers make when the bar fills up. */
133+.tab-slot {
134+ position: relative; display: flex; align-items: stretch;
135+ flex: 0 1 auto; min-width: 5.5rem; max-width: 11rem;
136+}
137+/* The window you are on gives up its characters last. */
138+.tab-slot.active { min-width: 7rem; }
139+
140+button.tab {
141+ flex: 1 1 auto; min-width: 0;
142+ display: inline-flex; align-items: center; gap: 5px;
143+ padding: 4px 8px 5px; margin-bottom: 4px;
144+ border: 0; border-radius: 6px 6px 0 0;
145+ background: transparent; color: var(--fg-muted);
146+ font: inherit; font-size: 11px; line-height: 1.4;
147+ white-space: nowrap; cursor: pointer; text-align: left;
148+}
149+button.tab:hover { background: rgba(128, 128, 128, 0.16); color: var(--fg); }
150+button.tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
151+button.tab .name { overflow: hidden; text-overflow: ellipsis; min-width: 0; }
152+button.tab .index {
153+ font-size: 9.5px; opacity: .6; font-variant-numeric: tabular-nums; flex: none;
154+}
155+/* tmux's own "something happened here while you were away". */
156+button.tab.activity { color: var(--fg); }
157+button.tab.activity .name { font-weight: 600; }
158+
159+/* The favicon slot: what Claude Code is doing in that window, and nothing at
160+ all for a window that is just a shell — an always-lit glyph would be a status
161+ light reporting on nothing.
162+
163+ The glyph is Claude Code's own asterisk spinner, so a working window in the
164+ strip and the transcript in the pane are saying the same thing the same way.
165+ The box is sized and centred once, for the widest frame, so the name beside
166+ it does not step sideways as the animation cycles. */
167+button.tab .glyph {
168+ flex: none;
169+ width: 10px; height: 11px;
170+ display: inline-flex; align-items: center; justify-content: center;
171+ font-size: 10px; line-height: 1;
172+ color: var(--fg-muted); opacity: .5;
173+}
174+button.tab .glyph.none { display: none; }
175+button.tab .glyph.working { color: var(--warn); opacity: 1; }
176+button.tab .glyph.waiting { color: var(--accent-hover); opacity: 1; animation: pulse 1.4s ease-in-out infinite; }
177+
178+/* The lift: the terminal's background, shoulders drawn with an inset border,
179+ and a negative bottom margin that punches through the header's seam so the
180+ tab and the terminal read as one surface. */
181+button.tab[aria-selected="true"] {
182+ background: var(--bg); color: var(--fg);
183+ margin-bottom: -1px; padding-bottom: 10px;
184+ box-shadow: inset 1px 1px 0 0 var(--border), inset -1px 0 0 0 var(--border);
185+}
186+button.tab[aria-selected="true"]:hover { background: var(--bg); }
187+
188+/* Room for the ✕ is reserved rather than made on hover, so a name does not
189+ re-ellipsis under the pointer. */
190+.tab-slot.closable button.tab { padding-right: 20px; }
191+
192+/* Pinned: down to the dot and the index, sized to its content and sitting at
193+ the head of the strip. The index is what stays visible rather than the name,
194+ because the index is the thing you can type — `prefix 3` still selects it,
195+ pinned or not. */
196+.tab-slot.pinned { flex: 0 0 auto; min-width: 0; max-width: none; }
197+.tab-slot.pinned button.tab { padding-left: 7px; padding-right: 7px; gap: 4px; }
198+.tab-slot.pinned button.tab .index { opacity: 1; font-size: 11px; }
199+/* A hairline against the unpinned run, the way a browser separates them. */
200+.tab-slot.pinned + .tab-slot:not(.pinned) { margin-left: 5px; }
201+.tab-slot.pinned + .tab-slot:not(.pinned)::before {
202+ content: ""; position: absolute; left: -3px; top: 6px; bottom: 6px;
203+ border-left: 1px solid var(--border);
204+}
205+
206+/* Right-click menu. A plain positioned element: an extension page would
207+ otherwise get the browser's own menu, which has nothing to say about tmux. */
208+.tab-menu {
209+ position: fixed; z-index: 20; min-width: 8rem;
210+ display: flex; flex-direction: column; padding: 3px;
211+ background: var(--bg-panel); color: var(--fg);
212+ border: 1px solid var(--border); border-radius: 6px;
213+ box-shadow: 0 6px 20px var(--shadow);
214+}
215+.tab-menu button {
216+ background: transparent; color: var(--fg);
217+ border: 0; border-radius: 4px; padding: 5px 8px;
218+ font: inherit; font-size: 11px; text-align: left; cursor: pointer;
219+}
220+.tab-menu button:hover { background: var(--border); }
221+.tab-menu button.danger { color: var(--err); }
222+.tab-menu button.danger:hover { background: var(--err); color: var(--accent-fg); }
223+.tab-menu button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
224+
225+button.tab-close {
226+ position: absolute; right: 4px; top: 5px;
227+ width: 15px; height: 15px; padding: 0;
228+ display: inline-flex; align-items: center; justify-content: center;
229+ border: 0; border-radius: 4px;
230+ background: transparent; color: var(--fg-muted); cursor: pointer;
231+ opacity: 0; pointer-events: none; transition: opacity 90ms linear;
232+}
233+button.tab-close svg { width: 9px; height: 9px; display: block; }
234+/* On the tab you are on and the one you are pointing at — a full strip of ✕ is
235+ noise, and every one of them is destructive. */
236+.tab-slot.active button.tab-close,
237+.tab-slot:hover button.tab-close,
238+.tab-slot:focus-within button.tab-close { opacity: 1; pointer-events: auto; }
239+/* Red on hover rather than the usual grey: this is the one control in the
240+ panel that destroys something, and it takes effect on the first click. */
241+button.tab-close:hover { background: var(--err); color: var(--accent-fg); }
242+button.tab-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 0; }
243+
244+
245+button.icon {
246+ flex: none;
247+ width: 26px; height: 26px; padding: 0;
248+ display: inline-flex; align-items: center; justify-content: center;
249+ background: transparent; border: 0; border-radius: 5px;
250+ color: var(--fg-muted); cursor: pointer;
251+}
252+button.icon svg { width: 15px; height: 15px; display: block; }
253+button.icon:hover { background: var(--border); color: var(--fg); }
254+button.icon:active { transform: translateY(0.5px); }
255+button.icon:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; }
256+#pick.active { background: var(--accent); color: var(--accent-fg); }
257+
258+/* The session name lives in the switcher once there is one, so the status text
259+ stands down rather than saying it twice. The dot stays: it is the only thing
260+ reporting on the connection itself. */
261+body.has-session > header > #status-text { display: none; }
262+
263+/* Compact: lose the status text, keep the targets clickable. */
264+body.compact header { padding: 2px 4px 0; }
265+body.compact #status-text { display: none; }
266+body.compact button.icon { width: 24px; height: 24px; }
267+body.compact > header > #session-chip { height: 20px; }
268+body.compact #session-switch { line-height: 18px; }
269+body.compact .tab-slot { max-width: 8rem; }
270+body.compact button.tab { padding: 3px 6px 4px; }
271+body.compact .tab-slot.closable button.tab { padding-right: 19px; }
272+body.compact button.tab[aria-selected="true"] { padding-bottom: 9px; }
273+body.compact button.tab .index { display: none; }
274+body.compact button.tab-close { top: 4px; }
275+body.compact #term { padding: 2px; }
276+
277+/* Hidden: no header at all. A 4px strip along the top brings it back on
278+ hover, so the settings panel is never unreachable. */
279+body.chromeless header {
280+ position: absolute; top: 0; left: 0; right: 0; z-index: 5;
281+ opacity: 0; pointer-events: none; transition: opacity 90ms linear;
282+}
283+body.chromeless #reveal-strip {
284+ display: block; position: absolute; top: 0; left: 0; right: 0;
285+ height: 4px; z-index: 6;
286+}
287+body.chromeless #reveal-strip:hover ~ header,
288+body.chromeless header:hover { opacity: 1; pointer-events: auto; }
289+body.chromeless #term { padding: 2px; }
290+#reveal-strip { display: none; }
291+
292+.dot.on { background: var(--ok); }
293+.dot.off { background: var(--err); }
294+.dot.pending { background: var(--warn); }
295+
296+@keyframes pulse { 50% { opacity: .25; } }
297+/* The spinner is driven from script, which parks it on a single frame when
298+ motion is reduced; this is only the waiting glyph's own breathing. */
299+@media (prefers-reduced-motion: reduce) {
300+ button.tab .glyph.waiting { animation: none; }
301+}
302+#term { flex: 1; min-height: 0; padding: 4px; overflow: hidden; }
303+#term .xterm { height: 100%; }
304+aside {
305+ flex: none; max-height: 55%; overflow-y: auto;
306+ padding: 10px; background: var(--bg-panel); border-top: 1px solid var(--border);
307+}
308+h2 { font-size: 11px; text-transform: uppercase; letter-spacing: .06em;
309+ color: var(--fg-muted); margin: 12px 0 4px; font-weight: 600; }
310+h2:first-child { margin-top: 0; }
311+.hint { color: var(--fg-muted); margin: 0 0 6px; font-size: 11px; }
312+.row { display: flex; gap: 6px; align-items: center; }
313+input[type=text], input[type=password], input:not([type]), #url {
314+ width: 100%; background: var(--bg-input); color: var(--fg);
315+ border: 1px solid var(--border); border-radius: 4px; padding: 5px 7px;
316+ font-family: ui-monospace, monospace; font-size: 11px;
317+}
318+button {
319+ background: var(--accent); color: var(--accent-fg); border: 0; border-radius: 4px;
320+ padding: 5px 10px; font-size: 11px; cursor: pointer;
321+}
322+button:hover { background: var(--accent-hover); }
323+button.ghost { background: transparent; border: 1px solid var(--border);
324+ color: var(--fg-muted); padding: 3px 7px; }
325+button.ghost:hover { background: var(--border); }
326+pre {
327+ background: var(--bg-input); border: 1px solid var(--border); border-radius: 4px;
328+ padding: 6px; margin: 0 0 6px; overflow-x: auto;
329+ font-family: ui-monospace, monospace; font-size: 10px;
330+ white-space: pre-wrap; word-break: break-all;
331+}
332+#log { max-height: 120px; overflow-y: auto; color: var(--fg-muted); }
333+code { font-family: ui-monospace, monospace; background: var(--bg-input);
334+ padding: 1px 3px; border-radius: 3px; font-size: 10px; }
335+label.row { color: var(--fg-muted); font-size: 11px; margin-top: 5px; }
336+label.row input { width: auto; }
337+
338+#trust-hint { margin-top: 8px; padding: 8px; border: 1px solid var(--warn-border);
339+ background: var(--warn-bg); border-radius: 4px; }
340+.hint.warn { color: var(--warn); }
341+
342+/* Picked-element panel. A results card, not a toolbar: one primary action,
343+ the rest reduced to icons so the value itself is what draws the eye. */
344+#picked {
345+ flex: none;
346+ background: var(--bg-panel);
347+ border-bottom: 1px solid var(--border);
348+ border-top: 2px solid var(--accent);
349+ padding: 7px 8px 9px;
350+}
351+
352+.picked-top { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; }
353+.chip {
354+ font-family: ui-monospace, monospace; font-size: 11px;
355+ color: var(--accent); background: var(--bg-input);
356+ border: 1px solid var(--border); border-radius: 4px;
357+ padding: 1px 6px; flex: none;
358+}
359+#picked-meta {
360+ flex: 1; min-width: 0; font-size: 11px; color: var(--fg-muted);
361+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
362+}
363+.picked-top button.icon { width: 22px; height: 22px; }
364+.picked-top button.icon svg { width: 13px; height: 13px; }
365+
366+/* Segmented control instead of a native <select>: the options are few, short,
367+ and worth seeing all at once. */
368+.segmented {
369+ display: flex; gap: 2px; margin-bottom: 6px;
370+ background: var(--bg-input);
371+ border: 1px solid var(--border); border-radius: 6px; padding: 2px;
372+}
373+.segmented button {
374+ flex: 1 1 0; min-width: 0;
375+ background: transparent; color: var(--fg-muted);
376+ border: 0; border-radius: 4px; padding: 3px 4px;
377+ font-size: 10px; font-weight: 500; letter-spacing: .02em;
378+ cursor: pointer; white-space: nowrap;
379+ overflow: hidden; text-overflow: ellipsis;
380+}
381+.segmented button:hover { color: var(--fg); background: var(--border); }
382+.segmented button[aria-selected="true"] {
383+ background: var(--accent); color: var(--accent-fg);
384+}
385+.segmented button[aria-selected="true"]:hover { background: var(--accent-hover); }
386+
387+#picked-value {
388+ background: var(--bg-input);
389+ border: 1px solid var(--border); border-radius: 6px;
390+ padding: 7px 8px;
391+ font-family: ui-monospace, monospace; font-size: 11px; line-height: 1.45;
392+ color: var(--fg);
393+ word-break: break-all; white-space: pre-wrap;
394+ max-height: 84px; overflow-y: auto;
395+ user-select: all;
396+}
397+#picked-value.empty { color: var(--fg-muted); font-style: italic; user-select: none; }
398+
399+#picked-insert { width: 100%; margin-top: 7px; padding: 6px 10px; font-size: 11px; }
400+#picked-warn { margin: 6px 0 0; font-size: 11px; }
401+#picked-grant { width: 100%; margin-top: 6px; }
402+
403+select { font-family: inherit; }
404+
405+#density-hint { margin-top: 6px; }
406+
407+#font-row { margin-top: 7px; }
408+#font-row .hint { margin: 0; margin-right: auto; }
409+#font-row button.icon { width: 24px; height: 24px; border: 1px solid var(--border); }
410+#font-size { font-variant-numeric: tabular-nums; font-size: 11px; min-width: 2ch;
411+ text-align: center; color: var(--fg); }
412+#font-reset { padding: 4px 8px; font-size: 11px; }
413+#theme-select, #density-select { width: auto; background: var(--bg-input); color: var(--fg);
414+ border: 1px solid var(--border); border-radius: 4px; padding: 5px 7px; font-size: 11px; }
addedextension/sidebar.html+184 −0
1+<!doctype html>
2+<html>
3+ <head>
4+ <meta charset="utf-8" />
5+ <title>terminal</title>
6+ <link rel="stylesheet" href="vendor/xterm.css" />
7+ <link rel="stylesheet" href="sidebar.css" />
8+ </head>
9+ <body>
10+ <div id="reveal-strip"></div>
11+
12+ <header>
13+ <!-- The panel's own controls, held together at the left edge so the tab
14+ strip owns the rest of the row and grows rightwards into it.
15+ Reconnect appears only when it is actually actionable; theme and
16+ density live in settings. Inline SVG rather than unicode glyphs,
17+ which render at wildly different weights depending on which font the
18+ platform falls back to. -->
19+ <button id="reconnect" class="icon" title="Reconnect" hidden>
20+ <svg viewBox="0 0 16 16" aria-hidden="true">
21+ <path d="M13.5 8a5.5 5.5 0 1 1-1.7-4" fill="none" stroke="currentColor"
22+ stroke-width="1.6" stroke-linecap="round"/>
23+ <path d="M13.5 1.5v3.2h-3.2" fill="none" stroke="currentColor"
24+ stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
25+ </svg>
26+ </button>
27+
28+ <button id="pick" class="icon" title="Pick an element on the page (Esc cancels)">
29+ <svg viewBox="0 0 16 16" aria-hidden="true">
30+ <circle cx="8" cy="8" r="3.1" fill="none" stroke="currentColor" stroke-width="1.6"/>
31+ <path d="M8 .8v2.6M8 12.6v2.6M.8 8h2.6M12.6 8h2.6" stroke="currentColor"
32+ stroke-width="1.6" stroke-linecap="round"/>
33+ </svg>
34+ </button>
35+
36+ <button id="settings-toggle" class="icon" title="Settings">
37+ <svg viewBox="0 0 16 16" aria-hidden="true">
38+ <path d="M2 4.5h12M2 8h12M2 11.5h12" stroke="currentColor"
39+ stroke-width="1.6" stroke-linecap="round"/>
40+ <circle cx="5.5" cy="4.5" r="1.9" fill="currentColor"/>
41+ <circle cx="10.5" cy="11.5" r="1.9" fill="currentColor"/>
42+ </svg>
43+ </button>
44+
45+ <!-- The session the tabs belong to. Switching here moves the live tmux
46+ client (switch-client), so the terminal below stays connected and
47+ nothing is re-spawned. It also carries the session name that the
48+ status text used to, since the tabs push that out of the row.
49+
50+ Dot and select are one chip so the session reads as a control and not
51+ as another tab: the chip is a recessed pill with a full border, where
52+ a tab is borderless and the selected one is *lifted* onto the
53+ terminal's background. Same row, opposite direction. -->
54+ <div id="session-chip">
55+ <span id="status" class="dot off" title="disconnected"></span>
56+ <select id="session-switch" title="tmux session" hidden></select>
57+ </div>
58+
59+ <!-- Only while there is no session name to stand in for it. -->
60+ <span id="status-text">disconnected</span>
61+
62+ <!-- One tab per window in the attached session, browser-style: the active
63+ tab is lifted to the terminal's own background so the two read as one
64+ surface, and the dot in its favicon slot is what Claude Code is doing
65+ in that window. Clicking a tab runs select-window — the same thing
66+ `prefix 2` does in the terminal, and it moves every client watching
67+ the session, because a window belongs to the session and not to us.
68+ Populated from the daemon's status frames. -->
69+ <div id="tabs" role="tablist" aria-label="tmux windows" hidden></div>
70+
71+ <button id="tab-new" class="icon" title="New window" hidden>
72+ <svg viewBox="0 0 16 16" aria-hidden="true">
73+ <path d="M3.5 8h9M8 3.5v9" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/>
74+ </svg>
75+ </button>
76+
77+ </header>
78+
79+ <div id="picked" hidden>
80+ <div class="picked-top">
81+ <span id="picked-tag" class="chip"></span>
82+ <span id="picked-meta"></span>
83+ <button id="picked-copy" class="icon" title="Copy to clipboard">
84+ <svg viewBox="0 0 16 16" aria-hidden="true">
85+ <rect x="5.2" y="5.2" width="8.6" height="8.6" rx="1.8"
86+ fill="none" stroke="currentColor" stroke-width="1.5"/>
87+ <path d="M10.8 5.2V4a1.8 1.8 0 0 0-1.8-1.8H4A1.8 1.8 0 0 0 2.2 4v5a1.8 1.8 0 0 0 1.8 1.8h1.2"
88+ fill="none" stroke="currentColor" stroke-width="1.5"/>
89+ </svg>
90+ </button>
91+ <button id="picked-close" class="icon" title="Dismiss">
92+ <svg viewBox="0 0 16 16" aria-hidden="true">
93+ <path d="M4 4l8 8M12 4l-8 8" stroke="currentColor"
94+ stroke-width="1.6" stroke-linecap="round"/>
95+ </svg>
96+ </button>
97+ </div>
98+
99+ <div id="picked-formats" class="segmented" role="tablist"></div>
100+ <div id="picked-value"></div>
101+
102+ <p id="picked-warn" class="hint warn" hidden></p>
103+ <button id="picked-grant" hidden></button>
104+ <button id="picked-insert">Insert into terminal</button>
105+ </div>
106+
107+ <div id="term"></div>
108+
109+ <aside id="settings" hidden>
110+ <h2>Pair this extension</h2>
111+ <p class="hint">Run this once, then restart the daemon:</p>
112+ <pre id="pair-cmd">loading…</pre>
113+ <button id="copy-origin" class="ghost">copy</button>
114+
115+ <h2>Token</h2>
116+ <p class="hint"><code>termbridge token</code> prints it.</p>
117+ <input id="token" type="password" placeholder="64 hex chars" autocomplete="off" spellcheck="false" />
118+ <label class="row"><input id="reveal" type="checkbox" /> reveal</label>
119+ <p id="token-hint" class="hint warn" hidden></p>
120+
121+ <h2>Appearance</h2>
122+ <div class="row">
123+ <select id="theme-select">
124+ <option value="auto">Follow system</option>
125+ <option value="light">Light</option>
126+ <option value="dark">Dark</option>
127+ </select>
128+ <select id="density-select" title="How much room the header takes">
129+ <option value="normal">Normal header</option>
130+ <option value="compact">Compact header</option>
131+ <option value="hidden">Hide header</option>
132+ </select>
133+ </div>
134+ <p class="hint" id="density-hint"></p>
135+
136+ <div class="row" id="font-row">
137+ <span class="hint">Font size</span>
138+ <button id="font-smaller" class="icon" title="Smaller (Ctrl+Alt+- or Ctrl+wheel)">
139+ <svg viewBox="0 0 16 16" aria-hidden="true">
140+ <path d="M3.5 8h9" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/>
141+ </svg>
142+ </button>
143+ <span id="font-size">12</span>
144+ <button id="font-bigger" class="icon" title="Bigger (Ctrl+Alt+= or Ctrl+wheel)">
145+ <svg viewBox="0 0 16 16" aria-hidden="true">
146+ <path d="M3.5 8h9M8 3.5v9" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/>
147+ </svg>
148+ </button>
149+ <button id="font-reset" class="ghost" title="Reset (Ctrl+Alt+0)">reset</button>
150+ </div>
151+
152+ <h2>tmux session</h2>
153+ <p class="hint">Attaches if it exists, creates it otherwise.</p>
154+ <div class="row">
155+ <input id="session" list="session-list" placeholder="browser" spellcheck="false" />
156+ <datalist id="session-list"></datalist>
157+ <button id="session-apply" class="ghost">attach</button>
158+ </div>
159+
160+ <h2>Daemon</h2>
161+ <input id="url" value="ws://127.0.0.1:7681" spellcheck="false" />
162+ <button id="connect">connect</button>
163+
164+ <div id="trust-hint" hidden>
165+ <p class="hint warn">
166+ Firefox upgrades <code>ws://</code> to <code>wss://</code>. The
167+ daemon's certificate is self-signed, so it needs a one-time exception.
168+ </p>
169+ <button id="trust-cert" class="ghost">open https:// to trust the cert</button>
170+ </div>
171+
172+ <h2>Log</h2>
173+ <pre id="log"></pre>
174+ </aside>
175+
176+ <script src="lib/theme.js"></script>
177+ <script src="lib/sanitize.js"></script>
178+ <script src="lib/shot.js"></script>
179+ <script src="picker.js"></script>
180+ <script src="vendor/xterm.js"></script>
181+ <script src="vendor/addon-fit.js"></script>
182+ <script src="sidebar.js"></script>
183+ </body>
184+</html>
addedextension/sidebar.js+1383 −0
1+// termbridge sidebar.
2+//
3+// Owns the WebSocket directly. There is deliberately no relay through the
4+// service worker: `runtime.sendMessage` is reachable from content scripts, so
5+// routing terminal data through it would create exactly the bridge that lets a
6+// hostile page reach the shell. This document has no inbound message listeners.
7+
8+// One of the two always exists — this file only ever runs as sidebar.html — but
9+// neither is declared unconditionally, so say which.
10+const api = /** @type {TbExtensionApi} */ (globalThis.browser ?? globalThis.chrome);
11+const storage = api.storage.local;
12+
13+/**
14+ * Every id passed here is one this file's own sidebar.html declares, so a miss
15+ * is a bug in the pair rather than a runtime condition to handle. Asserting
16+ * that is what keeps the call sites readable.
17+ *
18+ * @param {string} id
19+ * @returns {HTMLElement}
20+ */
21+const $ = (id) => /** @type {HTMLElement} */ (document.getElementById(id));
22+
23+/** @param {string} id @returns {HTMLInputElement} */
24+const $input = (id) => /** @type {HTMLInputElement} */ ($(id));
25+/** @param {string} id @returns {HTMLSelectElement} */
26+const $select = (id) => /** @type {HTMLSelectElement} */ ($(id));
27+
28+const logEl = $("log");
29+/** @param {string} m */
30+const log = (m) => {
31+ logEl.textContent += `${m}\n`;
32+ logEl.scrollTop = logEl.scrollHeight;
33+};
34+
35+const ORIGIN = location.origin;
36+$("pair-cmd").textContent = `termbridge pair ${ORIGIN}`;
37+
38+// --- terminal ---------------------------------------------------------------
39+
40+// --- theme -------------------------------------------------------------------
41+
42+const darkQuery = window.matchMedia("(prefers-color-scheme: dark)");
43+let themePref = "auto";
44+let termReady = false;
45+
46+function currentTheme() {
47+ return Themes.resolveTheme(themePref, darkQuery.matches);
48+}
49+
50+function applyTheme() {
51+ const t = currentTheme();
52+ const root = document.documentElement;
53+ for (const [k, v] of Object.entries(t.ui)) root.style.setProperty(k, v);
54+ // Drives native scrollbars, form controls and the caret.
55+ root.style.colorScheme = t.colorScheme;
56+ if (termReady) term.options.theme = t.xterm;
57+ $select("theme-select").value = themePref;
58+}
59+
60+/** @type {Record<string, { cls: string, hint: string }>} */
61+const DENSITIES = {
62+ normal: { cls: "", hint: "Session tabs, status text and icons." },
63+ compact: { cls: "compact", hint: "Tighter tabs — about one extra terminal row." },
64+ hidden: { cls: "chromeless", hint: "Hover the top edge of the panel to bring it back." },
65+};
66+let density = "normal";
67+
68+function applyDensity() {
69+ const d = DENSITIES[density] ?? DENSITIES.normal;
70+ document.body.classList.remove("compact", "chromeless");
71+ if (d.cls) document.body.classList.add(d.cls);
72+ $select("density-select").value = density;
73+ $("density-hint").textContent = d.hint;
74+ // The terminal has a different number of rows now.
75+ if (termReady) {
76+ fit.fit();
77+ sendSize();
78+ }
79+}
80+
81+/** @param {string} value */
82+function setDensity(value) {
83+ density = DENSITIES[value] ? value : "normal";
84+ storage.set({ density });
85+ applyDensity();
86+}
87+
88+// Below ~8px xterm.js's glyph atlas stops being legible; above ~32 a sidebar
89+// holds too few columns to be a terminal.
90+const FONT_DEFAULT = 12;
91+const FONT_MIN = 8;
92+const FONT_MAX = 32;
93+let fontSize = FONT_DEFAULT;
94+
95+function applyFontSize() {
96+ $("font-size").textContent = String(fontSize);
97+ if (!termReady) return;
98+ term.options.fontSize = fontSize;
99+ // Cell metrics changed, so the row/column count did too.
100+ fit.fit();
101+ sendSize();
102+}
103+
104+/** @param {number|string} value */
105+function setFontSize(value) {
106+ const n = Math.round(Number(value));
107+ fontSize = Number.isFinite(n) ? Math.min(FONT_MAX, Math.max(FONT_MIN, n)) : FONT_DEFAULT;
108+ storage.set({ fontSize });
109+ applyFontSize();
110+}
111+
112+/** `_` and `+` are what the shifted keys report. @type {Record<string, number>} */
113+const FONT_STEP = { "=": 1, "+": 1, "-": -1, _: -1 };
114+
115+/**
116+ * Ctrl+Alt rather than plain Ctrl: Ctrl+= / Ctrl+- / Ctrl+0 are browser zoom
117+ * accelerators, which pages cannot cancel, so binding them would zoom the whole
118+ * panel instead. Ctrl+wheel *is* cancelable, so that gesture works as expected.
119+ *
120+ * Returns true if the event was a font-size command and has been handled.
121+ *
122+ * @param {KeyboardEvent} e
123+ */
124+function handleFontKey(e) {
125+ if (!e.ctrlKey || !e.altKey || e.metaKey) return false;
126+ const delta = FONT_STEP[e.key];
127+ if (delta) setFontSize(fontSize + delta);
128+ else if (e.key === "0") setFontSize(FONT_DEFAULT);
129+ else return false;
130+ e.preventDefault();
131+ return true;
132+}
133+
134+/** @param {string} pref */
135+function setTheme(pref) {
136+ themePref = Themes.PREFERENCES.includes(pref) ? pref : "auto";
137+ storage.set({ theme: themePref });
138+ applyTheme();
139+}
140+
141+// Only matters while the preference is "auto", but the listener is harmless
142+// otherwise and avoids add/remove churn.
143+darkQuery.addEventListener("change", () => {
144+ if (themePref === "auto") applyTheme();
145+});
146+
147+const term = new Terminal({
148+ fontFamily:
149+ 'ui-monospace, "JetBrains Mono", "Cascadia Code", Menlo, Consolas, monospace',
150+ fontSize: FONT_DEFAULT,
151+ lineHeight: 1.2,
152+ cursorBlink: true,
153+ scrollback: 5000,
154+ theme: Themes.resolveTheme("auto", darkQuery.matches).xterm,
155+ // tmux owns the scrollback (copy-mode). Letting xterm.js also scroll fights
156+ // with it and with mouse-mode applications.
157+ allowProposedApi: true,
158+});
159+
160+const fit = new FitAddon.FitAddon();
161+term.loadAddon(fit);
162+term.open($("term"));
163+fit.fit();
164+
165+// Returning false keeps the keystroke out of the pty.
166+term.attachCustomKeyEventHandler((e) => {
167+ if (e.type !== "keydown") return true;
168+ return !handleFontKey(e);
169+});
170+
171+$("term").addEventListener(
172+ "wheel",
173+ (e) => {
174+ if (!e.ctrlKey) return;
175+ e.preventDefault();
176+ setFontSize(fontSize + (e.deltaY < 0 ? 1 : -1));
177+ },
178+ { passive: false },
179+);
180+
181+// Set only once term *and* fit are usable — applyTheme/applyDensity both touch
182+// them, and a flag that runs ahead of the objects it guards is worse than none.
183+termReady = true;
184+
185+const enc = new TextEncoder();
186+
187+// --- connection -------------------------------------------------------------
188+
189+let ws = /** @type {WebSocket | null} */ (null);
190+let connected = false;
191+// Session tmux says we are on. The name we asked to attach to is only the
192+// starting point — switch-client moves us and the header should follow.
193+let sessionName = /** @type {string | null} */ (null);
194+
195+// Whether the daemon's program is tmux. Without it there are no windows to put
196+// in tabs, and "+" would have nothing to create.
197+let tmuxMode = false;
198+
199+/**
200+ * @param {"on"|"off"|"pending"} state drives the dot's colour class
201+ * @param {string} text
202+ */
203+function setStatus(state, text) {
204+ $("status").className = `dot ${state}`;
205+ $("status").title = text;
206+ $("status-text").textContent = text;
207+ $("reconnect").hidden = state === "on";
208+}
209+
210+// One place decides what the header says, so anything that repaints it (a
211+// finished pick, a fresh session name) can't quietly drop the other half.
212+function refreshStatus() {
213+ if (!connected) {
214+ setStatus("off", "disconnected");
215+ return;
216+ }
217+ setStatus("on", sessionName ? `connected · ${sessionName}` : "connected");
218+}
219+
220+function sendSize() {
221+ if (!connected || !ws) return;
222+ const { cols, rows } = term;
223+ ws.send(JSON.stringify({ type: "resize", cols, rows }));
224+}
225+
226+// The daemon can only answer "invalid token" on the wire; it cannot reach into
227+// the sidebar. Translate that into the one instruction that actually fixes it.
228+const TOKEN_HELP =
229+ "Run termbridge token in the terminal running the daemon, then paste " +
230+ "the 64 hex characters it prints into the Token field above.";
231+
232+/** @param {string} text */
233+function tokenProblem(text) {
234+ $("token-hint").textContent = `${text} ${TOKEN_HELP}`;
235+ $("token-hint").hidden = false;
236+ $("settings").hidden = false;
237+ term.write(
238+ `\r\n\x1b[33m ${text}\x1b[0m\r\n` +
239+ " Open \x1b[1msettings\x1b[0m (top right) and set the Token.\r\n" +
240+ " \x1b[90mtermbridge token\x1b[0m prints the value to paste.\r\n",
241+ );
242+ $("token").focus();
243+}
244+
245+function connect() {
246+ const url = $input("url").value.trim();
247+ const token = $input("token").value.trim();
248+
249+ if (!token) {
250+ // Connecting with an empty token just produces an "invalid token" reject in
251+ // the daemon's log, which reads like a wrong token rather than a missing one.
252+ setStatus("off", "no token");
253+ log("no token set — not connecting");
254+ tokenProblem("No token is set.");
255+ return;
256+ }
257+ $("token-hint").hidden = true;
258+
259+ if (ws) {
260+ ws.onclose = null;
261+ ws.close();
262+ }
263+ connected = false;
264+ setStatus("pending", "connecting…");
265+ log(`connecting to ${url}`);
266+
267+ try {
268+ ws = new WebSocket(url);
269+ } catch (e) {
270+ setStatus("off", "failed");
271+ log(`constructor threw: ${e}`);
272+ return;
273+ }
274+ // The handlers close over *this* socket rather than whatever `ws` points at
275+ // when they fire: a reconnect replaces the module-level reference, and a
276+ // late frame from the old socket must not be answered on the new one.
277+ const sock = ws;
278+ sock.binaryType = "arraybuffer";
279+
280+ sock.onopen = () => {
281+ // Token goes in a frame, never the URL — query strings end up in logs,
282+ // crash dumps and devtools history.
283+ sock.send(JSON.stringify({ type: "auth", token }));
284+ };
285+
286+ sock.onmessage = (ev) => {
287+ if (typeof ev.data === "string") {
288+ // A claim about the frame, not a guarantee — see types/globals.d.ts. It
289+ // buys the narrowing below, not trust.
290+ const msg = /** @type {TbFrame} */ (JSON.parse(ev.data));
291+ if (msg.type === "ok") {
292+ connected = true;
293+ tmuxMode = msg.tmux === true;
294+ $("trust-hint").hidden = true;
295+ $("token-hint").hidden = true;
296+ renderSessions(msg);
297+ const want = $input("session").value.trim() || undefined;
298+ // Optimistic; the daemon's first status frame replaces this with what
299+ // tmux actually attached us to, usually within a second.
300+ sessionName = want ?? msg.defaultSession ?? msg.profile;
301+ refreshStatus();
302+ // Session names only at this point — the window list needs the control
303+ // channel, which the first status frame brings a moment later.
304+ renderSessionSwitch(
305+ (msg.sessions ?? []).map((name) => ({ name, windows: 0, attached: false })),
306+ sessionName,
307+ );
308+ log(`authenticated, running ${msg.profile}`);
309+ fit.fit();
310+ // The daemon validates this name and falls back to its default if it
311+ // doesn't like it; we never get to choose the program.
312+ sock.send(
313+ JSON.stringify({
314+ type: "open",
315+ cols: term.cols,
316+ rows: term.rows,
317+ ...(want ? { session: want } : {}),
318+ }),
319+ );
320+ $("settings").hidden = true;
321+ term.focus();
322+ } else if (msg.type === "status") {
323+ if (msg.session && msg.session !== sessionName) {
324+ sessionName = msg.session;
325+ refreshStatus();
326+ }
327+ renderSessionSwitch(msg.sessions ?? [], msg.session);
328+ renderTabs(msg.windows ?? [], msg.agents ?? []);
329+ } else if (msg.type === "tmux-error") {
330+ // tmux refused the command (session gone, pane closed). The next status
331+ // frame already shows the truth, so this only needs to explain itself.
332+ log(`tmux: ${msg.reason}`);
333+ } else if (msg.type === "exit") {
334+ log(`session exited (${msg.code})`);
335+ term.write(`\r\n\x1b[90m[session exited: ${msg.code}]\x1b[0m\r\n`);
336+ } else if (msg.type === "error") {
337+ setStatus("off", msg.reason);
338+ log(`rejected: ${msg.reason}`);
339+ if (/token|auth/i.test(msg.reason)) {
340+ tokenProblem("The daemon rejected this token.");
341+ } else if (/origin/i.test(msg.reason)) {
342+ $("settings").hidden = false;
343+ term.write(
344+ `\r\n\x1b[33m ${msg.reason}\x1b[0m\r\n` +
345+ " Open \x1b[1msettings\x1b[0m and run the pairing command shown there.\r\n",
346+ );
347+ }
348+ }
349+ return;
350+ }
351+ // Raw terminal bytes. xterm.js takes Uint8Array directly, so nothing is
352+ // decoded, re-encoded, or mangled on the way in.
353+ term.write(new Uint8Array(ev.data));
354+ };
355+
356+ sock.onerror = () => {
357+ // The browser hides the reason from JS by design. The daemon's stdout has
358+ // the real one (unpaired origin / bad token / rate limited).
359+ log("socket error — check the daemon's output for the reason");
360+ // Firefox's HTTPS-Only Mode silently rewrites ws:// to wss://, so the most
361+ // likely cause here is that our self-signed certificate isn't trusted yet.
362+ $("trust-hint").hidden = false;
363+ };
364+
365+ sock.onclose = (ev) => {
366+ connected = false;
367+ sessionName = null;
368+ tmuxMode = false;
369+ renderTabs([], []);
370+ renderSessionSwitch([], null);
371+ refreshStatus();
372+ log(`closed code=${ev.code} ${ev.reason || ""}`);
373+ };
374+}
375+
376+// Ask the daemon to move tmux. The daemon accepts three commands and validates
377+// every argument; the sidebar cannot name a tmux command of its own.
378+/**
379+ * @param {{ cmd: "switch"|"create"|"focus"|"select-window"|"new-window"|"kill-window" }
380+ * & Record<string, string>} body
381+ */
382+function tmuxCommand(body) {
383+ if (!connected || !ws) return;
384+ ws.send(JSON.stringify({ type: "tmux", ...body }));
385+}
386+
387+// --- window tabs ------------------------------------------------------------
388+//
389+// A tab is a window of the attached session — the thing `prefix 2` selects, not
390+// a session and not a pane. Window names come from tmux (a shell sets them from
391+// whatever it is running, so they are as page-influenced as any other terminal
392+// output) and only ever reach the DOM through textContent.
393+
394+// --- pinning ----------------------------------------------------------------
395+//
396+// A pin is a property of this panel, not of the session. tmux is never asked to
397+// renumber anything: the window keeps its real index, so `prefix 4` still goes
398+// where it always went, and nothing about your terminal's status line changes.
399+// All a pin does is move the tab to the front of *this* strip and shrink it to
400+// its dot and index, which is what buys the room in a narrow sidebar.
401+//
402+// Keyed by session name, because window ids are only meaningful inside the tmux
403+// server that issued them and only prunable against the session we can see.
404+/** @type {Record<string, string[]>} */
405+let pins = {};
406+
407+// The last frame's worth of windows, so a pin — which changes nothing on the
408+// wire and so produces no status frame — can repaint the strip on its own.
409+/** @type {TbWindowInfo[]} */
410+let lastWindows = [];
411+/** @type {TbAgent[]} */
412+let lastAgents = [];
413+
414+/** @returns {Set<string>} pinned window ids in the attached session */
415+function pinnedIds() {
416+ return new Set((sessionName && pins[sessionName]) || []);
417+}
418+
419+/** @param {Set<string>} ids */
420+function savePins(ids) {
421+ if (!sessionName) return;
422+ if (ids.size) pins[sessionName] = [...ids];
423+ else delete pins[sessionName];
424+ storage.set({ pins });
425+}
426+
427+/**
428+ * @param {TbWindowInfo[]} windows in index order
429+ * @param {TbAgent[]} agents so a tab can say what Claude is doing in it
430+ */
431+function renderTabs(windows, agents) {
432+ lastWindows = windows;
433+ lastAgents = agents;
434+ const strip = $("tabs");
435+ const show = connected && tmuxMode && windows.length > 0;
436+ $("tab-new").hidden = !(connected && tmuxMode && sessionName);
437+ strip.hidden = !show;
438+ if (!show) {
439+ strip.textContent = "";
440+ strip.dataset.sig = "";
441+ syncSpinner();
442+ return;
443+ }
444+
445+ // A window that closed takes its pin with it. Pruning is safe here and only
446+ // here: this is the one moment we hold the full window list for a session.
447+ const pinned = pinnedIds();
448+ const live = new Set(windows.map((w) => w.id));
449+ for (const id of pinned) {
450+ if (!live.has(id)) pinned.delete(id);
451+ }
452+ if (pinned.size !== ((sessionName && pins[sessionName]) || []).length) savePins(pinned);
453+
454+ // Pinned first, each group still in tmux's own index order — nothing is
455+ // reordered on the server, so the indexes stay the ground truth they are.
456+ const ordered = [
457+ ...windows.filter((w) => pinned.has(w.id)),
458+ ...windows.filter((w) => !pinned.has(w.id)),
459+ ];
460+
461+ // Repainting drops hover and any focus ring; status frames arrive on every
462+ // tmux notification and once a second besides.
463+ const claude = agentByWindow(agents);
464+ const sig = JSON.stringify(
465+ ordered.map((w) => {
466+ const a = claude[w.id];
467+ return [w.id, w.index, w.name, w.active, w.activity, a?.state, agentLabel(a), pinned.has(w.id)];
468+ }),
469+ );
470+ if (strip.dataset.sig !== sig) {
471+ strip.dataset.sig = sig;
472+ strip.textContent = "";
473+ // Killing a session's last window kills the session, and with it the client
474+ // this panel is looking through. That is not a tab close, so it isn't
475+ // offered as one.
476+ const closable = windows.length > 1;
477+ for (const w of ordered) {
478+ strip.appendChild(windowTab(w, claude[w.id], closable, pinned.has(w.id)));
479+ }
480+ }
481+
482+ syncSpinner();
483+
484+ const active = strip.querySelector('[aria-selected="true"]');
485+ // Sidebars are narrow enough that the window you are on can be scrolled out
486+ // of the strip entirely.
487+ if (active) active.scrollIntoView({ block: "nearest", inline: "nearest" });
488+}
489+
490+// Loudest first: a window with something waiting on you outranks one that is
491+// merely busy. A window with no Claude in it gets no entry at all.
492+const AGENT_RANK = ["waiting", "working", "idle", "unknown"];
493+
494+/**
495+ * @param {TbAgent[]} agents
496+ * @returns {Record<string, TbAgent>} window id → the one worth reporting
497+ */
498+function agentByWindow(agents) {
499+ /** @type {Record<string, TbAgent>} */
500+ const out = {};
501+ for (const a of agents) {
502+ const seen = out[a.window_id];
503+ if (!seen || AGENT_RANK.indexOf(a.state) < AGENT_RANK.indexOf(seen.state)) {
504+ out[a.window_id] = a;
505+ }
506+ }
507+ return out;
508+}
509+
510+/**
511+ * A slot rather than a bare button, because the close ✕ has to be a sibling:
512+ * a button inside a button is invalid markup and unreachable to a screen
513+ * reader, and closing a window is not selecting it.
514+ *
515+ * @param {TbWindowInfo} w
516+ * @param {TbAgent} [claude] the agent worth reporting in this window, if any
517+ * @param {boolean} [closable]
518+ * @param {boolean} [isPinned]
519+ */
520+function windowTab(w, claude, closable, isPinned) {
521+ // A pinned tab drops its name and its ✕, the same two things Chrome takes
522+ // away: it is down to a dot and a number, and it cannot be closed by a
523+ // mis-aimed click. The name is still in the tooltip.
524+ const canClose = closable && !isPinned;
525+ const slot = document.createElement("div");
526+ slot.className =
527+ `tab-slot${w.active ? " active" : ""}${canClose ? " closable" : ""}` +
528+ `${isPinned ? " pinned" : ""}`;
529+
530+ const tab = document.createElement("button");
531+ tab.type = "button";
532+ tab.className = "tab";
533+ // Activity is tmux's own "something happened here while you were away", and
534+ // it is the whole reason a background tab is worth looking at.
535+ if (!w.active && w.activity) tab.classList.add("activity");
536+ tab.setAttribute("role", "tab");
537+ tab.setAttribute("aria-selected", String(w.active));
538+ tab.dataset.window = w.id;
539+
540+ const glyph = document.createElement("span");
541+ // The favicon slot. Empty unless Claude is in there, so a plain window of
542+ // shell doesn't wear a status light it has no state to report.
543+ const state = claude?.state ?? "none";
544+ glyph.className = `glyph ${state}`;
545+ glyph.textContent = state === "working" ? spinnerGlyph() : STATIC_GLYPH[state] ?? "";
546+ glyph.setAttribute("aria-hidden", "true");
547+ tab.appendChild(glyph);
548+
549+ const index = document.createElement("span");
550+ index.className = "index";
551+ index.textContent = String(w.index);
552+ tab.appendChild(index);
553+
554+ if (!isPinned) {
555+ const name = document.createElement("span");
556+ name.className = "name";
557+ name.textContent = w.name;
558+ tab.appendChild(name);
559+ }
560+
561+ // With no chip row left, the tooltip is where the detail lives: which tool is
562+ // in flight, what Claude is blocked on, and — for a pinned tab — the name it
563+ // gave up to fit.
564+ tab.title = [
565+ `window ${w.index}: ${w.name}`,
566+ w.panes > 1 ? `${w.panes} panes` : "",
567+ claude ? `claude ${claude.state} — ${agentLabel(claude)}` : "",
568+ claude?.title ?? "",
569+ !w.active && w.activity ? "activity" : "",
570+ isPinned ? "pinned — right-click to unpin" : "",
571+ ]
572+ .filter(Boolean)
573+ .join("\n");
574+
575+ tab.addEventListener("click", () => {
576+ // Selecting a window is a property of the session, so this moves anyone
577+ // else watching it too — exactly as pressing prefix-2 in the terminal does.
578+ if (!w.active) tmuxCommand({ cmd: "select-window", window: w.id });
579+ term.focus();
580+ });
581+ slot.appendChild(tab);
582+
583+ if (canClose) slot.appendChild(closeButton(w));
584+
585+ slot.addEventListener("contextmenu", (e) => {
586+ e.preventDefault();
587+ openTabMenu(e, w, !!isPinned, !!closable);
588+ });
589+ return slot;
590+}
591+
592+// --- tab context menu -------------------------------------------------------
593+//
594+// Built rather than native: an extension page gets the browser's own menu here,
595+// which has nothing to say about tmux windows. Deliberately not a <dialog> or
596+// anything modal — a modal in this panel would block the socket's message
597+// handler while it is up.
598+
599+let openMenu = /** @type {HTMLElement | null} */ (null);
600+
601+function closeTabMenu() {
602+ openMenu?.remove();
603+ openMenu = null;
604+}
605+
606+/**
607+ * @param {MouseEvent} e
608+ * @param {TbWindowInfo} w
609+ * @param {boolean} isPinned
610+ * @param {boolean} closable
611+ */
612+function openTabMenu(e, w, isPinned, closable) {
613+ closeTabMenu();
614+ const menu = document.createElement("div");
615+ menu.className = "tab-menu";
616+ menu.setAttribute("role", "menu");
617+
618+ /**
619+ * @param {string} label
620+ * @param {() => void} run
621+ */
622+ const item = (label, run) => {
623+ const b = document.createElement("button");
624+ b.type = "button";
625+ b.setAttribute("role", "menuitem");
626+ b.textContent = label;
627+ b.addEventListener("click", () => {
628+ closeTabMenu();
629+ run();
630+ });
631+ menu.appendChild(b);
632+ return b;
633+ };
634+
635+ item(isPinned ? "Unpin" : "Pin", () => {
636+ const ids = pinnedIds();
637+ if (isPinned) ids.delete(w.id);
638+ else ids.add(w.id);
639+ savePins(ids);
640+ // Nothing on the wire changed, so nothing will arrive to trigger a repaint.
641+ $("tabs").dataset.sig = "";
642+ renderTabs(lastWindows, lastAgents);
643+ });
644+
645+ if (!w.active) {
646+ item("Select", () => {
647+ tmuxCommand({ cmd: "select-window", window: w.id });
648+ term.focus();
649+ });
650+ }
651+
652+ if (closable) {
653+ const kill = item("Close window", () => {
654+ tmuxCommand({ cmd: "kill-window", window: w.id });
655+ log(`killed window ${w.index} (${w.name})`);
656+ term.focus();
657+ });
658+ kill.className = "danger";
659+ }
660+
661+ document.body.appendChild(menu);
662+ // Placed after measuring, so a menu opened near an edge folds back inside
663+ // rather than off the panel.
664+ const r = menu.getBoundingClientRect();
665+ const x = Math.min(e.clientX, Math.max(0, window.innerWidth - r.width - 4));
666+ const y = Math.min(e.clientY, Math.max(0, window.innerHeight - r.height - 4));
667+ menu.style.left = `${x}px`;
668+ menu.style.top = `${y}px`;
669+ openMenu = menu;
670+
671+ // Any click that isn't on the menu, anywhere, dismisses it.
672+ setTimeout(() => {
673+ window.addEventListener("pointerdown", onDismiss, { once: true, capture: true });
674+ }, 0);
675+}
676+
677+/** @param {Event} e */
678+function onDismiss(e) {
679+ if (openMenu && e.target instanceof Node && openMenu.contains(e.target)) return;
680+ closeTabMenu();
681+}
682+
683+/** @param {TbWindowInfo} w */
684+function closeButton(w) {
685+ const btn = document.createElement("button");
686+ btn.type = "button";
687+ btn.className = "tab-close";
688+ const label = `Close window ${w.index}: ${w.name}`;
689+ btn.title = label;
690+ btn.setAttribute("aria-label", label);
691+ btn.appendChild(strokeIcon("M4 4l8 8M12 4l-8 8"));
692+
693+ btn.addEventListener("click", (e) => {
694+ // The tab underneath would otherwise read this as "select me".
695+ e.stopPropagation();
696+ tmuxCommand({ cmd: "kill-window", window: w.id });
697+ // The one destructive thing in the panel, and tmux has no undo for it, so
698+ // it at least leaves a record of what went.
699+ log(`killed window ${w.index} (${w.name})`);
700+ term.focus();
701+ });
702+ return btn;
703+}
704+
705+const SVG_NS = "http://www.w3.org/2000/svg";
706+
707+/**
708+ * The header's icons are inline SVG rather than unicode glyphs, which render at
709+ * wildly different weights depending on the platform's fallback font. The ones
710+ * built here are the same, just built rather than written out.
711+ *
712+ * @param {string} d
713+ */
714+function strokeIcon(d) {
715+ const svg = document.createElementNS(SVG_NS, "svg");
716+ svg.setAttribute("viewBox", "0 0 16 16");
717+ svg.setAttribute("aria-hidden", "true");
718+ const path = document.createElementNS(SVG_NS, "path");
719+ path.setAttribute("d", d);
720+ path.setAttribute("stroke", "currentColor");
721+ path.setAttribute("stroke-width", "2");
722+ path.setAttribute("stroke-linecap", "round");
723+ svg.appendChild(path);
724+ return svg;
725+}
726+
727+// A new window needs no name: tmux names it after whatever it runs, and the
728+// terminal renames it as you cd around. So this is one click, no prompt.
729+$("tab-new").addEventListener("click", () => {
730+ if (sessionName) tmuxCommand({ cmd: "new-window", session: sessionName });
731+ term.focus();
732+});
733+
734+// The session the tabs belong to. One session is not a choice, but the control
735+// still earns its place: it is where the session name is shown now that the
736+// status text has given up the row.
737+/**
738+ * @param {TbSessionInfo[]} sessions
739+ * @param {string | null | undefined} current
740+ */
741+function renderSessionSwitch(sessions, current) {
742+ const select = $select("session-switch");
743+ // Rebuilding while the menu is open would close it under the user's cursor;
744+ // the next status frame repaints it anyway.
745+ if (document.activeElement === select) return;
746+
747+ const names = sessions.map((s) => s.name);
748+ const same = select.dataset.names === names.join(" ") && select.value === current;
749+ if (same) return;
750+
751+ select.textContent = "";
752+ for (const s of sessions) {
753+ const o = document.createElement("option");
754+ o.value = s.name;
755+ o.textContent = s.name;
756+ select.appendChild(o);
757+ }
758+ select.dataset.names = names.join(" ");
759+ if (current) select.value = current;
760+ select.hidden = sessions.length === 0;
761+ // Two things cannot both hold the row: the select shows the session name, so
762+ // the status text only speaks when there is no select to speak for it.
763+ document.body.classList.toggle("has-session", !select.hidden);
764+}
765+
766+$select("session-switch").addEventListener("change", (e) => {
767+ // Moves the existing client: no reconnect, no second pty, and whatever is
768+ // running in the session we leave keeps running.
769+ tmuxCommand({ cmd: "switch", session: /** @type {HTMLSelectElement} */ (e.target).value });
770+ term.focus();
771+});
772+
773+/* --- the working spinner ---------------------------------------------------
774+ Claude Code's own asterisk cycle, so a tab that is thinking looks like the
775+ transcript that is thinking. The frames grow and shrink rather than spin:
776+ a dot swelling to a full asterisk and back, which reads as activity at
777+ 10px where a rotating glyph would just shimmer. */
778+const SPINNER_FRAMES = ["·", "✢", "✳", "∗", "✻", "✽", "✻", "∗", "✳", "✢"];
779+/** What a tab shows when it is not mid-cycle. */
780+/** @type {Record<string, string>} */
781+const STATIC_GLYPH = { waiting: "✳", idle: "✻", unknown: "·", none: "" };
782+const SPINNER_MS = 130;
783+
784+const reducedMotion = matchMedia("(prefers-reduced-motion: reduce)");
785+let spinnerStep = 0;
786+/** @type {number | undefined} */
787+let spinnerTimer;
788+
789+// Reduced motion keeps the glyph — the tab still says "working" — and parks it
790+// on the frame the animation spends the most time looking like.
791+function spinnerGlyph() {
792+ return reducedMotion.matches ? "✻" : SPINNER_FRAMES[spinnerStep % SPINNER_FRAMES.length];
793+}
794+
795+/**
796+ * One timer for the whole strip, running only while something is working, so
797+ * an idle panel is not repainting four times a second forever. Repaints touch
798+ * textContent only: renderTabs owns the elements and skips its rebuild whenever
799+ * the signature is unchanged, so the spinner never fights it.
800+ */
801+function syncSpinner() {
802+ const working = document.querySelectorAll("#tabs .glyph.working");
803+ if (!working.length || reducedMotion.matches) {
804+ clearInterval(spinnerTimer);
805+ spinnerTimer = undefined;
806+ return;
807+ }
808+ if (spinnerTimer !== undefined) return;
809+ spinnerTimer = setInterval(() => {
810+ spinnerStep++;
811+ const frame = spinnerGlyph();
812+ const live = document.querySelectorAll("#tabs .glyph.working");
813+ if (!live.length) return syncSpinner();
814+ for (const el of live) el.textContent = frame;
815+ }, SPINNER_MS);
816+}
817+
818+// Turning the preference on mid-run has to stop the timer and settle the
819+// glyphs where they are, not leave them frozen on whatever frame was up.
820+reducedMotion.addEventListener("change", () => {
821+ const frame = spinnerGlyph();
822+ for (const el of document.querySelectorAll("#tabs .glyph.working")) el.textContent = frame;
823+ syncSpinner();
824+});
825+
826+// A tooltip's worth of room, so show the most specific thing known: what it is
827+// blocked on, what tool it is running, else the mode.
828+//
829+// Every value here comes from Claude Code's hook payloads by way of the daemon
830+// — a user prompt, a tool name, a notification message — and is only ever
831+// assigned through textContent/title, never parsed as markup.
832+/** @param {TbAgent} [a] */
833+function agentLabel(a) {
834+ if (!a) return "";
835+ if (a.state === "waiting") return a.message || "waiting";
836+ if (a.state === "working") return a.tool || shortMode(a.mode) || "working";
837+ if (a.state === "unknown") return "no hook records — run: termbridge hooks";
838+ return shortMode(a.mode) || "idle";
839+}
840+
841+// permission_mode arrives camelCased, straight from Claude's hook payload.
842+/** @type {Record<string, string>} */
843+const MODE_SHORT = {
844+ default: "idle",
845+ acceptEdits: "accept edits",
846+ plan: "plan",
847+ bypassPermissions: "bypass",
848+};
849+
850+/** @param {string | null | undefined} mode */
851+function shortMode(mode) {
852+ if (!mode) return "";
853+ return MODE_SHORT[mode] ?? mode;
854+}
855+
856+/** @param {TbOkFrame} msg */
857+function renderSessions(msg) {
858+ if (!msg.tmux) return;
859+ const names = msg.sessions ?? [];
860+ const list = $("session-list");
861+ list.innerHTML = "";
862+ for (const name of names) {
863+ const o = document.createElement("option");
864+ o.value = name;
865+ list.appendChild(o);
866+ }
867+ $input("session").placeholder = msg.defaultSession ?? "browser";
868+ if (names.length) log(`tmux sessions: ${names.join(", ")}`);
869+}
870+
871+$("session-apply").addEventListener("click", () => {
872+ const name = $input("session").value.trim();
873+ storage.set({ session: name });
874+ // Connected, this creates-or-attaches and moves the live client — the field
875+ // is how you reach a session that doesn't exist yet, which the header's
876+ // switcher (existing sessions only) can't do. Disconnected, it's the session
877+ // the next connection opens with.
878+ if (connected && name) {
879+ tmuxCommand({ cmd: "create", session: name });
880+ $("settings").hidden = true;
881+ term.focus();
882+ return;
883+ }
884+ connect();
885+});
886+
887+// --- element picker ---------------------------------------------------------
888+//
889+// Everything the picker returns is page-controlled data. It is displayed, and
890+// it only reaches the terminal when the user explicitly clicks "insert" — and
891+// then only after Sanitize.forTerminal has stripped control characters and
892+// shell-quoted it.
893+
894+let picked = /** @type {TbPicked | null} */ (null);
895+
896+/** Why the panel is open, kept so switching format doesn't erase it. */
897+let pickedProblem = "";
898+
899+// XPath by default: it always exists, it addresses exactly one node, and it
900+// survives the class-name churn that a CSS selector built from a framework's
901+// generated class names does not.
902+let pickedFormat = /** @type {TbPickedFormat} */ ("xpath");
903+
904+// Ordered by how often they're the one you want.
905+/** @type {[TbPickedFormat, string][]} */
906+const FORMATS = [
907+ ["xpath", "XPath"],
908+ ["css", "CSS"],
909+ ["id", "id"],
910+ ["testid", "test id"],
911+ ["text", "text"],
912+ ["href", "href"],
913+];
914+
915+async function activePickTab() {
916+ const [tab] = await api.tabs.query({ active: true, currentWindow: true });
917+ return tab;
918+}
919+
920+/**
921+ * `https://example.com/*` — the narrowest pattern that covers this page.
922+ * @param {string | undefined} url
923+ */
924+function originPattern(url) {
925+ if (!url) return null;
926+ try {
927+ return `${new URL(url).origin}/*`;
928+ } catch {
929+ return null;
930+ }
931+}
932+
933+/**
934+ * @param {string} text
935+ * @param {string} [bad] a warning to show alongside it
936+ */
937+function pickNote(text, bad) {
938+ // Loud enough to notice without opening the log: the picker failing silently
939+ // is the whole reason this feature felt broken.
940+ $("picked").hidden = false;
941+ $("picked-value").textContent = text;
942+ $("picked-warn").hidden = !bad;
943+ $("picked-warn").textContent = bad ?? "";
944+ $("picked-grant").hidden = true;
945+ log(text);
946+}
947+
948+/**
949+ * Offer a per-site grant rather than shipping a blanket <all_urls> permission.
950+ *
951+ * localhost is granted up front because it's your own machine; everything else
952+ * is opt-in, one origin at a time, via a prompt the browser shows.
953+ *
954+ * @param {string} pattern
955+ * @param {number | undefined} tabId
956+ */
957+function offerGrant(pattern, tabId) {
958+ $("picked").hidden = false;
959+ $("picked-value").textContent = `No access to ${pattern}`;
960+ $("picked-warn").hidden = false;
961+ $("picked-warn").textContent =
962+ "Grant access to this site, or use Alt+Shift+P which needs no permission.";
963+ const btn = $("picked-grant");
964+ btn.hidden = false;
965+ btn.textContent = `Allow ${pattern}`;
966+ btn.onclick = async () => {
967+ // Must be called from a user gesture, which this click is.
968+ const granted = await api.permissions.request({ origins: [pattern] });
969+ if (granted) {
970+ btn.hidden = true;
971+ log(`granted ${pattern}`);
972+ runPick(tabId);
973+ } else {
974+ log(`declined ${pattern}`);
975+ }
976+ };
977+}
978+
979+/**
980+ * The capture the picker wants is the one permission a per-origin grant cannot
981+ * buy. `tabs.captureVisibleTab` accepts exactly two things: the `activeTab`
982+ * grant a keyboard command mints, or a host permission set that contains the
983+ * literal `<all_urls>` pattern. A per-origin grant fails the check, and so does
984+ * the all-scheme-wildcard pattern in optional_host_permissions, which misses
985+ * `file:` and so isn't "all". That is why picking from the panel button used to
986+ * hand back a selector and no image on every site you'd approved.
987+ *
988+ * @param {string} reason the failure copyPickedShot reported
989+ */
990+function isCapturePermissionError(reason) {
991+ return /all_urls|activeTab/i.test(reason);
992+}
993+
994+/**
995+ * Offer the one grant that makes the panel button capture, alongside a pick
996+ * that already succeeded. Screenshots stay opt-in: nothing here is requested
997+ * until the button is pressed, and Alt+Shift+P keeps working without it.
998+ *
999+ * @param {string} reason
1000+ */
1001+function offerCaptureGrant(reason) {
1002+ const btn = /** @type {HTMLButtonElement} */ ($("picked-grant"));
1003+ btn.hidden = false;
1004+ btn.textContent = "Allow screenshots on all sites";
1005+ btn.onclick = async () => {
1006+ // Must be called from a user gesture, which this click is.
1007+ const granted = await api.permissions.request({ origins: ["<all_urls>"] });
1008+ if (!granted) {
1009+ log("declined <all_urls>");
1010+ return;
1011+ }
1012+ btn.hidden = true;
1013+ log("granted <all_urls>");
1014+ // The shot that prompted this is long gone from the viewport's timeline;
1015+ // re-picking is the honest way to get one, so say so rather than silently
1016+ // leaving the old warning up.
1017+ pickedProblem = "Screenshots are on. Pick again to get one.";
1018+ refreshPicked();
1019+ };
1020+ log(`screenshot needs <all_urls>: ${reason}`);
1021+}
1022+
1023+const SKIP_URL = /^(chrome|about|edge|moz-extension|chrome-extension|view-source|devtools):/;
1024+
1025+// The tab a pick is currently running in, or null. Also the "is picking" flag.
1026+let pickTabId = /** @type {number | null | undefined} */ (null);
1027+
1028+/**
1029+ * Cancel an in-flight pick.
1030+ *
1031+ * picker.js parks its cleanup function on `window.__tbPickerActive`, so we
1032+ * re-enter the page and call it. That resolves the original promise with null,
1033+ * which unwinds runPick exactly as a user Escape would.
1034+ */
1035+async function cancelPick() {
1036+ const tabId = pickTabId;
1037+ if (tabId == null) return;
1038+ try {
1039+ await api.scripting.executeScript({
1040+ target: { tabId },
1041+ func: () => {
1042+ if (window.__tbPickerActive) window.__tbPickerActive();
1043+ },
1044+ });
1045+ } catch {
1046+ // Tab closed or navigated; the picker went with it.
1047+ }
1048+}
1049+
1050+/**
1051+ * Run a pick in `tabId`. Returns true on success, or a message explaining why
1052+ * the injection failed.
1053+ *
1054+ * @param {number | undefined} tabId
1055+ * @returns {Promise<true | string>}
1056+ */
1057+async function runPick(tabId) {
1058+ const btn = $("pick");
1059+ pickTabId = tabId;
1060+ btn.classList.add("active");
1061+ setStatus("pending", "pick mode — click an element, Esc cancels");
1062+ try {
1063+ const results = await api.scripting.executeScript({
1064+ target: { tabId },
1065+ func: tbPickElement,
1066+ });
1067+ const value = results?.[0]?.result ?? null;
1068+ if (value) {
1069+ const shot = await Shot.copyPickedShot(api, tabId, value);
1070+ await deliverPick(value, shot);
1071+ } else log("pick cancelled");
1072+ return true;
1073+ } catch (e) {
1074+ return e instanceof Error ? e.message : String(e);
1075+ } finally {
1076+ pickTabId = null;
1077+ btn.classList.remove("active");
1078+ refreshStatus();
1079+ }
1080+}
1081+
1082+$("pick").addEventListener("click", async () => {
1083+ // Second press toggles it back off rather than doing nothing.
1084+ if (pickTabId != null) {
1085+ await cancelPick();
1086+ return;
1087+ }
1088+
1089+ const [tab] = await api.tabs.query({ active: true, currentWindow: true });
1090+ if (!tab || SKIP_URL.test(tab.url ?? "")) {
1091+ pickNote(
1092+ "Can't pick here.",
1093+ "Browser-internal and extension pages are off limits to all extensions. Switch to a normal web page.",
1094+ );
1095+ return;
1096+ }
1097+
1098+ setStatus("pending", "pick mode — click an element, Esc cancels");
1099+ const outcome = await runPick(tab.id);
1100+ if (outcome === true) return;
1101+
1102+ // The failure is nearly always a missing host permission for this origin.
1103+ const pattern = originPattern(tab.url);
1104+ if (pattern && /permission|access/i.test(outcome)) {
1105+ offerGrant(pattern, tab.id);
1106+ } else {
1107+ pickNote("Couldn't reach the page.", `Try Alt+Shift+P instead. [${outcome}]`);
1108+ }
1109+});
1110+
1111+// Escape from the sidebar too. The picker handles Escape itself, but only when
1112+// the page has keyboard focus — if you started the pick from here, focus is
1113+// still in the panel and the key never reaches the page.
1114+window.addEventListener("keydown", (e) => {
1115+ // Font shortcuts work anywhere in the panel, not just with the terminal
1116+ // focused. xterm.js consumes its own keydowns before they reach here, so it
1117+ // has its own handler for these.
1118+ if (handleFontKey(e)) return;
1119+ if (e.key !== "Escape") return;
1120+ if (openMenu) {
1121+ e.preventDefault();
1122+ closeTabMenu();
1123+ return;
1124+ }
1125+ if (pickTabId != null) {
1126+ e.preventDefault();
1127+ cancelPick();
1128+ }
1129+});
1130+
1131+// Results arriving from the background worker (the keyboard-shortcut path).
1132+//
1133+// Guarded: content scripts always have `sender.tab` set, so rejecting those
1134+// leaves only our own extension pages and worker. This is the one inbound
1135+// message path in the sidebar, and it exists solely because the shortcut has to
1136+// be handled in the background.
1137+api.runtime.onMessage.addListener((msg, sender) => {
1138+ if (sender?.id !== api.runtime.id) return;
1139+ if (sender?.tab) return;
1140+ if (msg?.type === "picked" && msg.value) {
1141+ // The worker owns the screenshot on this path: it holds the activeTab
1142+ // grant the shortcut just minted, and the clipboard write has to happen
1143+ // while the page it captured is still the focused one.
1144+ deliverPick(msg.value, msg.shot === true ? true : msg.shot || "not captured");
1145+ }
1146+});
1147+
1148+// A pick made while the sidebar was closed is parked in storage. Nothing is
1149+// typed for these: the terminal has moved on, and whatever screenshot went with
1150+// it left the clipboard long ago. Show it and let the user decide.
1151+storage.get("pendingPick").then((v) => {
1152+ if (v.pendingPick) {
1153+ showPicked(v.pendingPick);
1154+ storage.remove("pendingPick");
1155+ }
1156+});
1157+
1158+function currentPickedRaw() {
1159+ if (!picked) return "";
1160+ return picked[pickedFormat] ?? "";
1161+}
1162+
1163+/**
1164+ * Open the panel on a pick. Only called when the pick needs the user's
1165+ * attention — see deliverPick.
1166+ *
1167+ * @param {TbPicked} value everything in here is page-controlled
1168+ * @param {string} [problem] why the panel is opening
1169+ */
1170+function showPicked(value, problem) {
1171+ picked = value;
1172+ $("picked-warn").hidden = true;
1173+ $("picked-grant").hidden = true;
1174+
1175+ $("picked-tag").textContent = value.tag ? `<${value.tag}>` : "?";
1176+ $("picked-meta").textContent = value.text || value.href || value.pageUrl || "";
1177+ $("picked-meta").title = value.pageUrl || "";
1178+
1179+ // Only offer formats this element actually has — an empty tab is a dead end.
1180+ const available = FORMATS.filter(([key]) => value[key]);
1181+ if (!available.some(([key]) => key === pickedFormat)) {
1182+ pickedFormat = available[0]?.[0] ?? "css";
1183+ }
1184+
1185+ const bar = $("picked-formats");
1186+ bar.innerHTML = "";
1187+ for (const [key, label] of available) {
1188+ const b = document.createElement("button");
1189+ b.type = "button";
1190+ b.textContent = label;
1191+ b.setAttribute("role", "tab");
1192+ b.setAttribute("aria-selected", String(key === pickedFormat));
1193+ b.addEventListener("click", () => {
1194+ pickedFormat = key;
1195+ for (const other of bar.children) {
1196+ other.setAttribute("aria-selected", String(other === b));
1197+ }
1198+ refreshPicked();
1199+ });
1200+ bar.appendChild(b);
1201+ }
1202+
1203+ pickedProblem = problem ?? "";
1204+ $("picked").hidden = false;
1205+ refreshPicked();
1206+}
1207+
1208+function refreshPicked() {
1209+ const raw = currentPickedRaw();
1210+ const el = $("picked-value");
1211+ el.textContent = raw || "not present on this element";
1212+ el.classList.toggle("empty", !raw);
1213+
1214+ const { removedControl, truncated } = Sanitize.forTerminal(raw);
1215+ const notes = [];
1216+ if (removedControl) notes.push("control characters removed");
1217+ if (truncated) notes.push("truncated");
1218+ const warn = $("picked-warn");
1219+ const modified = notes.length ? `Modified before use: ${notes.join(", ")}.` : "";
1220+ warn.textContent = [pickedProblem, modified].filter(Boolean).join(" ");
1221+ warn.hidden = !warn.textContent;
1222+
1223+ /** @type {HTMLButtonElement} */ ($("picked-insert")).disabled = !raw;
1224+}
1225+
1226+$("picked-close").addEventListener("click", () => {
1227+ $("picked").hidden = true;
1228+ picked = null;
1229+});
1230+
1231+$("picked-copy").addEventListener("click", async () => {
1232+ await navigator.clipboard.writeText(Sanitize.forClipboard(currentPickedRaw()));
1233+ log("copied to clipboard");
1234+});
1235+
1236+/**
1237+ * Send the pick to the terminal: the screenshot first, then the selector.
1238+ *
1239+ * The screenshot is already on the system clipboard by the time this runs, so
1240+ * "sending" it is a literal ^V. That byte is not a paste as far as this panel
1241+ * is concerned — xterm.js never sees it — it goes down the wire, and an agent
1242+ * on the other end that handles image paste (Claude Code does) reads the
1243+ * clipboard itself and attaches the PNG. In a bare shell ^V is literal-next
1244+ * instead, which is why it is only sent when there is an image to fetch.
1245+ *
1246+ * @param {boolean} withShot
1247+ */
1248+async function insertPicked(withShot) {
1249+ if (!connected || !ws) {
1250+ log("not connected");
1251+ return;
1252+ }
1253+ const { text } = Sanitize.forTerminal(currentPickedRaw());
1254+ if (withShot) {
1255+ ws.send(enc.encode("\x16"));
1256+ // Reading the clipboard is a round trip out to a helper process on the
1257+ // agent's side. Text sent in the same breath can arrive first and end up
1258+ // ahead of the attachment on the line.
1259+ await new Promise((r) => setTimeout(r, 250));
1260+ ws.send(enc.encode(" "));
1261+ }
1262+ // No trailing newline, ever. The user presses Enter themselves.
1263+ ws.send(enc.encode(text));
1264+ term.focus();
1265+ log(withShot ? `inserted screenshot + ${text.length} chars` : `inserted ${text.length} chars`);
1266+}
1267+
1268+/**
1269+ * What a fresh pick does: type it into the terminal, and stay out of the way.
1270+ *
1271+ * The panel is deliberately *not* opened on the happy path. It shifts the
1272+ * terminal down the moment you pick something, which is a poor trade when the
1273+ * result has already been typed where you were looking. It opens only when
1274+ * there is something to decide — no connection, or no screenshot — and then it
1275+ * carries the reason.
1276+ *
1277+ * @param {TbPicked} value page-controlled, all of it
1278+ * @param {true | string} shot true, or why there is no screenshot
1279+ */
1280+async function deliverPick(value, shot) {
1281+ picked = value;
1282+ log(`picked ${value.tag} on ${value.pageUrl}`);
1283+ log(shot === true ? "screenshot on clipboard, sending ^V" : `no screenshot: ${shot}`);
1284+
1285+ const needsGrant = shot !== true && isCapturePermissionError(shot);
1286+
1287+ if (!connected || !ws) {
1288+ showPicked(value, "Not connected — nothing was typed.");
1289+ if (needsGrant) offerCaptureGrant(shot);
1290+ return;
1291+ }
1292+ await insertPicked(shot === true);
1293+ if (shot !== true) {
1294+ showPicked(
1295+ value,
1296+ needsGrant
1297+ ? "Selector only. Screenshots from this button need access to all sites; Alt+Shift+P needs none."
1298+ : `Selector only, no screenshot: ${shot}`,
1299+ );
1300+ if (needsGrant) offerCaptureGrant(shot);
1301+ }
1302+}
1303+
1304+$("picked-insert").addEventListener("click", () => insertPicked(false));
1305+
1306+// Keystrokes out as binary, so nothing is lost to UTF-8 round-tripping.
1307+term.onData((data) => {
1308+ if (connected && ws) ws.send(enc.encode(data));
1309+});
1310+term.onBinary((data) => {
1311+ if (!connected || !ws) return;
1312+ const buf = new Uint8Array(data.length);
1313+ for (let i = 0; i < data.length; i++) buf[i] = data.charCodeAt(i) & 255;
1314+ ws.send(buf);
1315+});
1316+
1317+new ResizeObserver(() => {
1318+ fit.fit();
1319+ sendSize();
1320+}).observe($("term"));
1321+
1322+// --- settings / persistence -------------------------------------------------
1323+
1324+const themeSelect = $select("theme-select");
1325+themeSelect.addEventListener("change", () => setTheme(themeSelect.value));
1326+const densitySelect = $select("density-select");
1327+densitySelect.addEventListener("change", () => setDensity(densitySelect.value));
1328+
1329+$("font-smaller").addEventListener("click", () => setFontSize(fontSize - 1));
1330+$("font-bigger").addEventListener("click", () => setFontSize(fontSize + 1));
1331+$("font-reset").addEventListener("click", () => setFontSize(FONT_DEFAULT));
1332+
1333+$("settings-toggle").addEventListener("click", () => {
1334+ $("settings").hidden = !$("settings").hidden;
1335+});
1336+$("reconnect").addEventListener("click", connect);
1337+$("connect").addEventListener("click", connect);
1338+$("trust-cert").addEventListener("click", () => {
1339+ const u = new URL($input("url").value.trim());
1340+ api.tabs.create({ url: `https://${u.host}/` });
1341+});
1342+$("copy-origin").addEventListener("click", () =>
1343+ navigator.clipboard.writeText(`termbridge pair ${ORIGIN}`),
1344+);
1345+const tokenInput = $input("token");
1346+const urlInput = $input("url");
1347+const revealBox = $input("reveal");
1348+revealBox.addEventListener("change", () => {
1349+ tokenInput.type = revealBox.checked ? "text" : "password";
1350+});
1351+tokenInput.addEventListener("change", () =>
1352+ storage.set({ token: tokenInput.value.trim() }),
1353+);
1354+urlInput.addEventListener("change", () => storage.set({ url: urlInput.value.trim() }));
1355+
1356+storage.get(["token", "url", "session", "theme", "density", "fontSize", "pins"]).then((v) => {
1357+ if (v.pins && typeof v.pins === "object") pins = v.pins;
1358+ themePref = Themes.PREFERENCES.includes(v.theme) ? v.theme : "auto";
1359+ density = DENSITIES[v.density] ? v.density : "normal";
1360+ const stored = Number(v.fontSize);
1361+ fontSize =
1362+ Number.isFinite(stored) && stored >= FONT_MIN && stored <= FONT_MAX
1363+ ? Math.round(stored)
1364+ : FONT_DEFAULT;
1365+ applyTheme();
1366+ applyDensity();
1367+ applyFontSize();
1368+ if (v.token) $input("token").value = v.token;
1369+ if (v.url) $input("url").value = v.url;
1370+ if (v.session) $input("session").value = v.session;
1371+ log(`origin: ${ORIGIN}`);
1372+ if (v.token) {
1373+ connect();
1374+ } else {
1375+ setStatus("off", "needs setup");
1376+ $("settings").hidden = false;
1377+ term.write(
1378+ "\x1b[90m terminal\x1b[0m\r\n\r\n" +
1379+ " Not configured yet. Open \x1b[1msettings\x1b[0m (top right)\r\n" +
1380+ " to pair and paste your token.\r\n",
1381+ );
1382+ }
1383+});
addedextension/sw.js+66 −0
1+// Background worker.
2+//
3+// Its one real job is the element picker's keyboard shortcut. `activeTab` — the
4+// permission that lets us touch a page without asking for <all_urls> — is only
5+// granted on specific user gestures: a toolbar-action click, a context menu
6+// item, or a keyboard command. A click inside the side panel is not one of
7+// them, so the in-panel button can fail while the shortcut always works. Both
8+// paths exist; this is the reliable one.
9+
10+// One of the two always exists — this file only ever runs as an extension
11+// background script — but neither is declared unconditionally, so say which.
12+const api = /** @type {TbExtensionApi} */ (globalThis.browser ?? globalThis.chrome);
13+
14+// Chrome's service worker starts empty; Firefox loads picker.js alongside this
15+// file via background.scripts, so there it is already defined.
16+if (typeof tbPickElement === "undefined" && typeof importScripts === "function") {
17+ importScripts("picker.js", "lib/shot.js");
18+}
19+
20+// Chrome only: make the toolbar button open the side panel.
21+if (api.sidePanel?.setPanelBehavior) {
22+ api.sidePanel
23+ .setPanelBehavior({ openPanelOnActionClick: true })
24+ .catch((e) => console.error(e));
25+}
26+
27+const SKIP = /^(chrome|about|edge|moz-extension|chrome-extension|view-source|devtools):/;
28+
29+async function runPicker() {
30+ const [tab] = await api.tabs.query({ active: true, currentWindow: true });
31+ if (!tab || SKIP.test(tab.url ?? "")) {
32+ console.warn("termbridge: cannot pick on this page", tab?.url);
33+ return;
34+ }
35+
36+ let value = null;
37+ try {
38+ const results = await api.scripting.executeScript({
39+ target: { tabId: tab.id },
40+ func: tbPickElement,
41+ });
42+ value = results?.[0]?.result ?? null;
43+ } catch (e) {
44+ console.error("termbridge: pick failed", e);
45+ return;
46+ }
47+ if (!value) return;
48+
49+ // Screenshot here rather than in the sidebar: this path's page access comes
50+ // from the activeTab grant the keyboard command just minted, and that grant
51+ // belongs to the worker.
52+ const shot = await Shot.copyPickedShot(api, tab.id, value);
53+ if (shot !== true) console.warn("termbridge: no screenshot", shot);
54+
55+ // The sidebar may not be open. Try to hand it over directly, and fall back to
56+ // storage so the pick isn't lost — the sidebar drains it when it next opens.
57+ try {
58+ await api.runtime.sendMessage({ type: "picked", value, shot: shot === true });
59+ } catch {
60+ await api.storage.local.set({ pendingPick: value });
61+ }
62+}
63+
64+api.commands?.onCommand.addListener((command) => {
65+ if (command === "pick-element") runPicker();
66+});
addedextension/types/globals.d.ts+255 −0
1+// Ambient declarations for the globals these scripts run against.
2+//
3+// Hand-written rather than pulled from @types/chrome on purpose: this file is
4+// the *inventory* of extension API surface termbridge touches. Adding a call
5+// means adding it here first, which is a small, useful speed bump — the
6+// manifest's permission list has to grow with it. It is also why `npm run
7+// check` needs exactly one dependency.
8+//
9+// Chrome and Firefox differ: `browser.*` is promise-based everywhere, and
10+// Chrome's `chrome.*` has been promise-based for MV3. Everything below is typed
11+// as promises because that is the only way it is called here.
12+
13+interface TbTab {
14+ id?: number;
15+ url?: string;
16+ title?: string;
17+}
18+
19+interface TbInjectionResult<T = unknown> {
20+ result?: T;
21+ frameId?: number;
22+}
23+
24+interface TbMessageSender {
25+ id?: string;
26+ /** Set for content scripts and only for content scripts — the check that
27+ keeps a hostile page from reaching the sidebar's message handler. */
28+ tab?: TbTab;
29+}
30+
31+interface TbStorageArea {
32+ get(keys?: string | string[] | null): Promise<Record<string, any>>;
33+ set(items: Record<string, any>): Promise<void>;
34+ remove(keys: string | string[]): Promise<void>;
35+}
36+
37+interface TbExtensionApi {
38+ runtime: {
39+ id: string;
40+ sendMessage(message: any): Promise<any>;
41+ onMessage: {
42+ addListener(
43+ cb: (message: any, sender: TbMessageSender) => void | boolean | Promise<any>,
44+ ): void;
45+ };
46+ };
47+ storage: { local: TbStorageArea };
48+ tabs: {
49+ query(info: { active?: boolean; currentWindow?: boolean }): Promise<TbTab[]>;
50+ create(props: { url: string }): Promise<TbTab>;
51+ /** The visible area of the active tab, as a PNG data URL. Needs a host
52+ permission for the page, exactly as the picker's injection does. */
53+ captureVisibleTab(options: { format: "png" }): Promise<string>;
54+ };
55+ permissions: {
56+ request(perms: { origins?: string[]; permissions?: string[] }): Promise<boolean>;
57+ };
58+ scripting: {
59+ /**
60+ * `Awaited<T>`: a func that returns a promise is awaited by the browser,
61+ * and `result` is what it resolved to — which is the whole reason the
62+ * picker can hand its value back as a return value.
63+ */
64+ executeScript<T, A extends any[] = []>(injection: {
65+ target: { tabId: number | undefined };
66+ func: (...args: A) => T;
67+ /** Structured-cloned into the page. Keep it small — a screenshot goes
68+ through here as base64. */
69+ args?: A;
70+ }): Promise<TbInjectionResult<Awaited<T>>[]>;
71+ };
72+ /** Firefox only: writes an image to the clipboard without a focused
73+ document, which the async clipboard API refuses to do. */
74+ clipboard?: {
75+ setImageData(image: ArrayBuffer, type: "png" | "jpeg"): Promise<void>;
76+ };
77+ /** Absent in Firefox, and absent in Chrome until the browser is new enough. */
78+ sidePanel?: {
79+ setPanelBehavior?(behavior: {
80+ openPanelOnActionClick: boolean;
81+ }): Promise<void>;
82+ };
83+ /** Absent when no keyboard shortcuts are declared in the manifest. */
84+ commands?: {
85+ onCommand: { addListener(cb: (command: string) => void): void };
86+ };
87+}
88+
89+declare var browser: TbExtensionApi | undefined;
90+declare var chrome: TbExtensionApi | undefined;
91+
92+// --- the daemon's wire protocol ---------------------------------------------
93+//
94+// The other half of these shapes is Rust: `Snapshot`, `SessionInfo` and `Agent`
95+// in daemon/src/status.rs, and the `ok` frame built in daemon/src/server.rs.
96+// Change one side and this side has to move with it — which is most of why
97+// they are written down at all.
98+//
99+// Everything here arrives over a socket. It is *claimed* structure, not
100+// guaranteed structure, and it is only ever assigned through textContent.
101+
102+interface TbSessionInfo {
103+ name: string;
104+ /** Window count. 0 when it isn't known yet — the `ok` frame sends names only. */
105+ windows: number;
106+ /** Some tmux client, anywhere, is on this session. */
107+ attached: boolean;
108+}
109+
110+/** One window of the attached session — one tab. */
111+interface TbWindowInfo {
112+ /** tmux window id (`@3`). Stable; the index is not. */
113+ id: string;
114+ index: number;
115+ name: string;
116+ active: boolean;
117+ panes: number;
118+ activity: boolean;
119+}
120+
121+interface TbAgent {
122+ /** tmux pane id (`%12`), stable for the pane's lifetime. */
123+ pane: string;
124+ /** tmux window id (`@3`) — the join to TbWindowInfo. */
125+ window_id: string;
126+ window: string;
127+ name: string;
128+ state: "working" | "waiting" | "idle" | "unknown";
129+ mode?: string | null;
130+ tool?: string | null;
131+ message?: string | null;
132+ title?: string | null;
133+}
134+
135+/**
136+ * First frame after a successful auth. It carries session *names* only — the
137+ * window list needs the control channel, which is up a moment later.
138+ */
139+interface TbOkFrame {
140+ type: "ok";
141+ profile: string;
142+ tmux: boolean;
143+ defaultSession?: string;
144+ /** Names only — window counts arrive with the first status frame. */
145+ sessions?: string[];
146+}
147+
148+/** Pushed whenever tmux says something changed. */
149+interface TbStatusFrame {
150+ type: "status";
151+ session?: string;
152+ sessions?: TbSessionInfo[];
153+ /** Windows of `session`, in index order. */
154+ windows?: TbWindowInfo[];
155+ agents?: TbAgent[];
156+}
157+
158+type TbFrame =
159+ | TbOkFrame
160+ | TbStatusFrame
161+ | { type: "exit"; code: number }
162+ | { type: "error"; reason: string }
163+ | { type: "tmux-error"; reason: string };
164+
165+/** The element's box in CSS pixels, relative to the viewport. */
166+interface TbPickedRect {
167+ x: number;
168+ y: number;
169+ width: number;
170+ height: number;
171+}
172+
173+/** The identifier flavours the panel can show — the string-valued keys of
174+ TbPicked, and the only ones that may reach the terminal. */
175+type TbPickedFormat = "css" | "xpath" | "id" | "testid" | "text" | "href";
176+
177+/** What picker.js's describe() returns — page-controlled, all of it. */
178+interface TbPicked {
179+ /** Where to crop the tab screenshot. Absent on picks made before 0.0.2. */
180+ rect?: TbPickedRect;
181+ /** Viewport size at pick time, which fixes the screenshot's scale factor. */
182+ viewport?: { w: number; h: number };
183+ css: string;
184+ xpath: string;
185+ id: string | null;
186+ testid: string | null;
187+ tag: string;
188+ text: string | null;
189+ href: string | null;
190+ pageUrl: string;
191+}
192+
193+// --- vendored xterm.js ------------------------------------------------------
194+//
195+// The vendored build ships no types. Only the handful of members used here are
196+// declared; anything else is a deliberate error rather than a silent `any`.
197+
198+interface TbTerminalTheme {
199+ [color: string]: string;
200+}
201+
202+interface TbTerminalOptions {
203+ fontFamily?: string;
204+ fontSize?: number;
205+ lineHeight?: number;
206+ cursorBlink?: boolean;
207+ scrollback?: number;
208+ allowProposedApi?: boolean;
209+ convertEol?: boolean;
210+ macOptionIsMeta?: boolean;
211+ theme?: TbTerminalTheme;
212+}
213+
214+declare class Terminal {
215+ constructor(options?: TbTerminalOptions);
216+ readonly cols: number;
217+ readonly rows: number;
218+ readonly element?: HTMLElement;
219+ options: TbTerminalOptions;
220+ open(parent: HTMLElement): void;
221+ write(data: string | Uint8Array): void;
222+ focus(): void;
223+ clear(): void;
224+ loadAddon(addon: unknown): void;
225+ attachCustomKeyEventHandler(handler: (e: KeyboardEvent) => boolean): void;
226+ onData(cb: (data: string) => void): { dispose(): void };
227+ /** Bytes xterm could not represent as UTF-16 — each char is one byte. */
228+ onBinary(cb: (data: string) => void): { dispose(): void };
229+ onResize(cb: (size: { cols: number; rows: number }) => void): { dispose(): void };
230+}
231+
232+declare namespace FitAddon {
233+ class FitAddon {
234+ fit(): void;
235+ proposeDimensions(): { cols: number; rows: number } | undefined;
236+ }
237+}
238+
239+// --- our own files, as the browser sees them --------------------------------
240+//
241+// lib/theme.js and lib/sanitize.js are loaded as classic scripts here and
242+// require()d by their node --test files, so each one ends with a CommonJS tail
243+// guarded on `typeof module`. That tail is enough to make TypeScript treat them
244+// as modules, so their globals have to be re-stated — from the files
245+// themselves, so the shapes cannot drift.
246+declare var module: { exports: any } | undefined;
247+
248+declare const Themes: typeof import("../lib/theme.js");
249+declare const Sanitize: typeof import("../lib/sanitize.js");
250+declare const Shot: typeof import("../lib/shot.js");
251+
252+/** Set by picker.js inside the *page*, not here — see cancelPick(). */
253+interface Window {
254+ __tbPickerActive?: (() => void) | null;
255+}
addedextension/types/worker.d.ts+7 −0
1+// Service-worker-only globals.
2+//
3+// The worker project loads lib.dom rather than lib.webworker (they redeclare
4+// each other, and picker.js's injected body is checked against page globals),
5+// so the one worker API sw.js uses is declared by hand.
6+
7+declare function importScripts(...urls: string[]): void;
addedextension/vendor/addon-fit.js+2 −0
1+!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.FitAddon=t():e.FitAddon=t()}(globalThis,(()=>(()=>{"use strict";var e={};return(()=>{var t=e;Object.defineProperty(t,"__esModule",{value:!0}),t.FitAddon=void 0,t.FitAddon=class{activate(e){this._terminal=e}dispose(){}fit(){const e=this.proposeDimensions();if(!e||!this._terminal||isNaN(e.cols)||isNaN(e.rows))return;const t=this._terminal._core;this._terminal.rows===e.rows&&this._terminal.cols===e.cols||(t._renderService.clear(),this._terminal.resize(e.cols,e.rows))}proposeDimensions(){if(!this._terminal)return;if(!this._terminal.element||!this._terminal.element.parentElement)return;const e=this._terminal._core._renderService.dimensions;if(0===e.css.cell.width||0===e.css.cell.height)return;const t=0===this._terminal.options.scrollback?0:this._terminal.options.overviewRuler?.width||14,r=window.getComputedStyle(this._terminal.element.parentElement),i=parseInt(r.getPropertyValue("height")),o=Math.max(0,parseInt(r.getPropertyValue("width"))),s=window.getComputedStyle(this._terminal.element),n=i-(parseInt(s.getPropertyValue("padding-top"))+parseInt(s.getPropertyValue("padding-bottom"))),l=o-(parseInt(s.getPropertyValue("padding-right"))+parseInt(s.getPropertyValue("padding-left")))-t;return{cols:Math.max(2,Math.floor(l/e.css.cell.width)),rows:Math.max(1,Math.floor(n/e.css.cell.height))}}}})(),e})()));
2+//# sourceMappingURL=addon-fit.js.map
addedextension/vendor/xterm.css+285 −0
1+/**
2+ * Copyright (c) 2014 The xterm.js authors. All rights reserved.
3+ * Copyright (c) 2012-2013, Christopher Jeffrey (MIT License)
4+ * https://github.com/chjj/term.js
5+ * @license MIT
6+ *
7+ * Permission is hereby granted, free of charge, to any person obtaining a copy
8+ * of this software and associated documentation files (the "Software"), to deal
9+ * in the Software without restriction, including without limitation the rights
10+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+ * copies of the Software, and to permit persons to whom the Software is
12+ * furnished to do so, subject to the following conditions:
13+ *
14+ * The above copyright notice and this permission notice shall be included in
15+ * all copies or substantial portions of the Software.
16+ *
17+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+ * THE SOFTWARE.
24+ *
25+ * Originally forked from (with the author's permission):
26+ * Fabrice Bellard's javascript vt100 for jslinux:
27+ * http://bellard.org/jslinux/
28+ * Copyright (c) 2011 Fabrice Bellard
29+ * The original design remains. The terminal itself
30+ * has been extended to include xterm CSI codes, among
31+ * other features.
32+ */
33+
34+/**
35+ * Default styles for xterm.js
36+ */
37+
38+.xterm {
39+ cursor: text;
40+ position: relative;
41+ user-select: none;
42+ -ms-user-select: none;
43+ -webkit-user-select: none;
44+}
45+
46+.xterm.focus,
47+.xterm:focus {
48+ outline: none;
49+}
50+
51+.xterm .xterm-helpers {
52+ position: absolute;
53+ top: 0;
54+ /**
55+ * The z-index of the helpers must be higher than the canvases in order for
56+ * IMEs to appear on top.
57+ */
58+ z-index: 5;
59+}
60+
61+.xterm .xterm-helper-textarea {
62+ padding: 0;
63+ border: 0;
64+ margin: 0;
65+ /* Move textarea out of the screen to the far left, so that the cursor is not visible */
66+ position: absolute;
67+ opacity: 0;
68+ left: -9999em;
69+ top: 0;
70+ width: 0;
71+ height: 0;
72+ z-index: -5;
73+ /** Prevent wrapping so the IME appears against the textarea at the correct position */
74+ white-space: nowrap;
75+ overflow: hidden;
76+ resize: none;
77+}
78+
79+.xterm .composition-view {
80+ /* TODO: Composition position got messed up somewhere */
81+ background: #000;
82+ color: #FFF;
83+ display: none;
84+ position: absolute;
85+ white-space: nowrap;
86+ z-index: 1;
87+}
88+
89+.xterm .composition-view.active {
90+ display: block;
91+}
92+
93+.xterm .xterm-viewport {
94+ /* On OS X this is required in order for the scroll bar to appear fully opaque */
95+ background-color: #000;
96+ overflow-y: scroll;
97+ cursor: default;
98+ position: absolute;
99+ right: 0;
100+ left: 0;
101+ top: 0;
102+ bottom: 0;
103+}
104+
105+.xterm .xterm-screen {
106+ position: relative;
107+}
108+
109+.xterm .xterm-screen canvas {
110+ position: absolute;
111+ left: 0;
112+ top: 0;
113+}
114+
115+.xterm-char-measure-element {
116+ display: inline-block;
117+ visibility: hidden;
118+ position: absolute;
119+ top: 0;
120+ left: -9999em;
121+ line-height: normal;
122+}
123+
124+.xterm.enable-mouse-events {
125+ /* When mouse events are enabled (eg. tmux), revert to the standard pointer cursor */
126+ cursor: default;
127+}
128+
129+.xterm.xterm-cursor-pointer,
130+.xterm .xterm-cursor-pointer {
131+ cursor: pointer;
132+}
133+
134+.xterm.column-select.focus {
135+ /* Column selection mode */
136+ cursor: crosshair;
137+}
138+
139+.xterm .xterm-accessibility:not(.debug),
140+.xterm .xterm-message {
141+ position: absolute;
142+ left: 0;
143+ top: 0;
144+ bottom: 0;
145+ right: 0;
146+ z-index: 10;
147+ color: transparent;
148+ pointer-events: none;
149+}
150+
151+.xterm .xterm-accessibility-tree:not(.debug) *::selection {
152+ color: transparent;
153+}
154+
155+.xterm .xterm-accessibility-tree {
156+ font-family: monospace;
157+ user-select: text;
158+ white-space: pre;
159+}
160+
161+.xterm .xterm-accessibility-tree > div {
162+ transform-origin: left;
163+ width: fit-content;
164+}
165+
166+.xterm .live-region {
167+ position: absolute;
168+ left: -9999px;
169+ width: 1px;
170+ height: 1px;
171+ overflow: hidden;
172+}
173+
174+.xterm-dim {
175+ /* Dim should not apply to background, so the opacity of the foreground color is applied
176+ * explicitly in the generated class and reset to 1 here */
177+ opacity: 1 !important;
178+}
179+
180+.xterm-underline-1 { text-decoration: underline; }
181+.xterm-underline-2 { text-decoration: double underline; }
182+.xterm-underline-3 { text-decoration: wavy underline; }
183+.xterm-underline-4 { text-decoration: dotted underline; }
184+.xterm-underline-5 { text-decoration: dashed underline; }
185+
186+.xterm-overline {
187+ text-decoration: overline;
188+}
189+
190+.xterm-overline.xterm-underline-1 { text-decoration: overline underline; }
191+.xterm-overline.xterm-underline-2 { text-decoration: overline double underline; }
192+.xterm-overline.xterm-underline-3 { text-decoration: overline wavy underline; }
193+.xterm-overline.xterm-underline-4 { text-decoration: overline dotted underline; }
194+.xterm-overline.xterm-underline-5 { text-decoration: overline dashed underline; }
195+
196+.xterm-strikethrough {
197+ text-decoration: line-through;
198+}
199+
200+.xterm-screen .xterm-decoration-container .xterm-decoration {
201+ z-index: 6;
202+ position: absolute;
203+}
204+
205+.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer {
206+ z-index: 7;
207+}
208+
209+.xterm-decoration-overview-ruler {
210+ z-index: 8;
211+ position: absolute;
212+ top: 0;
213+ right: 0;
214+ pointer-events: none;
215+}
216+
217+.xterm-decoration-top {
218+ z-index: 2;
219+ position: relative;
220+}
221+
222+
223+
224+/* Derived from vs/base/browser/ui/scrollbar/media/scrollbar.css */
225+
226+/* xterm.js customization: Override xterm's cursor style */
227+.xterm .xterm-scrollable-element > .scrollbar {
228+ cursor: default;
229+}
230+
231+/* Arrows */
232+.xterm .xterm-scrollable-element > .scrollbar > .scra {
233+ cursor: pointer;
234+ font-size: 11px !important;
235+}
236+
237+.xterm .xterm-scrollable-element > .visible {
238+ opacity: 1;
239+
240+ /* Background rule added for IE9 - to allow clicks on dom node */
241+ background:rgba(0,0,0,0);
242+
243+ transition: opacity 100ms linear;
244+ /* In front of peek view */
245+ z-index: 11;
246+}
247+.xterm .xterm-scrollable-element > .invisible {
248+ opacity: 0;
249+ pointer-events: none;
250+}
251+.xterm .xterm-scrollable-element > .invisible.fade {
252+ transition: opacity 800ms linear;
253+}
254+
255+/* Scrollable Content Inset Shadow */
256+.xterm .xterm-scrollable-element > .shadow {
257+ position: absolute;
258+ display: none;
259+}
260+.xterm .xterm-scrollable-element > .shadow.top {
261+ display: block;
262+ top: 0;
263+ left: 3px;
264+ height: 3px;
265+ width: 100%;
266+ box-shadow: var(--vscode-scrollbar-shadow, #000) 0 6px 6px -6px inset;
267+}
268+.xterm .xterm-scrollable-element > .shadow.left {
269+ display: block;
270+ top: 3px;
271+ left: 0;
272+ height: 100%;
273+ width: 3px;
274+ box-shadow: var(--vscode-scrollbar-shadow, #000) 6px 0 6px -6px inset;
275+}
276+.xterm .xterm-scrollable-element > .shadow.top-left-corner {
277+ display: block;
278+ top: 0;
279+ left: 0;
280+ height: 3px;
281+ width: 3px;
282+}
283+.xterm .xterm-scrollable-element > .shadow.top.left {
284+ box-shadow: var(--vscode-scrollbar-shadow, #000) 6px 0 6px -6px inset;
285+}
addedextension/vendor/xterm.js+2 −0
1+!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var i=t();for(var s in i)("object"==typeof exports?exports:e)[s]=i[s]}}(globalThis,(()=>(()=>{"use strict";var e={2840:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.AccessibilityManager=void 0;const n=i(7721),o=i(4292),a=i(7150),l=i(7098),h=i(6501),c=i(7093);let d=class extends a.Disposable{constructor(e,t,i,s){super(),this._terminal=e,this._coreBrowserService=i,this._renderService=s,this._rowColumns=new WeakMap,this._liveRegionLineCount=0,this._charsToConsume=[],this._charsToAnnounce="";const r=this._coreBrowserService.mainDocument;this._accessibilityContainer=r.createElement("div"),this._accessibilityContainer.classList.add("xterm-accessibility"),this._rowContainer=r.createElement("div"),this._rowContainer.setAttribute("role","list"),this._rowContainer.classList.add("xterm-accessibility-tree"),this._rowElements=[];for(let e=0;e<this._terminal.rows;e++)this._rowElements[e]=this._createAccessibilityTreeNode(),this._rowContainer.appendChild(this._rowElements[e]);if(this._topBoundaryFocusListener=e=>this._handleBoundaryFocus(e,0),this._bottomBoundaryFocusListener=e=>this._handleBoundaryFocus(e,1),this._rowElements[0].addEventListener("focus",this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._accessibilityContainer.appendChild(this._rowContainer),this._liveRegion=r.createElement("div"),this._liveRegion.classList.add("live-region"),this._liveRegion.setAttribute("aria-live","assertive"),this._accessibilityContainer.appendChild(this._liveRegion),this._liveRegionDebouncer=this._register(new o.TimeBasedDebouncer(this._renderRows.bind(this))),!this._terminal.element)throw new Error("Cannot enable accessibility before Terminal.open");this._terminal.element.insertAdjacentElement("afterbegin",this._accessibilityContainer),this._register(this._terminal.onResize((e=>this._handleResize(e.rows)))),this._register(this._terminal.onRender((e=>this._refreshRows(e.start,e.end)))),this._register(this._terminal.onScroll((()=>this._refreshRows()))),this._register(this._terminal.onA11yChar((e=>this._handleChar(e)))),this._register(this._terminal.onLineFeed((()=>this._handleChar("\n")))),this._register(this._terminal.onA11yTab((e=>this._handleTab(e)))),this._register(this._terminal.onKey((e=>this._handleKey(e.key)))),this._register(this._terminal.onBlur((()=>this._clearLiveRegion()))),this._register(this._renderService.onDimensionsChange((()=>this._refreshRowsDimensions()))),this._register((0,c.addDisposableListener)(r,"selectionchange",(()=>this._handleSelectionChange()))),this._register(this._coreBrowserService.onDprChange((()=>this._refreshRowsDimensions()))),this._refreshRowsDimensions(),this._refreshRows(),this._register((0,a.toDisposable)((()=>{this._accessibilityContainer.remove(),this._rowElements.length=0})))}_handleTab(e){for(let t=0;t<e;t++)this._handleChar(" ")}_handleChar(e){this._liveRegionLineCount<21&&(this._charsToConsume.length>0?this._charsToConsume.shift()!==e&&(this._charsToAnnounce+=e):this._charsToAnnounce+=e,"\n"===e&&(this._liveRegionLineCount++,21===this._liveRegionLineCount&&(this._liveRegion.textContent+=n.tooMuchOutput.get())))}_clearLiveRegion(){this._liveRegion.textContent="",this._liveRegionLineCount=0}_handleKey(e){this._clearLiveRegion(),/\p{Control}/u.test(e)||this._charsToConsume.push(e)}_refreshRows(e,t){this._liveRegionDebouncer.refresh(e,t,this._terminal.rows)}_renderRows(e,t){const i=this._terminal.buffer,s=i.lines.length.toString();for(let r=e;r<=t;r++){const e=i.lines.get(i.ydisp+r),t=[],n=e?.translateToString(!0,void 0,void 0,t)||"",o=(i.ydisp+r+1).toString(),a=this._rowElements[r];a&&(0===n.length?(a.textContent=" ",this._rowColumns.set(a,[0,1])):(a.textContent=n,this._rowColumns.set(a,t)),a.setAttribute("aria-posinset",o),a.setAttribute("aria-setsize",s),this._alignRowWidth(a))}this._announceCharacters()}_announceCharacters(){0!==this._charsToAnnounce.length&&(this._liveRegion.textContent+=this._charsToAnnounce,this._charsToAnnounce="")}_handleBoundaryFocus(e,t){const i=e.target,s=this._rowElements[0===t?1:this._rowElements.length-2];if(i.getAttribute("aria-posinset")===(0===t?"1":`${this._terminal.buffer.lines.length}`))return;if(e.relatedTarget!==s)return;let r,n;if(0===t?(r=i,n=this._rowElements.pop(),this._rowContainer.removeChild(n)):(r=this._rowElements.shift(),n=i,this._rowContainer.removeChild(r)),r.removeEventListener("focus",this._topBoundaryFocusListener),n.removeEventListener("focus",this._bottomBoundaryFocusListener),0===t){const e=this._createAccessibilityTreeNode();this._rowElements.unshift(e),this._rowContainer.insertAdjacentElement("afterbegin",e)}else{const e=this._createAccessibilityTreeNode();this._rowElements.push(e),this._rowContainer.appendChild(e)}this._rowElements[0].addEventListener("focus",this._topBoundaryFocusListener),this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._terminal.scrollLines(0===t?-1:1),this._rowElements[0===t?1:this._rowElements.length-2].focus(),e.preventDefault(),e.stopImmediatePropagation()}_handleSelectionChange(){if(0===this._rowElements.length)return;const e=this._coreBrowserService.mainDocument.getSelection();if(!e)return;if(e.isCollapsed)return void(this._rowContainer.contains(e.anchorNode)&&this._terminal.clearSelection());if(!e.anchorNode||!e.focusNode)return void console.error("anchorNode and/or focusNode are null");let t={node:e.anchorNode,offset:e.anchorOffset},i={node:e.focusNode,offset:e.focusOffset};if((t.node.compareDocumentPosition(i.node)&Node.DOCUMENT_POSITION_PRECEDING||t.node===i.node&&t.offset>i.offset)&&([t,i]=[i,t]),t.node.compareDocumentPosition(this._rowElements[0])&(Node.DOCUMENT_POSITION_CONTAINED_BY|Node.DOCUMENT_POSITION_FOLLOWING)&&(t={node:this._rowElements[0].childNodes[0],offset:0}),!this._rowContainer.contains(t.node))return;const s=this._rowElements.slice(-1)[0];if(i.node.compareDocumentPosition(s)&(Node.DOCUMENT_POSITION_CONTAINED_BY|Node.DOCUMENT_POSITION_PRECEDING)&&(i={node:s,offset:s.textContent?.length??0}),!this._rowContainer.contains(i.node))return;const r=({node:e,offset:t})=>{const i=e instanceof Text?e.parentNode:e;let s=parseInt(i?.getAttribute("aria-posinset"),10)-1;if(isNaN(s))return console.warn("row is invalid. Race condition?"),null;const r=this._rowColumns.get(i);if(!r)return console.warn("columns is null. Race condition?"),null;let n=t<r.length?r[t]:r.slice(-1)[0]+1;return n>=this._terminal.cols&&(++s,n=0),{row:s,column:n}},n=r(t),o=r(i);if(n&&o){if(n.row>o.row||n.row===o.row&&n.column>=o.column)throw new Error("invalid range");this._terminal.select(n.column,n.row,(o.row-n.row)*this._terminal.cols-n.column+o.column)}}_handleResize(e){this._rowElements[this._rowElements.length-1].removeEventListener("focus",this._bottomBoundaryFocusListener);for(let e=this._rowContainer.children.length;e<this._terminal.rows;e++)this._rowElements[e]=this._createAccessibilityTreeNode(),this._rowContainer.appendChild(this._rowElements[e]);for(;this._rowElements.length>e;)this._rowContainer.removeChild(this._rowElements.pop());this._rowElements[this._rowElements.length-1].addEventListener("focus",this._bottomBoundaryFocusListener),this._refreshRowsDimensions()}_createAccessibilityTreeNode(){const e=this._coreBrowserService.mainDocument.createElement("div");return e.setAttribute("role","listitem"),e.tabIndex=-1,this._refreshRowDimensions(e),e}_refreshRowsDimensions(){if(this._renderService.dimensions.css.cell.height){Object.assign(this._accessibilityContainer.style,{width:`${this._renderService.dimensions.css.canvas.width}px`,fontSize:`${this._terminal.options.fontSize}px`}),this._rowElements.length!==this._terminal.rows&&this._handleResize(this._terminal.rows);for(let e=0;e<this._terminal.rows;e++)this._refreshRowDimensions(this._rowElements[e]),this._alignRowWidth(this._rowElements[e])}}_refreshRowDimensions(e){e.style.height=`${this._renderService.dimensions.css.cell.height}px`}_alignRowWidth(e){e.style.transform="";const t=e.getBoundingClientRect().width,i=this._rowColumns.get(e)?.slice(-1)?.[0];if(!i)return;const s=i*this._renderService.dimensions.css.cell.width;e.style.transform=`scaleX(${s/t})`}};t.AccessibilityManager=d,t.AccessibilityManager=d=s([r(1,h.IInstantiationService),r(2,l.ICoreBrowserService),r(3,l.IRenderService)],d)},7861:(e,t)=>{function i(e){return e.replace(/\r?\n/g,"\r")}function s(e,t){return t?"[200~"+e+"[201~":e}function r(e,t,r,n){e=s(e=i(e),r.decPrivateModes.bracketedPasteMode&&!0!==n.rawOptions.ignoreBracketedPasteMode),r.triggerDataEvent(e,!0),t.value=""}function n(e,t,i){const s=i.getBoundingClientRect(),r=e.clientX-s.left-10,n=e.clientY-s.top-10;t.style.width="20px",t.style.height="20px",t.style.left=`${r}px`,t.style.top=`${n}px`,t.style.zIndex="1000",t.focus()}Object.defineProperty(t,"__esModule",{value:!0}),t.prepareTextForTerminal=i,t.bracketTextForPaste=s,t.copyHandler=function(e,t){e.clipboardData&&e.clipboardData.setData("text/plain",t.selectionText),e.preventDefault()},t.handlePasteEvent=function(e,t,i,s){e.stopPropagation(),e.clipboardData&&r(e.clipboardData.getData("text/plain"),t,i,s)},t.paste=r,t.moveTextAreaUnderMouseCursor=n,t.rightClickHandler=function(e,t,i,s,r){n(e,t,i),r&&s.rightClickSelect(e),t.value=s.selectionText,t.select()}},7174:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ColorContrastCache=void 0;const s=i(7710);t.ColorContrastCache=class{constructor(){this._color=new s.TwoKeyMap,this._css=new s.TwoKeyMap}setCss(e,t,i){this._css.set(e,t,i)}getCss(e,t){return this._css.get(e,t)}setColor(e,t,i){this._color.set(e,t,i)}getColor(e,t){return this._color.get(e,t)}clear(){this._color.clear(),this._css.clear()}}},1718:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.CoreBrowserTerminal=void 0;const s=i(7861),r=i(7721),n=i(3285),o=i(4017),a=i(4196),l=i(9925),h=i(3618),c=i(3955),d=i(4792),u=i(945),_=i(9574),f=i(9820),p=i(9784),g=i(5783),m=i(2079),v=i(7098),S=i(9078),b=i(4103),C=i(5777),y=i(701),w=i(6107),E=i(3534),D=i(706),L=i(8693),R=i(4720),A=i(6501),T=i(2486),k=i(2840),M=i(8906),O=i(802),I=i(7093),P=i(7150);class x extends C.CoreTerminal{get linkifier(){return this._linkifier.value}get onFocus(){return this._onFocus.event}get onBlur(){return this._onBlur.event}get onA11yChar(){return this._onA11yCharEmitter.event}get onA11yTab(){return this._onA11yTabEmitter.event}get onWillOpen(){return this._onWillOpen.event}constructor(e={}){super(e),this._linkifier=this._register(new P.MutableDisposable),this.browser=y,this._keyDownHandled=!1,this._keyDownSeen=!1,this._keyPressHandled=!1,this._unprocessedDeadKey=!1,this._accessibilityManager=this._register(new P.MutableDisposable),this._onCursorMove=this._register(new O.Emitter),this.onCursorMove=this._onCursorMove.event,this._onKey=this._register(new O.Emitter),this.onKey=this._onKey.event,this._onRender=this._register(new O.Emitter),this.onRender=this._onRender.event,this._onSelectionChange=this._register(new O.Emitter),this.onSelectionChange=this._onSelectionChange.event,this._onTitleChange=this._register(new O.Emitter),this.onTitleChange=this._onTitleChange.event,this._onBell=this._register(new O.Emitter),this.onBell=this._onBell.event,this._onFocus=this._register(new O.Emitter),this._onBlur=this._register(new O.Emitter),this._onA11yCharEmitter=this._register(new O.Emitter),this._onA11yTabEmitter=this._register(new O.Emitter),this._onWillOpen=this._register(new O.Emitter),this._setup(),this._decorationService=this._instantiationService.createInstance(R.DecorationService),this._instantiationService.setService(A.IDecorationService,this._decorationService),this._linkProviderService=this._instantiationService.createInstance(f.LinkProviderService),this._instantiationService.setService(v.ILinkProviderService,this._linkProviderService),this._linkProviderService.registerLinkProvider(this._instantiationService.createInstance(n.OscLinkProvider)),this._register(this._inputHandler.onRequestBell((()=>this._onBell.fire()))),this._register(this._inputHandler.onRequestRefreshRows((e=>this.refresh(e?.start??0,e?.end??this.rows-1)))),this._register(this._inputHandler.onRequestSendFocus((()=>this._reportFocus()))),this._register(this._inputHandler.onRequestReset((()=>this.reset()))),this._register(this._inputHandler.onRequestWindowsOptionsReport((e=>this._reportWindowsOptions(e)))),this._register(this._inputHandler.onColor((e=>this._handleColorEvent(e)))),this._register(O.Event.forward(this._inputHandler.onCursorMove,this._onCursorMove)),this._register(O.Event.forward(this._inputHandler.onTitleChange,this._onTitleChange)),this._register(O.Event.forward(this._inputHandler.onA11yChar,this._onA11yCharEmitter)),this._register(O.Event.forward(this._inputHandler.onA11yTab,this._onA11yTabEmitter)),this._register(this._bufferService.onResize((e=>this._afterResize(e.cols,e.rows)))),this._register((0,P.toDisposable)((()=>{this._customKeyEventHandler=void 0,this.element?.parentNode?.removeChild(this.element)})))}_handleColorEvent(e){if(this._themeService)for(const t of e){let e,i="";switch(t.index){case 256:e="foreground",i="10";break;case 257:e="background",i="11";break;case 258:e="cursor",i="12";break;default:e="ansi",i="4;"+t.index}switch(t.type){case 0:const s=b.color.toColorRGB("ansi"===e?this._themeService.colors.ansi[t.index]:this._themeService.colors[e]);this.coreService.triggerDataEvent(`${E.C0.ESC}]${i};${(0,L.toRgbString)(s)}${E.C1_ESCAPED.ST}`);break;case 1:if("ansi"===e)this._themeService.modifyColors((e=>e.ansi[t.index]=b.channels.toColor(...t.color)));else{const i=e;this._themeService.modifyColors((e=>e[i]=b.channels.toColor(...t.color)))}break;case 2:this._themeService.restoreColor(t.index)}}}_setup(){super._setup(),this._customKeyEventHandler=void 0}get buffer(){return this.buffers.active}focus(){this.textarea&&this.textarea.focus({preventScroll:!0})}_handleScreenReaderModeOptionChange(e){e?!this._accessibilityManager.value&&this._renderService&&(this._accessibilityManager.value=this._instantiationService.createInstance(k.AccessibilityManager,this)):this._accessibilityManager.clear()}_handleTextAreaFocus(e){this.coreService.decPrivateModes.sendFocus&&this.coreService.triggerDataEvent(E.C0.ESC+"[I"),this.element.classList.add("focus"),this._showCursor(),this._onFocus.fire()}blur(){return this.textarea?.blur()}_handleTextAreaBlur(){this.textarea.value="",this.refresh(this.buffer.y,this.buffer.y),this.coreService.decPrivateModes.sendFocus&&this.coreService.triggerDataEvent(E.C0.ESC+"[O"),this.element.classList.remove("focus"),this._onBlur.fire()}_syncTextArea(){if(!this.textarea||!this.buffer.isCursorInViewport||this._compositionHelper.isComposing||!this._renderService)return;const e=this.buffer.ybase+this.buffer.y,t=this.buffer.lines.get(e);if(!t)return;const i=Math.min(this.buffer.x,this.cols-1),s=this._renderService.dimensions.css.cell.height,r=t.getWidth(i),n=this._renderService.dimensions.css.cell.width*r,o=this.buffer.y*this._renderService.dimensions.css.cell.height,a=i*this._renderService.dimensions.css.cell.width;this.textarea.style.left=a+"px",this.textarea.style.top=o+"px",this.textarea.style.width=n+"px",this.textarea.style.height=s+"px",this.textarea.style.lineHeight=s+"px",this.textarea.style.zIndex="-5"}_initGlobal(){this._bindKeys(),this._register((0,I.addDisposableListener)(this.element,"copy",(e=>{this.hasSelection()&&(0,s.copyHandler)(e,this._selectionService)})));const e=e=>(0,s.handlePasteEvent)(e,this.textarea,this.coreService,this.optionsService);this._register((0,I.addDisposableListener)(this.textarea,"paste",e)),this._register((0,I.addDisposableListener)(this.element,"paste",e)),y.isFirefox?this._register((0,I.addDisposableListener)(this.element,"mousedown",(e=>{2===e.button&&(0,s.rightClickHandler)(e,this.textarea,this.screenElement,this._selectionService,this.options.rightClickSelectsWord)}))):this._register((0,I.addDisposableListener)(this.element,"contextmenu",(e=>{(0,s.rightClickHandler)(e,this.textarea,this.screenElement,this._selectionService,this.options.rightClickSelectsWord)}))),y.isLinux&&this._register((0,I.addDisposableListener)(this.element,"auxclick",(e=>{1===e.button&&(0,s.moveTextAreaUnderMouseCursor)(e,this.textarea,this.screenElement)})))}_bindKeys(){this._register((0,I.addDisposableListener)(this.textarea,"keyup",(e=>this._keyUp(e)),!0)),this._register((0,I.addDisposableListener)(this.textarea,"keydown",(e=>this._keyDown(e)),!0)),this._register((0,I.addDisposableListener)(this.textarea,"keypress",(e=>this._keyPress(e)),!0)),this._register((0,I.addDisposableListener)(this.textarea,"compositionstart",(()=>this._compositionHelper.compositionstart()))),this._register((0,I.addDisposableListener)(this.textarea,"compositionupdate",(e=>this._compositionHelper.compositionupdate(e)))),this._register((0,I.addDisposableListener)(this.textarea,"compositionend",(()=>this._compositionHelper.compositionend()))),this._register((0,I.addDisposableListener)(this.textarea,"input",(e=>this._inputEvent(e)),!0)),this._register(this.onRender((()=>this._compositionHelper.updateCompositionElements())))}open(e){if(!e)throw new Error("Terminal requires a parent element.");if(e.isConnected||this._logService.debug("Terminal.open was called on an element that was not attached to the DOM"),this.element?.ownerDocument.defaultView&&this._coreBrowserService)return void(this.element.ownerDocument.defaultView!==this._coreBrowserService.window&&(this._coreBrowserService.window=this.element.ownerDocument.defaultView));this._document=e.ownerDocument,this.options.documentOverride&&this.options.documentOverride instanceof Document&&(this._document=this.optionsService.rawOptions.documentOverride),this.element=this._document.createElement("div"),this.element.dir="ltr",this.element.classList.add("terminal"),this.element.classList.add("xterm"),e.appendChild(this.element);const t=this._document.createDocumentFragment();this._viewportElement=this._document.createElement("div"),this._viewportElement.classList.add("xterm-viewport"),t.appendChild(this._viewportElement),this.screenElement=this._document.createElement("div"),this.screenElement.classList.add("xterm-screen"),this._register((0,I.addDisposableListener)(this.screenElement,"mousemove",(e=>this.updateCursorStyle(e)))),this._helperContainer=this._document.createElement("div"),this._helperContainer.classList.add("xterm-helpers"),this.screenElement.appendChild(this._helperContainer),t.appendChild(this.screenElement);const i=this.textarea=this._document.createElement("textarea");this.textarea.classList.add("xterm-helper-textarea"),this.textarea.setAttribute("aria-label",r.promptLabel.get()),y.isChromeOS||this.textarea.setAttribute("aria-multiline","false"),this.textarea.setAttribute("autocorrect","off"),this.textarea.setAttribute("autocapitalize","off"),this.textarea.setAttribute("spellcheck","false"),this.textarea.tabIndex=0,this._register(this.optionsService.onSpecificOptionChange("disableStdin",(()=>i.readOnly=this.optionsService.rawOptions.disableStdin))),this.textarea.readOnly=this.optionsService.rawOptions.disableStdin,this._coreBrowserService=this._register(this._instantiationService.createInstance(_.CoreBrowserService,this.textarea,e.ownerDocument.defaultView??window,this._document??"undefined"!=typeof window?window.document:null)),this._instantiationService.setService(v.ICoreBrowserService,this._coreBrowserService),this._register((0,I.addDisposableListener)(this.textarea,"focus",(e=>this._handleTextAreaFocus(e)))),this._register((0,I.addDisposableListener)(this.textarea,"blur",(()=>this._handleTextAreaBlur()))),this._helperContainer.appendChild(this.textarea),this._charSizeService=this._instantiationService.createInstance(d.CharSizeService,this._document,this._helperContainer),this._instantiationService.setService(v.ICharSizeService,this._charSizeService),this._themeService=this._instantiationService.createInstance(S.ThemeService),this._instantiationService.setService(v.IThemeService,this._themeService),this._characterJoinerService=this._instantiationService.createInstance(u.CharacterJoinerService),this._instantiationService.setService(v.ICharacterJoinerService,this._characterJoinerService),this._renderService=this._register(this._instantiationService.createInstance(g.RenderService,this.rows,this.screenElement)),this._instantiationService.setService(v.IRenderService,this._renderService),this._register(this._renderService.onRenderedViewportChange((e=>this._onRender.fire(e)))),this.onResize((e=>this._renderService.resize(e.cols,e.rows))),this._compositionView=this._document.createElement("div"),this._compositionView.classList.add("composition-view"),this._compositionHelper=this._instantiationService.createInstance(h.CompositionHelper,this.textarea,this._compositionView),this._helperContainer.appendChild(this._compositionView),this._mouseService=this._instantiationService.createInstance(p.MouseService),this._instantiationService.setService(v.IMouseService,this._mouseService);const s=this._linkifier.value=this._register(this._instantiationService.createInstance(M.Linkifier,this.screenElement));this.element.appendChild(t);try{this._onWillOpen.fire(this.element)}catch{}this._renderService.hasRenderer()||this._renderService.setRenderer(this._createRenderer()),this._register(this.onCursorMove((()=>{this._renderService.handleCursorMove(),this._syncTextArea()}))),this._register(this.onResize((()=>this._renderService.handleResize(this.cols,this.rows)))),this._register(this.onBlur((()=>this._renderService.handleBlur()))),this._register(this.onFocus((()=>this._renderService.handleFocus()))),this._viewport=this._register(this._instantiationService.createInstance(o.Viewport,this.element,this.screenElement)),this._register(this._viewport.onRequestScrollLines((e=>{super.scrollLines(e,!1),this.refresh(0,this.rows-1)}))),this._selectionService=this._register(this._instantiationService.createInstance(m.SelectionService,this.element,this.screenElement,s)),this._instantiationService.setService(v.ISelectionService,this._selectionService),this._register(this._selectionService.onRequestScrollLines((e=>this.scrollLines(e.amount,e.suppressScrollEvent)))),this._register(this._selectionService.onSelectionChange((()=>this._onSelectionChange.fire()))),this._register(this._selectionService.onRequestRedraw((e=>this._renderService.handleSelectionChanged(e.start,e.end,e.columnSelectMode)))),this._register(this._selectionService.onLinuxMouseSelection((e=>{this.textarea.value=e,this.textarea.focus(),this.textarea.select()}))),this._register(O.Event.any(this._onScroll.event,this._inputHandler.onScroll)((()=>{this._selectionService.refresh(),this._viewport?.queueSync()}))),this._register(this._instantiationService.createInstance(a.BufferDecorationRenderer,this.screenElement)),this._register((0,I.addDisposableListener)(this.element,"mousedown",(e=>this._selectionService.handleMouseDown(e)))),this.coreMouseService.areMouseEventsActive?(this._selectionService.disable(),this.element.classList.add("enable-mouse-events")):this._selectionService.enable(),this.options.screenReaderMode&&(this._accessibilityManager.value=this._instantiationService.createInstance(k.AccessibilityManager,this)),this._register(this.optionsService.onSpecificOptionChange("screenReaderMode",(e=>this._handleScreenReaderModeOptionChange(e)))),this.options.overviewRuler.width&&(this._overviewRulerRenderer=this._register(this._instantiationService.createInstance(l.OverviewRulerRenderer,this._viewportElement,this.screenElement))),this.optionsService.onSpecificOptionChange("overviewRuler",(e=>{!this._overviewRulerRenderer&&e&&this._viewportElement&&this.screenElement&&(this._overviewRulerRenderer=this._register(this._instantiationService.createInstance(l.OverviewRulerRenderer,this._viewportElement,this.screenElement)))})),this._charSizeService.measure(),this.refresh(0,this.rows-1),this._initGlobal(),this.bindMouse()}_createRenderer(){return this._instantiationService.createInstance(c.DomRenderer,this,this._document,this.element,this.screenElement,this._viewportElement,this._helperContainer,this.linkifier)}bindMouse(){const e=this,t=this.element;function i(t){const i=e._mouseService.getMouseReportCoords(t,e.screenElement);if(!i)return!1;let s,r;switch(t.overrideType||t.type){case"mousemove":r=32,void 0===t.buttons?(s=3,void 0!==t.button&&(s=t.button<3?t.button:3)):s=1&t.buttons?0:4&t.buttons?1:2&t.buttons?2:3;break;case"mouseup":r=0,s=t.button<3?t.button:3;break;case"mousedown":r=1,s=t.button<3?t.button:3;break;case"wheel":if(e._customWheelEventHandler&&!1===e._customWheelEventHandler(t))return!1;const i=t.deltaY;if(0===i)return!1;if(0===e.coreMouseService.consumeWheelEvent(t,e._renderService?.dimensions?.device?.cell?.height,e._coreBrowserService?.dpr))return!1;r=i<0?0:1,s=4;break;default:return!1}return!(void 0===r||void 0===s||s>4)&&e.coreMouseService.triggerMouseEvent({col:i.col,row:i.row,x:i.x,y:i.y,button:s,action:r,ctrl:t.ctrlKey,alt:t.altKey,shift:t.shiftKey})}const s={mouseup:null,wheel:null,mousedrag:null,mousemove:null},r={mouseup:e=>(i(e),e.buttons||(this._document.removeEventListener("mouseup",s.mouseup),s.mousedrag&&this._document.removeEventListener("mousemove",s.mousedrag)),this.cancel(e)),wheel:e=>(i(e),this.cancel(e,!0)),mousedrag:e=>{e.buttons&&i(e)},mousemove:e=>{e.buttons||i(e)}};this._register(this.coreMouseService.onProtocolChange((e=>{e?("debug"===this.optionsService.rawOptions.logLevel&&this._logService.debug("Binding to mouse events:",this.coreMouseService.explainEvents(e)),this.element.classList.add("enable-mouse-events"),this._selectionService.disable()):(this._logService.debug("Unbinding from mouse events."),this.element.classList.remove("enable-mouse-events"),this._selectionService.enable()),8&e?s.mousemove||(t.addEventListener("mousemove",r.mousemove),s.mousemove=r.mousemove):(t.removeEventListener("mousemove",s.mousemove),s.mousemove=null),16&e?s.wheel||(t.addEventListener("wheel",r.wheel,{passive:!1}),s.wheel=r.wheel):(t.removeEventListener("wheel",s.wheel),s.wheel=null),2&e?s.mouseup||(s.mouseup=r.mouseup):(this._document.removeEventListener("mouseup",s.mouseup),s.mouseup=null),4&e?s.mousedrag||(s.mousedrag=r.mousedrag):(this._document.removeEventListener("mousemove",s.mousedrag),s.mousedrag=null)}))),this.coreMouseService.activeProtocol=this.coreMouseService.activeProtocol,this._register((0,I.addDisposableListener)(t,"mousedown",(e=>{if(e.preventDefault(),this.focus(),this.coreMouseService.areMouseEventsActive&&!this._selectionService.shouldForceSelection(e))return i(e),s.mouseup&&this._document.addEventListener("mouseup",s.mouseup),s.mousedrag&&this._document.addEventListener("mousemove",s.mousedrag),this.cancel(e)}))),this._register((0,I.addDisposableListener)(t,"wheel",(t=>{if(!s.wheel){if(this._customWheelEventHandler&&!1===this._customWheelEventHandler(t))return!1;if(!this.buffer.hasScrollback){if(0===t.deltaY)return!1;if(0===e.coreMouseService.consumeWheelEvent(t,e._renderService?.dimensions?.device?.cell?.height,e._coreBrowserService?.dpr))return this.cancel(t,!0);const i=E.C0.ESC+(this.coreService.decPrivateModes.applicationCursorKeys?"O":"[")+(t.deltaY<0?"A":"B");return this.coreService.triggerDataEvent(i,!0),this.cancel(t,!0)}}}),{passive:!1}))}refresh(e,t){this._renderService?.refreshRows(e,t)}updateCursorStyle(e){this._selectionService?.shouldColumnSelect(e)?this.element.classList.add("column-select"):this.element.classList.remove("column-select")}_showCursor(){this.coreService.isCursorInitialized||(this.coreService.isCursorInitialized=!0,this.refresh(this.buffer.y,this.buffer.y))}scrollLines(e,t){this._viewport?this._viewport.scrollLines(e):super.scrollLines(e,t),this.refresh(0,this.rows-1)}scrollPages(e){this.scrollLines(e*(this.rows-1))}scrollToTop(){this.scrollLines(-this._bufferService.buffer.ydisp)}scrollToBottom(e){e&&this._viewport?this._viewport.scrollToLine(this.buffer.ybase,!0):this.scrollLines(this._bufferService.buffer.ybase-this._bufferService.buffer.ydisp)}scrollToLine(e){const t=e-this._bufferService.buffer.ydisp;0!==t&&this.scrollLines(t)}paste(e){(0,s.paste)(e,this.textarea,this.coreService,this.optionsService)}attachCustomKeyEventHandler(e){this._customKeyEventHandler=e}attachCustomWheelEventHandler(e){this._customWheelEventHandler=e}registerLinkProvider(e){return this._linkProviderService.registerLinkProvider(e)}registerCharacterJoiner(e){if(!this._characterJoinerService)throw new Error("Terminal must be opened first");const t=this._characterJoinerService.register(e);return this.refresh(0,this.rows-1),t}deregisterCharacterJoiner(e){if(!this._characterJoinerService)throw new Error("Terminal must be opened first");this._characterJoinerService.deregister(e)&&this.refresh(0,this.rows-1)}get markers(){return this.buffer.markers}registerMarker(e){return this.buffer.addMarker(this.buffer.ybase+this.buffer.y+e)}registerDecoration(e){return this._decorationService.registerDecoration(e)}hasSelection(){return!!this._selectionService&&this._selectionService.hasSelection}select(e,t,i){this._selectionService.setSelection(e,t,i)}getSelection(){return this._selectionService?this._selectionService.selectionText:""}getSelectionPosition(){if(this._selectionService&&this._selectionService.hasSelection)return{start:{x:this._selectionService.selectionStart[0],y:this._selectionService.selectionStart[1]},end:{x:this._selectionService.selectionEnd[0],y:this._selectionService.selectionEnd[1]}}}clearSelection(){this._selectionService?.clearSelection()}selectAll(){this._selectionService?.selectAll()}selectLines(e,t){this._selectionService?.selectLines(e,t)}_keyDown(e){if(this._keyDownHandled=!1,this._keyDownSeen=!0,this._customKeyEventHandler&&!1===this._customKeyEventHandler(e))return!1;const t=this.browser.isMac&&this.options.macOptionIsMeta&&e.altKey;if(!t&&!this._compositionHelper.keydown(e))return this.options.scrollOnUserInput&&this.buffer.ybase!==this.buffer.ydisp&&this.scrollToBottom(!0),!1;t||"Dead"!==e.key&&"AltGraph"!==e.key||(this._unprocessedDeadKey=!0);const i=(0,D.evaluateKeyboardEvent)(e,this.coreService.decPrivateModes.applicationCursorKeys,this.browser.isMac,this.options.macOptionIsMeta);if(this.updateCursorStyle(e),3===i.type||2===i.type){const t=this.rows-1;return this.scrollLines(2===i.type?-t:t),this.cancel(e,!0)}return 1===i.type&&this.selectAll(),!!this._isThirdLevelShift(this.browser,e)||(i.cancel&&this.cancel(e,!0),!i.key||!!(e.key&&!e.ctrlKey&&!e.altKey&&!e.metaKey&&1===e.key.length&&e.key.charCodeAt(0)>=65&&e.key.charCodeAt(0)<=90)||(this._unprocessedDeadKey?(this._unprocessedDeadKey=!1,!0):(i.key!==E.C0.ETX&&i.key!==E.C0.CR||(this.textarea.value=""),this._onKey.fire({key:i.key,domEvent:e}),this._showCursor(),this.coreService.triggerDataEvent(i.key,!0),!this.optionsService.rawOptions.screenReaderMode||e.altKey||e.ctrlKey?this.cancel(e,!0):void(this._keyDownHandled=!0))))}_isThirdLevelShift(e,t){const i=e.isMac&&!this.options.macOptionIsMeta&&t.altKey&&!t.ctrlKey&&!t.metaKey||e.isWindows&&t.altKey&&t.ctrlKey&&!t.metaKey||e.isWindows&&t.getModifierState("AltGraph");return"keypress"===t.type?i:i&&(!t.keyCode||t.keyCode>47)}_keyUp(e){this._keyDownSeen=!1,this._customKeyEventHandler&&!1===this._customKeyEventHandler(e)||(function(e){return 16===e.keyCode||17===e.keyCode||18===e.keyCode}(e)||this.focus(),this.updateCursorStyle(e),this._keyPressHandled=!1)}_keyPress(e){let t;if(this._keyPressHandled=!1,this._keyDownHandled)return!1;if(this._customKeyEventHandler&&!1===this._customKeyEventHandler(e))return!1;if(this.cancel(e),e.charCode)t=e.charCode;else if(null===e.which||void 0===e.which)t=e.keyCode;else{if(0===e.which||0===e.charCode)return!1;t=e.which}return!(!t||(e.altKey||e.ctrlKey||e.metaKey)&&!this._isThirdLevelShift(this.browser,e)||(t=String.fromCharCode(t),this._onKey.fire({key:t,domEvent:e}),this._showCursor(),this.coreService.triggerDataEvent(t,!0),this._keyPressHandled=!0,this._unprocessedDeadKey=!1,0))}_inputEvent(e){if(e.data&&"insertText"===e.inputType&&(!e.composed||!this._keyDownSeen)&&!this.optionsService.rawOptions.screenReaderMode){if(this._keyPressHandled)return!1;this._unprocessedDeadKey=!1;const t=e.data;return this.coreService.triggerDataEvent(t,!0),this.cancel(e),!0}return!1}resize(e,t){e!==this.cols||t!==this.rows?super.resize(e,t):this._charSizeService&&!this._charSizeService.hasValidSize&&this._charSizeService.measure()}_afterResize(e,t){this._charSizeService?.measure()}clear(){if(0!==this.buffer.ybase||0!==this.buffer.y){this.buffer.clearAllMarkers(),this.buffer.lines.set(0,this.buffer.lines.get(this.buffer.ybase+this.buffer.y)),this.buffer.lines.length=1,this.buffer.ydisp=0,this.buffer.ybase=0,this.buffer.y=0;for(let e=1;e<this.rows;e++)this.buffer.lines.push(this.buffer.getBlankLine(w.DEFAULT_ATTR_DATA));this._onScroll.fire({position:this.buffer.ydisp}),this.refresh(0,this.rows-1)}}reset(){this.options.rows=this.rows,this.options.cols=this.cols;const e=this._customKeyEventHandler;this._setup(),super.reset(),this._selectionService?.reset(),this._decorationService.reset(),this._customKeyEventHandler=e,this.refresh(0,this.rows-1)}clearTextureAtlas(){this._renderService?.clearTextureAtlas()}_reportFocus(){this.element?.classList.contains("focus")?this.coreService.triggerDataEvent(E.C0.ESC+"[I"):this.coreService.triggerDataEvent(E.C0.ESC+"[O")}_reportWindowsOptions(e){if(this._renderService)switch(e){case T.WindowsOptionsReportType.GET_WIN_SIZE_PIXELS:const e=this._renderService.dimensions.css.canvas.width.toFixed(0),t=this._renderService.dimensions.css.canvas.height.toFixed(0);this.coreService.triggerDataEvent(`${E.C0.ESC}[4;${t};${e}t`);break;case T.WindowsOptionsReportType.GET_CELL_SIZE_PIXELS:const i=this._renderService.dimensions.css.cell.width.toFixed(0),s=this._renderService.dimensions.css.cell.height.toFixed(0);this.coreService.triggerDataEvent(`${E.C0.ESC}[6;${s};${i}t`)}}cancel(e,t){if(this.options.cancelEvents||t)return e.preventDefault(),e.stopPropagation(),!1}}t.CoreBrowserTerminal=x},8906:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.Linkifier=void 0;const n=i(7150),o=i(6501),a=i(7098),l=i(802),h=i(7093);let c=class extends n.Disposable{get currentLink(){return this._currentLink}constructor(e,t,i,s,r){super(),this._element=e,this._mouseService=t,this._renderService=i,this._bufferService=s,this._linkProviderService=r,this._linkCacheDisposables=[],this._isMouseOut=!0,this._wasResized=!1,this._activeLine=-1,this._onShowLinkUnderline=this._register(new l.Emitter),this.onShowLinkUnderline=this._onShowLinkUnderline.event,this._onHideLinkUnderline=this._register(new l.Emitter),this.onHideLinkUnderline=this._onHideLinkUnderline.event,this._register((0,n.toDisposable)((()=>{(0,n.dispose)(this._linkCacheDisposables),this._linkCacheDisposables.length=0,this._lastMouseEvent=void 0,this._activeProviderReplies?.clear()}))),this._register(this._bufferService.onResize((()=>{this._clearCurrentLink(),this._wasResized=!0}))),this._register((0,h.addDisposableListener)(this._element,"mouseleave",(()=>{this._isMouseOut=!0,this._clearCurrentLink()}))),this._register((0,h.addDisposableListener)(this._element,"mousemove",this._handleMouseMove.bind(this))),this._register((0,h.addDisposableListener)(this._element,"mousedown",this._handleMouseDown.bind(this))),this._register((0,h.addDisposableListener)(this._element,"mouseup",this._handleMouseUp.bind(this)))}_handleMouseMove(e){this._lastMouseEvent=e;const t=this._positionFromMouseEvent(e,this._element,this._mouseService);if(!t)return;this._isMouseOut=!1;const i=e.composedPath();for(let e=0;e<i.length;e++){const t=i[e];if(t.classList.contains("xterm"))break;if(t.classList.contains("xterm-hover"))return}this._lastBufferCell&&t.x===this._lastBufferCell.x&&t.y===this._lastBufferCell.y||(this._handleHover(t),this._lastBufferCell=t)}_handleHover(e){if(this._activeLine!==e.y||this._wasResized)return this._clearCurrentLink(),this._askForLink(e,!1),void(this._wasResized=!1);this._currentLink&&this._linkAtPosition(this._currentLink.link,e)||(this._clearCurrentLink(),this._askForLink(e,!0))}_askForLink(e,t){this._activeProviderReplies&&t||(this._activeProviderReplies?.forEach((e=>{e?.forEach((e=>{e.link.dispose&&e.link.dispose()}))})),this._activeProviderReplies=new Map,this._activeLine=e.y);let i=!1;for(const[s,r]of this._linkProviderService.linkProviders.entries())if(t){const t=this._activeProviderReplies?.get(s);t&&(i=this._checkLinkProviderResult(s,e,i))}else r.provideLinks(e.y,(t=>{if(this._isMouseOut)return;const r=t?.map((e=>({link:e})));this._activeProviderReplies?.set(s,r),i=this._checkLinkProviderResult(s,e,i),this._activeProviderReplies?.size===this._linkProviderService.linkProviders.length&&this._removeIntersectingLinks(e.y,this._activeProviderReplies)}))}_removeIntersectingLinks(e,t){const i=new Set;for(let s=0;s<t.size;s++){const r=t.get(s);if(r)for(let t=0;t<r.length;t++){const s=r[t],n=s.link.range.start.y<e?0:s.link.range.start.x,o=s.link.range.end.y>e?this._bufferService.cols:s.link.range.end.x;for(let e=n;e<=o;e++){if(i.has(e)){r.splice(t--,1);break}i.add(e)}}}}_checkLinkProviderResult(e,t,i){if(!this._activeProviderReplies)return i;const s=this._activeProviderReplies.get(e);let r=!1;for(let t=0;t<e;t++)this._activeProviderReplies.has(t)&&!this._activeProviderReplies.get(t)||(r=!0);if(!r&&s){const e=s.find((e=>this._linkAtPosition(e.link,t)));e&&(i=!0,this._handleNewLink(e))}if(this._activeProviderReplies.size===this._linkProviderService.linkProviders.length&&!i)for(let e=0;e<this._activeProviderReplies.size;e++){const s=this._activeProviderReplies.get(e)?.find((e=>this._linkAtPosition(e.link,t)));if(s){i=!0,this._handleNewLink(s);break}}return i}_handleMouseDown(){this._mouseDownLink=this._currentLink}_handleMouseUp(e){if(!this._currentLink)return;const t=this._positionFromMouseEvent(e,this._element,this._mouseService);var i,s;t&&this._mouseDownLink&&(i=this._mouseDownLink.link,s=this._currentLink.link,i.text===s.text&&i.range.start.x===s.range.start.x&&i.range.start.y===s.range.start.y&&i.range.end.x===s.range.end.x&&i.range.end.y===s.range.end.y)&&this._linkAtPosition(this._currentLink.link,t)&&this._currentLink.link.activate(e,this._currentLink.link.text)}_clearCurrentLink(e,t){this._currentLink&&this._lastMouseEvent&&(!e||!t||this._currentLink.link.range.start.y>=e&&this._currentLink.link.range.end.y<=t)&&(this._linkLeave(this._element,this._currentLink.link,this._lastMouseEvent),this._currentLink=void 0,(0,n.dispose)(this._linkCacheDisposables),this._linkCacheDisposables.length=0)}_handleNewLink(e){if(!this._lastMouseEvent)return;const t=this._positionFromMouseEvent(this._lastMouseEvent,this._element,this._mouseService);t&&this._linkAtPosition(e.link,t)&&(this._currentLink=e,this._currentLink.state={decorations:{underline:void 0===e.link.decorations||e.link.decorations.underline,pointerCursor:void 0===e.link.decorations||e.link.decorations.pointerCursor},isHovered:!0},this._linkHover(this._element,e.link,this._lastMouseEvent),e.link.decorations={},Object.defineProperties(e.link.decorations,{pointerCursor:{get:()=>this._currentLink?.state?.decorations.pointerCursor,set:e=>{this._currentLink?.state&&this._currentLink.state.decorations.pointerCursor!==e&&(this._currentLink.state.decorations.pointerCursor=e,this._currentLink.state.isHovered&&this._element.classList.toggle("xterm-cursor-pointer",e))}},underline:{get:()=>this._currentLink?.state?.decorations.underline,set:t=>{this._currentLink?.state&&this._currentLink?.state?.decorations.underline!==t&&(this._currentLink.state.decorations.underline=t,this._currentLink.state.isHovered&&this._fireUnderlineEvent(e.link,t))}}}),this._linkCacheDisposables.push(this._renderService.onRenderedViewportChange((e=>{if(!this._currentLink)return;const t=0===e.start?0:e.start+1+this._bufferService.buffer.ydisp,i=this._bufferService.buffer.ydisp+1+e.end;if(this._currentLink.link.range.start.y>=t&&this._currentLink.link.range.end.y<=i&&(this._clearCurrentLink(t,i),this._lastMouseEvent)){const e=this._positionFromMouseEvent(this._lastMouseEvent,this._element,this._mouseService);e&&this._askForLink(e,!1)}}))))}_linkHover(e,t,i){this._currentLink?.state&&(this._currentLink.state.isHovered=!0,this._currentLink.state.decorations.underline&&this._fireUnderlineEvent(t,!0),this._currentLink.state.decorations.pointerCursor&&e.classList.add("xterm-cursor-pointer")),t.hover&&t.hover(i,t.text)}_fireUnderlineEvent(e,t){const i=e.range,s=this._bufferService.buffer.ydisp,r=this._createLinkUnderlineEvent(i.start.x-1,i.start.y-s-1,i.end.x,i.end.y-s-1,void 0);(t?this._onShowLinkUnderline:this._onHideLinkUnderline).fire(r)}_linkLeave(e,t,i){this._currentLink?.state&&(this._currentLink.state.isHovered=!1,this._currentLink.state.decorations.underline&&this._fireUnderlineEvent(t,!1),this._currentLink.state.decorations.pointerCursor&&e.classList.remove("xterm-cursor-pointer")),t.leave&&t.leave(i,t.text)}_linkAtPosition(e,t){const i=e.range.start.y*this._bufferService.cols+e.range.start.x,s=e.range.end.y*this._bufferService.cols+e.range.end.x,r=t.y*this._bufferService.cols+t.x;return i<=r&&r<=s}_positionFromMouseEvent(e,t,i){const s=i.getCoords(e,t,this._bufferService.cols,this._bufferService.rows);if(s)return{x:s[0],y:s[1]+this._bufferService.buffer.ydisp}}_createLinkUnderlineEvent(e,t,i,s,r){return{x1:e,y1:t,x2:i,y2:s,cols:this._bufferService.cols,fg:r}}};t.Linkifier=c,t.Linkifier=c=s([r(1,a.IMouseService),r(2,a.IRenderService),r(3,o.IBufferService),r(4,a.ILinkProviderService)],c)},7721:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.tooMuchOutput=t.promptLabel=void 0;let i="Terminal input";const s={get:()=>i,set:e=>i=e};t.promptLabel=s;let r="Too much output to announce, navigate to rows manually to read";const n={get:()=>r,set:e=>r=e};t.tooMuchOutput=n},3285:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.OscLinkProvider=void 0;const n=i(3055),o=i(6501);let a=class{constructor(e,t,i){this._bufferService=e,this._optionsService=t,this._oscLinkService=i}provideLinks(e,t){const i=this._bufferService.buffer.lines.get(e-1);if(!i)return void t(void 0);const s=[],r=this._optionsService.rawOptions.linkHandler,o=new n.CellData,a=i.getTrimmedLength();let h=-1,c=-1,d=!1;for(let t=0;t<a;t++)if(-1!==c||i.hasContent(t)){if(i.loadCell(t,o),o.hasExtendedAttrs()&&o.extended.urlId){if(-1===c){c=t,h=o.extended.urlId;continue}d=o.extended.urlId!==h}else-1!==c&&(d=!0);if(d||-1!==c&&t===a-1){const i=this._oscLinkService.getLinkData(h)?.uri;if(i){const n={start:{x:c+1,y:e},end:{x:t+(d||t!==a-1?0:1),y:e}};let o=!1;if(!r?.allowNonHttpProtocols)try{const e=new URL(i);["http:","https:"].includes(e.protocol)||(o=!0)}catch(e){o=!0}o||s.push({text:i,range:n,activate:(e,t)=>r?r.activate(e,t,n):l(0,t),hover:(e,t)=>r?.hover?.(e,t,n),leave:(e,t)=>r?.leave?.(e,t,n)})}d=!1,o.hasExtendedAttrs()&&o.extended.urlId?(c=t,h=o.extended.urlId):(c=-1,h=-1)}}t(s)}};function l(e,t){if(confirm(`Do you want to navigate to ${t}?\n\nWARNING: This link could potentially be dangerous`)){const e=window.open();if(e){try{e.opener=null}catch{}e.location.href=t}else console.warn("Opening link blocked as opener could not be cleared")}}t.OscLinkProvider=a,t.OscLinkProvider=a=s([r(0,o.IBufferService),r(1,o.IOptionsService),r(2,o.IOscLinkService)],a)},4852:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.RenderDebouncer=void 0,t.RenderDebouncer=class{constructor(e,t){this._renderCallback=e,this._coreBrowserService=t,this._refreshCallbacks=[]}dispose(){this._animationFrame&&(this._coreBrowserService.window.cancelAnimationFrame(this._animationFrame),this._animationFrame=void 0)}addRefreshCallback(e){return this._refreshCallbacks.push(e),this._animationFrame||(this._animationFrame=this._coreBrowserService.window.requestAnimationFrame((()=>this._innerRefresh()))),this._animationFrame}refresh(e,t,i){this._rowCount=i,e=void 0!==e?e:0,t=void 0!==t?t:this._rowCount-1,this._rowStart=void 0!==this._rowStart?Math.min(this._rowStart,e):e,this._rowEnd=void 0!==this._rowEnd?Math.max(this._rowEnd,t):t,this._animationFrame||(this._animationFrame=this._coreBrowserService.window.requestAnimationFrame((()=>this._innerRefresh())))}_innerRefresh(){if(this._animationFrame=void 0,void 0===this._rowStart||void 0===this._rowEnd||void 0===this._rowCount)return void this._runRefreshCallbacks();const e=Math.max(this._rowStart,0),t=Math.min(this._rowEnd,this._rowCount-1);this._rowStart=void 0,this._rowEnd=void 0,this._renderCallback(e,t),this._runRefreshCallbacks()}_runRefreshCallbacks(){for(const e of this._refreshCallbacks)e(0);this._refreshCallbacks=[]}}},4292:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.TimeBasedDebouncer=void 0,t.TimeBasedDebouncer=class{constructor(e,t=1e3){this._renderCallback=e,this._debounceThresholdMS=t,this._lastRefreshMs=0,this._additionalRefreshRequested=!1}dispose(){this._refreshTimeoutID&&clearTimeout(this._refreshTimeoutID)}refresh(e,t,i){this._rowCount=i,e=void 0!==e?e:0,t=void 0!==t?t:this._rowCount-1,this._rowStart=void 0!==this._rowStart?Math.min(this._rowStart,e):e,this._rowEnd=void 0!==this._rowEnd?Math.max(this._rowEnd,t):t;const s=performance.now();if(s-this._lastRefreshMs>=this._debounceThresholdMS)this._lastRefreshMs=s,this._innerRefresh();else if(!this._additionalRefreshRequested){const e=s-this._lastRefreshMs,t=this._debounceThresholdMS-e;this._additionalRefreshRequested=!0,this._refreshTimeoutID=window.setTimeout((()=>{this._lastRefreshMs=performance.now(),this._innerRefresh(),this._additionalRefreshRequested=!1,this._refreshTimeoutID=void 0}),t)}}_innerRefresh(){if(void 0===this._rowStart||void 0===this._rowEnd||void 0===this._rowCount)return;const e=Math.max(this._rowStart,0),t=Math.min(this._rowEnd,this._rowCount-1);this._rowStart=void 0,this._rowEnd=void 0,this._renderCallback(e,t)}}},9302:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DEFAULT_ANSI_COLORS=void 0;const s=i(4103);t.DEFAULT_ANSI_COLORS=Object.freeze((()=>{const e=[s.css.toColor("#2e3436"),s.css.toColor("#cc0000"),s.css.toColor("#4e9a06"),s.css.toColor("#c4a000"),s.css.toColor("#3465a4"),s.css.toColor("#75507b"),s.css.toColor("#06989a"),s.css.toColor("#d3d7cf"),s.css.toColor("#555753"),s.css.toColor("#ef2929"),s.css.toColor("#8ae234"),s.css.toColor("#fce94f"),s.css.toColor("#729fcf"),s.css.toColor("#ad7fa8"),s.css.toColor("#34e2e2"),s.css.toColor("#eeeeec")],t=[0,95,135,175,215,255];for(let i=0;i<216;i++){const r=t[i/36%6|0],n=t[i/6%6|0],o=t[i%6];e.push({css:s.channels.toCss(r,n,o),rgba:s.channels.toRgba(r,n,o)})}for(let t=0;t<24;t++){const i=8+10*t;e.push({css:s.channels.toCss(i,i,i),rgba:s.channels.toRgba(i,i,i)})}return e})())},4017:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.Viewport=void 0;const n=i(7098),o=i(7150),a=i(6501),l=i(7093),h=i(8234),c=i(802),d=i(9881);let u=class extends o.Disposable{constructor(e,t,i,s,r,n,a,u){super(),this._bufferService=i,this._optionsService=a,this._renderService=u,this._onRequestScrollLines=this._register(new c.Emitter),this.onRequestScrollLines=this._onRequestScrollLines.event,this._isSyncing=!1,this._isHandlingScroll=!1,this._suppressOnScrollHandler=!1;const _=this._register(new d.Scrollable({forceIntegerValues:!1,smoothScrollDuration:this._optionsService.rawOptions.smoothScrollDuration,scheduleAtNextAnimationFrame:e=>(0,l.scheduleAtNextAnimationFrame)(s.window,e)}));this._register(this._optionsService.onSpecificOptionChange("smoothScrollDuration",(()=>{_.setSmoothScrollDuration(this._optionsService.rawOptions.smoothScrollDuration)}))),this._scrollableElement=this._register(new h.SmoothScrollableElement(t,{vertical:1,horizontal:2,useShadows:!1,mouseWheelSmoothScroll:!0,...this._getChangeOptions()},_)),this._register(this._optionsService.onMultipleOptionChange(["scrollSensitivity","fastScrollSensitivity","overviewRuler"],(()=>this._scrollableElement.updateOptions(this._getChangeOptions())))),this._register(r.onProtocolChange((e=>{this._scrollableElement.updateOptions({handleMouseWheel:!(16&e)})}))),this._scrollableElement.setScrollDimensions({height:0,scrollHeight:0}),this._register(c.Event.runAndSubscribe(n.onChangeColors,(()=>{this._scrollableElement.getDomNode().style.backgroundColor=n.colors.background.css}))),e.appendChild(this._scrollableElement.getDomNode()),this._register((0,o.toDisposable)((()=>this._scrollableElement.getDomNode().remove()))),this._styleElement=s.mainDocument.createElement("style"),t.appendChild(this._styleElement),this._register((0,o.toDisposable)((()=>this._styleElement.remove()))),this._register(c.Event.runAndSubscribe(n.onChangeColors,(()=>{this._styleElement.textContent=[".xterm .xterm-scrollable-element > .scrollbar > .slider {",` background: ${n.colors.scrollbarSliderBackground.css};`,"}",".xterm .xterm-scrollable-element > .scrollbar > .slider:hover {",` background: ${n.colors.scrollbarSliderHoverBackground.css};`,"}",".xterm .xterm-scrollable-element > .scrollbar > .slider.active {",` background: ${n.colors.scrollbarSliderActiveBackground.css};`,"}"].join("\n")}))),this._register(this._bufferService.onResize((()=>this.queueSync()))),this._register(this._bufferService.buffers.onBufferActivate((()=>{this._latestYDisp=void 0,this.queueSync()}))),this._register(this._bufferService.onScroll((()=>this._sync()))),this._register(this._scrollableElement.onScroll((e=>this._handleScroll(e))))}scrollLines(e){const t=this._scrollableElement.getScrollPosition();this._scrollableElement.setScrollPosition({reuseAnimation:!0,scrollTop:t.scrollTop+e*this._renderService.dimensions.css.cell.height})}scrollToLine(e,t){t&&(this._latestYDisp=e),this._scrollableElement.setScrollPosition({reuseAnimation:!t,scrollTop:e*this._renderService.dimensions.css.cell.height})}_getChangeOptions(){return{mouseWheelScrollSensitivity:this._optionsService.rawOptions.scrollSensitivity,fastScrollSensitivity:this._optionsService.rawOptions.fastScrollSensitivity,verticalScrollbarSize:this._optionsService.rawOptions.overviewRuler?.width||14}}queueSync(e){void 0!==e&&(this._latestYDisp=e),void 0===this._queuedAnimationFrame&&(this._queuedAnimationFrame=this._renderService.addRefreshCallback((()=>{this._queuedAnimationFrame=void 0,this._sync(this._latestYDisp)})))}_sync(e=this._bufferService.buffer.ydisp){this._renderService&&!this._isSyncing&&(this._isSyncing=!0,this._suppressOnScrollHandler=!0,this._scrollableElement.setScrollDimensions({height:this._renderService.dimensions.css.canvas.height,scrollHeight:this._renderService.dimensions.css.cell.height*this._bufferService.buffer.lines.length}),this._suppressOnScrollHandler=!1,e!==this._latestYDisp&&this._scrollableElement.setScrollPosition({scrollTop:e*this._renderService.dimensions.css.cell.height}),this._isSyncing=!1)}_handleScroll(e){if(!this._renderService)return;if(this._isHandlingScroll||this._suppressOnScrollHandler)return;this._isHandlingScroll=!0;const t=Math.round(e.scrollTop/this._renderService.dimensions.css.cell.height),i=t-this._bufferService.buffer.ydisp;0!==i&&(this._latestYDisp=t,this._onRequestScrollLines.fire(i)),this._isHandlingScroll=!1}};t.Viewport=u,t.Viewport=u=s([r(2,a.IBufferService),r(3,n.ICoreBrowserService),r(4,a.ICoreMouseService),r(5,n.IThemeService),r(6,a.IOptionsService),r(7,n.IRenderService)],u)},4196:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.BufferDecorationRenderer=void 0;const n=i(7098),o=i(7150),a=i(6501);let l=class extends o.Disposable{constructor(e,t,i,s,r){super(),this._screenElement=e,this._bufferService=t,this._coreBrowserService=i,this._decorationService=s,this._renderService=r,this._decorationElements=new Map,this._altBufferIsActive=!1,this._dimensionsChanged=!1,this._container=document.createElement("div"),this._container.classList.add("xterm-decoration-container"),this._screenElement.appendChild(this._container),this._register(this._renderService.onRenderedViewportChange((()=>this._doRefreshDecorations()))),this._register(this._renderService.onDimensionsChange((()=>{this._dimensionsChanged=!0,this._queueRefresh()}))),this._register(this._coreBrowserService.onDprChange((()=>this._queueRefresh()))),this._register(this._bufferService.buffers.onBufferActivate((()=>{this._altBufferIsActive=this._bufferService.buffer===this._bufferService.buffers.alt}))),this._register(this._decorationService.onDecorationRegistered((()=>this._queueRefresh()))),this._register(this._decorationService.onDecorationRemoved((e=>this._removeDecoration(e)))),this._register((0,o.toDisposable)((()=>{this._container.remove(),this._decorationElements.clear()})))}_queueRefresh(){void 0===this._animationFrame&&(this._animationFrame=this._renderService.addRefreshCallback((()=>{this._doRefreshDecorations(),this._animationFrame=void 0})))}_doRefreshDecorations(){for(const e of this._decorationService.decorations)this._renderDecoration(e);this._dimensionsChanged=!1}_renderDecoration(e){this._refreshStyle(e),this._dimensionsChanged&&this._refreshXPosition(e)}_createElement(e){const t=this._coreBrowserService.mainDocument.createElement("div");t.classList.add("xterm-decoration"),t.classList.toggle("xterm-decoration-top-layer","top"===e?.options?.layer),t.style.width=`${Math.round((e.options.width||1)*this._renderService.dimensions.css.cell.width)}px`,t.style.height=(e.options.height||1)*this._renderService.dimensions.css.cell.height+"px",t.style.top=(e.marker.line-this._bufferService.buffers.active.ydisp)*this._renderService.dimensions.css.cell.height+"px",t.style.lineHeight=`${this._renderService.dimensions.css.cell.height}px`;const i=e.options.x??0;return i&&i>this._bufferService.cols&&(t.style.display="none"),this._refreshXPosition(e,t),t}_refreshStyle(e){const t=e.marker.line-this._bufferService.buffers.active.ydisp;if(t<0||t>=this._bufferService.rows)e.element&&(e.element.style.display="none",e.onRenderEmitter.fire(e.element));else{let i=this._decorationElements.get(e);i||(i=this._createElement(e),e.element=i,this._decorationElements.set(e,i),this._container.appendChild(i),e.onDispose((()=>{this._decorationElements.delete(e),i.remove()}))),i.style.display=this._altBufferIsActive?"none":"block",this._altBufferIsActive||(i.style.width=`${Math.round((e.options.width||1)*this._renderService.dimensions.css.cell.width)}px`,i.style.height=(e.options.height||1)*this._renderService.dimensions.css.cell.height+"px",i.style.top=t*this._renderService.dimensions.css.cell.height+"px",i.style.lineHeight=`${this._renderService.dimensions.css.cell.height}px`),e.onRenderEmitter.fire(i)}}_refreshXPosition(e,t=e.element){if(!t)return;const i=e.options.x??0;"right"===(e.options.anchor||"left")?t.style.right=i?i*this._renderService.dimensions.css.cell.width+"px":"":t.style.left=i?i*this._renderService.dimensions.css.cell.width+"px":""}_removeDecoration(e){this._decorationElements.get(e)?.remove(),this._decorationElements.delete(e),e.dispose()}};t.BufferDecorationRenderer=l,t.BufferDecorationRenderer=l=s([r(1,a.IBufferService),r(2,n.ICoreBrowserService),r(3,a.IDecorationService),r(4,n.IRenderService)],l)},957:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ColorZoneStore=void 0,t.ColorZoneStore=class{constructor(){this._zones=[],this._zonePool=[],this._zonePoolIndex=0,this._linePadding={full:0,left:0,center:0,right:0}}get zones(){return this._zonePool.length=Math.min(this._zonePool.length,this._zones.length),this._zones}clear(){this._zones.length=0,this._zonePoolIndex=0}addDecoration(e){if(e.options.overviewRulerOptions){for(const t of this._zones)if(t.color===e.options.overviewRulerOptions.color&&t.position===e.options.overviewRulerOptions.position){if(this._lineIntersectsZone(t,e.marker.line))return;if(this._lineAdjacentToZone(t,e.marker.line,e.options.overviewRulerOptions.position))return void this._addLineToZone(t,e.marker.line)}if(this._zonePoolIndex<this._zonePool.length)return this._zonePool[this._zonePoolIndex].color=e.options.overviewRulerOptions.color,this._zonePool[this._zonePoolIndex].position=e.options.overviewRulerOptions.position,this._zonePool[this._zonePoolIndex].startBufferLine=e.marker.line,this._zonePool[this._zonePoolIndex].endBufferLine=e.marker.line,void this._zones.push(this._zonePool[this._zonePoolIndex++]);this._zones.push({color:e.options.overviewRulerOptions.color,position:e.options.overviewRulerOptions.position,startBufferLine:e.marker.line,endBufferLine:e.marker.line}),this._zonePool.push(this._zones[this._zones.length-1]),this._zonePoolIndex++}}setPadding(e){this._linePadding=e}_lineIntersectsZone(e,t){return t>=e.startBufferLine&&t<=e.endBufferLine}_lineAdjacentToZone(e,t,i){return t>=e.startBufferLine-this._linePadding[i||"full"]&&t<=e.endBufferLine+this._linePadding[i||"full"]}_addLineToZone(e,t){e.startBufferLine=Math.min(e.startBufferLine,t),e.endBufferLine=Math.max(e.endBufferLine,t)}}},9925:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.OverviewRulerRenderer=void 0;const n=i(957),o=i(7098),a=i(7150),l=i(6501),h={full:0,left:0,center:0,right:0},c={full:0,left:0,center:0,right:0},d={full:0,left:0,center:0,right:0};let u=class extends a.Disposable{get _width(){return this._optionsService.options.overviewRuler?.width||0}constructor(e,t,i,s,r,o,l,h){super(),this._viewportElement=e,this._screenElement=t,this._bufferService=i,this._decorationService=s,this._renderService=r,this._optionsService=o,this._themeService=l,this._coreBrowserService=h,this._colorZoneStore=new n.ColorZoneStore,this._shouldUpdateDimensions=!0,this._shouldUpdateAnchor=!0,this._lastKnownBufferLength=0,this._canvas=this._coreBrowserService.mainDocument.createElement("canvas"),this._canvas.classList.add("xterm-decoration-overview-ruler"),this._refreshCanvasDimensions(),this._viewportElement.parentElement?.insertBefore(this._canvas,this._viewportElement),this._register((0,a.toDisposable)((()=>this._canvas?.remove())));const c=this._canvas.getContext("2d");if(!c)throw new Error("Ctx cannot be null");this._ctx=c,this._register(this._decorationService.onDecorationRegistered((()=>this._queueRefresh(void 0,!0)))),this._register(this._decorationService.onDecorationRemoved((()=>this._queueRefresh(void 0,!0)))),this._register(this._renderService.onRenderedViewportChange((()=>this._queueRefresh()))),this._register(this._bufferService.buffers.onBufferActivate((()=>{this._canvas.style.display=this._bufferService.buffer===this._bufferService.buffers.alt?"none":"block"}))),this._register(this._bufferService.onScroll((()=>{this._lastKnownBufferLength!==this._bufferService.buffers.normal.lines.length&&(this._refreshDrawHeightConstants(),this._refreshColorZonePadding())}))),this._register(this._renderService.onRender((()=>{this._containerHeight&&this._containerHeight===this._screenElement.clientHeight||(this._queueRefresh(!0),this._containerHeight=this._screenElement.clientHeight)}))),this._register(this._coreBrowserService.onDprChange((()=>this._queueRefresh(!0)))),this._register(this._optionsService.onSpecificOptionChange("overviewRuler",(()=>this._queueRefresh(!0)))),this._register(this._themeService.onChangeColors((()=>this._queueRefresh()))),this._queueRefresh(!0)}_refreshDrawConstants(){const e=Math.floor((this._canvas.width-1)/3),t=Math.ceil((this._canvas.width-1)/3);c.full=this._canvas.width,c.left=e,c.center=t,c.right=e,this._refreshDrawHeightConstants(),d.full=1,d.left=1,d.center=1+c.left,d.right=1+c.left+c.center}_refreshDrawHeightConstants(){h.full=Math.round(2*this._coreBrowserService.dpr);const e=this._canvas.height/this._bufferService.buffer.lines.length,t=Math.round(Math.max(Math.min(e,12),6)*this._coreBrowserService.dpr);h.left=t,h.center=t,h.right=t}_refreshColorZonePadding(){this._colorZoneStore.setPadding({full:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*h.full),left:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*h.left),center:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*h.center),right:Math.floor(this._bufferService.buffers.active.lines.length/(this._canvas.height-1)*h.right)}),this._lastKnownBufferLength=this._bufferService.buffers.normal.lines.length}_refreshCanvasDimensions(){this._canvas.style.width=`${this._width}px`,this._canvas.width=Math.round(this._width*this._coreBrowserService.dpr),this._canvas.style.height=`${this._screenElement.clientHeight}px`,this._canvas.height=Math.round(this._screenElement.clientHeight*this._coreBrowserService.dpr),this._refreshDrawConstants(),this._refreshColorZonePadding()}_refreshDecorations(){this._shouldUpdateDimensions&&this._refreshCanvasDimensions(),this._ctx.clearRect(0,0,this._canvas.width,this._canvas.height),this._colorZoneStore.clear();for(const e of this._decorationService.decorations)this._colorZoneStore.addDecoration(e);this._ctx.lineWidth=1,this._renderRulerOutline();const e=this._colorZoneStore.zones;for(const t of e)"full"!==t.position&&this._renderColorZone(t);for(const t of e)"full"===t.position&&this._renderColorZone(t);this._shouldUpdateDimensions=!1,this._shouldUpdateAnchor=!1}_renderRulerOutline(){this._ctx.fillStyle=this._themeService.colors.overviewRulerBorder.css,this._ctx.fillRect(0,0,1,this._canvas.height),this._optionsService.rawOptions.overviewRuler.showTopBorder&&this._ctx.fillRect(1,0,this._canvas.width-1,1),this._optionsService.rawOptions.overviewRuler.showBottomBorder&&this._ctx.fillRect(1,this._canvas.height-1,this._canvas.width-1,this._canvas.height)}_renderColorZone(e){this._ctx.fillStyle=e.color,this._ctx.fillRect(d[e.position||"full"],Math.round((this._canvas.height-1)*(e.startBufferLine/this._bufferService.buffers.active.lines.length)-h[e.position||"full"]/2),c[e.position||"full"],Math.round((this._canvas.height-1)*((e.endBufferLine-e.startBufferLine)/this._bufferService.buffers.active.lines.length)+h[e.position||"full"]))}_queueRefresh(e,t){this._shouldUpdateDimensions=e||this._shouldUpdateDimensions,this._shouldUpdateAnchor=t||this._shouldUpdateAnchor,void 0===this._animationFrame&&(this._animationFrame=this._coreBrowserService.window.requestAnimationFrame((()=>{this._refreshDecorations(),this._animationFrame=void 0})))}};t.OverviewRulerRenderer=u,t.OverviewRulerRenderer=u=s([r(2,l.IBufferService),r(3,l.IDecorationService),r(4,o.IRenderService),r(5,l.IOptionsService),r(6,o.IThemeService),r(7,o.ICoreBrowserService)],u)},3618:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CompositionHelper=void 0;const n=i(7098),o=i(6501),a=i(3534);let l=class{get isComposing(){return this._isComposing}constructor(e,t,i,s,r,n){this._textarea=e,this._compositionView=t,this._bufferService=i,this._optionsService=s,this._coreService=r,this._renderService=n,this._isComposing=!1,this._isSendingComposition=!1,this._compositionPosition={start:0,end:0},this._dataAlreadySent=""}compositionstart(){this._isComposing=!0,this._compositionPosition.start=this._textarea.value.length,this._compositionView.textContent="",this._dataAlreadySent="",this._compositionView.classList.add("active")}compositionupdate(e){this._compositionView.textContent=e.data,this.updateCompositionElements(),setTimeout((()=>{this._compositionPosition.end=this._textarea.value.length}),0)}compositionend(){this._finalizeComposition(!0)}keydown(e){if(this._isComposing||this._isSendingComposition){if(20===e.keyCode||229===e.keyCode)return!1;if(16===e.keyCode||17===e.keyCode||18===e.keyCode)return!1;this._finalizeComposition(!1)}return 229!==e.keyCode||(this._handleAnyTextareaChanges(),!1)}_finalizeComposition(e){if(this._compositionView.classList.remove("active"),this._isComposing=!1,e){const e={start:this._compositionPosition.start,end:this._compositionPosition.end};this._isSendingComposition=!0,setTimeout((()=>{if(this._isSendingComposition){let t;this._isSendingComposition=!1,e.start+=this._dataAlreadySent.length,t=this._isComposing?this._textarea.value.substring(e.start,this._compositionPosition.start):this._textarea.value.substring(e.start),t.length>0&&this._coreService.triggerDataEvent(t,!0)}}),0)}else{this._isSendingComposition=!1;const e=this._textarea.value.substring(this._compositionPosition.start,this._compositionPosition.end);this._coreService.triggerDataEvent(e,!0)}}_handleAnyTextareaChanges(){const e=this._textarea.value;setTimeout((()=>{if(!this._isComposing){const t=this._textarea.value,i=t.replace(e,"");this._dataAlreadySent=i,t.length>e.length?this._coreService.triggerDataEvent(i,!0):t.length<e.length?this._coreService.triggerDataEvent(`${a.C0.DEL}`,!0):t.length===e.length&&t!==e&&this._coreService.triggerDataEvent(t,!0)}}),0)}updateCompositionElements(e){if(this._isComposing){if(this._bufferService.buffer.isCursorInViewport){const e=Math.min(this._bufferService.buffer.x,this._bufferService.cols-1),t=this._renderService.dimensions.css.cell.height,i=this._bufferService.buffer.y*this._renderService.dimensions.css.cell.height,s=e*this._renderService.dimensions.css.cell.width;this._compositionView.style.left=s+"px",this._compositionView.style.top=i+"px",this._compositionView.style.height=t+"px",this._compositionView.style.lineHeight=t+"px",this._compositionView.style.fontFamily=this._optionsService.rawOptions.fontFamily,this._compositionView.style.fontSize=this._optionsService.rawOptions.fontSize+"px";const r=this._compositionView.getBoundingClientRect();this._textarea.style.left=s+"px",this._textarea.style.top=i+"px",this._textarea.style.width=Math.max(r.width,1)+"px",this._textarea.style.height=Math.max(r.height,1)+"px",this._textarea.style.lineHeight=r.height+"px"}e||setTimeout((()=>this.updateCompositionElements(!0)),0)}}};t.CompositionHelper=l,t.CompositionHelper=l=s([r(2,o.IBufferService),r(3,o.IOptionsService),r(4,o.ICoreService),r(5,n.IRenderService)],l)},5251:(e,t)=>{function i(e,t,i){const s=i.getBoundingClientRect(),r=e.getComputedStyle(i),n=parseInt(r.getPropertyValue("padding-left")),o=parseInt(r.getPropertyValue("padding-top"));return[t.clientX-s.left-n,t.clientY-s.top-o]}Object.defineProperty(t,"__esModule",{value:!0}),t.getCoordsRelativeToElement=i,t.getCoords=function(e,t,s,r,n,o,a,l,h){if(!o)return;const c=i(e,t,s);return c?(c[0]=Math.ceil((c[0]+(h?a/2:0))/a),c[1]=Math.ceil(c[1]/l),c[0]=Math.min(Math.max(c[0],1),r+(h?1:0)),c[1]=Math.min(Math.max(c[1],1),n),c):void 0}},9686:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.moveToCellSequence=function(e,t,i,s){const o=i.buffer.x,c=i.buffer.y;if(!i.buffer.hasScrollback)return function(e,t,i,s,o,c){return 0===r(t,s,o,c).length?"":h(a(e,t,e,t-n(t,o),!1,o).length,l("D",c))}(o,c,0,t,i,s)+r(c,t,i,s)+function(e,t,i,s,o,c){let d;d=r(t,s,o,c).length>0?s-n(s,o):t;const u=s,_=function(e,t,i,s,o,a){let l;return l=r(i,s,o,a).length>0?s-n(s,o):t,e<i&&l<=s||e>=i&&l<s?"C":"D"}(e,t,i,s,o,c);return h(a(e,d,i,u,"C"===_,o).length,l(_,c))}(o,c,e,t,i,s);let d;if(c===t)return d=o>e?"D":"C",h(Math.abs(o-e),l(d,s));d=c>t?"D":"C";const u=Math.abs(c-t);return h(function(e,t){return t.cols-e}(c>t?e:o,i)+(u-1)*i.cols+1+((c>t?o:e)-1),l(d,s))};const s=i(3534);function r(e,t,i,s){const r=e-n(e,i),a=t-n(t,i),c=Math.abs(r-a)-function(e,t,i){let s=0;const r=e-n(e,i),a=t-n(t,i);for(let n=0;n<Math.abs(r-a);n++){const a="A"===o(e,t)?-1:1,l=i.buffer.lines.get(r+a*n);l?.isWrapped&&s++}return s}(e,t,i);return h(c,l(o(e,t),s))}function n(e,t){let i=0,s=t.buffer.lines.get(e),r=s?.isWrapped;for(;r&&e>=0&&e<t.rows;)i++,s=t.buffer.lines.get(--e),r=s?.isWrapped;return i}function o(e,t){return e>t?"A":"B"}function a(e,t,i,s,r,n){let o=e,a=t,l="";for(;(o!==i||a!==s)&&a>=0&&a<n.buffer.lines.length;)o+=r?1:-1,r&&o>n.cols-1?(l+=n.buffer.translateBufferLineToString(a,!1,e,o),o=0,e=0,a++):!r&&o<0&&(l+=n.buffer.translateBufferLineToString(a,!1,0,e+1),o=n.cols-1,e=o,a--);return l+n.buffer.translateBufferLineToString(a,!1,e,o)}function l(e,t){const i=t?"O":"[";return s.C0.ESC+i+e}function h(e,t){e=Math.floor(e);let i="";for(let s=0;s<e;s++)i+=t;return i}},3955:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.DomRenderer=void 0;const n=i(1433),o=i(2744),a=i(9176),l=i(6181),h=i(2274),c=i(7098),d=i(4103),u=i(7150),_=i(6501),f=i(802),p="xterm-dom-renderer-owner-",g="xterm-rows",m="xterm-fg-",v="xterm-bg-",S="xterm-focus",b="xterm-selection";let C=1,y=class extends u.Disposable{constructor(e,t,i,s,r,a,c,d,_,m,v,S,y,w){super(),this._terminal=e,this._document=t,this._element=i,this._screenElement=s,this._viewportElement=r,this._helperContainer=a,this._linkifier2=c,this._charSizeService=_,this._optionsService=m,this._bufferService=v,this._coreService=S,this._coreBrowserService=y,this._themeService=w,this._terminalClass=C++,this._rowElements=[],this._selectionRenderModel=(0,h.createSelectionRenderModel)(),this.onRequestRedraw=this._register(new f.Emitter).event,this._rowContainer=this._document.createElement("div"),this._rowContainer.classList.add(g),this._rowContainer.style.lineHeight="normal",this._rowContainer.setAttribute("aria-hidden","true"),this._refreshRowElements(this._bufferService.cols,this._bufferService.rows),this._selectionContainer=this._document.createElement("div"),this._selectionContainer.classList.add(b),this._selectionContainer.setAttribute("aria-hidden","true"),this.dimensions=(0,l.createRenderDimensions)(),this._updateDimensions(),this._register(this._optionsService.onOptionChange((()=>this._handleOptionsChanged()))),this._register(this._themeService.onChangeColors((e=>this._injectCss(e)))),this._injectCss(this._themeService.colors),this._rowFactory=d.createInstance(n.DomRendererRowFactory,document),this._element.classList.add(p+this._terminalClass),this._screenElement.appendChild(this._rowContainer),this._screenElement.appendChild(this._selectionContainer),this._register(this._linkifier2.onShowLinkUnderline((e=>this._handleLinkHover(e)))),this._register(this._linkifier2.onHideLinkUnderline((e=>this._handleLinkLeave(e)))),this._register((0,u.toDisposable)((()=>{this._element.classList.remove(p+this._terminalClass),this._rowContainer.remove(),this._selectionContainer.remove(),this._widthCache.dispose(),this._themeStyleElement.remove(),this._dimensionsStyleElement.remove()}))),this._widthCache=new o.WidthCache(this._document,this._helperContainer),this._widthCache.setFont(this._optionsService.rawOptions.fontFamily,this._optionsService.rawOptions.fontSize,this._optionsService.rawOptions.fontWeight,this._optionsService.rawOptions.fontWeightBold),this._setDefaultSpacing()}_updateDimensions(){const e=this._coreBrowserService.dpr;this.dimensions.device.char.width=this._charSizeService.width*e,this.dimensions.device.char.height=Math.ceil(this._charSizeService.height*e),this.dimensions.device.cell.width=this.dimensions.device.char.width+Math.round(this._optionsService.rawOptions.letterSpacing),this.dimensions.device.cell.height=Math.floor(this.dimensions.device.char.height*this._optionsService.rawOptions.lineHeight),this.dimensions.device.char.left=0,this.dimensions.device.char.top=0,this.dimensions.device.canvas.width=this.dimensions.device.cell.width*this._bufferService.cols,this.dimensions.device.canvas.height=this.dimensions.device.cell.height*this._bufferService.rows,this.dimensions.css.canvas.width=Math.round(this.dimensions.device.canvas.width/e),this.dimensions.css.canvas.height=Math.round(this.dimensions.device.canvas.height/e),this.dimensions.css.cell.width=this.dimensions.css.canvas.width/this._bufferService.cols,this.dimensions.css.cell.height=this.dimensions.css.canvas.height/this._bufferService.rows;for(const e of this._rowElements)e.style.width=`${this.dimensions.css.canvas.width}px`,e.style.height=`${this.dimensions.css.cell.height}px`,e.style.lineHeight=`${this.dimensions.css.cell.height}px`,e.style.overflow="hidden";this._dimensionsStyleElement||(this._dimensionsStyleElement=this._document.createElement("style"),this._screenElement.appendChild(this._dimensionsStyleElement));const t=`${this._terminalSelector} .${g} span { display: inline-block; height: 100%; vertical-align: top;}`;this._dimensionsStyleElement.textContent=t,this._selectionContainer.style.height=this._viewportElement.style.height,this._screenElement.style.width=`${this.dimensions.css.canvas.width}px`,this._screenElement.style.height=`${this.dimensions.css.canvas.height}px`}_injectCss(e){this._themeStyleElement||(this._themeStyleElement=this._document.createElement("style"),this._screenElement.appendChild(this._themeStyleElement));let t=`${this._terminalSelector} .${g} { pointer-events: none; color: ${e.foreground.css}; font-family: ${this._optionsService.rawOptions.fontFamily}; font-size: ${this._optionsService.rawOptions.fontSize}px; font-kerning: none; white-space: pre}`;t+=`${this._terminalSelector} .${g} .xterm-dim { color: ${d.color.multiplyOpacity(e.foreground,.5).css};}`,t+=`${this._terminalSelector} span:not(.xterm-bold) { font-weight: ${this._optionsService.rawOptions.fontWeight};}${this._terminalSelector} span.xterm-bold { font-weight: ${this._optionsService.rawOptions.fontWeightBold};}${this._terminalSelector} span.xterm-italic { font-style: italic;}`;const i=`blink_underline_${this._terminalClass}`,s=`blink_bar_${this._terminalClass}`,r=`blink_block_${this._terminalClass}`;t+=`@keyframes ${i} { 50% { border-bottom-style: hidden; }}`,t+=`@keyframes ${s} { 50% { box-shadow: none; }}`,t+=`@keyframes ${r} { 0% { background-color: ${e.cursor.css}; color: ${e.cursorAccent.css}; } 50% { background-color: inherit; color: ${e.cursor.css}; }}`,t+=`${this._terminalSelector} .${g}.${S} .xterm-cursor.xterm-cursor-blink.xterm-cursor-underline { animation: ${i} 1s step-end infinite;}${this._terminalSelector} .${g}.${S} .xterm-cursor.xterm-cursor-blink.xterm-cursor-bar { animation: ${s} 1s step-end infinite;}${this._terminalSelector} .${g}.${S} .xterm-cursor.xterm-cursor-blink.xterm-cursor-block { animation: ${r} 1s step-end infinite;}${this._terminalSelector} .${g} .xterm-cursor.xterm-cursor-block { background-color: ${e.cursor.css}; color: ${e.cursorAccent.css};}${this._terminalSelector} .${g} .xterm-cursor.xterm-cursor-block:not(.xterm-cursor-blink) { background-color: ${e.cursor.css} !important; color: ${e.cursorAccent.css} !important;}${this._terminalSelector} .${g} .xterm-cursor.xterm-cursor-outline { outline: 1px solid ${e.cursor.css}; outline-offset: -1px;}${this._terminalSelector} .${g} .xterm-cursor.xterm-cursor-bar { box-shadow: ${this._optionsService.rawOptions.cursorWidth}px 0 0 ${e.cursor.css} inset;}${this._terminalSelector} .${g} .xterm-cursor.xterm-cursor-underline { border-bottom: 1px ${e.cursor.css}; border-bottom-style: solid; height: calc(100% - 1px);}`,t+=`${this._terminalSelector} .${b} { position: absolute; top: 0; left: 0; z-index: 1; pointer-events: none;}${this._terminalSelector}.focus .${b} div { position: absolute; background-color: ${e.selectionBackgroundOpaque.css};}${this._terminalSelector} .${b} div { position: absolute; background-color: ${e.selectionInactiveBackgroundOpaque.css};}`;for(const[i,s]of e.ansi.entries())t+=`${this._terminalSelector} .${m}${i} { color: ${s.css}; }${this._terminalSelector} .${m}${i}.xterm-dim { color: ${d.color.multiplyOpacity(s,.5).css}; }${this._terminalSelector} .${v}${i} { background-color: ${s.css}; }`;t+=`${this._terminalSelector} .${m}${a.INVERTED_DEFAULT_COLOR} { color: ${d.color.opaque(e.background).css}; }${this._terminalSelector} .${m}${a.INVERTED_DEFAULT_COLOR}.xterm-dim { color: ${d.color.multiplyOpacity(d.color.opaque(e.background),.5).css}; }${this._terminalSelector} .${v}${a.INVERTED_DEFAULT_COLOR} { background-color: ${e.foreground.css}; }`,this._themeStyleElement.textContent=t}_setDefaultSpacing(){const e=this.dimensions.css.cell.width-this._widthCache.get("W",!1,!1);this._rowContainer.style.letterSpacing=`${e}px`,this._rowFactory.defaultSpacing=e}handleDevicePixelRatioChange(){this._updateDimensions(),this._widthCache.clear(),this._setDefaultSpacing()}_refreshRowElements(e,t){for(let e=this._rowElements.length;e<=t;e++){const e=this._document.createElement("div");this._rowContainer.appendChild(e),this._rowElements.push(e)}for(;this._rowElements.length>t;)this._rowContainer.removeChild(this._rowElements.pop())}handleResize(e,t){this._refreshRowElements(e,t),this._updateDimensions(),this.handleSelectionChanged(this._selectionRenderModel.selectionStart,this._selectionRenderModel.selectionEnd,this._selectionRenderModel.columnSelectMode)}handleCharSizeChanged(){this._updateDimensions(),this._widthCache.clear(),this._setDefaultSpacing()}handleBlur(){this._rowContainer.classList.remove(S),this.renderRows(0,this._bufferService.rows-1)}handleFocus(){this._rowContainer.classList.add(S),this.renderRows(this._bufferService.buffer.y,this._bufferService.buffer.y)}handleSelectionChanged(e,t,i){if(this._selectionContainer.replaceChildren(),this._rowFactory.handleSelectionChanged(e,t,i),this.renderRows(0,this._bufferService.rows-1),!e||!t)return;if(this._selectionRenderModel.update(this._terminal,e,t,i),!this._selectionRenderModel.hasSelection)return;const s=this._selectionRenderModel.viewportStartRow,r=this._selectionRenderModel.viewportEndRow,n=this._selectionRenderModel.viewportCappedStartRow,o=this._selectionRenderModel.viewportCappedEndRow,a=this._document.createDocumentFragment();if(i){const i=e[0]>t[0];a.appendChild(this._createSelectionElement(n,i?t[0]:e[0],i?e[0]:t[0],o-n+1))}else{const i=s===n?e[0]:0,l=n===r?t[0]:this._bufferService.cols;a.appendChild(this._createSelectionElement(n,i,l));const h=o-n-1;if(a.appendChild(this._createSelectionElement(n+1,0,this._bufferService.cols,h)),n!==o){const e=r===o?t[0]:this._bufferService.cols;a.appendChild(this._createSelectionElement(o,0,e))}}this._selectionContainer.appendChild(a)}_createSelectionElement(e,t,i,s=1){const r=this._document.createElement("div"),n=t*this.dimensions.css.cell.width;let o=this.dimensions.css.cell.width*(i-t);return n+o>this.dimensions.css.canvas.width&&(o=this.dimensions.css.canvas.width-n),r.style.height=s*this.dimensions.css.cell.height+"px",r.style.top=e*this.dimensions.css.cell.height+"px",r.style.left=`${n}px`,r.style.width=`${o}px`,r}handleCursorMove(){}_handleOptionsChanged(){this._updateDimensions(),this._injectCss(this._themeService.colors),this._widthCache.setFont(this._optionsService.rawOptions.fontFamily,this._optionsService.rawOptions.fontSize,this._optionsService.rawOptions.fontWeight,this._optionsService.rawOptions.fontWeightBold),this._setDefaultSpacing()}clear(){for(const e of this._rowElements)e.replaceChildren()}renderRows(e,t){const i=this._bufferService.buffer,s=i.ybase+i.y,r=Math.min(i.x,this._bufferService.cols-1),n=this._coreService.decPrivateModes.cursorBlink??this._optionsService.rawOptions.cursorBlink,o=this._coreService.decPrivateModes.cursorStyle??this._optionsService.rawOptions.cursorStyle,a=this._optionsService.rawOptions.cursorInactiveStyle;for(let l=e;l<=t;l++){const e=l+i.ydisp,t=this._rowElements[l],h=i.lines.get(e);if(!t||!h)break;t.replaceChildren(...this._rowFactory.createRow(h,e,e===s,o,a,r,n,this.dimensions.css.cell.width,this._widthCache,-1,-1))}}get _terminalSelector(){return`.${p}${this._terminalClass}`}_handleLinkHover(e){this._setCellUnderline(e.x1,e.x2,e.y1,e.y2,e.cols,!0)}_handleLinkLeave(e){this._setCellUnderline(e.x1,e.x2,e.y1,e.y2,e.cols,!1)}_setCellUnderline(e,t,i,s,r,n){i<0&&(e=0),s<0&&(t=0);const o=this._bufferService.rows-1;i=Math.max(Math.min(i,o),0),s=Math.max(Math.min(s,o),0),r=Math.min(r,this._bufferService.cols);const a=this._bufferService.buffer,l=a.ybase+a.y,h=Math.min(a.x,r-1),c=this._optionsService.rawOptions.cursorBlink,d=this._optionsService.rawOptions.cursorStyle,u=this._optionsService.rawOptions.cursorInactiveStyle;for(let o=i;o<=s;++o){const _=o+a.ydisp,f=this._rowElements[o],p=a.lines.get(_);if(!f||!p)break;f.replaceChildren(...this._rowFactory.createRow(p,_,_===l,d,u,h,c,this.dimensions.css.cell.width,this._widthCache,n?o===i?e:0:-1,n?(o===s?t:r)-1:-1))}}};t.DomRenderer=y,t.DomRenderer=y=s([r(7,_.IInstantiationService),r(8,c.ICharSizeService),r(9,_.IOptionsService),r(10,_.IBufferService),r(11,_.ICoreService),r(12,c.ICoreBrowserService),r(13,c.IThemeService)],y)},1433:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.DomRendererRowFactory=void 0;const n=i(9176),o=i(8938),a=i(3055),l=i(6501),h=i(4103),c=i(7098),d=i(945),u=i(6181),_=i(5451);let f=class{constructor(e,t,i,s,r,n,o){this._document=e,this._characterJoinerService=t,this._optionsService=i,this._coreBrowserService=s,this._coreService=r,this._decorationService=n,this._themeService=o,this._workCell=new a.CellData,this._columnSelectMode=!1,this.defaultSpacing=0}handleSelectionChanged(e,t,i){this._selectionStart=e,this._selectionEnd=t,this._columnSelectMode=i}createRow(e,t,i,s,r,a,l,c,u,f,g){const m=[],v=this._characterJoinerService.getJoinedCharacters(t),S=this._themeService.colors;let b,C=e.getNoBgTrimmedLength();i&&C<a+1&&(C=a+1);let y=0,w="",E=0,D=0,L=0,R=0,A=!1,T=0,k=!1,M=0,O=0;const I=[],P=-1!==f&&-1!==g;for(let x=0;x<C;x++){e.loadCell(x,this._workCell);let C=this._workCell.getWidth();if(0===C)continue;let B=!1,N=x>=O,U=x,F=this._workCell;if(v.length>0&&x===v[0][0]&&N){const s=v.shift(),r=this._isCellInSelection(s[0],t);for(E=s[0]+1;E<s[1];E++)N&&=r===this._isCellInSelection(E,t);N&&=!i||a<s[0]||a>=s[1],N?(B=!0,F=new d.JoinedCellData(this._workCell,e.translateToString(!0,s[0],s[1]),s[1]-s[0]),U=s[1]-1,C=F.getWidth()):O=s[1]}const W=this._isCellInSelection(x,t),H=i&&x===a,K=P&&x>=f&&x<=g;let z=!1;this._decorationService.forEachDecorationAtCell(x,t,void 0,(e=>{z=!0}));let j=F.getChars()||o.WHITESPACE_CELL_CHAR;if(" "===j&&(F.isUnderline()||F.isOverline())&&(j=" "),M=C*c-u.get(j,F.isBold(),F.isItalic()),b){if(y&&(W&&k||!W&&!k&&F.bg===D)&&(W&&k&&S.selectionForeground||F.fg===L)&&F.extended.ext===R&&K===A&&M===T&&!H&&!B&&!z&&N){F.isInvisible()?w+=o.WHITESPACE_CELL_CHAR:w+=j,y++;continue}y&&(b.textContent=w),b=this._document.createElement("span"),y=0,w=""}else b=this._document.createElement("span");if(D=F.bg,L=F.fg,R=F.extended.ext,A=K,T=M,k=W,B&&a>=x&&a<=U&&(a=x),!this._coreService.isCursorHidden&&H&&this._coreService.isCursorInitialized)if(I.push("xterm-cursor"),this._coreBrowserService.isFocused)l&&I.push("xterm-cursor-blink"),I.push("bar"===s?"xterm-cursor-bar":"underline"===s?"xterm-cursor-underline":"xterm-cursor-block");else if(r)switch(r){case"outline":I.push("xterm-cursor-outline");break;case"block":I.push("xterm-cursor-block");break;case"bar":I.push("xterm-cursor-bar");break;case"underline":I.push("xterm-cursor-underline")}if(F.isBold()&&I.push("xterm-bold"),F.isItalic()&&I.push("xterm-italic"),F.isDim()&&I.push("xterm-dim"),w=F.isInvisible()?o.WHITESPACE_CELL_CHAR:F.getChars()||o.WHITESPACE_CELL_CHAR,F.isUnderline()&&(I.push(`xterm-underline-${F.extended.underlineStyle}`)," "===w&&(w=" "),!F.isUnderlineColorDefault()))if(F.isUnderlineColorRGB())b.style.textDecorationColor=`rgb(${_.AttributeData.toColorRGB(F.getUnderlineColor()).join(",")})`;else{let e=F.getUnderlineColor();this._optionsService.rawOptions.drawBoldTextInBrightColors&&F.isBold()&&e<8&&(e+=8),b.style.textDecorationColor=S.ansi[e].css}F.isOverline()&&(I.push("xterm-overline")," "===w&&(w=" ")),F.isStrikethrough()&&I.push("xterm-strikethrough"),K&&(b.style.textDecoration="underline");let $=F.getFgColor(),V=F.getFgColorMode(),G=F.getBgColor(),q=F.getBgColorMode();const X=!!F.isInverse();if(X){const e=$;$=G,G=e;const t=V;V=q,q=t}let Y,Z,J,Q=!1;switch(this._decorationService.forEachDecorationAtCell(x,t,void 0,(e=>{"top"!==e.options.layer&&Q||(e.backgroundColorRGB&&(q=50331648,G=e.backgroundColorRGB.rgba>>8&16777215,Y=e.backgroundColorRGB),e.foregroundColorRGB&&(V=50331648,$=e.foregroundColorRGB.rgba>>8&16777215,Z=e.foregroundColorRGB),Q="top"===e.options.layer)})),!Q&&W&&(Y=this._coreBrowserService.isFocused?S.selectionBackgroundOpaque:S.selectionInactiveBackgroundOpaque,G=Y.rgba>>8&16777215,q=50331648,Q=!0,S.selectionForeground&&(V=50331648,$=S.selectionForeground.rgba>>8&16777215,Z=S.selectionForeground)),Q&&I.push("xterm-decoration-top"),q){case 16777216:case 33554432:J=S.ansi[G],I.push(`xterm-bg-${G}`);break;case 50331648:J=h.channels.toColor(G>>16,G>>8&255,255&G),this._addStyle(b,`background-color:#${p((G>>>0).toString(16),"0",6)}`);break;default:X?(J=S.foreground,I.push(`xterm-bg-${n.INVERTED_DEFAULT_COLOR}`)):J=S.background}switch(Y||F.isDim()&&(Y=h.color.multiplyOpacity(J,.5)),V){case 16777216:case 33554432:F.isBold()&&$<8&&this._optionsService.rawOptions.drawBoldTextInBrightColors&&($+=8),this._applyMinimumContrast(b,J,S.ansi[$],F,Y,void 0)||I.push(`xterm-fg-${$}`);break;case 50331648:const e=h.channels.toColor($>>16&255,$>>8&255,255&$);this._applyMinimumContrast(b,J,e,F,Y,Z)||this._addStyle(b,`color:#${p($.toString(16),"0",6)}`);break;default:this._applyMinimumContrast(b,J,S.foreground,F,Y,Z)||X&&I.push(`xterm-fg-${n.INVERTED_DEFAULT_COLOR}`)}I.length&&(b.className=I.join(" "),I.length=0),H||B||z||!N?b.textContent=w:y++,M!==this.defaultSpacing&&(b.style.letterSpacing=`${M}px`),m.push(b),x=U}return b&&y&&(b.textContent=w),m}_applyMinimumContrast(e,t,i,s,r,n){if(1===this._optionsService.rawOptions.minimumContrastRatio||(0,u.treatGlyphAsBackgroundColor)(s.getCode()))return!1;const o=this._getContrastCache(s);let a;if(r||n||(a=o.getColor(t.rgba,i.rgba)),void 0===a){const e=this._optionsService.rawOptions.minimumContrastRatio/(s.isDim()?2:1);a=h.color.ensureContrastRatio(r||t,n||i,e),o.setColor((r||t).rgba,(n||i).rgba,a??null)}return!!a&&(this._addStyle(e,`color:${a.css}`),!0)}_getContrastCache(e){return e.isDim()?this._themeService.colors.halfContrastCache:this._themeService.colors.contrastCache}_addStyle(e,t){e.setAttribute("style",`${e.getAttribute("style")||""}${t};`)}_isCellInSelection(e,t){const i=this._selectionStart,s=this._selectionEnd;return!(!i||!s)&&(this._columnSelectMode?i[0]<=s[0]?e>=i[0]&&t>=i[1]&&e<s[0]&&t<=s[1]:e<i[0]&&t>=i[1]&&e>=s[0]&&t<=s[1]:t>i[1]&&t<s[1]||i[1]===s[1]&&t===i[1]&&e>=i[0]&&e<s[0]||i[1]<s[1]&&t===s[1]&&e<s[0]||i[1]<s[1]&&t===i[1]&&e>=i[0])}};function p(e,t,i){for(;e.length<i;)e=t+e;return e}t.DomRendererRowFactory=f,t.DomRendererRowFactory=f=s([r(1,c.ICharacterJoinerService),r(2,l.IOptionsService),r(3,c.ICoreBrowserService),r(4,l.ICoreService),r(5,l.IDecorationService),r(6,c.IThemeService)],f)},2744:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.WidthCache=void 0,t.WidthCache=class{constructor(e,t){this._flat=new Float32Array(256),this._font="",this._fontSize=0,this._weight="normal",this._weightBold="bold",this._measureElements=[],this._container=e.createElement("div"),this._container.classList.add("xterm-width-cache-measure-container"),this._container.setAttribute("aria-hidden","true"),this._container.style.whiteSpace="pre",this._container.style.fontKerning="none";const i=e.createElement("span");i.classList.add("xterm-char-measure-element");const s=e.createElement("span");s.classList.add("xterm-char-measure-element"),s.style.fontWeight="bold";const r=e.createElement("span");r.classList.add("xterm-char-measure-element"),r.style.fontStyle="italic";const n=e.createElement("span");n.classList.add("xterm-char-measure-element"),n.style.fontWeight="bold",n.style.fontStyle="italic",this._measureElements=[i,s,r,n],this._container.appendChild(i),this._container.appendChild(s),this._container.appendChild(r),this._container.appendChild(n),t.appendChild(this._container),this.clear()}dispose(){this._container.remove(),this._measureElements.length=0,this._holey=void 0}clear(){this._flat.fill(-9999),this._holey=new Map}setFont(e,t,i,s){e===this._font&&t===this._fontSize&&i===this._weight&&s===this._weightBold||(this._font=e,this._fontSize=t,this._weight=i,this._weightBold=s,this._container.style.fontFamily=this._font,this._container.style.fontSize=`${this._fontSize}px`,this._measureElements[0].style.fontWeight=`${i}`,this._measureElements[1].style.fontWeight=`${s}`,this._measureElements[2].style.fontWeight=`${i}`,this._measureElements[3].style.fontWeight=`${s}`,this.clear())}get(e,t,i){let s=0;if(!t&&!i&&1===e.length&&(s=e.charCodeAt(0))<256){if(-9999!==this._flat[s])return this._flat[s];const t=this._measure(e,0);return t>0&&(this._flat[s]=t),t}let r=e;t&&(r+="B"),i&&(r+="I");let n=this._holey.get(r);if(void 0===n){let s=0;t&&(s|=1),i&&(s|=2),n=this._measure(e,s),n>0&&this._holey.set(r,n)}return n}_measure(e,t){const i=this._measureElements[t];return i.textContent=e.repeat(32),i.offsetWidth/32}}},9176:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.INVERTED_DEFAULT_COLOR=void 0,t.INVERTED_DEFAULT_COLOR=257},6181:(e,t)=>{function i(e){return 57508<=e&&e<=57558}function s(e){return e>=128512&&e<=128591||e>=127744&&e<=128511||e>=128640&&e<=128767||e>=9728&&e<=9983||e>=9984&&e<=10175||e>=65024&&e<=65039||e>=129280&&e<=129535||e>=127462&&e<=127487}Object.defineProperty(t,"__esModule",{value:!0}),t.throwIfFalsy=function(e){if(!e)throw new Error("value must not be falsy");return e},t.isPowerlineGlyph=i,t.isRestrictedPowerlineGlyph=function(e){return 57520<=e&&e<=57527},t.isEmoji=s,t.allowRescaling=function(e,t,r,n){return 1===t&&r>Math.ceil(1.5*n)&&void 0!==e&&e>255&&!s(e)&&!i(e)&&!function(e){return 57344<=e&&e<=63743}(e)},t.treatGlyphAsBackgroundColor=function(e){return i(e)||function(e){return 9472<=e&&e<=9631}(e)},t.createRenderDimensions=function(){return{css:{canvas:{width:0,height:0},cell:{width:0,height:0}},device:{canvas:{width:0,height:0},cell:{width:0,height:0},char:{width:0,height:0,left:0,top:0}}}},t.computeNextVariantOffset=function(e,t,i=0){return(e-(2*Math.round(t)-i))%(2*Math.round(t))}},2274:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.createSelectionRenderModel=function(){return new i};class i{constructor(){this.clear()}clear(){this.hasSelection=!1,this.columnSelectMode=!1,this.viewportStartRow=0,this.viewportEndRow=0,this.viewportCappedStartRow=0,this.viewportCappedEndRow=0,this.startCol=0,this.endCol=0,this.selectionStart=void 0,this.selectionEnd=void 0}update(e,t,i,s=!1){if(this.selectionStart=t,this.selectionEnd=i,!t||!i||t[0]===i[0]&&t[1]===i[1])return void this.clear();const r=e.buffers.active.ydisp,n=t[1]-r,o=i[1]-r,a=Math.max(n,0),l=Math.min(o,e.rows-1);a>=e.rows||l<0?this.clear():(this.hasSelection=!0,this.columnSelectMode=s,this.viewportStartRow=n,this.viewportEndRow=o,this.viewportCappedStartRow=a,this.viewportCappedEndRow=l,this.startCol=t[0],this.endCol=i[0])}isCellSelected(e,t,i){return!!this.hasSelection&&(i-=e.buffer.active.viewportY,this.columnSelectMode?this.startCol<=this.endCol?t>=this.startCol&&i>=this.viewportCappedStartRow&&t<this.endCol&&i<=this.viewportCappedEndRow:t<this.startCol&&i>=this.viewportCappedStartRow&&t>=this.endCol&&i<=this.viewportCappedEndRow:i>this.viewportStartRow&&i<this.viewportEndRow||this.viewportStartRow===this.viewportEndRow&&i===this.viewportStartRow&&t>=this.startCol&&t<this.endCol||this.viewportStartRow<this.viewportEndRow&&i===this.viewportEndRow&&t<this.endCol||this.viewportStartRow<this.viewportEndRow&&i===this.viewportStartRow&&t>=this.startCol)}}},5959:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.SelectionModel=void 0,t.SelectionModel=class{constructor(e){this._bufferService=e,this.isSelectAllActive=!1,this.selectionStartLength=0}clearSelection(){this.selectionStart=void 0,this.selectionEnd=void 0,this.isSelectAllActive=!1,this.selectionStartLength=0}get finalSelectionStart(){return this.isSelectAllActive?[0,0]:this.selectionEnd&&this.selectionStart&&this.areSelectionValuesReversed()?this.selectionEnd:this.selectionStart}get finalSelectionEnd(){if(this.isSelectAllActive)return[this._bufferService.cols,this._bufferService.buffer.ybase+this._bufferService.rows-1];if(this.selectionStart){if(!this.selectionEnd||this.areSelectionValuesReversed()){const e=this.selectionStart[0]+this.selectionStartLength;return e>this._bufferService.cols?e%this._bufferService.cols==0?[this._bufferService.cols,this.selectionStart[1]+Math.floor(e/this._bufferService.cols)-1]:[e%this._bufferService.cols,this.selectionStart[1]+Math.floor(e/this._bufferService.cols)]:[e,this.selectionStart[1]]}if(this.selectionStartLength&&this.selectionEnd[1]===this.selectionStart[1]){const e=this.selectionStart[0]+this.selectionStartLength;return e>this._bufferService.cols?[e%this._bufferService.cols,this.selectionStart[1]+Math.floor(e/this._bufferService.cols)]:[Math.max(e,this.selectionEnd[0]),this.selectionEnd[1]]}return this.selectionEnd}}areSelectionValuesReversed(){const e=this.selectionStart,t=this.selectionEnd;return!(!e||!t)&&(e[1]>t[1]||e[1]===t[1]&&e[0]>t[0])}handleTrim(e){return this.selectionStart&&(this.selectionStart[1]-=e),this.selectionEnd&&(this.selectionEnd[1]-=e),this.selectionEnd&&this.selectionEnd[1]<0?(this.clearSelection(),!0):(this.selectionStart&&this.selectionStart[1]<0&&(this.selectionStart[1]=0),!1)}}},4792:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CharSizeService=void 0;const n=i(6501),o=i(7150),a=i(802);let l=class extends o.Disposable{get hasValidSize(){return this.width>0&&this.height>0}constructor(e,t,i){super(),this._optionsService=i,this.width=0,this.height=0,this._onCharSizeChange=this._register(new a.Emitter),this.onCharSizeChange=this._onCharSizeChange.event;try{this._measureStrategy=this._register(new d(this._optionsService))}catch{this._measureStrategy=this._register(new c(e,t,this._optionsService))}this._register(this._optionsService.onMultipleOptionChange(["fontFamily","fontSize"],(()=>this.measure())))}measure(){const e=this._measureStrategy.measure();e.width===this.width&&e.height===this.height||(this.width=e.width,this.height=e.height,this._onCharSizeChange.fire())}};t.CharSizeService=l,t.CharSizeService=l=s([r(2,n.IOptionsService)],l);class h extends o.Disposable{constructor(){super(...arguments),this._result={width:0,height:0}}_validateAndSet(e,t){void 0!==e&&e>0&&void 0!==t&&t>0&&(this._result.width=e,this._result.height=t)}}class c extends h{constructor(e,t,i){super(),this._document=e,this._parentElement=t,this._optionsService=i,this._measureElement=this._document.createElement("span"),this._measureElement.classList.add("xterm-char-measure-element"),this._measureElement.textContent="W".repeat(32),this._measureElement.setAttribute("aria-hidden","true"),this._measureElement.style.whiteSpace="pre",this._measureElement.style.fontKerning="none",this._parentElement.appendChild(this._measureElement)}measure(){return this._measureElement.style.fontFamily=this._optionsService.rawOptions.fontFamily,this._measureElement.style.fontSize=`${this._optionsService.rawOptions.fontSize}px`,this._validateAndSet(Number(this._measureElement.offsetWidth)/32,Number(this._measureElement.offsetHeight)),this._result}}class d extends h{constructor(e){super(),this._optionsService=e,this._canvas=new OffscreenCanvas(100,100),this._ctx=this._canvas.getContext("2d");const t=this._ctx.measureText("W");if(!("width"in t&&"fontBoundingBoxAscent"in t&&"fontBoundingBoxDescent"in t))throw new Error("Required font metrics not supported")}measure(){this._ctx.font=`${this._optionsService.rawOptions.fontSize}px ${this._optionsService.rawOptions.fontFamily}`;const e=this._ctx.measureText("W");return this._validateAndSet(e.width,e.fontBoundingBoxAscent+e.fontBoundingBoxDescent),this._result}}},945:function(e,t,i){var s,r=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},n=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CharacterJoinerService=t.JoinedCellData=void 0;const o=i(5451),a=i(8938),l=i(3055),h=i(6501);class c extends o.AttributeData{constructor(e,t,i){super(),this.content=0,this.combinedData="",this.fg=e.fg,this.bg=e.bg,this.combinedData=t,this._width=i}isCombined(){return 2097152}getWidth(){return this._width}getChars(){return this.combinedData}getCode(){return 2097151}setFromCharData(e){throw new Error("not implemented")}getAsCharData(){return[this.fg,this.getChars(),this.getWidth(),this.getCode()]}}t.JoinedCellData=c;let d=s=class{constructor(e){this._bufferService=e,this._characterJoiners=[],this._nextCharacterJoinerId=0,this._workCell=new l.CellData}register(e){const t={id:this._nextCharacterJoinerId++,handler:e};return this._characterJoiners.push(t),t.id}deregister(e){for(let t=0;t<this._characterJoiners.length;t++)if(this._characterJoiners[t].id===e)return this._characterJoiners.splice(t,1),!0;return!1}getJoinedCharacters(e){if(0===this._characterJoiners.length)return[];const t=this._bufferService.buffer.lines.get(e);if(!t||0===t.length)return[];const i=[],s=t.translateToString(!0);let r=0,n=0,o=0,l=t.getFg(0),h=t.getBg(0);for(let e=0;e<t.getTrimmedLength();e++)if(t.loadCell(e,this._workCell),0!==this._workCell.getWidth()){if(this._workCell.fg!==l||this._workCell.bg!==h){if(e-r>1){const e=this._getJoinedRanges(s,o,n,t,r);for(let t=0;t<e.length;t++)i.push(e[t])}r=e,o=n,l=this._workCell.fg,h=this._workCell.bg}n+=this._workCell.getChars().length||a.WHITESPACE_CELL_CHAR.length}if(this._bufferService.cols-r>1){const e=this._getJoinedRanges(s,o,n,t,r);for(let t=0;t<e.length;t++)i.push(e[t])}return i}_getJoinedRanges(e,t,i,r,n){const o=e.substring(t,i);let a=[];try{a=this._characterJoiners[0].handler(o)}catch(e){console.error(e)}for(let e=1;e<this._characterJoiners.length;e++)try{const t=this._characterJoiners[e].handler(o);for(let e=0;e<t.length;e++)s._mergeRanges(a,t[e])}catch(e){console.error(e)}return this._stringRangesToCellRanges(a,r,n),a}_stringRangesToCellRanges(e,t,i){let s=0,r=!1,n=0,o=e[s];if(o){for(let l=i;l<this._bufferService.cols;l++){const i=t.getWidth(l),h=t.getString(l).length||a.WHITESPACE_CELL_CHAR.length;if(0!==i){if(!r&&o[0]<=n&&(o[0]=l,r=!0),o[1]<=n){if(o[1]=l,o=e[++s],!o)break;o[0]<=n?(o[0]=l,r=!0):r=!1}n+=h}}o&&(o[1]=this._bufferService.cols)}}static _mergeRanges(e,t){let i=!1;for(let s=0;s<e.length;s++){const r=e[s];if(i){if(t[1]<=r[0])return e[s-1][1]=t[1],e;if(t[1]<=r[1])return e[s-1][1]=Math.max(t[1],r[1]),e.splice(s,1),e;e.splice(s,1),s--}else{if(t[1]<=r[0])return e.splice(s,0,t),e;if(t[1]<=r[1])return r[0]=Math.min(t[0],r[0]),e;t[0]<r[1]&&(r[0]=Math.min(t[0],r[0]),i=!0)}}return i?e[e.length-1][1]=t[1]:e.push(t),e}};t.CharacterJoinerService=d,t.CharacterJoinerService=d=s=r([n(0,h.IBufferService)],d)},9574:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.CoreBrowserService=void 0;const s=i(802),r=i(7093),n=i(7150);class o extends n.Disposable{constructor(e,t,i){super(),this._textarea=e,this._window=t,this.mainDocument=i,this._isFocused=!1,this._cachedIsFocused=void 0,this._screenDprMonitor=this._register(new a(this._window)),this._onDprChange=this._register(new s.Emitter),this.onDprChange=this._onDprChange.event,this._onWindowChange=this._register(new s.Emitter),this.onWindowChange=this._onWindowChange.event,this._register(this.onWindowChange((e=>this._screenDprMonitor.setWindow(e)))),this._register(s.Event.forward(this._screenDprMonitor.onDprChange,this._onDprChange)),this._register((0,r.addDisposableListener)(this._textarea,"focus",(()=>this._isFocused=!0))),this._register((0,r.addDisposableListener)(this._textarea,"blur",(()=>this._isFocused=!1)))}get window(){return this._window}set window(e){this._window!==e&&(this._window=e,this._onWindowChange.fire(this._window))}get dpr(){return this.window.devicePixelRatio}get isFocused(){return void 0===this._cachedIsFocused&&(this._cachedIsFocused=this._isFocused&&this._textarea.ownerDocument.hasFocus(),queueMicrotask((()=>this._cachedIsFocused=void 0))),this._cachedIsFocused}}t.CoreBrowserService=o;class a extends n.Disposable{constructor(e){super(),this._parentWindow=e,this._windowResizeListener=this._register(new n.MutableDisposable),this._onDprChange=this._register(new s.Emitter),this.onDprChange=this._onDprChange.event,this._outerListener=()=>this._setDprAndFireIfDiffers(),this._currentDevicePixelRatio=this._parentWindow.devicePixelRatio,this._updateDpr(),this._setWindowResizeListener(),this._register((0,n.toDisposable)((()=>this.clearListener())))}setWindow(e){this._parentWindow=e,this._setWindowResizeListener(),this._setDprAndFireIfDiffers()}_setWindowResizeListener(){this._windowResizeListener.value=(0,r.addDisposableListener)(this._parentWindow,"resize",(()=>this._setDprAndFireIfDiffers()))}_setDprAndFireIfDiffers(){this._parentWindow.devicePixelRatio!==this._currentDevicePixelRatio&&this._onDprChange.fire(this._parentWindow.devicePixelRatio),this._updateDpr()}_updateDpr(){this._outerListener&&(this._resolutionMediaMatchList?.removeListener(this._outerListener),this._currentDevicePixelRatio=this._parentWindow.devicePixelRatio,this._resolutionMediaMatchList=this._parentWindow.matchMedia(`screen and (resolution: ${this._parentWindow.devicePixelRatio}dppx)`),this._resolutionMediaMatchList.addListener(this._outerListener))}clearListener(){this._resolutionMediaMatchList&&this._outerListener&&(this._resolutionMediaMatchList.removeListener(this._outerListener),this._resolutionMediaMatchList=void 0,this._outerListener=void 0)}}},9820:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.LinkProviderService=void 0;const s=i(7150);class r extends s.Disposable{constructor(){super(),this.linkProviders=[],this._register((0,s.toDisposable)((()=>this.linkProviders.length=0)))}registerLinkProvider(e){return this.linkProviders.push(e),{dispose:()=>{const t=this.linkProviders.indexOf(e);-1!==t&&this.linkProviders.splice(t,1)}}}}t.LinkProviderService=r},9784:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.MouseService=void 0;const n=i(7098),o=i(5251);let a=class{constructor(e,t){this._renderService=e,this._charSizeService=t}getCoords(e,t,i,s,r){return(0,o.getCoords)(window,e,t,i,s,this._charSizeService.hasValidSize,this._renderService.dimensions.css.cell.width,this._renderService.dimensions.css.cell.height,r)}getMouseReportCoords(e,t){const i=(0,o.getCoordsRelativeToElement)(window,e,t);if(this._charSizeService.hasValidSize)return i[0]=Math.min(Math.max(i[0],0),this._renderService.dimensions.css.canvas.width-1),i[1]=Math.min(Math.max(i[1],0),this._renderService.dimensions.css.canvas.height-1),{col:Math.floor(i[0]/this._renderService.dimensions.css.cell.width),row:Math.floor(i[1]/this._renderService.dimensions.css.cell.height),x:Math.floor(i[0]),y:Math.floor(i[1])}}};t.MouseService=a,t.MouseService=a=s([r(0,n.IRenderService),r(1,n.ICharSizeService)],a)},5783:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.RenderService=void 0;const n=i(4852),o=i(7098),a=i(7150),l=i(6168),h=i(6501),c=i(802);let d=class extends a.Disposable{get dimensions(){return this._renderer.value.dimensions}constructor(e,t,i,s,r,o,h,d,_){super(),this._rowCount=e,this._optionsService=i,this._charSizeService=s,this._coreService=r,this._coreBrowserService=d,this._renderer=this._register(new a.MutableDisposable),this._pausedResizeTask=new l.DebouncedIdleTask,this._observerDisposable=this._register(new a.MutableDisposable),this._isPaused=!1,this._needsFullRefresh=!1,this._isNextRenderRedrawOnly=!0,this._needsSelectionRefresh=!1,this._canvasWidth=0,this._canvasHeight=0,this._selectionState={start:void 0,end:void 0,columnSelectMode:!1},this._onDimensionsChange=this._register(new c.Emitter),this.onDimensionsChange=this._onDimensionsChange.event,this._onRenderedViewportChange=this._register(new c.Emitter),this.onRenderedViewportChange=this._onRenderedViewportChange.event,this._onRender=this._register(new c.Emitter),this.onRender=this._onRender.event,this._onRefreshRequest=this._register(new c.Emitter),this.onRefreshRequest=this._onRefreshRequest.event,this._renderDebouncer=new n.RenderDebouncer(((e,t)=>this._renderRows(e,t)),this._coreBrowserService),this._register(this._renderDebouncer),this._syncOutputHandler=new u(this._coreBrowserService,this._coreService,(()=>this._fullRefresh())),this._register((0,a.toDisposable)((()=>this._syncOutputHandler.dispose()))),this._register(this._coreBrowserService.onDprChange((()=>this.handleDevicePixelRatioChange()))),this._register(h.onResize((()=>this._fullRefresh()))),this._register(h.buffers.onBufferActivate((()=>this._renderer.value?.clear()))),this._register(this._optionsService.onOptionChange((()=>this._handleOptionsChanged()))),this._register(this._charSizeService.onCharSizeChange((()=>this.handleCharSizeChanged()))),this._register(o.onDecorationRegistered((()=>this._fullRefresh()))),this._register(o.onDecorationRemoved((()=>this._fullRefresh()))),this._register(this._optionsService.onMultipleOptionChange(["customGlyphs","drawBoldTextInBrightColors","letterSpacing","lineHeight","fontFamily","fontSize","fontWeight","fontWeightBold","minimumContrastRatio","rescaleOverlappingGlyphs"],(()=>{this.clear(),this.handleResize(h.cols,h.rows),this._fullRefresh()}))),this._register(this._optionsService.onMultipleOptionChange(["cursorBlink","cursorStyle"],(()=>this.refreshRows(h.buffer.y,h.buffer.y,!0)))),this._register(_.onChangeColors((()=>this._fullRefresh()))),this._registerIntersectionObserver(this._coreBrowserService.window,t),this._register(this._coreBrowserService.onWindowChange((e=>this._registerIntersectionObserver(e,t))))}_registerIntersectionObserver(e,t){if("IntersectionObserver"in e){const i=new e.IntersectionObserver((e=>this._handleIntersectionChange(e[e.length-1])),{threshold:0});i.observe(t),this._observerDisposable.value=(0,a.toDisposable)((()=>i.disconnect()))}}_handleIntersectionChange(e){this._isPaused=void 0===e.isIntersecting?0===e.intersectionRatio:!e.isIntersecting,this._isPaused||this._charSizeService.hasValidSize||this._charSizeService.measure(),!this._isPaused&&this._needsFullRefresh&&(this._pausedResizeTask.flush(),this.refreshRows(0,this._rowCount-1),this._needsFullRefresh=!1)}refreshRows(e,t,i=!1){if(this._isPaused)return void(this._needsFullRefresh=!0);if(this._coreService.decPrivateModes.synchronizedOutput)return void this._syncOutputHandler.bufferRows(e,t);const s=this._syncOutputHandler.flush();s&&(e=Math.min(e,s.start),t=Math.max(t,s.end)),i||(this._isNextRenderRedrawOnly=!1),this._renderDebouncer.refresh(e,t,this._rowCount)}_renderRows(e,t){this._renderer.value&&(this._coreService.decPrivateModes.synchronizedOutput?this._syncOutputHandler.bufferRows(e,t):(e=Math.min(e,this._rowCount-1),t=Math.min(t,this._rowCount-1),this._renderer.value.renderRows(e,t),this._needsSelectionRefresh&&(this._renderer.value.handleSelectionChanged(this._selectionState.start,this._selectionState.end,this._selectionState.columnSelectMode),this._needsSelectionRefresh=!1),this._isNextRenderRedrawOnly||this._onRenderedViewportChange.fire({start:e,end:t}),this._onRender.fire({start:e,end:t}),this._isNextRenderRedrawOnly=!0))}resize(e,t){this._rowCount=t,this._fireOnCanvasResize()}_handleOptionsChanged(){this._renderer.value&&(this.refreshRows(0,this._rowCount-1),this._fireOnCanvasResize())}_fireOnCanvasResize(){this._renderer.value&&(this._renderer.value.dimensions.css.canvas.width===this._canvasWidth&&this._renderer.value.dimensions.css.canvas.height===this._canvasHeight||this._onDimensionsChange.fire(this._renderer.value.dimensions))}hasRenderer(){return!!this._renderer.value}setRenderer(e){this._renderer.value=e,this._renderer.value&&(this._renderer.value.onRequestRedraw((e=>this.refreshRows(e.start,e.end,!0))),this._needsSelectionRefresh=!0,this._fullRefresh())}addRefreshCallback(e){return this._renderDebouncer.addRefreshCallback(e)}_fullRefresh(){this._isPaused?this._needsFullRefresh=!0:this.refreshRows(0,this._rowCount-1)}clearTextureAtlas(){this._renderer.value&&(this._renderer.value.clearTextureAtlas?.(),this._fullRefresh())}handleDevicePixelRatioChange(){this._charSizeService.measure(),this._renderer.value&&(this._renderer.value.handleDevicePixelRatioChange(),this.refreshRows(0,this._rowCount-1))}handleResize(e,t){this._renderer.value&&(this._isPaused?this._pausedResizeTask.set((()=>this._renderer.value?.handleResize(e,t))):this._renderer.value.handleResize(e,t),this._fullRefresh())}handleCharSizeChanged(){this._renderer.value?.handleCharSizeChanged()}handleBlur(){this._renderer.value?.handleBlur()}handleFocus(){this._renderer.value?.handleFocus()}handleSelectionChanged(e,t,i){this._selectionState.start=e,this._selectionState.end=t,this._selectionState.columnSelectMode=i,this._renderer.value?.handleSelectionChanged(e,t,i)}handleCursorMove(){this._renderer.value?.handleCursorMove()}clear(){this._renderer.value?.clear()}};t.RenderService=d,t.RenderService=d=s([r(2,h.IOptionsService),r(3,o.ICharSizeService),r(4,h.ICoreService),r(5,h.IDecorationService),r(6,h.IBufferService),r(7,o.ICoreBrowserService),r(8,o.IThemeService)],d);class u{constructor(e,t,i){this._coreBrowserService=e,this._coreService=t,this._onTimeout=i,this._start=0,this._end=0,this._isBuffering=!1}bufferRows(e,t){this._isBuffering?(this._start=Math.min(this._start,e),this._end=Math.max(this._end,t)):(this._start=e,this._end=t,this._isBuffering=!0),void 0===this._timeout&&(this._timeout=this._coreBrowserService.window.setTimeout((()=>{this._timeout=void 0,this._coreService.decPrivateModes.synchronizedOutput=!1,this._onTimeout()}),1e3))}flush(){if(void 0!==this._timeout&&(this._coreBrowserService.window.clearTimeout(this._timeout),this._timeout=void 0),!this._isBuffering)return;const e={start:this._start,end:this._end};return this._isBuffering=!1,e}dispose(){void 0!==this._timeout&&(this._coreBrowserService.window.clearTimeout(this._timeout),this._timeout=void 0)}}},2079:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.SelectionService=void 0;const n=i(5251),o=i(9686),a=i(5959),l=i(7098),h=i(7150),c=i(701),d=i(9384),u=i(3055),_=i(6501),f=i(802),p=String.fromCharCode(160),g=new RegExp(p,"g");let m=class extends h.Disposable{constructor(e,t,i,s,r,n,o,l,c){super(),this._element=e,this._screenElement=t,this._linkifier=i,this._bufferService=s,this._coreService=r,this._mouseService=n,this._optionsService=o,this._renderService=l,this._coreBrowserService=c,this._dragScrollAmount=0,this._enabled=!0,this._workCell=new u.CellData,this._mouseDownTimeStamp=0,this._oldHasSelection=!1,this._oldSelectionStart=void 0,this._oldSelectionEnd=void 0,this._onLinuxMouseSelection=this._register(new f.Emitter),this.onLinuxMouseSelection=this._onLinuxMouseSelection.event,this._onRedrawRequest=this._register(new f.Emitter),this.onRequestRedraw=this._onRedrawRequest.event,this._onSelectionChange=this._register(new f.Emitter),this.onSelectionChange=this._onSelectionChange.event,this._onRequestScrollLines=this._register(new f.Emitter),this.onRequestScrollLines=this._onRequestScrollLines.event,this._mouseMoveListener=e=>this._handleMouseMove(e),this._mouseUpListener=e=>this._handleMouseUp(e),this._coreService.onUserInput((()=>{this.hasSelection&&this.clearSelection()})),this._trimListener=this._bufferService.buffer.lines.onTrim((e=>this._handleTrim(e))),this._register(this._bufferService.buffers.onBufferActivate((e=>this._handleBufferActivate(e)))),this.enable(),this._model=new a.SelectionModel(this._bufferService),this._activeSelectionMode=0,this._register((0,h.toDisposable)((()=>{this._removeMouseDownListeners()}))),this._register(this._bufferService.onResize((e=>{e.rowsChanged&&this.clearSelection()})))}reset(){this.clearSelection()}disable(){this.clearSelection(),this._enabled=!1}enable(){this._enabled=!0}get selectionStart(){return this._model.finalSelectionStart}get selectionEnd(){return this._model.finalSelectionEnd}get hasSelection(){const e=this._model.finalSelectionStart,t=this._model.finalSelectionEnd;return!(!e||!t||e[0]===t[0]&&e[1]===t[1])}get selectionText(){const e=this._model.finalSelectionStart,t=this._model.finalSelectionEnd;if(!e||!t)return"";const i=this._bufferService.buffer,s=[];if(3===this._activeSelectionMode){if(e[0]===t[0])return"";const r=e[0]<t[0]?e[0]:t[0],n=e[0]<t[0]?t[0]:e[0];for(let o=e[1];o<=t[1];o++){const e=i.translateBufferLineToString(o,!0,r,n);s.push(e)}}else{const r=e[1]===t[1]?t[0]:void 0;s.push(i.translateBufferLineToString(e[1],!0,e[0],r));for(let r=e[1]+1;r<=t[1]-1;r++){const e=i.lines.get(r),t=i.translateBufferLineToString(r,!0);e?.isWrapped?s[s.length-1]+=t:s.push(t)}if(e[1]!==t[1]){const e=i.lines.get(t[1]),r=i.translateBufferLineToString(t[1],!0,0,t[0]);e&&e.isWrapped?s[s.length-1]+=r:s.push(r)}}return s.map((e=>e.replace(g," "))).join(c.isWindows?"\r\n":"\n")}clearSelection(){this._model.clearSelection(),this._removeMouseDownListeners(),this.refresh(),this._onSelectionChange.fire()}refresh(e){this._refreshAnimationFrame||(this._refreshAnimationFrame=this._coreBrowserService.window.requestAnimationFrame((()=>this._refresh()))),c.isLinux&&e&&this.selectionText.length&&this._onLinuxMouseSelection.fire(this.selectionText)}_refresh(){this._refreshAnimationFrame=void 0,this._onRedrawRequest.fire({start:this._model.finalSelectionStart,end:this._model.finalSelectionEnd,columnSelectMode:3===this._activeSelectionMode})}_isClickInSelection(e){const t=this._getMouseBufferCoords(e),i=this._model.finalSelectionStart,s=this._model.finalSelectionEnd;return!!(i&&s&&t)&&this._areCoordsInSelection(t,i,s)}isCellInSelection(e,t){const i=this._model.finalSelectionStart,s=this._model.finalSelectionEnd;return!(!i||!s)&&this._areCoordsInSelection([e,t],i,s)}_areCoordsInSelection(e,t,i){return e[1]>t[1]&&e[1]<i[1]||t[1]===i[1]&&e[1]===t[1]&&e[0]>=t[0]&&e[0]<i[0]||t[1]<i[1]&&e[1]===i[1]&&e[0]<i[0]||t[1]<i[1]&&e[1]===t[1]&&e[0]>=t[0]}_selectWordAtCursor(e,t){const i=this._linkifier.currentLink?.link?.range;if(i)return this._model.selectionStart=[i.start.x-1,i.start.y-1],this._model.selectionStartLength=(0,d.getRangeLength)(i,this._bufferService.cols),this._model.selectionEnd=void 0,!0;const s=this._getMouseBufferCoords(e);return!!s&&(this._selectWordAt(s,t),this._model.selectionEnd=void 0,!0)}selectAll(){this._model.isSelectAllActive=!0,this.refresh(),this._onSelectionChange.fire()}selectLines(e,t){this._model.clearSelection(),e=Math.max(e,0),t=Math.min(t,this._bufferService.buffer.lines.length-1),this._model.selectionStart=[0,e],this._model.selectionEnd=[this._bufferService.cols,t],this.refresh(),this._onSelectionChange.fire()}_handleTrim(e){this._model.handleTrim(e)&&this.refresh()}_getMouseBufferCoords(e){const t=this._mouseService.getCoords(e,this._screenElement,this._bufferService.cols,this._bufferService.rows,!0);if(t)return t[0]--,t[1]--,t[1]+=this._bufferService.buffer.ydisp,t}_getMouseEventScrollAmount(e){let t=(0,n.getCoordsRelativeToElement)(this._coreBrowserService.window,e,this._screenElement)[1];const i=this._renderService.dimensions.css.canvas.height;return t>=0&&t<=i?0:(t>i&&(t-=i),t=Math.min(Math.max(t,-50),50),t/=50,t/Math.abs(t)+Math.round(14*t))}shouldForceSelection(e){return c.isMac?e.altKey&&this._optionsService.rawOptions.macOptionClickForcesSelection:e.shiftKey}handleMouseDown(e){if(this._mouseDownTimeStamp=e.timeStamp,(2!==e.button||!this.hasSelection)&&0===e.button){if(!this._enabled){if(!this.shouldForceSelection(e))return;e.stopPropagation()}e.preventDefault(),this._dragScrollAmount=0,this._enabled&&e.shiftKey?this._handleIncrementalClick(e):1===e.detail?this._handleSingleClick(e):2===e.detail?this._handleDoubleClick(e):3===e.detail&&this._handleTripleClick(e),this._addMouseDownListeners(),this.refresh(!0)}}_addMouseDownListeners(){this._screenElement.ownerDocument&&(this._screenElement.ownerDocument.addEventListener("mousemove",this._mouseMoveListener),this._screenElement.ownerDocument.addEventListener("mouseup",this._mouseUpListener)),this._dragScrollIntervalTimer=this._coreBrowserService.window.setInterval((()=>this._dragScroll()),50)}_removeMouseDownListeners(){this._screenElement.ownerDocument&&(this._screenElement.ownerDocument.removeEventListener("mousemove",this._mouseMoveListener),this._screenElement.ownerDocument.removeEventListener("mouseup",this._mouseUpListener)),this._coreBrowserService.window.clearInterval(this._dragScrollIntervalTimer),this._dragScrollIntervalTimer=void 0}_handleIncrementalClick(e){this._model.selectionStart&&(this._model.selectionEnd=this._getMouseBufferCoords(e))}_handleSingleClick(e){if(this._model.selectionStartLength=0,this._model.isSelectAllActive=!1,this._activeSelectionMode=this.shouldColumnSelect(e)?3:0,this._model.selectionStart=this._getMouseBufferCoords(e),!this._model.selectionStart)return;this._model.selectionEnd=void 0;const t=this._bufferService.buffer.lines.get(this._model.selectionStart[1]);t&&t.length!==this._model.selectionStart[0]&&0===t.hasWidth(this._model.selectionStart[0])&&this._model.selectionStart[0]++}_handleDoubleClick(e){this._selectWordAtCursor(e,!0)&&(this._activeSelectionMode=1)}_handleTripleClick(e){const t=this._getMouseBufferCoords(e);t&&(this._activeSelectionMode=2,this._selectLineAt(t[1]))}shouldColumnSelect(e){return e.altKey&&!(c.isMac&&this._optionsService.rawOptions.macOptionClickForcesSelection)}_handleMouseMove(e){if(e.stopImmediatePropagation(),!this._model.selectionStart)return;const t=this._model.selectionEnd?[this._model.selectionEnd[0],this._model.selectionEnd[1]]:null;if(this._model.selectionEnd=this._getMouseBufferCoords(e),!this._model.selectionEnd)return void this.refresh(!0);2===this._activeSelectionMode?this._model.selectionEnd[1]<this._model.selectionStart[1]?this._model.selectionEnd[0]=0:this._model.selectionEnd[0]=this._bufferService.cols:1===this._activeSelectionMode&&this._selectToWordAt(this._model.selectionEnd),this._dragScrollAmount=this._getMouseEventScrollAmount(e),3!==this._activeSelectionMode&&(this._dragScrollAmount>0?this._model.selectionEnd[0]=this._bufferService.cols:this._dragScrollAmount<0&&(this._model.selectionEnd[0]=0));const i=this._bufferService.buffer;if(this._model.selectionEnd[1]<i.lines.length){const e=i.lines.get(this._model.selectionEnd[1]);e&&0===e.hasWidth(this._model.selectionEnd[0])&&this._model.selectionEnd[0]<this._bufferService.cols&&this._model.selectionEnd[0]++}t&&t[0]===this._model.selectionEnd[0]&&t[1]===this._model.selectionEnd[1]||this.refresh(!0)}_dragScroll(){if(this._model.selectionEnd&&this._model.selectionStart&&this._dragScrollAmount){this._onRequestScrollLines.fire({amount:this._dragScrollAmount,suppressScrollEvent:!1});const e=this._bufferService.buffer;this._dragScrollAmount>0?(3!==this._activeSelectionMode&&(this._model.selectionEnd[0]=this._bufferService.cols),this._model.selectionEnd[1]=Math.min(e.ydisp+this._bufferService.rows,e.lines.length-1)):(3!==this._activeSelectionMode&&(this._model.selectionEnd[0]=0),this._model.selectionEnd[1]=e.ydisp),this.refresh()}}_handleMouseUp(e){const t=e.timeStamp-this._mouseDownTimeStamp;if(this._removeMouseDownListeners(),this.selectionText.length<=1&&t<500&&e.altKey&&this._optionsService.rawOptions.altClickMovesCursor){if(this._bufferService.buffer.ybase===this._bufferService.buffer.ydisp){const t=this._mouseService.getCoords(e,this._element,this._bufferService.cols,this._bufferService.rows,!1);if(t&&void 0!==t[0]&&void 0!==t[1]){const e=(0,o.moveToCellSequence)(t[0]-1,t[1]-1,this._bufferService,this._coreService.decPrivateModes.applicationCursorKeys);this._coreService.triggerDataEvent(e,!0)}}}else this._fireEventIfSelectionChanged()}_fireEventIfSelectionChanged(){const e=this._model.finalSelectionStart,t=this._model.finalSelectionEnd,i=!(!e||!t||e[0]===t[0]&&e[1]===t[1]);i?e&&t&&(this._oldSelectionStart&&this._oldSelectionEnd&&e[0]===this._oldSelectionStart[0]&&e[1]===this._oldSelectionStart[1]&&t[0]===this._oldSelectionEnd[0]&&t[1]===this._oldSelectionEnd[1]||this._fireOnSelectionChange(e,t,i)):this._oldHasSelection&&this._fireOnSelectionChange(e,t,i)}_fireOnSelectionChange(e,t,i){this._oldSelectionStart=e,this._oldSelectionEnd=t,this._oldHasSelection=i,this._onSelectionChange.fire()}_handleBufferActivate(e){this.clearSelection(),this._trimListener.dispose(),this._trimListener=e.activeBuffer.lines.onTrim((e=>this._handleTrim(e)))}_convertViewportColToCharacterIndex(e,t){let i=t;for(let s=0;t>=s;s++){const r=e.loadCell(s,this._workCell).getChars().length;0===this._workCell.getWidth()?i--:r>1&&t!==s&&(i+=r-1)}return i}setSelection(e,t,i){this._model.clearSelection(),this._removeMouseDownListeners(),this._model.selectionStart=[e,t],this._model.selectionStartLength=i,this.refresh(),this._fireEventIfSelectionChanged()}rightClickSelect(e){this._isClickInSelection(e)||(this._selectWordAtCursor(e,!1)&&this.refresh(!0),this._fireEventIfSelectionChanged())}_getWordAt(e,t,i=!0,s=!0){if(e[0]>=this._bufferService.cols)return;const r=this._bufferService.buffer,n=r.lines.get(e[1]);if(!n)return;const o=r.translateBufferLineToString(e[1],!1);let a=this._convertViewportColToCharacterIndex(n,e[0]),l=a;const h=e[0]-a;let c=0,d=0,u=0,_=0;if(" "===o.charAt(a)){for(;a>0&&" "===o.charAt(a-1);)a--;for(;l<o.length&&" "===o.charAt(l+1);)l++}else{let t=e[0],i=e[0];0===n.getWidth(t)&&(c++,t--),2===n.getWidth(i)&&(d++,i++);const s=n.getString(i).length;for(s>1&&(_+=s-1,l+=s-1);t>0&&a>0&&!this._isCharWordSeparator(n.loadCell(t-1,this._workCell));){n.loadCell(t-1,this._workCell);const e=this._workCell.getChars().length;0===this._workCell.getWidth()?(c++,t--):e>1&&(u+=e-1,a-=e-1),a--,t--}for(;i<n.length&&l+1<o.length&&!this._isCharWordSeparator(n.loadCell(i+1,this._workCell));){n.loadCell(i+1,this._workCell);const e=this._workCell.getChars().length;2===this._workCell.getWidth()?(d++,i++):e>1&&(_+=e-1,l+=e-1),l++,i++}}l++;let f=a+h-c+u,p=Math.min(this._bufferService.cols,l-a+c+d-u-_);if(t||""!==o.slice(a,l).trim()){if(i&&0===f&&32!==n.getCodePoint(0)){const t=r.lines.get(e[1]-1);if(t&&n.isWrapped&&32!==t.getCodePoint(this._bufferService.cols-1)){const t=this._getWordAt([this._bufferService.cols-1,e[1]-1],!1,!0,!1);if(t){const e=this._bufferService.cols-t.start;f-=e,p+=e}}}if(s&&f+p===this._bufferService.cols&&32!==n.getCodePoint(this._bufferService.cols-1)){const t=r.lines.get(e[1]+1);if(t?.isWrapped&&32!==t.getCodePoint(0)){const t=this._getWordAt([0,e[1]+1],!1,!1,!0);t&&(p+=t.length)}}return{start:f,length:p}}}_selectWordAt(e,t){const i=this._getWordAt(e,t);if(i){for(;i.start<0;)i.start+=this._bufferService.cols,e[1]--;this._model.selectionStart=[i.start,e[1]],this._model.selectionStartLength=i.length}}_selectToWordAt(e){const t=this._getWordAt(e,!0);if(t){let i=e[1];for(;t.start<0;)t.start+=this._bufferService.cols,i--;if(!this._model.areSelectionValuesReversed())for(;t.start+t.length>this._bufferService.cols;)t.length-=this._bufferService.cols,i++;this._model.selectionEnd=[this._model.areSelectionValuesReversed()?t.start:t.start+t.length,i]}}_isCharWordSeparator(e){return 0!==e.getWidth()&&this._optionsService.rawOptions.wordSeparator.indexOf(e.getChars())>=0}_selectLineAt(e){const t=this._bufferService.buffer.getWrappedRangeForLine(e),i={start:{x:0,y:t.first},end:{x:this._bufferService.cols-1,y:t.last}};this._model.selectionStart=[0,t.first],this._model.selectionEnd=void 0,this._model.selectionStartLength=(0,d.getRangeLength)(i,this._bufferService.cols)}};t.SelectionService=m,t.SelectionService=m=s([r(3,_.IBufferService),r(4,_.ICoreService),r(5,l.IMouseService),r(6,_.IOptionsService),r(7,l.IRenderService),r(8,l.ICoreBrowserService)],m)},7098:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ILinkProviderService=t.IThemeService=t.ICharacterJoinerService=t.ISelectionService=t.IRenderService=t.IMouseService=t.ICoreBrowserService=t.ICharSizeService=void 0;const s=i(6201);t.ICharSizeService=(0,s.createDecorator)("CharSizeService"),t.ICoreBrowserService=(0,s.createDecorator)("CoreBrowserService"),t.IMouseService=(0,s.createDecorator)("MouseService"),t.IRenderService=(0,s.createDecorator)("RenderService"),t.ISelectionService=(0,s.createDecorator)("SelectionService"),t.ICharacterJoinerService=(0,s.createDecorator)("CharacterJoinerService"),t.IThemeService=(0,s.createDecorator)("ThemeService"),t.ILinkProviderService=(0,s.createDecorator)("LinkProviderService")},9078:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.ThemeService=void 0;const n=i(7174),o=i(9302),a=i(4103),l=i(7150),h=i(6501),c=i(802),d=a.css.toColor("#ffffff"),u=a.css.toColor("#000000"),_=a.css.toColor("#ffffff"),f=u,p={css:"rgba(255, 255, 255, 0.3)",rgba:4294967117},g=d;let m=class extends l.Disposable{get colors(){return this._colors}constructor(e){super(),this._optionsService=e,this._contrastCache=new n.ColorContrastCache,this._halfContrastCache=new n.ColorContrastCache,this._onChangeColors=this._register(new c.Emitter),this.onChangeColors=this._onChangeColors.event,this._colors={foreground:d,background:u,cursor:_,cursorAccent:f,selectionForeground:void 0,selectionBackgroundTransparent:p,selectionBackgroundOpaque:a.color.blend(u,p),selectionInactiveBackgroundTransparent:p,selectionInactiveBackgroundOpaque:a.color.blend(u,p),scrollbarSliderBackground:a.color.opacity(d,.2),scrollbarSliderHoverBackground:a.color.opacity(d,.4),scrollbarSliderActiveBackground:a.color.opacity(d,.5),overviewRulerBorder:d,ansi:o.DEFAULT_ANSI_COLORS.slice(),contrastCache:this._contrastCache,halfContrastCache:this._halfContrastCache},this._updateRestoreColors(),this._setTheme(this._optionsService.rawOptions.theme),this._register(this._optionsService.onSpecificOptionChange("minimumContrastRatio",(()=>this._contrastCache.clear()))),this._register(this._optionsService.onSpecificOptionChange("theme",(()=>this._setTheme(this._optionsService.rawOptions.theme))))}_setTheme(e={}){const t=this._colors;if(t.foreground=v(e.foreground,d),t.background=v(e.background,u),t.cursor=a.color.blend(t.background,v(e.cursor,_)),t.cursorAccent=a.color.blend(t.background,v(e.cursorAccent,f)),t.selectionBackgroundTransparent=v(e.selectionBackground,p),t.selectionBackgroundOpaque=a.color.blend(t.background,t.selectionBackgroundTransparent),t.selectionInactiveBackgroundTransparent=v(e.selectionInactiveBackground,t.selectionBackgroundTransparent),t.selectionInactiveBackgroundOpaque=a.color.blend(t.background,t.selectionInactiveBackgroundTransparent),t.selectionForeground=e.selectionForeground?v(e.selectionForeground,a.NULL_COLOR):void 0,t.selectionForeground===a.NULL_COLOR&&(t.selectionForeground=void 0),a.color.isOpaque(t.selectionBackgroundTransparent)){const e=.3;t.selectionBackgroundTransparent=a.color.opacity(t.selectionBackgroundTransparent,e)}if(a.color.isOpaque(t.selectionInactiveBackgroundTransparent)){const e=.3;t.selectionInactiveBackgroundTransparent=a.color.opacity(t.selectionInactiveBackgroundTransparent,e)}if(t.scrollbarSliderBackground=v(e.scrollbarSliderBackground,a.color.opacity(t.foreground,.2)),t.scrollbarSliderHoverBackground=v(e.scrollbarSliderHoverBackground,a.color.opacity(t.foreground,.4)),t.scrollbarSliderActiveBackground=v(e.scrollbarSliderActiveBackground,a.color.opacity(t.foreground,.5)),t.overviewRulerBorder=v(e.overviewRulerBorder,g),t.ansi=o.DEFAULT_ANSI_COLORS.slice(),t.ansi[0]=v(e.black,o.DEFAULT_ANSI_COLORS[0]),t.ansi[1]=v(e.red,o.DEFAULT_ANSI_COLORS[1]),t.ansi[2]=v(e.green,o.DEFAULT_ANSI_COLORS[2]),t.ansi[3]=v(e.yellow,o.DEFAULT_ANSI_COLORS[3]),t.ansi[4]=v(e.blue,o.DEFAULT_ANSI_COLORS[4]),t.ansi[5]=v(e.magenta,o.DEFAULT_ANSI_COLORS[5]),t.ansi[6]=v(e.cyan,o.DEFAULT_ANSI_COLORS[6]),t.ansi[7]=v(e.white,o.DEFAULT_ANSI_COLORS[7]),t.ansi[8]=v(e.brightBlack,o.DEFAULT_ANSI_COLORS[8]),t.ansi[9]=v(e.brightRed,o.DEFAULT_ANSI_COLORS[9]),t.ansi[10]=v(e.brightGreen,o.DEFAULT_ANSI_COLORS[10]),t.ansi[11]=v(e.brightYellow,o.DEFAULT_ANSI_COLORS[11]),t.ansi[12]=v(e.brightBlue,o.DEFAULT_ANSI_COLORS[12]),t.ansi[13]=v(e.brightMagenta,o.DEFAULT_ANSI_COLORS[13]),t.ansi[14]=v(e.brightCyan,o.DEFAULT_ANSI_COLORS[14]),t.ansi[15]=v(e.brightWhite,o.DEFAULT_ANSI_COLORS[15]),e.extendedAnsi){const i=Math.min(t.ansi.length-16,e.extendedAnsi.length);for(let s=0;s<i;s++)t.ansi[s+16]=v(e.extendedAnsi[s],o.DEFAULT_ANSI_COLORS[s+16])}this._contrastCache.clear(),this._halfContrastCache.clear(),this._updateRestoreColors(),this._onChangeColors.fire(this.colors)}restoreColor(e){this._restoreColor(e),this._onChangeColors.fire(this.colors)}_restoreColor(e){if(void 0!==e)switch(e){case 256:this._colors.foreground=this._restoreColors.foreground;break;case 257:this._colors.background=this._restoreColors.background;break;case 258:this._colors.cursor=this._restoreColors.cursor;break;default:this._colors.ansi[e]=this._restoreColors.ansi[e]}else for(let e=0;e<this._restoreColors.ansi.length;++e)this._colors.ansi[e]=this._restoreColors.ansi[e]}modifyColors(e){e(this._colors),this._onChangeColors.fire(this.colors)}_updateRestoreColors(){this._restoreColors={foreground:this._colors.foreground,background:this._colors.background,cursor:this._colors.cursor,ansi:this._colors.ansi.slice()}}};function v(e,t){if(void 0!==e)try{return a.css.toColor(e)}catch{}return t}t.ThemeService=m,t.ThemeService=m=s([r(0,h.IOptionsService)],m)},5639:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.CircularList=void 0;const s=i(7150),r=i(802);class n extends s.Disposable{constructor(e){super(),this._maxLength=e,this.onDeleteEmitter=this._register(new r.Emitter),this.onDelete=this.onDeleteEmitter.event,this.onInsertEmitter=this._register(new r.Emitter),this.onInsert=this.onInsertEmitter.event,this.onTrimEmitter=this._register(new r.Emitter),this.onTrim=this.onTrimEmitter.event,this._array=new Array(this._maxLength),this._startIndex=0,this._length=0}get maxLength(){return this._maxLength}set maxLength(e){if(this._maxLength===e)return;const t=new Array(e);for(let i=0;i<Math.min(e,this.length);i++)t[i]=this._array[this._getCyclicIndex(i)];this._array=t,this._maxLength=e,this._startIndex=0}get length(){return this._length}set length(e){if(e>this._length)for(let t=this._length;t<e;t++)this._array[t]=void 0;this._length=e}get(e){return this._array[this._getCyclicIndex(e)]}set(e,t){this._array[this._getCyclicIndex(e)]=t}push(e){this._array[this._getCyclicIndex(this._length)]=e,this._length===this._maxLength?(this._startIndex=++this._startIndex%this._maxLength,this.onTrimEmitter.fire(1)):this._length++}recycle(){if(this._length!==this._maxLength)throw new Error("Can only recycle when the buffer is full");return this._startIndex=++this._startIndex%this._maxLength,this.onTrimEmitter.fire(1),this._array[this._getCyclicIndex(this._length-1)]}get isFull(){return this._length===this._maxLength}pop(){return this._array[this._getCyclicIndex(this._length---1)]}splice(e,t,...i){if(t){for(let i=e;i<this._length-t;i++)this._array[this._getCyclicIndex(i)]=this._array[this._getCyclicIndex(i+t)];this._length-=t,this.onDeleteEmitter.fire({index:e,amount:t})}for(let t=this._length-1;t>=e;t--)this._array[this._getCyclicIndex(t+i.length)]=this._array[this._getCyclicIndex(t)];for(let t=0;t<i.length;t++)this._array[this._getCyclicIndex(e+t)]=i[t];if(i.length&&this.onInsertEmitter.fire({index:e,amount:i.length}),this._length+i.length>this._maxLength){const e=this._length+i.length-this._maxLength;this._startIndex+=e,this._length=this._maxLength,this.onTrimEmitter.fire(e)}else this._length+=i.length}trimStart(e){e>this._length&&(e=this._length),this._startIndex+=e,this._length-=e,this.onTrimEmitter.fire(e)}shiftElements(e,t,i){if(!(t<=0)){if(e<0||e>=this._length)throw new Error("start argument out of range");if(e+i<0)throw new Error("Cannot shift elements in list beyond index 0");if(i>0){for(let s=t-1;s>=0;s--)this.set(e+s+i,this.get(e+s));const s=e+t+i-this._length;if(s>0)for(this._length+=s;this._length>this._maxLength;)this._length--,this._startIndex++,this.onTrimEmitter.fire(1)}else for(let s=0;s<t;s++)this.set(e+s+i,this.get(e+s))}}_getCyclicIndex(e){return(this._startIndex+e)%this._maxLength}}t.CircularList=n},7453:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.clone=function e(t,i=5){if("object"!=typeof t)return t;const s=Array.isArray(t)?[]:{};for(const r in t)s[r]=i<=1?t[r]:t[r]&&e(t[r],i-1);return s}},4103:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.rgba=t.rgb=t.css=t.color=t.channels=t.NULL_COLOR=void 0,t.toPaddedHex=d,t.contrastRatio=u;let i=0,s=0,r=0,n=0;var o,a,l,h,c;function d(e){const t=e.toString(16);return t.length<2?"0"+t:t}function u(e,t){return e<t?(t+.05)/(e+.05):(e+.05)/(t+.05)}t.NULL_COLOR={css:"#00000000",rgba:0},function(e){e.toCss=function(e,t,i,s){return void 0!==s?`#${d(e)}${d(t)}${d(i)}${d(s)}`:`#${d(e)}${d(t)}${d(i)}`},e.toRgba=function(e,t,i,s=255){return(e<<24|t<<16|i<<8|s)>>>0},e.toColor=function(t,i,s,r){return{css:e.toCss(t,i,s,r),rgba:e.toRgba(t,i,s,r)}}}(o||(t.channels=o={})),function(e){function t(e,t){return n=Math.round(255*t),[i,s,r]=c.toChannels(e.rgba),{css:o.toCss(i,s,r,n),rgba:o.toRgba(i,s,r,n)}}e.blend=function(e,t){if(n=(255&t.rgba)/255,1===n)return{css:t.css,rgba:t.rgba};const a=t.rgba>>24&255,l=t.rgba>>16&255,h=t.rgba>>8&255,c=e.rgba>>24&255,d=e.rgba>>16&255,u=e.rgba>>8&255;return i=c+Math.round((a-c)*n),s=d+Math.round((l-d)*n),r=u+Math.round((h-u)*n),{css:o.toCss(i,s,r),rgba:o.toRgba(i,s,r)}},e.isOpaque=function(e){return!(255&~e.rgba)},e.ensureContrastRatio=function(e,t,i){const s=c.ensureContrastRatio(e.rgba,t.rgba,i);if(s)return o.toColor(s>>24&255,s>>16&255,s>>8&255)},e.opaque=function(e){const t=(255|e.rgba)>>>0;return[i,s,r]=c.toChannels(t),{css:o.toCss(i,s,r),rgba:t}},e.opacity=t,e.multiplyOpacity=function(e,i){return n=255&e.rgba,t(e,n*i/255)},e.toColorRGB=function(e){return[e.rgba>>24&255,e.rgba>>16&255,e.rgba>>8&255]}}(a||(t.color=a={})),function(e){let t,a;try{const e=document.createElement("canvas");e.width=1,e.height=1;const i=e.getContext("2d",{willReadFrequently:!0});i&&(t=i,t.globalCompositeOperation="copy",a=t.createLinearGradient(0,0,1,1))}catch{}e.toColor=function(e){if(e.match(/#[\da-f]{3,8}/i))switch(e.length){case 4:return i=parseInt(e.slice(1,2).repeat(2),16),s=parseInt(e.slice(2,3).repeat(2),16),r=parseInt(e.slice(3,4).repeat(2),16),o.toColor(i,s,r);case 5:return i=parseInt(e.slice(1,2).repeat(2),16),s=parseInt(e.slice(2,3).repeat(2),16),r=parseInt(e.slice(3,4).repeat(2),16),n=parseInt(e.slice(4,5).repeat(2),16),o.toColor(i,s,r,n);case 7:return{css:e,rgba:(parseInt(e.slice(1),16)<<8|255)>>>0};case 9:return{css:e,rgba:parseInt(e.slice(1),16)>>>0}}const l=e.match(/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(,\s*(0|1|\d?\.(\d+))\s*)?\)/);if(l)return i=parseInt(l[1]),s=parseInt(l[2]),r=parseInt(l[3]),n=Math.round(255*(void 0===l[5]?1:parseFloat(l[5]))),o.toColor(i,s,r,n);if(!t||!a)throw new Error("css.toColor: Unsupported css format");if(t.fillStyle=a,t.fillStyle=e,"string"!=typeof t.fillStyle)throw new Error("css.toColor: Unsupported css format");if(t.fillRect(0,0,1,1),[i,s,r,n]=t.getImageData(0,0,1,1).data,255!==n)throw new Error("css.toColor: Unsupported css format");return{rgba:o.toRgba(i,s,r,n),css:e}}}(l||(t.css=l={})),function(e){function t(e,t,i){const s=e/255,r=t/255,n=i/255;return.2126*(s<=.03928?s/12.92:Math.pow((s+.055)/1.055,2.4))+.7152*(r<=.03928?r/12.92:Math.pow((r+.055)/1.055,2.4))+.0722*(n<=.03928?n/12.92:Math.pow((n+.055)/1.055,2.4))}e.relativeLuminance=function(e){return t(e>>16&255,e>>8&255,255&e)},e.relativeLuminance2=t}(h||(t.rgb=h={})),function(e){function t(e,t,i){const s=e>>24&255,r=e>>16&255,n=e>>8&255;let o=t>>24&255,a=t>>16&255,l=t>>8&255,c=u(h.relativeLuminance2(o,a,l),h.relativeLuminance2(s,r,n));for(;c<i&&(o>0||a>0||l>0);)o-=Math.max(0,Math.ceil(.1*o)),a-=Math.max(0,Math.ceil(.1*a)),l-=Math.max(0,Math.ceil(.1*l)),c=u(h.relativeLuminance2(o,a,l),h.relativeLuminance2(s,r,n));return(o<<24|a<<16|l<<8|255)>>>0}function a(e,t,i){const s=e>>24&255,r=e>>16&255,n=e>>8&255;let o=t>>24&255,a=t>>16&255,l=t>>8&255,c=u(h.relativeLuminance2(o,a,l),h.relativeLuminance2(s,r,n));for(;c<i&&(o<255||a<255||l<255);)o=Math.min(255,o+Math.ceil(.1*(255-o))),a=Math.min(255,a+Math.ceil(.1*(255-a))),l=Math.min(255,l+Math.ceil(.1*(255-l))),c=u(h.relativeLuminance2(o,a,l),h.relativeLuminance2(s,r,n));return(o<<24|a<<16|l<<8|255)>>>0}e.blend=function(e,t){if(n=(255&t)/255,1===n)return t;const a=t>>24&255,l=t>>16&255,h=t>>8&255,c=e>>24&255,d=e>>16&255,u=e>>8&255;return i=c+Math.round((a-c)*n),s=d+Math.round((l-d)*n),r=u+Math.round((h-u)*n),o.toRgba(i,s,r)},e.ensureContrastRatio=function(e,i,s){const r=h.relativeLuminance(e>>8),n=h.relativeLuminance(i>>8);if(u(r,n)<s){if(n<r){const n=t(e,i,s),o=u(r,h.relativeLuminance(n>>8));if(o<s){const t=a(e,i,s);return o>u(r,h.relativeLuminance(t>>8))?n:t}return n}const o=a(e,i,s),l=u(r,h.relativeLuminance(o>>8));if(l<s){const n=t(e,i,s);return l>u(r,h.relativeLuminance(n>>8))?o:n}return o}},e.reduceLuminance=t,e.increaseLuminance=a,e.toChannels=function(e){return[e>>24&255,e>>16&255,e>>8&255,255&e]}}(c||(t.rgba=c={}))},5777:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.CoreTerminal=void 0;const s=i(6501),r=i(6025),n=i(7276),o=i(9640),a=i(56),l=i(4071),h=i(7792),c=i(6415),d=i(5746),u=i(5882),_=i(2486),f=i(3562),p=i(8811),g=i(802),m=i(7150);let v=!1;class S extends m.Disposable{get onScroll(){return this._onScrollApi||(this._onScrollApi=this._register(new g.Emitter),this._onScroll.event((e=>{this._onScrollApi?.fire(e.position)}))),this._onScrollApi.event}get cols(){return this._bufferService.cols}get rows(){return this._bufferService.rows}get buffers(){return this._bufferService.buffers}get options(){return this.optionsService.options}set options(e){for(const t in e)this.optionsService.options[t]=e[t]}constructor(e){super(),this._windowsWrappingHeuristics=this._register(new m.MutableDisposable),this._onBinary=this._register(new g.Emitter),this.onBinary=this._onBinary.event,this._onData=this._register(new g.Emitter),this.onData=this._onData.event,this._onLineFeed=this._register(new g.Emitter),this.onLineFeed=this._onLineFeed.event,this._onResize=this._register(new g.Emitter),this.onResize=this._onResize.event,this._onWriteParsed=this._register(new g.Emitter),this.onWriteParsed=this._onWriteParsed.event,this._onScroll=this._register(new g.Emitter),this._instantiationService=new r.InstantiationService,this.optionsService=this._register(new a.OptionsService(e)),this._instantiationService.setService(s.IOptionsService,this.optionsService),this._bufferService=this._register(this._instantiationService.createInstance(o.BufferService)),this._instantiationService.setService(s.IBufferService,this._bufferService),this._logService=this._register(this._instantiationService.createInstance(n.LogService)),this._instantiationService.setService(s.ILogService,this._logService),this.coreService=this._register(this._instantiationService.createInstance(l.CoreService)),this._instantiationService.setService(s.ICoreService,this.coreService),this.coreMouseService=this._register(this._instantiationService.createInstance(h.CoreMouseService)),this._instantiationService.setService(s.ICoreMouseService,this.coreMouseService),this.unicodeService=this._register(this._instantiationService.createInstance(c.UnicodeService)),this._instantiationService.setService(s.IUnicodeService,this.unicodeService),this._charsetService=this._instantiationService.createInstance(d.CharsetService),this._instantiationService.setService(s.ICharsetService,this._charsetService),this._oscLinkService=this._instantiationService.createInstance(p.OscLinkService),this._instantiationService.setService(s.IOscLinkService,this._oscLinkService),this._inputHandler=this._register(new _.InputHandler(this._bufferService,this._charsetService,this.coreService,this._logService,this.optionsService,this._oscLinkService,this.coreMouseService,this.unicodeService)),this._register(g.Event.forward(this._inputHandler.onLineFeed,this._onLineFeed)),this._register(this._inputHandler),this._register(g.Event.forward(this._bufferService.onResize,this._onResize)),this._register(g.Event.forward(this.coreService.onData,this._onData)),this._register(g.Event.forward(this.coreService.onBinary,this._onBinary)),this._register(this.coreService.onRequestScrollToBottom((()=>this.scrollToBottom(!0)))),this._register(this.coreService.onUserInput((()=>this._writeBuffer.handleUserInput()))),this._register(this.optionsService.onMultipleOptionChange(["windowsMode","windowsPty"],(()=>this._handleWindowsPtyOptionChange()))),this._register(this._bufferService.onScroll((()=>{this._onScroll.fire({position:this._bufferService.buffer.ydisp}),this._inputHandler.markRangeDirty(this._bufferService.buffer.scrollTop,this._bufferService.buffer.scrollBottom)}))),this._writeBuffer=this._register(new f.WriteBuffer(((e,t)=>this._inputHandler.parse(e,t)))),this._register(g.Event.forward(this._writeBuffer.onWriteParsed,this._onWriteParsed))}write(e,t){this._writeBuffer.write(e,t)}writeSync(e,t){this._logService.logLevel<=s.LogLevelEnum.WARN&&!v&&(this._logService.warn("writeSync is unreliable and will be removed soon."),v=!0),this._writeBuffer.writeSync(e,t)}input(e,t=!0){this.coreService.triggerDataEvent(e,t)}resize(e,t){isNaN(e)||isNaN(t)||(e=Math.max(e,o.MINIMUM_COLS),t=Math.max(t,o.MINIMUM_ROWS),this._bufferService.resize(e,t))}scroll(e,t=!1){this._bufferService.scroll(e,t)}scrollLines(e,t){this._bufferService.scrollLines(e,t)}scrollPages(e){this.scrollLines(e*(this.rows-1))}scrollToTop(){this.scrollLines(-this._bufferService.buffer.ydisp)}scrollToBottom(e){this.scrollLines(this._bufferService.buffer.ybase-this._bufferService.buffer.ydisp)}scrollToLine(e){const t=e-this._bufferService.buffer.ydisp;0!==t&&this.scrollLines(t)}registerEscHandler(e,t){return this._inputHandler.registerEscHandler(e,t)}registerDcsHandler(e,t){return this._inputHandler.registerDcsHandler(e,t)}registerCsiHandler(e,t){return this._inputHandler.registerCsiHandler(e,t)}registerOscHandler(e,t){return this._inputHandler.registerOscHandler(e,t)}_setup(){this._handleWindowsPtyOptionChange()}reset(){this._inputHandler.reset(),this._bufferService.reset(),this._charsetService.reset(),this.coreService.reset(),this.coreMouseService.reset()}_handleWindowsPtyOptionChange(){let e=!1;const t=this.optionsService.rawOptions.windowsPty;t&&void 0!==t.buildNumber&&void 0!==t.buildNumber?e=!!("conpty"===t.backend&&t.buildNumber<21376):this.optionsService.rawOptions.windowsMode&&(e=!0),e?this._enableWindowsWrappingHeuristics():this._windowsWrappingHeuristics.clear()}_enableWindowsWrappingHeuristics(){if(!this._windowsWrappingHeuristics.value){const e=[];e.push(this.onLineFeed(u.updateWindowsModeWrappedState.bind(null,this._bufferService))),e.push(this.registerCsiHandler({final:"H"},(()=>((0,u.updateWindowsModeWrappedState)(this._bufferService),!1)))),this._windowsWrappingHeuristics.value=(0,m.toDisposable)((()=>{for(const t of e)t.dispose()}))}}}t.CoreTerminal=S},2486:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.InputHandler=t.WindowsOptionsReportType=void 0,t.isValidColorIndex=R;const n=i(3534),o=i(6760),a=i(6717),l=i(7150),h=i(726),c=i(6107),d=i(8938),u=i(3055),_=i(5451),f=i(6501),p=i(6415),g=i(1346),m=i(9823),v=i(8693),S=i(802),b={"(":0,")":1,"*":2,"+":3,"-":1,".":2},C=131072;function y(e,t){if(e>24)return t.setWinLines||!1;switch(e){case 1:return!!t.restoreWin;case 2:return!!t.minimizeWin;case 3:return!!t.setWinPosition;case 4:return!!t.setWinSizePixels;case 5:return!!t.raiseWin;case 6:return!!t.lowerWin;case 7:return!!t.refreshWin;case 8:return!!t.setWinSizeChars;case 9:return!!t.maximizeWin;case 10:return!!t.fullscreenWin;case 11:return!!t.getWinState;case 13:return!!t.getWinPosition;case 14:return!!t.getWinSizePixels;case 15:return!!t.getScreenSizePixels;case 16:return!!t.getCellSizePixels;case 18:return!!t.getWinSizeChars;case 19:return!!t.getScreenSizeChars;case 20:return!!t.getIconTitle;case 21:return!!t.getWinTitle;case 22:return!!t.pushTitle;case 23:return!!t.popTitle;case 24:return!!t.setWinLines}return!1}var w;!function(e){e[e.GET_WIN_SIZE_PIXELS=0]="GET_WIN_SIZE_PIXELS",e[e.GET_CELL_SIZE_PIXELS=1]="GET_CELL_SIZE_PIXELS"}(w||(t.WindowsOptionsReportType=w={}));let E=0;class D extends l.Disposable{getAttrData(){return this._curAttrData}constructor(e,t,i,s,r,l,d,u,_=new a.EscapeSequenceParser){super(),this._bufferService=e,this._charsetService=t,this._coreService=i,this._logService=s,this._optionsService=r,this._oscLinkService=l,this._coreMouseService=d,this._unicodeService=u,this._parser=_,this._parseBuffer=new Uint32Array(4096),this._stringDecoder=new h.StringToUtf32,this._utf8Decoder=new h.Utf8ToUtf32,this._windowTitle="",this._iconName="",this._windowTitleStack=[],this._iconNameStack=[],this._curAttrData=c.DEFAULT_ATTR_DATA.clone(),this._eraseAttrDataInternal=c.DEFAULT_ATTR_DATA.clone(),this._onRequestBell=this._register(new S.Emitter),this.onRequestBell=this._onRequestBell.event,this._onRequestRefreshRows=this._register(new S.Emitter),this.onRequestRefreshRows=this._onRequestRefreshRows.event,this._onRequestReset=this._register(new S.Emitter),this.onRequestReset=this._onRequestReset.event,this._onRequestSendFocus=this._register(new S.Emitter),this.onRequestSendFocus=this._onRequestSendFocus.event,this._onRequestSyncScrollBar=this._register(new S.Emitter),this.onRequestSyncScrollBar=this._onRequestSyncScrollBar.event,this._onRequestWindowsOptionsReport=this._register(new S.Emitter),this.onRequestWindowsOptionsReport=this._onRequestWindowsOptionsReport.event,this._onA11yChar=this._register(new S.Emitter),this.onA11yChar=this._onA11yChar.event,this._onA11yTab=this._register(new S.Emitter),this.onA11yTab=this._onA11yTab.event,this._onCursorMove=this._register(new S.Emitter),this.onCursorMove=this._onCursorMove.event,this._onLineFeed=this._register(new S.Emitter),this.onLineFeed=this._onLineFeed.event,this._onScroll=this._register(new S.Emitter),this.onScroll=this._onScroll.event,this._onTitleChange=this._register(new S.Emitter),this.onTitleChange=this._onTitleChange.event,this._onColor=this._register(new S.Emitter),this.onColor=this._onColor.event,this._parseStack={paused:!1,cursorStartX:0,cursorStartY:0,decodedLength:0,position:0},this._specialColors=[256,257,258],this._register(this._parser),this._dirtyRowTracker=new L(this._bufferService),this._activeBuffer=this._bufferService.buffer,this._register(this._bufferService.buffers.onBufferActivate((e=>this._activeBuffer=e.activeBuffer))),this._parser.setCsiHandlerFallback(((e,t)=>{this._logService.debug("Unknown CSI code: ",{identifier:this._parser.identToString(e),params:t.toArray()})})),this._parser.setEscHandlerFallback((e=>{this._logService.debug("Unknown ESC code: ",{identifier:this._parser.identToString(e)})})),this._parser.setExecuteHandlerFallback((e=>{this._logService.debug("Unknown EXECUTE code: ",{code:e})})),this._parser.setOscHandlerFallback(((e,t,i)=>{this._logService.debug("Unknown OSC code: ",{identifier:e,action:t,data:i})})),this._parser.setDcsHandlerFallback(((e,t,i)=>{"HOOK"===t&&(i=i.toArray()),this._logService.debug("Unknown DCS code: ",{identifier:this._parser.identToString(e),action:t,payload:i})})),this._parser.setPrintHandler(((e,t,i)=>this.print(e,t,i))),this._parser.registerCsiHandler({final:"@"},(e=>this.insertChars(e))),this._parser.registerCsiHandler({intermediates:" ",final:"@"},(e=>this.scrollLeft(e))),this._parser.registerCsiHandler({final:"A"},(e=>this.cursorUp(e))),this._parser.registerCsiHandler({intermediates:" ",final:"A"},(e=>this.scrollRight(e))),this._parser.registerCsiHandler({final:"B"},(e=>this.cursorDown(e))),this._parser.registerCsiHandler({final:"C"},(e=>this.cursorForward(e))),this._parser.registerCsiHandler({final:"D"},(e=>this.cursorBackward(e))),this._parser.registerCsiHandler({final:"E"},(e=>this.cursorNextLine(e))),this._parser.registerCsiHandler({final:"F"},(e=>this.cursorPrecedingLine(e))),this._parser.registerCsiHandler({final:"G"},(e=>this.cursorCharAbsolute(e))),this._parser.registerCsiHandler({final:"H"},(e=>this.cursorPosition(e))),this._parser.registerCsiHandler({final:"I"},(e=>this.cursorForwardTab(e))),this._parser.registerCsiHandler({final:"J"},(e=>this.eraseInDisplay(e,!1))),this._parser.registerCsiHandler({prefix:"?",final:"J"},(e=>this.eraseInDisplay(e,!0))),this._parser.registerCsiHandler({final:"K"},(e=>this.eraseInLine(e,!1))),this._parser.registerCsiHandler({prefix:"?",final:"K"},(e=>this.eraseInLine(e,!0))),this._parser.registerCsiHandler({final:"L"},(e=>this.insertLines(e))),this._parser.registerCsiHandler({final:"M"},(e=>this.deleteLines(e))),this._parser.registerCsiHandler({final:"P"},(e=>this.deleteChars(e))),this._parser.registerCsiHandler({final:"S"},(e=>this.scrollUp(e))),this._parser.registerCsiHandler({final:"T"},(e=>this.scrollDown(e))),this._parser.registerCsiHandler({final:"X"},(e=>this.eraseChars(e))),this._parser.registerCsiHandler({final:"Z"},(e=>this.cursorBackwardTab(e))),this._parser.registerCsiHandler({final:"`"},(e=>this.charPosAbsolute(e))),this._parser.registerCsiHandler({final:"a"},(e=>this.hPositionRelative(e))),this._parser.registerCsiHandler({final:"b"},(e=>this.repeatPrecedingCharacter(e))),this._parser.registerCsiHandler({final:"c"},(e=>this.sendDeviceAttributesPrimary(e))),this._parser.registerCsiHandler({prefix:">",final:"c"},(e=>this.sendDeviceAttributesSecondary(e))),this._parser.registerCsiHandler({final:"d"},(e=>this.linePosAbsolute(e))),this._parser.registerCsiHandler({final:"e"},(e=>this.vPositionRelative(e))),this._parser.registerCsiHandler({final:"f"},(e=>this.hVPosition(e))),this._parser.registerCsiHandler({final:"g"},(e=>this.tabClear(e))),this._parser.registerCsiHandler({final:"h"},(e=>this.setMode(e))),this._parser.registerCsiHandler({prefix:"?",final:"h"},(e=>this.setModePrivate(e))),this._parser.registerCsiHandler({final:"l"},(e=>this.resetMode(e))),this._parser.registerCsiHandler({prefix:"?",final:"l"},(e=>this.resetModePrivate(e))),this._parser.registerCsiHandler({final:"m"},(e=>this.charAttributes(e))),this._parser.registerCsiHandler({final:"n"},(e=>this.deviceStatus(e))),this._parser.registerCsiHandler({prefix:"?",final:"n"},(e=>this.deviceStatusPrivate(e))),this._parser.registerCsiHandler({intermediates:"!",final:"p"},(e=>this.softReset(e))),this._parser.registerCsiHandler({intermediates:" ",final:"q"},(e=>this.setCursorStyle(e))),this._parser.registerCsiHandler({final:"r"},(e=>this.setScrollRegion(e))),this._parser.registerCsiHandler({final:"s"},(e=>this.saveCursor(e))),this._parser.registerCsiHandler({final:"t"},(e=>this.windowOptions(e))),this._parser.registerCsiHandler({final:"u"},(e=>this.restoreCursor(e))),this._parser.registerCsiHandler({intermediates:"'",final:"}"},(e=>this.insertColumns(e))),this._parser.registerCsiHandler({intermediates:"'",final:"~"},(e=>this.deleteColumns(e))),this._parser.registerCsiHandler({intermediates:'"',final:"q"},(e=>this.selectProtected(e))),this._parser.registerCsiHandler({intermediates:"$",final:"p"},(e=>this.requestMode(e,!0))),this._parser.registerCsiHandler({prefix:"?",intermediates:"$",final:"p"},(e=>this.requestMode(e,!1))),this._parser.setExecuteHandler(n.C0.BEL,(()=>this.bell())),this._parser.setExecuteHandler(n.C0.LF,(()=>this.lineFeed())),this._parser.setExecuteHandler(n.C0.VT,(()=>this.lineFeed())),this._parser.setExecuteHandler(n.C0.FF,(()=>this.lineFeed())),this._parser.setExecuteHandler(n.C0.CR,(()=>this.carriageReturn())),this._parser.setExecuteHandler(n.C0.BS,(()=>this.backspace())),this._parser.setExecuteHandler(n.C0.HT,(()=>this.tab())),this._parser.setExecuteHandler(n.C0.SO,(()=>this.shiftOut())),this._parser.setExecuteHandler(n.C0.SI,(()=>this.shiftIn())),this._parser.setExecuteHandler(n.C1.IND,(()=>this.index())),this._parser.setExecuteHandler(n.C1.NEL,(()=>this.nextLine())),this._parser.setExecuteHandler(n.C1.HTS,(()=>this.tabSet())),this._parser.registerOscHandler(0,new g.OscHandler((e=>(this.setTitle(e),this.setIconName(e),!0)))),this._parser.registerOscHandler(1,new g.OscHandler((e=>this.setIconName(e)))),this._parser.registerOscHandler(2,new g.OscHandler((e=>this.setTitle(e)))),this._parser.registerOscHandler(4,new g.OscHandler((e=>this.setOrReportIndexedColor(e)))),this._parser.registerOscHandler(8,new g.OscHandler((e=>this.setHyperlink(e)))),this._parser.registerOscHandler(10,new g.OscHandler((e=>this.setOrReportFgColor(e)))),this._parser.registerOscHandler(11,new g.OscHandler((e=>this.setOrReportBgColor(e)))),this._parser.registerOscHandler(12,new g.OscHandler((e=>this.setOrReportCursorColor(e)))),this._parser.registerOscHandler(104,new g.OscHandler((e=>this.restoreIndexedColor(e)))),this._parser.registerOscHandler(110,new g.OscHandler((e=>this.restoreFgColor(e)))),this._parser.registerOscHandler(111,new g.OscHandler((e=>this.restoreBgColor(e)))),this._parser.registerOscHandler(112,new g.OscHandler((e=>this.restoreCursorColor(e)))),this._parser.registerEscHandler({final:"7"},(()=>this.saveCursor())),this._parser.registerEscHandler({final:"8"},(()=>this.restoreCursor())),this._parser.registerEscHandler({final:"D"},(()=>this.index())),this._parser.registerEscHandler({final:"E"},(()=>this.nextLine())),this._parser.registerEscHandler({final:"H"},(()=>this.tabSet())),this._parser.registerEscHandler({final:"M"},(()=>this.reverseIndex())),this._parser.registerEscHandler({final:"="},(()=>this.keypadApplicationMode())),this._parser.registerEscHandler({final:">"},(()=>this.keypadNumericMode())),this._parser.registerEscHandler({final:"c"},(()=>this.fullReset())),this._parser.registerEscHandler({final:"n"},(()=>this.setgLevel(2))),this._parser.registerEscHandler({final:"o"},(()=>this.setgLevel(3))),this._parser.registerEscHandler({final:"|"},(()=>this.setgLevel(3))),this._parser.registerEscHandler({final:"}"},(()=>this.setgLevel(2))),this._parser.registerEscHandler({final:"~"},(()=>this.setgLevel(1))),this._parser.registerEscHandler({intermediates:"%",final:"@"},(()=>this.selectDefaultCharset())),this._parser.registerEscHandler({intermediates:"%",final:"G"},(()=>this.selectDefaultCharset()));for(const e in o.CHARSETS)this._parser.registerEscHandler({intermediates:"(",final:e},(()=>this.selectCharset("("+e))),this._parser.registerEscHandler({intermediates:")",final:e},(()=>this.selectCharset(")"+e))),this._parser.registerEscHandler({intermediates:"*",final:e},(()=>this.selectCharset("*"+e))),this._parser.registerEscHandler({intermediates:"+",final:e},(()=>this.selectCharset("+"+e))),this._parser.registerEscHandler({intermediates:"-",final:e},(()=>this.selectCharset("-"+e))),this._parser.registerEscHandler({intermediates:".",final:e},(()=>this.selectCharset("."+e))),this._parser.registerEscHandler({intermediates:"/",final:e},(()=>this.selectCharset("/"+e)));this._parser.registerEscHandler({intermediates:"#",final:"8"},(()=>this.screenAlignmentPattern())),this._parser.setErrorHandler((e=>(this._logService.error("Parsing error: ",e),e))),this._parser.registerDcsHandler({intermediates:"$",final:"q"},new m.DcsHandler(((e,t)=>this.requestStatusString(e,t))))}_preserveStack(e,t,i,s){this._parseStack.paused=!0,this._parseStack.cursorStartX=e,this._parseStack.cursorStartY=t,this._parseStack.decodedLength=i,this._parseStack.position=s}_logSlowResolvingAsync(e){this._logService.logLevel<=f.LogLevelEnum.WARN&&Promise.race([e,new Promise(((e,t)=>setTimeout((()=>t("#SLOW_TIMEOUT")),5e3)))]).catch((e=>{if("#SLOW_TIMEOUT"!==e)throw e;console.warn("async parser handler taking longer than 5000 ms")}))}_getCurrentLinkId(){return this._curAttrData.extended.urlId}parse(e,t){let i,s=this._activeBuffer.x,r=this._activeBuffer.y,n=0;const o=this._parseStack.paused;if(o){if(i=this._parser.parse(this._parseBuffer,this._parseStack.decodedLength,t))return this._logSlowResolvingAsync(i),i;s=this._parseStack.cursorStartX,r=this._parseStack.cursorStartY,this._parseStack.paused=!1,e.length>C&&(n=this._parseStack.position+C)}if(this._logService.logLevel<=f.LogLevelEnum.DEBUG&&this._logService.debug("parsing data "+("string"==typeof e?` "${e}"`:` "${Array.prototype.map.call(e,(e=>String.fromCharCode(e))).join("")}"`)),this._logService.logLevel===f.LogLevelEnum.TRACE&&this._logService.trace("parsing data (codes)","string"==typeof e?e.split("").map((e=>e.charCodeAt(0))):e),this._parseBuffer.length<e.length&&this._parseBuffer.length<C&&(this._parseBuffer=new Uint32Array(Math.min(e.length,C))),o||this._dirtyRowTracker.clearRange(),e.length>C)for(let t=n;t<e.length;t+=C){const n=t+C<e.length?t+C:e.length,o="string"==typeof e?this._stringDecoder.decode(e.substring(t,n),this._parseBuffer):this._utf8Decoder.decode(e.subarray(t,n),this._parseBuffer);if(i=this._parser.parse(this._parseBuffer,o))return this._preserveStack(s,r,o,t),this._logSlowResolvingAsync(i),i}else if(!o){const t="string"==typeof e?this._stringDecoder.decode(e,this._parseBuffer):this._utf8Decoder.decode(e,this._parseBuffer);if(i=this._parser.parse(this._parseBuffer,t))return this._preserveStack(s,r,t,0),this._logSlowResolvingAsync(i),i}this._activeBuffer.x===s&&this._activeBuffer.y===r||this._onCursorMove.fire();const a=this._dirtyRowTracker.end+(this._bufferService.buffer.ybase-this._bufferService.buffer.ydisp),l=this._dirtyRowTracker.start+(this._bufferService.buffer.ybase-this._bufferService.buffer.ydisp);l<this._bufferService.rows&&this._onRequestRefreshRows.fire({start:Math.min(l,this._bufferService.rows-1),end:Math.min(a,this._bufferService.rows-1)})}print(e,t,i){let s,r;const n=this._charsetService.charset,o=this._optionsService.rawOptions.screenReaderMode,a=this._bufferService.cols,l=this._coreService.decPrivateModes.wraparound,u=this._coreService.modes.insertMode,_=this._curAttrData;let f=this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y);this._dirtyRowTracker.markDirty(this._activeBuffer.y),this._activeBuffer.x&&i-t>0&&2===f.getWidth(this._activeBuffer.x-1)&&f.setCellFromCodepoint(this._activeBuffer.x-1,0,1,_);let g=this._parser.precedingJoinState;for(let m=t;m<i;++m){if(s=e[m],s<127&&n){const e=n[String.fromCharCode(s)];e&&(s=e.charCodeAt(0))}const t=this._unicodeService.charProperties(s,g);r=p.UnicodeService.extractWidth(t);const i=p.UnicodeService.extractShouldJoin(t),v=i?p.UnicodeService.extractWidth(g):0;if(g=t,o&&this._onA11yChar.fire((0,h.stringFromCodePoint)(s)),this._getCurrentLinkId()&&this._oscLinkService.addLineToLink(this._getCurrentLinkId(),this._activeBuffer.ybase+this._activeBuffer.y),this._activeBuffer.x+r-v>a)if(l){const e=f;let t=this._activeBuffer.x-v;for(this._activeBuffer.x=v,this._activeBuffer.y++,this._activeBuffer.y===this._activeBuffer.scrollBottom+1?(this._activeBuffer.y--,this._bufferService.scroll(this._eraseAttrData(),!0)):(this._activeBuffer.y>=this._bufferService.rows&&(this._activeBuffer.y=this._bufferService.rows-1),this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y).isWrapped=!0),f=this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y),v>0&&f instanceof c.BufferLine&&f.copyCellsFrom(e,t,0,v,!1);t<a;)e.setCellFromCodepoint(t++,0,1,_)}else if(this._activeBuffer.x=a-1,2===r)continue;if(i&&this._activeBuffer.x){const e=f.getWidth(this._activeBuffer.x-1)?1:2;f.addCodepointToCell(this._activeBuffer.x-e,s,r);for(let e=r-v;--e>=0;)f.setCellFromCodepoint(this._activeBuffer.x++,0,0,_)}else if(u&&(f.insertCells(this._activeBuffer.x,r-v,this._activeBuffer.getNullCell(_)),2===f.getWidth(a-1)&&f.setCellFromCodepoint(a-1,d.NULL_CELL_CODE,d.NULL_CELL_WIDTH,_)),f.setCellFromCodepoint(this._activeBuffer.x++,s,r,_),r>0)for(;--r;)f.setCellFromCodepoint(this._activeBuffer.x++,0,0,_)}this._parser.precedingJoinState=g,this._activeBuffer.x<a&&i-t>0&&0===f.getWidth(this._activeBuffer.x)&&!f.hasContent(this._activeBuffer.x)&&f.setCellFromCodepoint(this._activeBuffer.x,0,1,_),this._dirtyRowTracker.markDirty(this._activeBuffer.y)}registerCsiHandler(e,t){return"t"!==e.final||e.prefix||e.intermediates?this._parser.registerCsiHandler(e,t):this._parser.registerCsiHandler(e,(e=>!y(e.params[0],this._optionsService.rawOptions.windowOptions)||t(e)))}registerDcsHandler(e,t){return this._parser.registerDcsHandler(e,new m.DcsHandler(t))}registerEscHandler(e,t){return this._parser.registerEscHandler(e,t)}registerOscHandler(e,t){return this._parser.registerOscHandler(e,new g.OscHandler(t))}bell(){return this._onRequestBell.fire(),!0}lineFeed(){return this._dirtyRowTracker.markDirty(this._activeBuffer.y),this._optionsService.rawOptions.convertEol&&(this._activeBuffer.x=0),this._activeBuffer.y++,this._activeBuffer.y===this._activeBuffer.scrollBottom+1?(this._activeBuffer.y--,this._bufferService.scroll(this._eraseAttrData())):this._activeBuffer.y>=this._bufferService.rows?this._activeBuffer.y=this._bufferService.rows-1:this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y).isWrapped=!1,this._activeBuffer.x>=this._bufferService.cols&&this._activeBuffer.x--,this._dirtyRowTracker.markDirty(this._activeBuffer.y),this._onLineFeed.fire(),!0}carriageReturn(){return this._activeBuffer.x=0,!0}backspace(){if(!this._coreService.decPrivateModes.reverseWraparound)return this._restrictCursor(),this._activeBuffer.x>0&&this._activeBuffer.x--,!0;if(this._restrictCursor(this._bufferService.cols),this._activeBuffer.x>0)this._activeBuffer.x--;else if(0===this._activeBuffer.x&&this._activeBuffer.y>this._activeBuffer.scrollTop&&this._activeBuffer.y<=this._activeBuffer.scrollBottom&&this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y)?.isWrapped){this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y).isWrapped=!1,this._activeBuffer.y--,this._activeBuffer.x=this._bufferService.cols-1;const e=this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y);e.hasWidth(this._activeBuffer.x)&&!e.hasContent(this._activeBuffer.x)&&this._activeBuffer.x--}return this._restrictCursor(),!0}tab(){if(this._activeBuffer.x>=this._bufferService.cols)return!0;const e=this._activeBuffer.x;return this._activeBuffer.x=this._activeBuffer.nextStop(),this._optionsService.rawOptions.screenReaderMode&&this._onA11yTab.fire(this._activeBuffer.x-e),!0}shiftOut(){return this._charsetService.setgLevel(1),!0}shiftIn(){return this._charsetService.setgLevel(0),!0}_restrictCursor(e=this._bufferService.cols-1){this._activeBuffer.x=Math.min(e,Math.max(0,this._activeBuffer.x)),this._activeBuffer.y=this._coreService.decPrivateModes.origin?Math.min(this._activeBuffer.scrollBottom,Math.max(this._activeBuffer.scrollTop,this._activeBuffer.y)):Math.min(this._bufferService.rows-1,Math.max(0,this._activeBuffer.y)),this._dirtyRowTracker.markDirty(this._activeBuffer.y)}_setCursor(e,t){this._dirtyRowTracker.markDirty(this._activeBuffer.y),this._coreService.decPrivateModes.origin?(this._activeBuffer.x=e,this._activeBuffer.y=this._activeBuffer.scrollTop+t):(this._activeBuffer.x=e,this._activeBuffer.y=t),this._restrictCursor(),this._dirtyRowTracker.markDirty(this._activeBuffer.y)}_moveCursor(e,t){this._restrictCursor(),this._setCursor(this._activeBuffer.x+e,this._activeBuffer.y+t)}cursorUp(e){const t=this._activeBuffer.y-this._activeBuffer.scrollTop;return t>=0?this._moveCursor(0,-Math.min(t,e.params[0]||1)):this._moveCursor(0,-(e.params[0]||1)),!0}cursorDown(e){const t=this._activeBuffer.scrollBottom-this._activeBuffer.y;return t>=0?this._moveCursor(0,Math.min(t,e.params[0]||1)):this._moveCursor(0,e.params[0]||1),!0}cursorForward(e){return this._moveCursor(e.params[0]||1,0),!0}cursorBackward(e){return this._moveCursor(-(e.params[0]||1),0),!0}cursorNextLine(e){return this.cursorDown(e),this._activeBuffer.x=0,!0}cursorPrecedingLine(e){return this.cursorUp(e),this._activeBuffer.x=0,!0}cursorCharAbsolute(e){return this._setCursor((e.params[0]||1)-1,this._activeBuffer.y),!0}cursorPosition(e){return this._setCursor(e.length>=2?(e.params[1]||1)-1:0,(e.params[0]||1)-1),!0}charPosAbsolute(e){return this._setCursor((e.params[0]||1)-1,this._activeBuffer.y),!0}hPositionRelative(e){return this._moveCursor(e.params[0]||1,0),!0}linePosAbsolute(e){return this._setCursor(this._activeBuffer.x,(e.params[0]||1)-1),!0}vPositionRelative(e){return this._moveCursor(0,e.params[0]||1),!0}hVPosition(e){return this.cursorPosition(e),!0}tabClear(e){const t=e.params[0];return 0===t?delete this._activeBuffer.tabs[this._activeBuffer.x]:3===t&&(this._activeBuffer.tabs={}),!0}cursorForwardTab(e){if(this._activeBuffer.x>=this._bufferService.cols)return!0;let t=e.params[0]||1;for(;t--;)this._activeBuffer.x=this._activeBuffer.nextStop();return!0}cursorBackwardTab(e){if(this._activeBuffer.x>=this._bufferService.cols)return!0;let t=e.params[0]||1;for(;t--;)this._activeBuffer.x=this._activeBuffer.prevStop();return!0}selectProtected(e){const t=e.params[0];return 1===t&&(this._curAttrData.bg|=536870912),2!==t&&0!==t||(this._curAttrData.bg&=-536870913),!0}_eraseInBufferLine(e,t,i,s=!1,r=!1){const n=this._activeBuffer.lines.get(this._activeBuffer.ybase+e);n.replaceCells(t,i,this._activeBuffer.getNullCell(this._eraseAttrData()),r),s&&(n.isWrapped=!1)}_resetBufferLine(e,t=!1){const i=this._activeBuffer.lines.get(this._activeBuffer.ybase+e);i&&(i.fill(this._activeBuffer.getNullCell(this._eraseAttrData()),t),this._bufferService.buffer.clearMarkers(this._activeBuffer.ybase+e),i.isWrapped=!1)}eraseInDisplay(e,t=!1){let i;switch(this._restrictCursor(this._bufferService.cols),e.params[0]){case 0:for(i=this._activeBuffer.y,this._dirtyRowTracker.markDirty(i),this._eraseInBufferLine(i++,this._activeBuffer.x,this._bufferService.cols,0===this._activeBuffer.x,t);i<this._bufferService.rows;i++)this._resetBufferLine(i,t);this._dirtyRowTracker.markDirty(i);break;case 1:for(i=this._activeBuffer.y,this._dirtyRowTracker.markDirty(i),this._eraseInBufferLine(i,0,this._activeBuffer.x+1,!0,t),this._activeBuffer.x+1>=this._bufferService.cols&&(this._activeBuffer.lines.get(i+1).isWrapped=!1);i--;)this._resetBufferLine(i,t);this._dirtyRowTracker.markDirty(0);break;case 2:if(this._optionsService.rawOptions.scrollOnEraseInDisplay){for(i=this._bufferService.rows,this._dirtyRowTracker.markRangeDirty(0,i-1);i--;){const e=this._activeBuffer.lines.get(this._activeBuffer.ybase+i);if(e?.getTrimmedLength())break}for(;i>=0;i--)this._bufferService.scroll(this._eraseAttrData())}else{for(i=this._bufferService.rows,this._dirtyRowTracker.markDirty(i-1);i--;)this._resetBufferLine(i,t);this._dirtyRowTracker.markDirty(0)}break;case 3:const e=this._activeBuffer.lines.length-this._bufferService.rows;e>0&&(this._activeBuffer.lines.trimStart(e),this._activeBuffer.ybase=Math.max(this._activeBuffer.ybase-e,0),this._activeBuffer.ydisp=Math.max(this._activeBuffer.ydisp-e,0),this._onScroll.fire(0))}return!0}eraseInLine(e,t=!1){switch(this._restrictCursor(this._bufferService.cols),e.params[0]){case 0:this._eraseInBufferLine(this._activeBuffer.y,this._activeBuffer.x,this._bufferService.cols,0===this._activeBuffer.x,t);break;case 1:this._eraseInBufferLine(this._activeBuffer.y,0,this._activeBuffer.x+1,!1,t);break;case 2:this._eraseInBufferLine(this._activeBuffer.y,0,this._bufferService.cols,!0,t)}return this._dirtyRowTracker.markDirty(this._activeBuffer.y),!0}insertLines(e){this._restrictCursor();let t=e.params[0]||1;if(this._activeBuffer.y>this._activeBuffer.scrollBottom||this._activeBuffer.y<this._activeBuffer.scrollTop)return!0;const i=this._activeBuffer.ybase+this._activeBuffer.y,s=this._bufferService.rows-1-this._activeBuffer.scrollBottom,r=this._bufferService.rows-1+this._activeBuffer.ybase-s+1;for(;t--;)this._activeBuffer.lines.splice(r-1,1),this._activeBuffer.lines.splice(i,0,this._activeBuffer.getBlankLine(this._eraseAttrData()));return this._dirtyRowTracker.markRangeDirty(this._activeBuffer.y,this._activeBuffer.scrollBottom),this._activeBuffer.x=0,!0}deleteLines(e){this._restrictCursor();let t=e.params[0]||1;if(this._activeBuffer.y>this._activeBuffer.scrollBottom||this._activeBuffer.y<this._activeBuffer.scrollTop)return!0;const i=this._activeBuffer.ybase+this._activeBuffer.y;let s;for(s=this._bufferService.rows-1-this._activeBuffer.scrollBottom,s=this._bufferService.rows-1+this._activeBuffer.ybase-s;t--;)this._activeBuffer.lines.splice(i,1),this._activeBuffer.lines.splice(s,0,this._activeBuffer.getBlankLine(this._eraseAttrData()));return this._dirtyRowTracker.markRangeDirty(this._activeBuffer.y,this._activeBuffer.scrollBottom),this._activeBuffer.x=0,!0}insertChars(e){this._restrictCursor();const t=this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y);return t&&(t.insertCells(this._activeBuffer.x,e.params[0]||1,this._activeBuffer.getNullCell(this._eraseAttrData())),this._dirtyRowTracker.markDirty(this._activeBuffer.y)),!0}deleteChars(e){this._restrictCursor();const t=this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y);return t&&(t.deleteCells(this._activeBuffer.x,e.params[0]||1,this._activeBuffer.getNullCell(this._eraseAttrData())),this._dirtyRowTracker.markDirty(this._activeBuffer.y)),!0}scrollUp(e){let t=e.params[0]||1;for(;t--;)this._activeBuffer.lines.splice(this._activeBuffer.ybase+this._activeBuffer.scrollTop,1),this._activeBuffer.lines.splice(this._activeBuffer.ybase+this._activeBuffer.scrollBottom,0,this._activeBuffer.getBlankLine(this._eraseAttrData()));return this._dirtyRowTracker.markRangeDirty(this._activeBuffer.scrollTop,this._activeBuffer.scrollBottom),!0}scrollDown(e){let t=e.params[0]||1;for(;t--;)this._activeBuffer.lines.splice(this._activeBuffer.ybase+this._activeBuffer.scrollBottom,1),this._activeBuffer.lines.splice(this._activeBuffer.ybase+this._activeBuffer.scrollTop,0,this._activeBuffer.getBlankLine(c.DEFAULT_ATTR_DATA));return this._dirtyRowTracker.markRangeDirty(this._activeBuffer.scrollTop,this._activeBuffer.scrollBottom),!0}scrollLeft(e){if(this._activeBuffer.y>this._activeBuffer.scrollBottom||this._activeBuffer.y<this._activeBuffer.scrollTop)return!0;const t=e.params[0]||1;for(let e=this._activeBuffer.scrollTop;e<=this._activeBuffer.scrollBottom;++e){const i=this._activeBuffer.lines.get(this._activeBuffer.ybase+e);i.deleteCells(0,t,this._activeBuffer.getNullCell(this._eraseAttrData())),i.isWrapped=!1}return this._dirtyRowTracker.markRangeDirty(this._activeBuffer.scrollTop,this._activeBuffer.scrollBottom),!0}scrollRight(e){if(this._activeBuffer.y>this._activeBuffer.scrollBottom||this._activeBuffer.y<this._activeBuffer.scrollTop)return!0;const t=e.params[0]||1;for(let e=this._activeBuffer.scrollTop;e<=this._activeBuffer.scrollBottom;++e){const i=this._activeBuffer.lines.get(this._activeBuffer.ybase+e);i.insertCells(0,t,this._activeBuffer.getNullCell(this._eraseAttrData())),i.isWrapped=!1}return this._dirtyRowTracker.markRangeDirty(this._activeBuffer.scrollTop,this._activeBuffer.scrollBottom),!0}insertColumns(e){if(this._activeBuffer.y>this._activeBuffer.scrollBottom||this._activeBuffer.y<this._activeBuffer.scrollTop)return!0;const t=e.params[0]||1;for(let e=this._activeBuffer.scrollTop;e<=this._activeBuffer.scrollBottom;++e){const i=this._activeBuffer.lines.get(this._activeBuffer.ybase+e);i.insertCells(this._activeBuffer.x,t,this._activeBuffer.getNullCell(this._eraseAttrData())),i.isWrapped=!1}return this._dirtyRowTracker.markRangeDirty(this._activeBuffer.scrollTop,this._activeBuffer.scrollBottom),!0}deleteColumns(e){if(this._activeBuffer.y>this._activeBuffer.scrollBottom||this._activeBuffer.y<this._activeBuffer.scrollTop)return!0;const t=e.params[0]||1;for(let e=this._activeBuffer.scrollTop;e<=this._activeBuffer.scrollBottom;++e){const i=this._activeBuffer.lines.get(this._activeBuffer.ybase+e);i.deleteCells(this._activeBuffer.x,t,this._activeBuffer.getNullCell(this._eraseAttrData())),i.isWrapped=!1}return this._dirtyRowTracker.markRangeDirty(this._activeBuffer.scrollTop,this._activeBuffer.scrollBottom),!0}eraseChars(e){this._restrictCursor();const t=this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y);return t&&(t.replaceCells(this._activeBuffer.x,this._activeBuffer.x+(e.params[0]||1),this._activeBuffer.getNullCell(this._eraseAttrData())),this._dirtyRowTracker.markDirty(this._activeBuffer.y)),!0}repeatPrecedingCharacter(e){const t=this._parser.precedingJoinState;if(!t)return!0;const i=e.params[0]||1,s=p.UnicodeService.extractWidth(t),r=this._activeBuffer.x-s,n=this._activeBuffer.lines.get(this._activeBuffer.ybase+this._activeBuffer.y).getString(r),o=new Uint32Array(n.length*i);let a=0;for(let e=0;e<n.length;){const t=n.codePointAt(e)||0;o[a++]=t,e+=t>65535?2:1}let l=a;for(let e=1;e<i;++e)o.copyWithin(l,0,a),l+=a;return this.print(o,0,l),!0}sendDeviceAttributesPrimary(e){return e.params[0]>0||(this._is("xterm")||this._is("rxvt-unicode")||this._is("screen")?this._coreService.triggerDataEvent(n.C0.ESC+"[?1;2c"):this._is("linux")&&this._coreService.triggerDataEvent(n.C0.ESC+"[?6c")),!0}sendDeviceAttributesSecondary(e){return e.params[0]>0||(this._is("xterm")?this._coreService.triggerDataEvent(n.C0.ESC+"[>0;276;0c"):this._is("rxvt-unicode")?this._coreService.triggerDataEvent(n.C0.ESC+"[>85;95;0c"):this._is("linux")?this._coreService.triggerDataEvent(e.params[0]+"c"):this._is("screen")&&this._coreService.triggerDataEvent(n.C0.ESC+"[>83;40003;0c")),!0}_is(e){return 0===(this._optionsService.rawOptions.termName+"").indexOf(e)}setMode(e){for(let t=0;t<e.length;t++)switch(e.params[t]){case 4:this._coreService.modes.insertMode=!0;break;case 20:this._optionsService.options.convertEol=!0}return!0}setModePrivate(e){for(let t=0;t<e.length;t++)switch(e.params[t]){case 1:this._coreService.decPrivateModes.applicationCursorKeys=!0;break;case 2:this._charsetService.setgCharset(0,o.DEFAULT_CHARSET),this._charsetService.setgCharset(1,o.DEFAULT_CHARSET),this._charsetService.setgCharset(2,o.DEFAULT_CHARSET),this._charsetService.setgCharset(3,o.DEFAULT_CHARSET);break;case 3:this._optionsService.rawOptions.windowOptions.setWinLines&&(this._bufferService.resize(132,this._bufferService.rows),this._onRequestReset.fire());break;case 6:this._coreService.decPrivateModes.origin=!0,this._setCursor(0,0);break;case 7:this._coreService.decPrivateModes.wraparound=!0;break;case 12:this._optionsService.options.cursorBlink=!0;break;case 45:this._coreService.decPrivateModes.reverseWraparound=!0;break;case 66:this._logService.debug("Serial port requested application keypad."),this._coreService.decPrivateModes.applicationKeypad=!0,this._onRequestSyncScrollBar.fire();break;case 9:this._coreMouseService.activeProtocol="X10";break;case 1e3:this._coreMouseService.activeProtocol="VT200";break;case 1002:this._coreMouseService.activeProtocol="DRAG";break;case 1003:this._coreMouseService.activeProtocol="ANY";break;case 1004:this._coreService.decPrivateModes.sendFocus=!0,this._onRequestSendFocus.fire();break;case 1005:this._logService.debug("DECSET 1005 not supported (see #2507)");break;case 1006:this._coreMouseService.activeEncoding="SGR";break;case 1015:this._logService.debug("DECSET 1015 not supported (see #2507)");break;case 1016:this._coreMouseService.activeEncoding="SGR_PIXELS";break;case 25:this._coreService.isCursorHidden=!1;break;case 1048:this.saveCursor();break;case 1049:this.saveCursor();case 47:case 1047:this._bufferService.buffers.activateAltBuffer(this._eraseAttrData()),this._coreService.isCursorInitialized=!0,this._onRequestRefreshRows.fire(void 0),this._onRequestSyncScrollBar.fire();break;case 2004:this._coreService.decPrivateModes.bracketedPasteMode=!0;break;case 2026:this._coreService.decPrivateModes.synchronizedOutput=!0}return!0}resetMode(e){for(let t=0;t<e.length;t++)switch(e.params[t]){case 4:this._coreService.modes.insertMode=!1;break;case 20:this._optionsService.options.convertEol=!1}return!0}resetModePrivate(e){for(let t=0;t<e.length;t++)switch(e.params[t]){case 1:this._coreService.decPrivateModes.applicationCursorKeys=!1;break;case 3:this._optionsService.rawOptions.windowOptions.setWinLines&&(this._bufferService.resize(80,this._bufferService.rows),this._onRequestReset.fire());break;case 6:this._coreService.decPrivateModes.origin=!1,this._setCursor(0,0);break;case 7:this._coreService.decPrivateModes.wraparound=!1;break;case 12:this._optionsService.options.cursorBlink=!1;break;case 45:this._coreService.decPrivateModes.reverseWraparound=!1;break;case 66:this._logService.debug("Switching back to normal keypad."),this._coreService.decPrivateModes.applicationKeypad=!1,this._onRequestSyncScrollBar.fire();break;case 9:case 1e3:case 1002:case 1003:this._coreMouseService.activeProtocol="NONE";break;case 1004:this._coreService.decPrivateModes.sendFocus=!1;break;case 1005:this._logService.debug("DECRST 1005 not supported (see #2507)");break;case 1006:case 1016:this._coreMouseService.activeEncoding="DEFAULT";break;case 1015:this._logService.debug("DECRST 1015 not supported (see #2507)");break;case 25:this._coreService.isCursorHidden=!0;break;case 1048:this.restoreCursor();break;case 1049:case 47:case 1047:this._bufferService.buffers.activateNormalBuffer(),1049===e.params[t]&&this.restoreCursor(),this._coreService.isCursorInitialized=!0,this._onRequestRefreshRows.fire(void 0),this._onRequestSyncScrollBar.fire();break;case 2004:this._coreService.decPrivateModes.bracketedPasteMode=!1;break;case 2026:this._coreService.decPrivateModes.synchronizedOutput=!1,this._onRequestRefreshRows.fire(void 0)}return!0}requestMode(e,t){const i=this._coreService.decPrivateModes,{activeProtocol:s,activeEncoding:r}=this._coreMouseService,o=this._coreService,{buffers:a,cols:l}=this._bufferService,{active:h,alt:c}=a,d=this._optionsService.rawOptions,u=e=>e?1:2,_=e.params[0];return f=_,p=t?2===_?4:4===_?u(o.modes.insertMode):12===_?3:20===_?u(d.convertEol):0:1===_?u(i.applicationCursorKeys):3===_?d.windowOptions.setWinLines?80===l?2:132===l?1:0:0:6===_?u(i.origin):7===_?u(i.wraparound):8===_?3:9===_?u("X10"===s):12===_?u(d.cursorBlink):25===_?u(!o.isCursorHidden):45===_?u(i.reverseWraparound):66===_?u(i.applicationKeypad):67===_?4:1e3===_?u("VT200"===s):1002===_?u("DRAG"===s):1003===_?u("ANY"===s):1004===_?u(i.sendFocus):1005===_?4:1006===_?u("SGR"===r):1015===_?4:1016===_?u("SGR_PIXELS"===r):1048===_?1:47===_||1047===_||1049===_?u(h===c):2004===_?u(i.bracketedPasteMode):2026===_?u(i.synchronizedOutput):0,o.triggerDataEvent(`${n.C0.ESC}[${t?"":"?"}${f};${p}$y`),!0;var f,p}_updateAttrColor(e,t,i,s,r){return 2===t?(e|=50331648,e&=-16777216,e|=_.AttributeData.fromColorRGB([i,s,r])):5===t&&(e&=-50331904,e|=33554432|255&i),e}_extractColor(e,t,i){const s=[0,0,-1,0,0,0];let r=0,n=0;do{if(s[n+r]=e.params[t+n],e.hasSubParams(t+n)){const i=e.getSubParams(t+n);let o=0;do{5===s[1]&&(r=1),s[n+o+1+r]=i[o]}while(++o<i.length&&o+n+1+r<s.length);break}if(5===s[1]&&n+r>=2||2===s[1]&&n+r>=5)break;s[1]&&(r=1)}while(++n+t<e.length&&n+r<s.length);for(let e=2;e<s.length;++e)-1===s[e]&&(s[e]=0);switch(s[0]){case 38:i.fg=this._updateAttrColor(i.fg,s[1],s[3],s[4],s[5]);break;case 48:i.bg=this._updateAttrColor(i.bg,s[1],s[3],s[4],s[5]);break;case 58:i.extended=i.extended.clone(),i.extended.underlineColor=this._updateAttrColor(i.extended.underlineColor,s[1],s[3],s[4],s[5])}return n}_processUnderline(e,t){t.extended=t.extended.clone(),(!~e||e>5)&&(e=1),t.extended.underlineStyle=e,t.fg|=268435456,0===e&&(t.fg&=-268435457),t.updateExtended()}_processSGR0(e){e.fg=c.DEFAULT_ATTR_DATA.fg,e.bg=c.DEFAULT_ATTR_DATA.bg,e.extended=e.extended.clone(),e.extended.underlineStyle=0,e.extended.underlineColor&=-67108864,e.updateExtended()}charAttributes(e){if(1===e.length&&0===e.params[0])return this._processSGR0(this._curAttrData),!0;const t=e.length;let i;const s=this._curAttrData;for(let r=0;r<t;r++)i=e.params[r],i>=30&&i<=37?(s.fg&=-50331904,s.fg|=16777216|i-30):i>=40&&i<=47?(s.bg&=-50331904,s.bg|=16777216|i-40):i>=90&&i<=97?(s.fg&=-50331904,s.fg|=16777224|i-90):i>=100&&i<=107?(s.bg&=-50331904,s.bg|=16777224|i-100):0===i?this._processSGR0(s):1===i?s.fg|=134217728:3===i?s.bg|=67108864:4===i?(s.fg|=268435456,this._processUnderline(e.hasSubParams(r)?e.getSubParams(r)[0]:1,s)):5===i?s.fg|=536870912:7===i?s.fg|=67108864:8===i?s.fg|=1073741824:9===i?s.fg|=2147483648:2===i?s.bg|=134217728:21===i?this._processUnderline(2,s):22===i?(s.fg&=-134217729,s.bg&=-134217729):23===i?s.bg&=-67108865:24===i?(s.fg&=-268435457,this._processUnderline(0,s)):25===i?s.fg&=-536870913:27===i?s.fg&=-67108865:28===i?s.fg&=-1073741825:29===i?s.fg&=2147483647:39===i?(s.fg&=-67108864,s.fg|=16777215&c.DEFAULT_ATTR_DATA.fg):49===i?(s.bg&=-67108864,s.bg|=16777215&c.DEFAULT_ATTR_DATA.bg):38===i||48===i||58===i?r+=this._extractColor(e,r,s):53===i?s.bg|=1073741824:55===i?s.bg&=-1073741825:59===i?(s.extended=s.extended.clone(),s.extended.underlineColor=-1,s.updateExtended()):100===i?(s.fg&=-67108864,s.fg|=16777215&c.DEFAULT_ATTR_DATA.fg,s.bg&=-67108864,s.bg|=16777215&c.DEFAULT_ATTR_DATA.bg):this._logService.debug("Unknown SGR attribute: %d.",i);return!0}deviceStatus(e){switch(e.params[0]){case 5:this._coreService.triggerDataEvent(`${n.C0.ESC}[0n`);break;case 6:const e=this._activeBuffer.y+1,t=this._activeBuffer.x+1;this._coreService.triggerDataEvent(`${n.C0.ESC}[${e};${t}R`)}return!0}deviceStatusPrivate(e){if(6===e.params[0]){const e=this._activeBuffer.y+1,t=this._activeBuffer.x+1;this._coreService.triggerDataEvent(`${n.C0.ESC}[?${e};${t}R`)}return!0}softReset(e){return this._coreService.isCursorHidden=!1,this._onRequestSyncScrollBar.fire(),this._activeBuffer.scrollTop=0,this._activeBuffer.scrollBottom=this._bufferService.rows-1,this._curAttrData=c.DEFAULT_ATTR_DATA.clone(),this._coreService.reset(),this._charsetService.reset(),this._activeBuffer.savedX=0,this._activeBuffer.savedY=this._activeBuffer.ybase,this._activeBuffer.savedCurAttrData.fg=this._curAttrData.fg,this._activeBuffer.savedCurAttrData.bg=this._curAttrData.bg,this._activeBuffer.savedCharset=this._charsetService.charset,this._coreService.decPrivateModes.origin=!1,!0}setCursorStyle(e){const t=0===e.length?1:e.params[0];if(0===t)this._coreService.decPrivateModes.cursorStyle=void 0,this._coreService.decPrivateModes.cursorBlink=void 0;else{switch(t){case 1:case 2:this._coreService.decPrivateModes.cursorStyle="block";break;case 3:case 4:this._coreService.decPrivateModes.cursorStyle="underline";break;case 5:case 6:this._coreService.decPrivateModes.cursorStyle="bar"}const e=t%2==1;this._coreService.decPrivateModes.cursorBlink=e}return!0}setScrollRegion(e){const t=e.params[0]||1;let i;return(e.length<2||(i=e.params[1])>this._bufferService.rows||0===i)&&(i=this._bufferService.rows),i>t&&(this._activeBuffer.scrollTop=t-1,this._activeBuffer.scrollBottom=i-1,this._setCursor(0,0)),!0}windowOptions(e){if(!y(e.params[0],this._optionsService.rawOptions.windowOptions))return!0;const t=e.length>1?e.params[1]:0;switch(e.params[0]){case 14:2!==t&&this._onRequestWindowsOptionsReport.fire(w.GET_WIN_SIZE_PIXELS);break;case 16:this._onRequestWindowsOptionsReport.fire(w.GET_CELL_SIZE_PIXELS);break;case 18:this._bufferService&&this._coreService.triggerDataEvent(`${n.C0.ESC}[8;${this._bufferService.rows};${this._bufferService.cols}t`);break;case 22:0!==t&&2!==t||(this._windowTitleStack.push(this._windowTitle),this._windowTitleStack.length>10&&this._windowTitleStack.shift()),0!==t&&1!==t||(this._iconNameStack.push(this._iconName),this._iconNameStack.length>10&&this._iconNameStack.shift());break;case 23:0!==t&&2!==t||this._windowTitleStack.length&&this.setTitle(this._windowTitleStack.pop()),0!==t&&1!==t||this._iconNameStack.length&&this.setIconName(this._iconNameStack.pop())}return!0}saveCursor(e){return this._activeBuffer.savedX=this._activeBuffer.x,this._activeBuffer.savedY=this._activeBuffer.ybase+this._activeBuffer.y,this._activeBuffer.savedCurAttrData.fg=this._curAttrData.fg,this._activeBuffer.savedCurAttrData.bg=this._curAttrData.bg,this._activeBuffer.savedCharset=this._charsetService.charset,!0}restoreCursor(e){return this._activeBuffer.x=this._activeBuffer.savedX||0,this._activeBuffer.y=Math.max(this._activeBuffer.savedY-this._activeBuffer.ybase,0),this._curAttrData.fg=this._activeBuffer.savedCurAttrData.fg,this._curAttrData.bg=this._activeBuffer.savedCurAttrData.bg,this._charsetService.charset=this._savedCharset,this._activeBuffer.savedCharset&&(this._charsetService.charset=this._activeBuffer.savedCharset),this._restrictCursor(),!0}setTitle(e){return this._windowTitle=e,this._onTitleChange.fire(e),!0}setIconName(e){return this._iconName=e,!0}setOrReportIndexedColor(e){const t=[],i=e.split(";");for(;i.length>1;){const e=i.shift(),s=i.shift();if(/^\d+$/.exec(e)){const i=parseInt(e);if(R(i))if("?"===s)t.push({type:0,index:i});else{const e=(0,v.parseColor)(s);e&&t.push({type:1,index:i,color:e})}}}return t.length&&this._onColor.fire(t),!0}setHyperlink(e){const t=e.indexOf(";");if(-1===t)return!0;const i=e.slice(0,t).trim(),s=e.slice(t+1);return s?this._createHyperlink(i,s):!i.trim()&&this._finishHyperlink()}_createHyperlink(e,t){this._getCurrentLinkId()&&this._finishHyperlink();const i=e.split(":");let s;const r=i.findIndex((e=>e.startsWith("id=")));return-1!==r&&(s=i[r].slice(3)||void 0),this._curAttrData.extended=this._curAttrData.extended.clone(),this._curAttrData.extended.urlId=this._oscLinkService.registerLink({id:s,uri:t}),this._curAttrData.updateExtended(),!0}_finishHyperlink(){return this._curAttrData.extended=this._curAttrData.extended.clone(),this._curAttrData.extended.urlId=0,this._curAttrData.updateExtended(),!0}_setOrReportSpecialColor(e,t){const i=e.split(";");for(let e=0;e<i.length&&!(t>=this._specialColors.length);++e,++t)if("?"===i[e])this._onColor.fire([{type:0,index:this._specialColors[t]}]);else{const s=(0,v.parseColor)(i[e]);s&&this._onColor.fire([{type:1,index:this._specialColors[t],color:s}])}return!0}setOrReportFgColor(e){return this._setOrReportSpecialColor(e,0)}setOrReportBgColor(e){return this._setOrReportSpecialColor(e,1)}setOrReportCursorColor(e){return this._setOrReportSpecialColor(e,2)}restoreIndexedColor(e){if(!e)return this._onColor.fire([{type:2}]),!0;const t=[],i=e.split(";");for(let e=0;e<i.length;++e)if(/^\d+$/.exec(i[e])){const s=parseInt(i[e]);R(s)&&t.push({type:2,index:s})}return t.length&&this._onColor.fire(t),!0}restoreFgColor(e){return this._onColor.fire([{type:2,index:256}]),!0}restoreBgColor(e){return this._onColor.fire([{type:2,index:257}]),!0}restoreCursorColor(e){return this._onColor.fire([{type:2,index:258}]),!0}nextLine(){return this._activeBuffer.x=0,this.index(),!0}keypadApplicationMode(){return this._logService.debug("Serial port requested application keypad."),this._coreService.decPrivateModes.applicationKeypad=!0,this._onRequestSyncScrollBar.fire(),!0}keypadNumericMode(){return this._logService.debug("Switching back to normal keypad."),this._coreService.decPrivateModes.applicationKeypad=!1,this._onRequestSyncScrollBar.fire(),!0}selectDefaultCharset(){return this._charsetService.setgLevel(0),this._charsetService.setgCharset(0,o.DEFAULT_CHARSET),!0}selectCharset(e){return 2!==e.length?(this.selectDefaultCharset(),!0):("/"===e[0]||this._charsetService.setgCharset(b[e[0]],o.CHARSETS[e[1]]||o.DEFAULT_CHARSET),!0)}index(){return this._restrictCursor(),this._activeBuffer.y++,this._activeBuffer.y===this._activeBuffer.scrollBottom+1?(this._activeBuffer.y--,this._bufferService.scroll(this._eraseAttrData())):this._activeBuffer.y>=this._bufferService.rows&&(this._activeBuffer.y=this._bufferService.rows-1),this._restrictCursor(),!0}tabSet(){return this._activeBuffer.tabs[this._activeBuffer.x]=!0,!0}reverseIndex(){if(this._restrictCursor(),this._activeBuffer.y===this._activeBuffer.scrollTop){const e=this._activeBuffer.scrollBottom-this._activeBuffer.scrollTop;this._activeBuffer.lines.shiftElements(this._activeBuffer.ybase+this._activeBuffer.y,e,1),this._activeBuffer.lines.set(this._activeBuffer.ybase+this._activeBuffer.y,this._activeBuffer.getBlankLine(this._eraseAttrData())),this._dirtyRowTracker.markRangeDirty(this._activeBuffer.scrollTop,this._activeBuffer.scrollBottom)}else this._activeBuffer.y--,this._restrictCursor();return!0}fullReset(){return this._parser.reset(),this._onRequestReset.fire(),!0}reset(){this._curAttrData=c.DEFAULT_ATTR_DATA.clone(),this._eraseAttrDataInternal=c.DEFAULT_ATTR_DATA.clone()}_eraseAttrData(){return this._eraseAttrDataInternal.bg&=-67108864,this._eraseAttrDataInternal.bg|=67108863&this._curAttrData.bg,this._eraseAttrDataInternal}setgLevel(e){return this._charsetService.setgLevel(e),!0}screenAlignmentPattern(){const e=new u.CellData;e.content=1<<22|"E".charCodeAt(0),e.fg=this._curAttrData.fg,e.bg=this._curAttrData.bg,this._setCursor(0,0);for(let t=0;t<this._bufferService.rows;++t){const i=this._activeBuffer.ybase+this._activeBuffer.y+t,s=this._activeBuffer.lines.get(i);s&&(s.fill(e),s.isWrapped=!1)}return this._dirtyRowTracker.markAllDirty(),this._setCursor(0,0),!0}requestStatusString(e,t){const i=this._bufferService.buffer,s=this._optionsService.rawOptions;return(e=>(this._coreService.triggerDataEvent(`${n.C0.ESC}${e}${n.C0.ESC}\\`),!0))('"q'===e?`P1$r${this._curAttrData.isProtected()?1:0}"q`:'"p'===e?'P1$r61;1"p':"r"===e?`P1$r${i.scrollTop+1};${i.scrollBottom+1}r`:"m"===e?"P1$r0m":" q"===e?`P1$r${{block:2,underline:4,bar:6}[s.cursorStyle]-(s.cursorBlink?1:0)} q`:"P0$r")}markRangeDirty(e,t){this._dirtyRowTracker.markRangeDirty(e,t)}}t.InputHandler=D;let L=class{constructor(e){this._bufferService=e,this.clearRange()}clearRange(){this.start=this._bufferService.buffer.y,this.end=this._bufferService.buffer.y}markDirty(e){e<this.start?this.start=e:e>this.end&&(this.end=e)}markRangeDirty(e,t){e>t&&(E=e,e=t,t=E),e<this.start&&(this.start=e),t>this.end&&(this.end=t)}markAllDirty(){this.markRangeDirty(0,this._bufferService.rows-1)}};function R(e){return 0<=e&&e<256}L=s([r(0,f.IBufferService)],L)},7710:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.FourKeyMap=t.TwoKeyMap=void 0;class i{constructor(){this._data={}}set(e,t,i){this._data[e]||(this._data[e]={}),this._data[e][t]=i}get(e,t){return this._data[e]?this._data[e][t]:void 0}clear(){this._data={}}}t.TwoKeyMap=i,t.FourKeyMap=class{constructor(){this._data=new i}set(e,t,s,r,n){this._data.get(e,t)||this._data.set(e,t,new i),this._data.get(e,t).set(s,r,n)}get(e,t,i,s){return this._data.get(e,t)?.get(i,s)}clear(){this._data.clear()}}},701:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.isChromeOS=t.isLinux=t.isWindows=t.isIphone=t.isIpad=t.isMac=t.isSafari=t.isLegacyEdge=t.isFirefox=t.isNode=void 0,t.getSafariVersion=function(){if(!t.isSafari)return 0;const e=i.match(/Version\/(\d+)/);return null===e||e.length<2?0:parseInt(e[1])},t.isNode="undefined"!=typeof process&&"title"in process;const i=t.isNode?"node":navigator.userAgent,s=t.isNode?"node":navigator.platform;t.isFirefox=i.includes("Firefox"),t.isLegacyEdge=i.includes("Edge"),t.isSafari=/^((?!chrome|android).)*safari/i.test(i),t.isMac=["Macintosh","MacIntel","MacPPC","Mac68K"].includes(s),t.isIpad="iPad"===s,t.isIphone="iPhone"===s,t.isWindows=["Windows","Win16","Win32","WinCE"].includes(s),t.isLinux=s.indexOf("Linux")>=0,t.isChromeOS=/\bCrOS\b/.test(i)},3087:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.SortedList=void 0;const s=i(6168);let r=0;t.SortedList=class{constructor(e){this._getKey=e,this._array=[],this._insertedValues=[],this._flushInsertedTask=new s.IdleTaskQueue,this._isFlushingInserted=!1,this._deletedIndices=[],this._flushDeletedTask=new s.IdleTaskQueue,this._isFlushingDeleted=!1}clear(){this._array.length=0,this._insertedValues.length=0,this._flushInsertedTask.clear(),this._isFlushingInserted=!1,this._deletedIndices.length=0,this._flushDeletedTask.clear(),this._isFlushingDeleted=!1}insert(e){this._flushCleanupDeleted(),0===this._insertedValues.length&&this._flushInsertedTask.enqueue((()=>this._flushInserted())),this._insertedValues.push(e)}_flushInserted(){const e=this._insertedValues.sort(((e,t)=>this._getKey(e)-this._getKey(t)));let t=0,i=0;const s=new Array(this._array.length+this._insertedValues.length);for(let r=0;r<s.length;r++)i>=this._array.length||this._getKey(e[t])<=this._getKey(this._array[i])?(s[r]=e[t],t++):s[r]=this._array[i++];this._array=s,this._insertedValues.length=0}_flushCleanupInserted(){!this._isFlushingInserted&&this._insertedValues.length>0&&this._flushInsertedTask.flush()}delete(e){if(this._flushCleanupInserted(),0===this._array.length)return!1;const t=this._getKey(e);if(void 0===t)return!1;if(r=this._search(t),-1===r)return!1;if(this._getKey(this._array[r])!==t)return!1;do{if(this._array[r]===e)return 0===this._deletedIndices.length&&this._flushDeletedTask.enqueue((()=>this._flushDeleted())),this._deletedIndices.push(r),!0}while(++r<this._array.length&&this._getKey(this._array[r])===t);return!1}_flushDeleted(){this._isFlushingDeleted=!0;const e=this._deletedIndices.sort(((e,t)=>e-t));let t=0;const i=new Array(this._array.length-e.length);let s=0;for(let r=0;r<this._array.length;r++)e[t]===r?t++:i[s++]=this._array[r];this._array=i,this._deletedIndices.length=0,this._isFlushingDeleted=!1}_flushCleanupDeleted(){!this._isFlushingDeleted&&this._deletedIndices.length>0&&this._flushDeletedTask.flush()}*getKeyIterator(e){if(this._flushCleanupInserted(),this._flushCleanupDeleted(),0!==this._array.length&&(r=this._search(e),!(r<0||r>=this._array.length)&&this._getKey(this._array[r])===e))do{yield this._array[r]}while(++r<this._array.length&&this._getKey(this._array[r])===e)}forEachByKey(e,t){if(this._flushCleanupInserted(),this._flushCleanupDeleted(),0!==this._array.length&&(r=this._search(e),!(r<0||r>=this._array.length)&&this._getKey(this._array[r])===e))do{t(this._array[r])}while(++r<this._array.length&&this._getKey(this._array[r])===e)}values(){return this._flushCleanupInserted(),this._flushCleanupDeleted(),[...this._array].values()}_search(e){let t=0,i=this._array.length-1;for(;i>=t;){let s=t+i>>1;const r=this._getKey(this._array[s]);if(r>e)i=s-1;else{if(!(r<e)){for(;s>0&&this._getKey(this._array[s-1])===e;)s--;return s}t=s+1}}return t}}},6168:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DebouncedIdleTask=t.IdleTaskQueue=t.PriorityTaskQueue=void 0;const s=i(701);class r{constructor(){this._tasks=[],this._i=0}enqueue(e){this._tasks.push(e),this._start()}flush(){for(;this._i<this._tasks.length;)this._tasks[this._i]()||this._i++;this.clear()}clear(){this._idleCallback&&(this._cancelCallback(this._idleCallback),this._idleCallback=void 0),this._i=0,this._tasks.length=0}_start(){this._idleCallback||(this._idleCallback=this._requestCallback(this._process.bind(this)))}_process(e){this._idleCallback=void 0;let t=0,i=0,s=e.timeRemaining(),r=0;for(;this._i<this._tasks.length;){if(t=performance.now(),this._tasks[this._i]()||this._i++,t=Math.max(1,performance.now()-t),i=Math.max(t,i),r=e.timeRemaining(),1.5*i>r)return s-t<-20&&console.warn(`task queue exceeded allotted deadline by ${Math.abs(Math.round(s-t))}ms`),void this._start();s=r}this.clear()}}class n extends r{_requestCallback(e){return setTimeout((()=>e(this._createDeadline(16))))}_cancelCallback(e){clearTimeout(e)}_createDeadline(e){const t=performance.now()+e;return{timeRemaining:()=>Math.max(0,t-performance.now())}}}t.PriorityTaskQueue=n,t.IdleTaskQueue=!s.isNode&&"requestIdleCallback"in window?class extends r{_requestCallback(e){return requestIdleCallback(e)}_cancelCallback(e){cancelIdleCallback(e)}}:n,t.DebouncedIdleTask=class{constructor(){this._queue=new t.IdleTaskQueue}set(e){this._queue.clear(),this._queue.enqueue(e)}flush(){this._queue.flush()}}},5882:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.updateWindowsModeWrappedState=function(e){const t=e.buffer.lines.get(e.buffer.ybase+e.buffer.y-1),i=t?.get(e.cols-1),r=e.buffer.lines.get(e.buffer.ybase+e.buffer.y);r&&i&&(r.isWrapped=i[s.CHAR_DATA_CODE_INDEX]!==s.NULL_CELL_CODE&&i[s.CHAR_DATA_CODE_INDEX]!==s.WHITESPACE_CELL_CODE)};const s=i(8938)},5451:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ExtendedAttrs=t.AttributeData=void 0;class i{constructor(){this.fg=0,this.bg=0,this.extended=new s}static toColorRGB(e){return[e>>>16&255,e>>>8&255,255&e]}static fromColorRGB(e){return(255&e[0])<<16|(255&e[1])<<8|255&e[2]}clone(){const e=new i;return e.fg=this.fg,e.bg=this.bg,e.extended=this.extended.clone(),e}isInverse(){return 67108864&this.fg}isBold(){return 134217728&this.fg}isUnderline(){return this.hasExtendedAttrs()&&0!==this.extended.underlineStyle?1:268435456&this.fg}isBlink(){return 536870912&this.fg}isInvisible(){return 1073741824&this.fg}isItalic(){return 67108864&this.bg}isDim(){return 134217728&this.bg}isStrikethrough(){return 2147483648&this.fg}isProtected(){return 536870912&this.bg}isOverline(){return 1073741824&this.bg}getFgColorMode(){return 50331648&this.fg}getBgColorMode(){return 50331648&this.bg}isFgRGB(){return!(50331648&~this.fg)}isBgRGB(){return!(50331648&~this.bg)}isFgPalette(){return 16777216==(50331648&this.fg)||33554432==(50331648&this.fg)}isBgPalette(){return 16777216==(50331648&this.bg)||33554432==(50331648&this.bg)}isFgDefault(){return!(50331648&this.fg)}isBgDefault(){return!(50331648&this.bg)}isAttributeDefault(){return 0===this.fg&&0===this.bg}getFgColor(){switch(50331648&this.fg){case 16777216:case 33554432:return 255&this.fg;case 50331648:return 16777215&this.fg;default:return-1}}getBgColor(){switch(50331648&this.bg){case 16777216:case 33554432:return 255&this.bg;case 50331648:return 16777215&this.bg;default:return-1}}hasExtendedAttrs(){return 268435456&this.bg}updateExtended(){this.extended.isEmpty()?this.bg&=-268435457:this.bg|=268435456}getUnderlineColor(){if(268435456&this.bg&&~this.extended.underlineColor)switch(50331648&this.extended.underlineColor){case 16777216:case 33554432:return 255&this.extended.underlineColor;case 50331648:return 16777215&this.extended.underlineColor;default:return this.getFgColor()}return this.getFgColor()}getUnderlineColorMode(){return 268435456&this.bg&&~this.extended.underlineColor?50331648&this.extended.underlineColor:this.getFgColorMode()}isUnderlineColorRGB(){return 268435456&this.bg&&~this.extended.underlineColor?!(50331648&~this.extended.underlineColor):this.isFgRGB()}isUnderlineColorPalette(){return 268435456&this.bg&&~this.extended.underlineColor?16777216==(50331648&this.extended.underlineColor)||33554432==(50331648&this.extended.underlineColor):this.isFgPalette()}isUnderlineColorDefault(){return 268435456&this.bg&&~this.extended.underlineColor?!(50331648&this.extended.underlineColor):this.isFgDefault()}getUnderlineStyle(){return 268435456&this.fg?268435456&this.bg?this.extended.underlineStyle:1:0}getUnderlineVariantOffset(){return this.extended.underlineVariantOffset}}t.AttributeData=i;class s{get ext(){return this._urlId?-469762049&this._ext|this.underlineStyle<<26:this._ext}set ext(e){this._ext=e}get underlineStyle(){return this._urlId?5:(469762048&this._ext)>>26}set underlineStyle(e){this._ext&=-469762049,this._ext|=e<<26&469762048}get underlineColor(){return 67108863&this._ext}set underlineColor(e){this._ext&=-67108864,this._ext|=67108863&e}get urlId(){return this._urlId}set urlId(e){this._urlId=e}get underlineVariantOffset(){const e=(3758096384&this._ext)>>29;return e<0?4294967288^e:e}set underlineVariantOffset(e){this._ext&=536870911,this._ext|=e<<29&3758096384}constructor(e=0,t=0){this._ext=0,this._urlId=0,this._ext=e,this._urlId=t}clone(){return new s(this._ext,this._urlId)}isEmpty(){return 0===this.underlineStyle&&0===this._urlId}}t.ExtendedAttrs=s},1073:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Buffer=t.MAX_BUFFER_SIZE=void 0;const s=i(5639),r=i(6168),n=i(5451),o=i(6107),a=i(732),l=i(3055),h=i(8938),c=i(8158),d=i(6760);t.MAX_BUFFER_SIZE=4294967295,t.Buffer=class{constructor(e,t,i){this._hasScrollback=e,this._optionsService=t,this._bufferService=i,this.ydisp=0,this.ybase=0,this.y=0,this.x=0,this.tabs={},this.savedY=0,this.savedX=0,this.savedCurAttrData=o.DEFAULT_ATTR_DATA.clone(),this.savedCharset=d.DEFAULT_CHARSET,this.markers=[],this._nullCell=l.CellData.fromCharData([0,h.NULL_CELL_CHAR,h.NULL_CELL_WIDTH,h.NULL_CELL_CODE]),this._whitespaceCell=l.CellData.fromCharData([0,h.WHITESPACE_CELL_CHAR,h.WHITESPACE_CELL_WIDTH,h.WHITESPACE_CELL_CODE]),this._isClearing=!1,this._memoryCleanupQueue=new r.IdleTaskQueue,this._memoryCleanupPosition=0,this._cols=this._bufferService.cols,this._rows=this._bufferService.rows,this.lines=new s.CircularList(this._getCorrectBufferLength(this._rows)),this.scrollTop=0,this.scrollBottom=this._rows-1,this.setupTabStops()}getNullCell(e){return e?(this._nullCell.fg=e.fg,this._nullCell.bg=e.bg,this._nullCell.extended=e.extended):(this._nullCell.fg=0,this._nullCell.bg=0,this._nullCell.extended=new n.ExtendedAttrs),this._nullCell}getWhitespaceCell(e){return e?(this._whitespaceCell.fg=e.fg,this._whitespaceCell.bg=e.bg,this._whitespaceCell.extended=e.extended):(this._whitespaceCell.fg=0,this._whitespaceCell.bg=0,this._whitespaceCell.extended=new n.ExtendedAttrs),this._whitespaceCell}getBlankLine(e,t){return new o.BufferLine(this._bufferService.cols,this.getNullCell(e),t)}get hasScrollback(){return this._hasScrollback&&this.lines.maxLength>this._rows}get isCursorInViewport(){const e=this.ybase+this.y-this.ydisp;return e>=0&&e<this._rows}_getCorrectBufferLength(e){if(!this._hasScrollback)return e;const i=e+this._optionsService.rawOptions.scrollback;return i>t.MAX_BUFFER_SIZE?t.MAX_BUFFER_SIZE:i}fillViewportRows(e){if(0===this.lines.length){void 0===e&&(e=o.DEFAULT_ATTR_DATA);let t=this._rows;for(;t--;)this.lines.push(this.getBlankLine(e))}}clear(){this.ydisp=0,this.ybase=0,this.y=0,this.x=0,this.lines=new s.CircularList(this._getCorrectBufferLength(this._rows)),this.scrollTop=0,this.scrollBottom=this._rows-1,this.setupTabStops()}resize(e,t){const i=this.getNullCell(o.DEFAULT_ATTR_DATA);let s=0;const r=this._getCorrectBufferLength(t);if(r>this.lines.maxLength&&(this.lines.maxLength=r),this.lines.length>0){if(this._cols<e)for(let t=0;t<this.lines.length;t++)s+=+this.lines.get(t).resize(e,i);let n=0;if(this._rows<t)for(let s=this._rows;s<t;s++)this.lines.length<t+this.ybase&&(this._optionsService.rawOptions.windowsMode||void 0!==this._optionsService.rawOptions.windowsPty.backend||void 0!==this._optionsService.rawOptions.windowsPty.buildNumber?this.lines.push(new o.BufferLine(e,i)):this.ybase>0&&this.lines.length<=this.ybase+this.y+n+1?(this.ybase--,n++,this.ydisp>0&&this.ydisp--):this.lines.push(new o.BufferLine(e,i)));else for(let e=this._rows;e>t;e--)this.lines.length>t+this.ybase&&(this.lines.length>this.ybase+this.y+1?this.lines.pop():(this.ybase++,this.ydisp++));if(r<this.lines.maxLength){const e=this.lines.length-r;e>0&&(this.lines.trimStart(e),this.ybase=Math.max(this.ybase-e,0),this.ydisp=Math.max(this.ydisp-e,0),this.savedY=Math.max(this.savedY-e,0)),this.lines.maxLength=r}this.x=Math.min(this.x,e-1),this.y=Math.min(this.y,t-1),n&&(this.y+=n),this.savedX=Math.min(this.savedX,e-1),this.scrollTop=0}if(this.scrollBottom=t-1,this._isReflowEnabled&&(this._reflow(e,t),this._cols>e))for(let t=0;t<this.lines.length;t++)s+=+this.lines.get(t).resize(e,i);this._cols=e,this._rows=t,this._memoryCleanupQueue.clear(),s>.1*this.lines.length&&(this._memoryCleanupPosition=0,this._memoryCleanupQueue.enqueue((()=>this._batchedMemoryCleanup())))}_batchedMemoryCleanup(){let e=!0;this._memoryCleanupPosition>=this.lines.length&&(this._memoryCleanupPosition=0,e=!1);let t=0;for(;this._memoryCleanupPosition<this.lines.length;)if(t+=this.lines.get(this._memoryCleanupPosition++).cleanupMemory(),t>100)return!0;return e}get _isReflowEnabled(){const e=this._optionsService.rawOptions.windowsPty;return e&&e.buildNumber?this._hasScrollback&&"conpty"===e.backend&&e.buildNumber>=21376:this._hasScrollback&&!this._optionsService.rawOptions.windowsMode}_reflow(e,t){this._cols!==e&&(e>this._cols?this._reflowLarger(e,t):this._reflowSmaller(e,t))}_reflowLarger(e,t){const i=this._optionsService.rawOptions.reflowCursorLine,s=(0,a.reflowLargerGetLinesToRemove)(this.lines,this._cols,e,this.ybase+this.y,this.getNullCell(o.DEFAULT_ATTR_DATA),i);if(s.length>0){const i=(0,a.reflowLargerCreateNewLayout)(this.lines,s);(0,a.reflowLargerApplyNewLayout)(this.lines,i.layout),this._reflowLargerAdjustViewport(e,t,i.countRemoved)}}_reflowLargerAdjustViewport(e,t,i){const s=this.getNullCell(o.DEFAULT_ATTR_DATA);let r=i;for(;r-- >0;)0===this.ybase?(this.y>0&&this.y--,this.lines.length<t&&this.lines.push(new o.BufferLine(e,s))):(this.ydisp===this.ybase&&this.ydisp--,this.ybase--);this.savedY=Math.max(this.savedY-i,0)}_reflowSmaller(e,t){const i=this._optionsService.rawOptions.reflowCursorLine,s=this.getNullCell(o.DEFAULT_ATTR_DATA),r=[];let n=0;for(let l=this.lines.length-1;l>=0;l--){let h=this.lines.get(l);if(!h||!h.isWrapped&&h.getTrimmedLength()<=e)continue;const c=[h];for(;h.isWrapped&&l>0;)h=this.lines.get(--l),c.unshift(h);if(!i){const e=this.ybase+this.y;if(e>=l&&e<l+c.length)continue}const d=c[c.length-1].getTrimmedLength(),u=(0,a.reflowSmallerGetNewLineLengths)(c,this._cols,e),_=u.length-c.length;let f;f=0===this.ybase&&this.y!==this.lines.length-1?Math.max(0,this.y-this.lines.maxLength+_):Math.max(0,this.lines.length-this.lines.maxLength+_);const p=[];for(let e=0;e<_;e++){const e=this.getBlankLine(o.DEFAULT_ATTR_DATA,!0);p.push(e)}p.length>0&&(r.push({start:l+c.length+n,newLines:p}),n+=p.length),c.push(...p);let g=u.length-1,m=u[g];0===m&&(g--,m=u[g]);let v=c.length-_-1,S=d;for(;v>=0;){const e=Math.min(S,m);if(void 0===c[g])break;if(c[g].copyCellsFrom(c[v],S-e,m-e,e,!0),m-=e,0===m&&(g--,m=u[g]),S-=e,0===S){v--;const e=Math.max(v,0);S=(0,a.getWrappedLineTrimmedLength)(c,e,this._cols)}}for(let t=0;t<c.length;t++)u[t]<e&&c[t].setCell(u[t],s);let b=_-f;for(;b-- >0;)0===this.ybase?this.y<t-1?(this.y++,this.lines.pop()):(this.ybase++,this.ydisp++):this.ybase<Math.min(this.lines.maxLength,this.lines.length+n)-t&&(this.ybase===this.ydisp&&this.ydisp++,this.ybase++);this.savedY=Math.min(this.savedY+_,this.ybase+t-1)}if(r.length>0){const e=[],t=[];for(let e=0;e<this.lines.length;e++)t.push(this.lines.get(e));const i=this.lines.length;let s=i-1,o=0,a=r[o];this.lines.length=Math.min(this.lines.maxLength,this.lines.length+n);let l=0;for(let h=Math.min(this.lines.maxLength-1,i+n-1);h>=0;h--)if(a&&a.start>s+l){for(let e=a.newLines.length-1;e>=0;e--)this.lines.set(h--,a.newLines[e]);h++,e.push({index:s+1,amount:a.newLines.length}),l+=a.newLines.length,a=r[++o]}else this.lines.set(h,t[s--]);let h=0;for(let t=e.length-1;t>=0;t--)e[t].index+=h,this.lines.onInsertEmitter.fire(e[t]),h+=e[t].amount;const c=Math.max(0,i+n-this.lines.maxLength);c>0&&this.lines.onTrimEmitter.fire(c)}}translateBufferLineToString(e,t,i=0,s){const r=this.lines.get(e);return r?r.translateToString(t,i,s):""}getWrappedRangeForLine(e){let t=e,i=e;for(;t>0&&this.lines.get(t).isWrapped;)t--;for(;i+1<this.lines.length&&this.lines.get(i+1).isWrapped;)i++;return{first:t,last:i}}setupTabStops(e){for(null!=e?this.tabs[e]||(e=this.prevStop(e)):(this.tabs={},e=0);e<this._cols;e+=this._optionsService.rawOptions.tabStopWidth)this.tabs[e]=!0}prevStop(e){for(null==e&&(e=this.x);!this.tabs[--e]&&e>0;);return e>=this._cols?this._cols-1:e<0?0:e}nextStop(e){for(null==e&&(e=this.x);!this.tabs[++e]&&e<this._cols;);return e>=this._cols?this._cols-1:e<0?0:e}clearMarkers(e){this._isClearing=!0;for(let t=0;t<this.markers.length;t++)this.markers[t].line===e&&(this.markers[t].dispose(),this.markers.splice(t--,1));this._isClearing=!1}clearAllMarkers(){this._isClearing=!0;for(let e=0;e<this.markers.length;e++)this.markers[e].dispose();this.markers.length=0,this._isClearing=!1}addMarker(e){const t=new c.Marker(e);return this.markers.push(t),t.register(this.lines.onTrim((e=>{t.line-=e,t.line<0&&t.dispose()}))),t.register(this.lines.onInsert((e=>{t.line>=e.index&&(t.line+=e.amount)}))),t.register(this.lines.onDelete((e=>{t.line>=e.index&&t.line<e.index+e.amount&&t.dispose(),t.line>e.index&&(t.line-=e.amount)}))),t.register(t.onDispose((()=>this._removeMarker(t)))),t}_removeMarker(e){this._isClearing||this.markers.splice(this.markers.indexOf(e),1)}}},6107:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferLine=t.DEFAULT_ATTR_DATA=void 0;const s=i(5451),r=i(3055),n=i(8938),o=i(726);t.DEFAULT_ATTR_DATA=Object.freeze(new s.AttributeData);let a=0;class l{constructor(e,t,i=!1){this.isWrapped=i,this._combined={},this._extendedAttrs={},this._data=new Uint32Array(3*e);const s=t||r.CellData.fromCharData([0,n.NULL_CELL_CHAR,n.NULL_CELL_WIDTH,n.NULL_CELL_CODE]);for(let t=0;t<e;++t)this.setCell(t,s);this.length=e}get(e){const t=this._data[3*e+0],i=2097151&t;return[this._data[3*e+1],2097152&t?this._combined[e]:i?(0,o.stringFromCodePoint)(i):"",t>>22,2097152&t?this._combined[e].charCodeAt(this._combined[e].length-1):i]}set(e,t){this._data[3*e+1]=t[n.CHAR_DATA_ATTR_INDEX],t[n.CHAR_DATA_CHAR_INDEX].length>1?(this._combined[e]=t[1],this._data[3*e+0]=2097152|e|t[n.CHAR_DATA_WIDTH_INDEX]<<22):this._data[3*e+0]=t[n.CHAR_DATA_CHAR_INDEX].charCodeAt(0)|t[n.CHAR_DATA_WIDTH_INDEX]<<22}getWidth(e){return this._data[3*e+0]>>22}hasWidth(e){return 12582912&this._data[3*e+0]}getFg(e){return this._data[3*e+1]}getBg(e){return this._data[3*e+2]}hasContent(e){return 4194303&this._data[3*e+0]}getCodePoint(e){const t=this._data[3*e+0];return 2097152&t?this._combined[e].charCodeAt(this._combined[e].length-1):2097151&t}isCombined(e){return 2097152&this._data[3*e+0]}getString(e){const t=this._data[3*e+0];return 2097152&t?this._combined[e]:2097151&t?(0,o.stringFromCodePoint)(2097151&t):""}isProtected(e){return 536870912&this._data[3*e+2]}loadCell(e,t){return a=3*e,t.content=this._data[a+0],t.fg=this._data[a+1],t.bg=this._data[a+2],2097152&t.content&&(t.combinedData=this._combined[e]),268435456&t.bg&&(t.extended=this._extendedAttrs[e]),t}setCell(e,t){2097152&t.content&&(this._combined[e]=t.combinedData),268435456&t.bg&&(this._extendedAttrs[e]=t.extended),this._data[3*e+0]=t.content,this._data[3*e+1]=t.fg,this._data[3*e+2]=t.bg}setCellFromCodepoint(e,t,i,s){268435456&s.bg&&(this._extendedAttrs[e]=s.extended),this._data[3*e+0]=t|i<<22,this._data[3*e+1]=s.fg,this._data[3*e+2]=s.bg}addCodepointToCell(e,t,i){let s=this._data[3*e+0];2097152&s?this._combined[e]+=(0,o.stringFromCodePoint)(t):2097151&s?(this._combined[e]=(0,o.stringFromCodePoint)(2097151&s)+(0,o.stringFromCodePoint)(t),s&=-2097152,s|=2097152):s=t|1<<22,i&&(s&=-12582913,s|=i<<22),this._data[3*e+0]=s}insertCells(e,t,i){if((e%=this.length)&&2===this.getWidth(e-1)&&this.setCellFromCodepoint(e-1,0,1,i),t<this.length-e){const s=new r.CellData;for(let i=this.length-e-t-1;i>=0;--i)this.setCell(e+t+i,this.loadCell(e+i,s));for(let s=0;s<t;++s)this.setCell(e+s,i)}else for(let t=e;t<this.length;++t)this.setCell(t,i);2===this.getWidth(this.length-1)&&this.setCellFromCodepoint(this.length-1,0,1,i)}deleteCells(e,t,i){if(e%=this.length,t<this.length-e){const s=new r.CellData;for(let i=0;i<this.length-e-t;++i)this.setCell(e+i,this.loadCell(e+t+i,s));for(let e=this.length-t;e<this.length;++e)this.setCell(e,i)}else for(let t=e;t<this.length;++t)this.setCell(t,i);e&&2===this.getWidth(e-1)&&this.setCellFromCodepoint(e-1,0,1,i),0!==this.getWidth(e)||this.hasContent(e)||this.setCellFromCodepoint(e,0,1,i)}replaceCells(e,t,i,s=!1){if(s)for(e&&2===this.getWidth(e-1)&&!this.isProtected(e-1)&&this.setCellFromCodepoint(e-1,0,1,i),t<this.length&&2===this.getWidth(t-1)&&!this.isProtected(t)&&this.setCellFromCodepoint(t,0,1,i);e<t&&e<this.length;)this.isProtected(e)||this.setCell(e,i),e++;else for(e&&2===this.getWidth(e-1)&&this.setCellFromCodepoint(e-1,0,1,i),t<this.length&&2===this.getWidth(t-1)&&this.setCellFromCodepoint(t,0,1,i);e<t&&e<this.length;)this.setCell(e++,i)}resize(e,t){if(e===this.length)return 4*this._data.length*2<this._data.buffer.byteLength;const i=3*e;if(e>this.length){if(this._data.buffer.byteLength>=4*i)this._data=new Uint32Array(this._data.buffer,0,i);else{const e=new Uint32Array(i);e.set(this._data),this._data=e}for(let i=this.length;i<e;++i)this.setCell(i,t)}else{this._data=this._data.subarray(0,i);const t=Object.keys(this._combined);for(let i=0;i<t.length;i++){const s=parseInt(t[i],10);s>=e&&delete this._combined[s]}const s=Object.keys(this._extendedAttrs);for(let t=0;t<s.length;t++){const i=parseInt(s[t],10);i>=e&&delete this._extendedAttrs[i]}}return this.length=e,4*i*2<this._data.buffer.byteLength}cleanupMemory(){if(4*this._data.length*2<this._data.buffer.byteLength){const e=new Uint32Array(this._data.length);return e.set(this._data),this._data=e,1}return 0}fill(e,t=!1){if(t)for(let t=0;t<this.length;++t)this.isProtected(t)||this.setCell(t,e);else{this._combined={},this._extendedAttrs={};for(let t=0;t<this.length;++t)this.setCell(t,e)}}copyFrom(e){this.length!==e.length?this._data=new Uint32Array(e._data):this._data.set(e._data),this.length=e.length,this._combined={};for(const t in e._combined)this._combined[t]=e._combined[t];this._extendedAttrs={};for(const t in e._extendedAttrs)this._extendedAttrs[t]=e._extendedAttrs[t];this.isWrapped=e.isWrapped}clone(){const e=new l(0);e._data=new Uint32Array(this._data),e.length=this.length;for(const t in this._combined)e._combined[t]=this._combined[t];for(const t in this._extendedAttrs)e._extendedAttrs[t]=this._extendedAttrs[t];return e.isWrapped=this.isWrapped,e}getTrimmedLength(){for(let e=this.length-1;e>=0;--e)if(4194303&this._data[3*e+0])return e+(this._data[3*e+0]>>22);return 0}getNoBgTrimmedLength(){for(let e=this.length-1;e>=0;--e)if(4194303&this._data[3*e+0]||50331648&this._data[3*e+2])return e+(this._data[3*e+0]>>22);return 0}copyCellsFrom(e,t,i,s,r){const n=e._data;if(r)for(let r=s-1;r>=0;r--){for(let e=0;e<3;e++)this._data[3*(i+r)+e]=n[3*(t+r)+e];268435456&n[3*(t+r)+2]&&(this._extendedAttrs[i+r]=e._extendedAttrs[t+r])}else for(let r=0;r<s;r++){for(let e=0;e<3;e++)this._data[3*(i+r)+e]=n[3*(t+r)+e];268435456&n[3*(t+r)+2]&&(this._extendedAttrs[i+r]=e._extendedAttrs[t+r])}const o=Object.keys(e._combined);for(let s=0;s<o.length;s++){const r=parseInt(o[s],10);r>=t&&(this._combined[r-t+i]=e._combined[r])}}translateToString(e,t,i,s){t=t??0,i=i??this.length,e&&(i=Math.min(i,this.getTrimmedLength())),s&&(s.length=0);let r="";for(;t<i;){const e=this._data[3*t+0],i=2097151&e,a=2097152&e?this._combined[t]:i?(0,o.stringFromCodePoint)(i):n.WHITESPACE_CELL_CHAR;if(r+=a,s)for(let e=0;e<a.length;++e)s.push(t);t+=e>>22||1}return s&&s.push(t),r}}t.BufferLine=l},9384:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.getRangeLength=function(e,t){if(e.start.y>e.end.y)throw new Error(`Buffer range end (${e.end.x}, ${e.end.y}) cannot be before start (${e.start.x}, ${e.start.y})`);return t*(e.end.y-e.start.y)+(e.end.x-e.start.x+1)}},732:(e,t)=>{function i(e,t,i){if(t===e.length-1)return e[t].getTrimmedLength();const s=!e[t].hasContent(i-1)&&1===e[t].getWidth(i-1),r=2===e[t+1].getWidth(0);return s&&r?i-1:i}Object.defineProperty(t,"__esModule",{value:!0}),t.reflowLargerGetLinesToRemove=function(e,t,s,r,n,o){const a=[];for(let l=0;l<e.length-1;l++){let h=l,c=e.get(++h);if(!c.isWrapped)continue;const d=[e.get(l)];for(;h<e.length&&c.isWrapped;)d.push(c),c=e.get(++h);if(!o&&r>=l&&r<h){l+=d.length-1;continue}let u=0,_=i(d,u,t),f=1,p=0;for(;f<d.length;){const e=i(d,f,t),r=e-p,o=s-_,a=Math.min(r,o);d[u].copyCellsFrom(d[f],p,_,a,!1),_+=a,_===s&&(u++,_=0),p+=a,p===e&&(f++,p=0),0===_&&0!==u&&2===d[u-1].getWidth(s-1)&&(d[u].copyCellsFrom(d[u-1],s-1,_++,1,!1),d[u-1].setCell(s-1,n))}d[u].replaceCells(_,s,n);let g=0;for(let e=d.length-1;e>0&&(e>u||0===d[e].getTrimmedLength());e--)g++;g>0&&(a.push(l+d.length-g),a.push(g)),l+=d.length-1}return a},t.reflowLargerCreateNewLayout=function(e,t){const i=[];let s=0,r=t[s],n=0;for(let o=0;o<e.length;o++)if(r===o){const i=t[++s];e.onDeleteEmitter.fire({index:o-n,amount:i}),o+=i-1,n+=i,r=t[++s]}else i.push(o);return{layout:i,countRemoved:n}},t.reflowLargerApplyNewLayout=function(e,t){const i=[];for(let s=0;s<t.length;s++)i.push(e.get(t[s]));for(let t=0;t<i.length;t++)e.set(t,i[t]);e.length=t.length},t.reflowSmallerGetNewLineLengths=function(e,t,s){const r=[],n=e.map(((s,r)=>i(e,r,t))).reduce(((e,t)=>e+t));let o=0,a=0,l=0;for(;l<n;){if(n-l<s){r.push(n-l);break}o+=s;const h=i(e,a,t);o>h&&(o-=h,a++);const c=2===e[a].getWidth(o-1);c&&o--;const d=c?s-1:s;r.push(d),l+=d}return r},t.getWrappedLineTrimmedLength=i},4097:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferSet=void 0;const s=i(7150),r=i(1073),n=i(802);class o extends s.Disposable{constructor(e,t){super(),this._optionsService=e,this._bufferService=t,this._onBufferActivate=this._register(new n.Emitter),this.onBufferActivate=this._onBufferActivate.event,this.reset(),this._register(this._optionsService.onSpecificOptionChange("scrollback",(()=>this.resize(this._bufferService.cols,this._bufferService.rows)))),this._register(this._optionsService.onSpecificOptionChange("tabStopWidth",(()=>this.setupTabStops())))}reset(){this._normal=new r.Buffer(!0,this._optionsService,this._bufferService),this._normal.fillViewportRows(),this._alt=new r.Buffer(!1,this._optionsService,this._bufferService),this._activeBuffer=this._normal,this._onBufferActivate.fire({activeBuffer:this._normal,inactiveBuffer:this._alt}),this.setupTabStops()}get alt(){return this._alt}get active(){return this._activeBuffer}get normal(){return this._normal}activateNormalBuffer(){this._activeBuffer!==this._normal&&(this._normal.x=this._alt.x,this._normal.y=this._alt.y,this._alt.clearAllMarkers(),this._alt.clear(),this._activeBuffer=this._normal,this._onBufferActivate.fire({activeBuffer:this._normal,inactiveBuffer:this._alt}))}activateAltBuffer(e){this._activeBuffer!==this._alt&&(this._alt.fillViewportRows(e),this._alt.x=this._normal.x,this._alt.y=this._normal.y,this._activeBuffer=this._alt,this._onBufferActivate.fire({activeBuffer:this._alt,inactiveBuffer:this._normal}))}resize(e,t){this._normal.resize(e,t),this._alt.resize(e,t),this.setupTabStops(e)}setupTabStops(e){this._normal.setupTabStops(e),this._alt.setupTabStops(e)}}t.BufferSet=o},3055:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.CellData=void 0;const s=i(726),r=i(8938),n=i(5451);class o extends n.AttributeData{constructor(){super(...arguments),this.content=0,this.fg=0,this.bg=0,this.extended=new n.ExtendedAttrs,this.combinedData=""}static fromCharData(e){const t=new o;return t.setFromCharData(e),t}isCombined(){return 2097152&this.content}getWidth(){return this.content>>22}getChars(){return 2097152&this.content?this.combinedData:2097151&this.content?(0,s.stringFromCodePoint)(2097151&this.content):""}getCode(){return this.isCombined()?this.combinedData.charCodeAt(this.combinedData.length-1):2097151&this.content}setFromCharData(e){this.fg=e[r.CHAR_DATA_ATTR_INDEX],this.bg=0;let t=!1;if(e[r.CHAR_DATA_CHAR_INDEX].length>2)t=!0;else if(2===e[r.CHAR_DATA_CHAR_INDEX].length){const i=e[r.CHAR_DATA_CHAR_INDEX].charCodeAt(0);if(55296<=i&&i<=56319){const s=e[r.CHAR_DATA_CHAR_INDEX].charCodeAt(1);56320<=s&&s<=57343?this.content=1024*(i-55296)+s-56320+65536|e[r.CHAR_DATA_WIDTH_INDEX]<<22:t=!0}else t=!0}else this.content=e[r.CHAR_DATA_CHAR_INDEX].charCodeAt(0)|e[r.CHAR_DATA_WIDTH_INDEX]<<22;t&&(this.combinedData=e[r.CHAR_DATA_CHAR_INDEX],this.content=2097152|e[r.CHAR_DATA_WIDTH_INDEX]<<22)}getAsCharData(){return[this.fg,this.getChars(),this.getWidth(),this.getCode()]}}t.CellData=o},8938:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.WHITESPACE_CELL_CODE=t.WHITESPACE_CELL_WIDTH=t.WHITESPACE_CELL_CHAR=t.NULL_CELL_CODE=t.NULL_CELL_WIDTH=t.NULL_CELL_CHAR=t.CHAR_DATA_CODE_INDEX=t.CHAR_DATA_WIDTH_INDEX=t.CHAR_DATA_CHAR_INDEX=t.CHAR_DATA_ATTR_INDEX=t.DEFAULT_EXT=t.DEFAULT_ATTR=t.DEFAULT_COLOR=void 0,t.DEFAULT_COLOR=0,t.DEFAULT_ATTR=t.DEFAULT_COLOR<<9|256,t.DEFAULT_EXT=0,t.CHAR_DATA_ATTR_INDEX=0,t.CHAR_DATA_CHAR_INDEX=1,t.CHAR_DATA_WIDTH_INDEX=2,t.CHAR_DATA_CODE_INDEX=3,t.NULL_CELL_CHAR="",t.NULL_CELL_WIDTH=1,t.NULL_CELL_CODE=0,t.WHITESPACE_CELL_CHAR=" ",t.WHITESPACE_CELL_WIDTH=1,t.WHITESPACE_CELL_CODE=32},8158:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Marker=void 0;const s=i(802),r=i(7150);class n{get id(){return this._id}constructor(e){this.line=e,this.isDisposed=!1,this._disposables=[],this._id=n._nextId++,this._onDispose=this.register(new s.Emitter),this.onDispose=this._onDispose.event}dispose(){this.isDisposed||(this.isDisposed=!0,this.line=-1,this._onDispose.fire(),(0,r.dispose)(this._disposables),this._disposables.length=0)}register(e){return this._disposables.push(e),e}}t.Marker=n,n._nextId=1},6760:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DEFAULT_CHARSET=t.CHARSETS=void 0,t.CHARSETS={},t.DEFAULT_CHARSET=t.CHARSETS.B,t.CHARSETS[0]={"`":"◆",a:"▒",b:"␉",c:"␌",d:"␍",e:"␊",f:"°",g:"±",h:"␤",i:"␋",j:"┘",k:"┐",l:"┌",m:"└",n:"┼",o:"⎺",p:"⎻",q:"─",r:"⎼",s:"⎽",t:"├",u:"┤",v:"┴",w:"┬",x:"│",y:"≤",z:"≥","{":"π","|":"≠","}":"£","~":"·"},t.CHARSETS.A={"#":"£"},t.CHARSETS.B=void 0,t.CHARSETS[4]={"#":"£","@":"¾","[":"ij","\\":"½","]":"|","{":"¨","|":"f","}":"¼","~":"´"},t.CHARSETS.C=t.CHARSETS[5]={"[":"Ä","\\":"Ö","]":"Å","^":"Ü","`":"é","{":"ä","|":"ö","}":"å","~":"ü"},t.CHARSETS.R={"#":"£","@":"à","[":"°","\\":"ç","]":"§","{":"é","|":"ù","}":"è","~":"¨"},t.CHARSETS.Q={"@":"à","[":"â","\\":"ç","]":"ê","^":"î","`":"ô","{":"é","|":"ù","}":"è","~":"û"},t.CHARSETS.K={"@":"§","[":"Ä","\\":"Ö","]":"Ü","{":"ä","|":"ö","}":"ü","~":"ß"},t.CHARSETS.Y={"#":"£","@":"§","[":"°","\\":"ç","]":"é","`":"ù","{":"à","|":"ò","}":"è","~":"ì"},t.CHARSETS.E=t.CHARSETS[6]={"@":"Ä","[":"Æ","\\":"Ø","]":"Å","^":"Ü","`":"ä","{":"æ","|":"ø","}":"å","~":"ü"},t.CHARSETS.Z={"#":"£","@":"§","[":"¡","\\":"Ñ","]":"¿","{":"°","|":"ñ","}":"ç"},t.CHARSETS.H=t.CHARSETS[7]={"@":"É","[":"Ä","\\":"Ö","]":"Å","^":"Ü","`":"é","{":"ä","|":"ö","}":"å","~":"ü"},t.CHARSETS["="]={"#":"ù","@":"à","[":"é","\\":"ç","]":"ê","^":"î",_:"è","`":"ô","{":"ä","|":"ö","}":"ü","~":"û"}},3534:(e,t)=>{var i,s,r;Object.defineProperty(t,"__esModule",{value:!0}),t.C1_ESCAPED=t.C1=t.C0=void 0,function(e){e.NUL="\0",e.SOH="",e.STX="",e.ETX="",e.EOT="",e.ENQ="",e.ACK="",e.BEL="",e.BS="\b",e.HT="\t",e.LF="\n",e.VT="\v",e.FF="\f",e.CR="\r",e.SO="",e.SI="",e.DLE="",e.DC1="",e.DC2="",e.DC3="",e.DC4="",e.NAK="",e.SYN="",e.ETB="",e.CAN="",e.EM="",e.SUB="",e.ESC="",e.FS="",e.GS="",e.RS="",e.US="",e.SP=" ",e.DEL=""}(i||(t.C0=i={})),function(e){e.PAD="€",e.HOP="",e.BPH="‚",e.NBH="ƒ",e.IND="„",e.NEL="…",e.SSA="†",e.ESA="‡",e.HTS="ˆ",e.HTJ="‰",e.VTS="Š",e.PLD="‹",e.PLU="Œ",e.RI="",e.SS2="Ž",e.SS3="",e.DCS="",e.PU1="‘",e.PU2="’",e.STS="“",e.CCH="”",e.MW="•",e.SPA="–",e.EPA="—",e.SOS="˜",e.SGCI="™",e.SCI="š",e.CSI="›",e.ST="œ",e.OSC="",e.PM="ž",e.APC="Ÿ"}(s||(t.C1=s={})),function(e){e.ST=`${i.ESC}\\`}(r||(t.C1_ESCAPED=r={}))},706:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.evaluateKeyboardEvent=function(e,t,i,n){const o={type:0,cancel:!1,key:void 0},a=(e.shiftKey?1:0)|(e.altKey?2:0)|(e.ctrlKey?4:0)|(e.metaKey?8:0);switch(e.keyCode){case 0:"UIKeyInputUpArrow"===e.key?o.key=t?s.C0.ESC+"OA":s.C0.ESC+"[A":"UIKeyInputLeftArrow"===e.key?o.key=t?s.C0.ESC+"OD":s.C0.ESC+"[D":"UIKeyInputRightArrow"===e.key?o.key=t?s.C0.ESC+"OC":s.C0.ESC+"[C":"UIKeyInputDownArrow"===e.key&&(o.key=t?s.C0.ESC+"OB":s.C0.ESC+"[B");break;case 8:o.key=e.ctrlKey?"\b":s.C0.DEL,e.altKey&&(o.key=s.C0.ESC+o.key);break;case 9:if(e.shiftKey){o.key=s.C0.ESC+"[Z";break}o.key=s.C0.HT,o.cancel=!0;break;case 13:o.key=e.altKey?s.C0.ESC+s.C0.CR:s.C0.CR,o.cancel=!0;break;case 27:o.key=s.C0.ESC,e.altKey&&(o.key=s.C0.ESC+s.C0.ESC),o.cancel=!0;break;case 37:if(e.metaKey)break;o.key=a?s.C0.ESC+"[1;"+(a+1)+"D":t?s.C0.ESC+"OD":s.C0.ESC+"[D";break;case 39:if(e.metaKey)break;o.key=a?s.C0.ESC+"[1;"+(a+1)+"C":t?s.C0.ESC+"OC":s.C0.ESC+"[C";break;case 38:if(e.metaKey)break;o.key=a?s.C0.ESC+"[1;"+(a+1)+"A":t?s.C0.ESC+"OA":s.C0.ESC+"[A";break;case 40:if(e.metaKey)break;o.key=a?s.C0.ESC+"[1;"+(a+1)+"B":t?s.C0.ESC+"OB":s.C0.ESC+"[B";break;case 45:e.shiftKey||e.ctrlKey||(o.key=s.C0.ESC+"[2~");break;case 46:o.key=a?s.C0.ESC+"[3;"+(a+1)+"~":s.C0.ESC+"[3~";break;case 36:o.key=a?s.C0.ESC+"[1;"+(a+1)+"H":t?s.C0.ESC+"OH":s.C0.ESC+"[H";break;case 35:o.key=a?s.C0.ESC+"[1;"+(a+1)+"F":t?s.C0.ESC+"OF":s.C0.ESC+"[F";break;case 33:e.shiftKey?o.type=2:e.ctrlKey?o.key=s.C0.ESC+"[5;"+(a+1)+"~":o.key=s.C0.ESC+"[5~";break;case 34:e.shiftKey?o.type=3:e.ctrlKey?o.key=s.C0.ESC+"[6;"+(a+1)+"~":o.key=s.C0.ESC+"[6~";break;case 112:o.key=a?s.C0.ESC+"[1;"+(a+1)+"P":s.C0.ESC+"OP";break;case 113:o.key=a?s.C0.ESC+"[1;"+(a+1)+"Q":s.C0.ESC+"OQ";break;case 114:o.key=a?s.C0.ESC+"[1;"+(a+1)+"R":s.C0.ESC+"OR";break;case 115:o.key=a?s.C0.ESC+"[1;"+(a+1)+"S":s.C0.ESC+"OS";break;case 116:o.key=a?s.C0.ESC+"[15;"+(a+1)+"~":s.C0.ESC+"[15~";break;case 117:o.key=a?s.C0.ESC+"[17;"+(a+1)+"~":s.C0.ESC+"[17~";break;case 118:o.key=a?s.C0.ESC+"[18;"+(a+1)+"~":s.C0.ESC+"[18~";break;case 119:o.key=a?s.C0.ESC+"[19;"+(a+1)+"~":s.C0.ESC+"[19~";break;case 120:o.key=a?s.C0.ESC+"[20;"+(a+1)+"~":s.C0.ESC+"[20~";break;case 121:o.key=a?s.C0.ESC+"[21;"+(a+1)+"~":s.C0.ESC+"[21~";break;case 122:o.key=a?s.C0.ESC+"[23;"+(a+1)+"~":s.C0.ESC+"[23~";break;case 123:o.key=a?s.C0.ESC+"[24;"+(a+1)+"~":s.C0.ESC+"[24~";break;default:if(!e.ctrlKey||e.shiftKey||e.altKey||e.metaKey)if(i&&!n||!e.altKey||e.metaKey)!i||e.altKey||e.ctrlKey||e.shiftKey||!e.metaKey?e.key&&!e.ctrlKey&&!e.altKey&&!e.metaKey&&e.keyCode>=48&&1===e.key.length?o.key=e.key:e.key&&e.ctrlKey&&("_"===e.key&&(o.key=s.C0.US),"@"===e.key&&(o.key=s.C0.NUL)):65===e.keyCode&&(o.type=1);else{const t=r[e.keyCode],i=t?.[e.shiftKey?1:0];if(i)o.key=s.C0.ESC+i;else if(e.keyCode>=65&&e.keyCode<=90){const t=e.ctrlKey?e.keyCode-64:e.keyCode+32;let i=String.fromCharCode(t);e.shiftKey&&(i=i.toUpperCase()),o.key=s.C0.ESC+i}else if(32===e.keyCode)o.key=s.C0.ESC+(e.ctrlKey?s.C0.NUL:" ");else if("Dead"===e.key&&e.code.startsWith("Key")){let t=e.code.slice(3,4);e.shiftKey||(t=t.toLowerCase()),o.key=s.C0.ESC+t,o.cancel=!0}}else e.keyCode>=65&&e.keyCode<=90?o.key=String.fromCharCode(e.keyCode-64):32===e.keyCode?o.key=s.C0.NUL:e.keyCode>=51&&e.keyCode<=55?o.key=String.fromCharCode(e.keyCode-51+27):56===e.keyCode?o.key=s.C0.DEL:219===e.keyCode?o.key=s.C0.ESC:220===e.keyCode?o.key=s.C0.FS:221===e.keyCode&&(o.key=s.C0.GS)}return o};const s=i(3534),r={48:["0",")"],49:["1","!"],50:["2","@"],51:["3","#"],52:["4","$"],53:["5","%"],54:["6","^"],55:["7","&"],56:["8","*"],57:["9","("],186:[";",":"],187:["=","+"],188:[",","<"],189:["-","_"],190:[".",">"],191:["/","?"],192:["`","~"],219:["[","{"],220:["\\","|"],221:["]","}"],222:["'",'"']}},726:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Utf8ToUtf32=t.StringToUtf32=void 0,t.stringFromCodePoint=function(e){return e>65535?(e-=65536,String.fromCharCode(55296+(e>>10))+String.fromCharCode(e%1024+56320)):String.fromCharCode(e)},t.utf32ToString=function(e,t=0,i=e.length){let s="";for(let r=t;r<i;++r){let t=e[r];t>65535?(t-=65536,s+=String.fromCharCode(55296+(t>>10))+String.fromCharCode(t%1024+56320)):s+=String.fromCharCode(t)}return s},t.StringToUtf32=class{constructor(){this._interim=0}clear(){this._interim=0}decode(e,t){const i=e.length;if(!i)return 0;let s=0,r=0;if(this._interim){const i=e.charCodeAt(r++);56320<=i&&i<=57343?t[s++]=1024*(this._interim-55296)+i-56320+65536:(t[s++]=this._interim,t[s++]=i),this._interim=0}for(let n=r;n<i;++n){const r=e.charCodeAt(n);if(55296<=r&&r<=56319){if(++n>=i)return this._interim=r,s;const o=e.charCodeAt(n);56320<=o&&o<=57343?t[s++]=1024*(r-55296)+o-56320+65536:(t[s++]=r,t[s++]=o)}else 65279!==r&&(t[s++]=r)}return s}},t.Utf8ToUtf32=class{constructor(){this.interim=new Uint8Array(3)}clear(){this.interim.fill(0)}decode(e,t){const i=e.length;if(!i)return 0;let s,r,n,o,a=0,l=0,h=0;if(this.interim[0]){let s=!1,r=this.interim[0];r&=192==(224&r)?31:224==(240&r)?15:7;let n,o=0;for(;(n=63&this.interim[++o])&&o<4;)r<<=6,r|=n;const l=192==(224&this.interim[0])?2:224==(240&this.interim[0])?3:4,c=l-o;for(;h<c;){if(h>=i)return 0;if(n=e[h++],128!=(192&n)){h--,s=!0;break}this.interim[o++]=n,r<<=6,r|=63&n}s||(2===l?r<128?h--:t[a++]=r:3===l?r<2048||r>=55296&&r<=57343||65279===r||(t[a++]=r):r<65536||r>1114111||(t[a++]=r)),this.interim.fill(0)}const c=i-4;let d=h;for(;d<i;){for(;!(!(d<c)||128&(s=e[d])||128&(r=e[d+1])||128&(n=e[d+2])||128&(o=e[d+3]));)t[a++]=s,t[a++]=r,t[a++]=n,t[a++]=o,d+=4;if(s=e[d++],s<128)t[a++]=s;else if(192==(224&s)){if(d>=i)return this.interim[0]=s,a;if(r=e[d++],128!=(192&r)){d--;continue}if(l=(31&s)<<6|63&r,l<128){d--;continue}t[a++]=l}else if(224==(240&s)){if(d>=i)return this.interim[0]=s,a;if(r=e[d++],128!=(192&r)){d--;continue}if(d>=i)return this.interim[0]=s,this.interim[1]=r,a;if(n=e[d++],128!=(192&n)){d--;continue}if(l=(15&s)<<12|(63&r)<<6|63&n,l<2048||l>=55296&&l<=57343||65279===l)continue;t[a++]=l}else if(240==(248&s)){if(d>=i)return this.interim[0]=s,a;if(r=e[d++],128!=(192&r)){d--;continue}if(d>=i)return this.interim[0]=s,this.interim[1]=r,a;if(n=e[d++],128!=(192&n)){d--;continue}if(d>=i)return this.interim[0]=s,this.interim[1]=r,this.interim[2]=n,a;if(o=e[d++],128!=(192&o)){d--;continue}if(l=(7&s)<<18|(63&r)<<12|(63&n)<<6|63&o,l<65536||l>1114111)continue;t[a++]=l}}return a}}},7428:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.UnicodeV6=void 0;const s=i(6415),r=[[768,879],[1155,1158],[1160,1161],[1425,1469],[1471,1471],[1473,1474],[1476,1477],[1479,1479],[1536,1539],[1552,1557],[1611,1630],[1648,1648],[1750,1764],[1767,1768],[1770,1773],[1807,1807],[1809,1809],[1840,1866],[1958,1968],[2027,2035],[2305,2306],[2364,2364],[2369,2376],[2381,2381],[2385,2388],[2402,2403],[2433,2433],[2492,2492],[2497,2500],[2509,2509],[2530,2531],[2561,2562],[2620,2620],[2625,2626],[2631,2632],[2635,2637],[2672,2673],[2689,2690],[2748,2748],[2753,2757],[2759,2760],[2765,2765],[2786,2787],[2817,2817],[2876,2876],[2879,2879],[2881,2883],[2893,2893],[2902,2902],[2946,2946],[3008,3008],[3021,3021],[3134,3136],[3142,3144],[3146,3149],[3157,3158],[3260,3260],[3263,3263],[3270,3270],[3276,3277],[3298,3299],[3393,3395],[3405,3405],[3530,3530],[3538,3540],[3542,3542],[3633,3633],[3636,3642],[3655,3662],[3761,3761],[3764,3769],[3771,3772],[3784,3789],[3864,3865],[3893,3893],[3895,3895],[3897,3897],[3953,3966],[3968,3972],[3974,3975],[3984,3991],[3993,4028],[4038,4038],[4141,4144],[4146,4146],[4150,4151],[4153,4153],[4184,4185],[4448,4607],[4959,4959],[5906,5908],[5938,5940],[5970,5971],[6002,6003],[6068,6069],[6071,6077],[6086,6086],[6089,6099],[6109,6109],[6155,6157],[6313,6313],[6432,6434],[6439,6440],[6450,6450],[6457,6459],[6679,6680],[6912,6915],[6964,6964],[6966,6970],[6972,6972],[6978,6978],[7019,7027],[7616,7626],[7678,7679],[8203,8207],[8234,8238],[8288,8291],[8298,8303],[8400,8431],[12330,12335],[12441,12442],[43014,43014],[43019,43019],[43045,43046],[64286,64286],[65024,65039],[65056,65059],[65279,65279],[65529,65531]],n=[[68097,68099],[68101,68102],[68108,68111],[68152,68154],[68159,68159],[119143,119145],[119155,119170],[119173,119179],[119210,119213],[119362,119364],[917505,917505],[917536,917631],[917760,917999]];let o;t.UnicodeV6=class{constructor(){if(this.version="6",!o){o=new Uint8Array(65536),o.fill(1),o[0]=0,o.fill(0,1,32),o.fill(0,127,160),o.fill(2,4352,4448),o[9001]=2,o[9002]=2,o.fill(2,11904,42192),o[12351]=1,o.fill(2,44032,55204),o.fill(2,63744,64256),o.fill(2,65040,65050),o.fill(2,65072,65136),o.fill(2,65280,65377),o.fill(2,65504,65511);for(let e=0;e<r.length;++e)o.fill(0,r[e][0],r[e][1]+1)}}wcwidth(e){return e<32?0:e<127?1:e<65536?o[e]:function(e,t){let i,s=0,r=t.length-1;if(e<t[0][0]||e>t[r][1])return!1;for(;r>=s;)if(i=s+r>>1,e>t[i][1])s=i+1;else{if(!(e<t[i][0]))return!0;r=i-1}return!1}(e,n)?0:e>=131072&&e<=196605||e>=196608&&e<=262141?2:1}charProperties(e,t){let i=this.wcwidth(e),r=0===i&&0!==t;if(r){const e=s.UnicodeService.extractWidth(t);0===e?r=!1:e>i&&(i=e)}return s.UnicodeService.createPropertyValue(0,i,r)}}},3562:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.WriteBuffer=void 0;const s=i(7150),r=i(802);class n extends s.Disposable{constructor(e){super(),this._action=e,this._writeBuffer=[],this._callbacks=[],this._pendingData=0,this._bufferOffset=0,this._isSyncWriting=!1,this._syncCalls=0,this._didUserInput=!1,this._onWriteParsed=this._register(new r.Emitter),this.onWriteParsed=this._onWriteParsed.event}handleUserInput(){this._didUserInput=!0}writeSync(e,t){if(void 0!==t&&this._syncCalls>t)return void(this._syncCalls=0);if(this._pendingData+=e.length,this._writeBuffer.push(e),this._callbacks.push(void 0),this._syncCalls++,this._isSyncWriting)return;let i;for(this._isSyncWriting=!0;i=this._writeBuffer.shift();){this._action(i);const e=this._callbacks.shift();e&&e()}this._pendingData=0,this._bufferOffset=2147483647,this._isSyncWriting=!1,this._syncCalls=0}write(e,t){if(this._pendingData>5e7)throw new Error("write data discarded, use flow control to avoid losing data");if(!this._writeBuffer.length){if(this._bufferOffset=0,this._didUserInput)return this._didUserInput=!1,this._pendingData+=e.length,this._writeBuffer.push(e),this._callbacks.push(t),void this._innerWrite();setTimeout((()=>this._innerWrite()))}this._pendingData+=e.length,this._writeBuffer.push(e),this._callbacks.push(t)}_innerWrite(e=0,t=!0){const i=e||performance.now();for(;this._writeBuffer.length>this._bufferOffset;){const e=this._writeBuffer[this._bufferOffset],s=this._action(e,t);if(s){const e=e=>performance.now()-i>=12?setTimeout((()=>this._innerWrite(0,e))):this._innerWrite(i,e);return void s.catch((e=>(queueMicrotask((()=>{throw e})),Promise.resolve(!1)))).then(e)}const r=this._callbacks[this._bufferOffset];if(r&&r(),this._bufferOffset++,this._pendingData-=e.length,performance.now()-i>=12)break}this._writeBuffer.length>this._bufferOffset?(this._bufferOffset>50&&(this._writeBuffer=this._writeBuffer.slice(this._bufferOffset),this._callbacks=this._callbacks.slice(this._bufferOffset),this._bufferOffset=0),setTimeout((()=>this._innerWrite()))):(this._writeBuffer.length=0,this._callbacks.length=0,this._pendingData=0,this._bufferOffset=0),this._onWriteParsed.fire()}}t.WriteBuffer=n},8693:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.parseColor=function(e){if(!e)return;let t=e.toLowerCase();if(0===t.indexOf("rgb:")){t=t.slice(4);const e=i.exec(t);if(e){const t=e[1]?15:e[4]?255:e[7]?4095:65535;return[Math.round(parseInt(e[1]||e[4]||e[7]||e[10],16)/t*255),Math.round(parseInt(e[2]||e[5]||e[8]||e[11],16)/t*255),Math.round(parseInt(e[3]||e[6]||e[9]||e[12],16)/t*255)]}}else if(0===t.indexOf("#")&&(t=t.slice(1),s.exec(t)&&[3,6,9,12].includes(t.length))){const e=t.length/3,i=[0,0,0];for(let s=0;s<3;++s){const r=parseInt(t.slice(e*s,e*s+e),16);i[s]=1===e?r<<4:2===e?r:3===e?r>>4:r>>8}return i}},t.toRgbString=function(e,t=16){const[i,s,n]=e;return`rgb:${r(i,t)}/${r(s,t)}/${r(n,t)}`};const i=/^([\da-f])\/([\da-f])\/([\da-f])$|^([\da-f]{2})\/([\da-f]{2})\/([\da-f]{2})$|^([\da-f]{3})\/([\da-f]{3})\/([\da-f]{3})$|^([\da-f]{4})\/([\da-f]{4})\/([\da-f]{4})$/,s=/^[\da-f]+$/;function r(e,t){const i=e.toString(16),s=i.length<2?"0"+i:i;switch(t){case 4:return i[0];case 8:return s;case 12:return(s+s).slice(0,3);default:return s+s}}},1263:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.PAYLOAD_LIMIT=void 0,t.PAYLOAD_LIMIT=1e7},9823:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DcsHandler=t.DcsParser=void 0;const s=i(726),r=i(7262),n=i(1263),o=[];t.DcsParser=class{constructor(){this._handlers=Object.create(null),this._active=o,this._ident=0,this._handlerFb=()=>{},this._stack={paused:!1,loopPosition:0,fallThrough:!1}}dispose(){this._handlers=Object.create(null),this._handlerFb=()=>{},this._active=o}registerHandler(e,t){void 0===this._handlers[e]&&(this._handlers[e]=[]);const i=this._handlers[e];return i.push(t),{dispose:()=>{const e=i.indexOf(t);-1!==e&&i.splice(e,1)}}}clearHandler(e){this._handlers[e]&&delete this._handlers[e]}setHandlerFallback(e){this._handlerFb=e}reset(){if(this._active.length)for(let e=this._stack.paused?this._stack.loopPosition-1:this._active.length-1;e>=0;--e)this._active[e].unhook(!1);this._stack.paused=!1,this._active=o,this._ident=0}hook(e,t){if(this.reset(),this._ident=e,this._active=this._handlers[e]||o,this._active.length)for(let e=this._active.length-1;e>=0;e--)this._active[e].hook(t);else this._handlerFb(this._ident,"HOOK",t)}put(e,t,i){if(this._active.length)for(let s=this._active.length-1;s>=0;s--)this._active[s].put(e,t,i);else this._handlerFb(this._ident,"PUT",(0,s.utf32ToString)(e,t,i))}unhook(e,t=!0){if(this._active.length){let i=!1,s=this._active.length-1,r=!1;if(this._stack.paused&&(s=this._stack.loopPosition-1,i=t,r=this._stack.fallThrough,this._stack.paused=!1),!r&&!1===i){for(;s>=0&&(i=this._active[s].unhook(e),!0!==i);s--)if(i instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=s,this._stack.fallThrough=!1,i;s--}for(;s>=0;s--)if(i=this._active[s].unhook(!1),i instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=s,this._stack.fallThrough=!0,i}else this._handlerFb(this._ident,"UNHOOK",e);this._active=o,this._ident=0}};const a=new r.Params;a.addParam(0),t.DcsHandler=class{constructor(e){this._handler=e,this._data="",this._params=a,this._hitLimit=!1}hook(e){this._params=e.length>1||e.params[0]?e.clone():a,this._data="",this._hitLimit=!1}put(e,t,i){this._hitLimit||(this._data+=(0,s.utf32ToString)(e,t,i),this._data.length>n.PAYLOAD_LIMIT&&(this._data="",this._hitLimit=!0))}unhook(e){let t=!1;if(this._hitLimit)t=!1;else if(e&&(t=this._handler(this._data,this._params),t instanceof Promise))return t.then((e=>(this._params=a,this._data="",this._hitLimit=!1,e)));return this._params=a,this._data="",this._hitLimit=!1,t}}},6717:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.EscapeSequenceParser=t.VT500_TRANSITION_TABLE=t.TransitionTable=void 0;const s=i(7150),r=i(7262),n=i(1346),o=i(9823);class a{constructor(e){this.table=new Uint8Array(e)}setDefault(e,t){this.table.fill(e<<4|t)}add(e,t,i,s){this.table[t<<8|e]=i<<4|s}addMany(e,t,i,s){for(let r=0;r<e.length;r++)this.table[t<<8|e[r]]=i<<4|s}}t.TransitionTable=a;const l=160;t.VT500_TRANSITION_TABLE=function(){const e=new a(4095),t=Array.apply(null,Array(256)).map(((e,t)=>t)),i=(e,i)=>t.slice(e,i),s=i(32,127),r=i(0,24);r.push(25),r.push.apply(r,i(28,32));const n=i(0,14);let o;for(o in e.setDefault(1,0),e.addMany(s,0,2,0),n)e.addMany([24,26,153,154],o,3,0),e.addMany(i(128,144),o,3,0),e.addMany(i(144,152),o,3,0),e.add(156,o,0,0),e.add(27,o,11,1),e.add(157,o,4,8),e.addMany([152,158,159],o,0,7),e.add(155,o,11,3),e.add(144,o,11,9);return e.addMany(r,0,3,0),e.addMany(r,1,3,1),e.add(127,1,0,1),e.addMany(r,8,0,8),e.addMany(r,3,3,3),e.add(127,3,0,3),e.addMany(r,4,3,4),e.add(127,4,0,4),e.addMany(r,6,3,6),e.addMany(r,5,3,5),e.add(127,5,0,5),e.addMany(r,2,3,2),e.add(127,2,0,2),e.add(93,1,4,8),e.addMany(s,8,5,8),e.add(127,8,5,8),e.addMany([156,27,24,26,7],8,6,0),e.addMany(i(28,32),8,0,8),e.addMany([88,94,95],1,0,7),e.addMany(s,7,0,7),e.addMany(r,7,0,7),e.add(156,7,0,0),e.add(127,7,0,7),e.add(91,1,11,3),e.addMany(i(64,127),3,7,0),e.addMany(i(48,60),3,8,4),e.addMany([60,61,62,63],3,9,4),e.addMany(i(48,60),4,8,4),e.addMany(i(64,127),4,7,0),e.addMany([60,61,62,63],4,0,6),e.addMany(i(32,64),6,0,6),e.add(127,6,0,6),e.addMany(i(64,127),6,0,0),e.addMany(i(32,48),3,9,5),e.addMany(i(32,48),5,9,5),e.addMany(i(48,64),5,0,6),e.addMany(i(64,127),5,7,0),e.addMany(i(32,48),4,9,5),e.addMany(i(32,48),1,9,2),e.addMany(i(32,48),2,9,2),e.addMany(i(48,127),2,10,0),e.addMany(i(48,80),1,10,0),e.addMany(i(81,88),1,10,0),e.addMany([89,90,92],1,10,0),e.addMany(i(96,127),1,10,0),e.add(80,1,11,9),e.addMany(r,9,0,9),e.add(127,9,0,9),e.addMany(i(28,32),9,0,9),e.addMany(i(32,48),9,9,12),e.addMany(i(48,60),9,8,10),e.addMany([60,61,62,63],9,9,10),e.addMany(r,11,0,11),e.addMany(i(32,128),11,0,11),e.addMany(i(28,32),11,0,11),e.addMany(r,10,0,10),e.add(127,10,0,10),e.addMany(i(28,32),10,0,10),e.addMany(i(48,60),10,8,10),e.addMany([60,61,62,63],10,0,11),e.addMany(i(32,48),10,9,12),e.addMany(r,12,0,12),e.add(127,12,0,12),e.addMany(i(28,32),12,0,12),e.addMany(i(32,48),12,9,12),e.addMany(i(48,64),12,0,11),e.addMany(i(64,127),12,12,13),e.addMany(i(64,127),10,12,13),e.addMany(i(64,127),9,12,13),e.addMany(r,13,13,13),e.addMany(s,13,13,13),e.add(127,13,0,13),e.addMany([27,156,24,26],13,14,0),e.add(l,0,2,0),e.add(l,8,5,8),e.add(l,6,0,6),e.add(l,11,0,11),e.add(l,13,13,13),e}();class h extends s.Disposable{constructor(e=t.VT500_TRANSITION_TABLE){super(),this._transitions=e,this._parseStack={state:0,handlers:[],handlerPos:0,transition:0,chunkPos:0},this.initialState=0,this.currentState=this.initialState,this._params=new r.Params,this._params.addParam(0),this._collect=0,this.precedingJoinState=0,this._printHandlerFb=(e,t,i)=>{},this._executeHandlerFb=e=>{},this._csiHandlerFb=(e,t)=>{},this._escHandlerFb=e=>{},this._errorHandlerFb=e=>e,this._printHandler=this._printHandlerFb,this._executeHandlers=Object.create(null),this._csiHandlers=Object.create(null),this._escHandlers=Object.create(null),this._register((0,s.toDisposable)((()=>{this._csiHandlers=Object.create(null),this._executeHandlers=Object.create(null),this._escHandlers=Object.create(null)}))),this._oscParser=this._register(new n.OscParser),this._dcsParser=this._register(new o.DcsParser),this._errorHandler=this._errorHandlerFb,this.registerEscHandler({final:"\\"},(()=>!0))}_identifier(e,t=[64,126]){let i=0;if(e.prefix){if(e.prefix.length>1)throw new Error("only one byte as prefix supported");if(i=e.prefix.charCodeAt(0),i&&60>i||i>63)throw new Error("prefix must be in range 0x3c .. 0x3f")}if(e.intermediates){if(e.intermediates.length>2)throw new Error("only two bytes as intermediates are supported");for(let t=0;t<e.intermediates.length;++t){const s=e.intermediates.charCodeAt(t);if(32>s||s>47)throw new Error("intermediate must be in range 0x20 .. 0x2f");i<<=8,i|=s}}if(1!==e.final.length)throw new Error("final must be a single byte");const s=e.final.charCodeAt(0);if(t[0]>s||s>t[1])throw new Error(`final must be in range ${t[0]} .. ${t[1]}`);return i<<=8,i|=s,i}identToString(e){const t=[];for(;e;)t.push(String.fromCharCode(255&e)),e>>=8;return t.reverse().join("")}setPrintHandler(e){this._printHandler=e}clearPrintHandler(){this._printHandler=this._printHandlerFb}registerEscHandler(e,t){const i=this._identifier(e,[48,126]);void 0===this._escHandlers[i]&&(this._escHandlers[i]=[]);const s=this._escHandlers[i];return s.push(t),{dispose:()=>{const e=s.indexOf(t);-1!==e&&s.splice(e,1)}}}clearEscHandler(e){this._escHandlers[this._identifier(e,[48,126])]&&delete this._escHandlers[this._identifier(e,[48,126])]}setEscHandlerFallback(e){this._escHandlerFb=e}setExecuteHandler(e,t){this._executeHandlers[e.charCodeAt(0)]=t}clearExecuteHandler(e){this._executeHandlers[e.charCodeAt(0)]&&delete this._executeHandlers[e.charCodeAt(0)]}setExecuteHandlerFallback(e){this._executeHandlerFb=e}registerCsiHandler(e,t){const i=this._identifier(e);void 0===this._csiHandlers[i]&&(this._csiHandlers[i]=[]);const s=this._csiHandlers[i];return s.push(t),{dispose:()=>{const e=s.indexOf(t);-1!==e&&s.splice(e,1)}}}clearCsiHandler(e){this._csiHandlers[this._identifier(e)]&&delete this._csiHandlers[this._identifier(e)]}setCsiHandlerFallback(e){this._csiHandlerFb=e}registerDcsHandler(e,t){return this._dcsParser.registerHandler(this._identifier(e),t)}clearDcsHandler(e){this._dcsParser.clearHandler(this._identifier(e))}setDcsHandlerFallback(e){this._dcsParser.setHandlerFallback(e)}registerOscHandler(e,t){return this._oscParser.registerHandler(e,t)}clearOscHandler(e){this._oscParser.clearHandler(e)}setOscHandlerFallback(e){this._oscParser.setHandlerFallback(e)}setErrorHandler(e){this._errorHandler=e}clearErrorHandler(){this._errorHandler=this._errorHandlerFb}reset(){this.currentState=this.initialState,this._oscParser.reset(),this._dcsParser.reset(),this._params.reset(),this._params.addParam(0),this._collect=0,this.precedingJoinState=0,0!==this._parseStack.state&&(this._parseStack.state=2,this._parseStack.handlers=[])}_preserveStack(e,t,i,s,r){this._parseStack.state=e,this._parseStack.handlers=t,this._parseStack.handlerPos=i,this._parseStack.transition=s,this._parseStack.chunkPos=r}parse(e,t,i){let s,r=0,n=0,o=0;if(this._parseStack.state)if(2===this._parseStack.state)this._parseStack.state=0,o=this._parseStack.chunkPos+1;else{if(void 0===i||1===this._parseStack.state)throw this._parseStack.state=1,new Error("improper continuation due to previous async handler, giving up parsing");const t=this._parseStack.handlers;let n=this._parseStack.handlerPos-1;switch(this._parseStack.state){case 3:if(!1===i&&n>-1)for(;n>=0&&(s=t[n](this._params),!0!==s);n--)if(s instanceof Promise)return this._parseStack.handlerPos=n,s;this._parseStack.handlers=[];break;case 4:if(!1===i&&n>-1)for(;n>=0&&(s=t[n](),!0!==s);n--)if(s instanceof Promise)return this._parseStack.handlerPos=n,s;this._parseStack.handlers=[];break;case 6:if(r=e[this._parseStack.chunkPos],s=this._dcsParser.unhook(24!==r&&26!==r,i),s)return s;27===r&&(this._parseStack.transition|=1),this._params.reset(),this._params.addParam(0),this._collect=0;break;case 5:if(r=e[this._parseStack.chunkPos],s=this._oscParser.end(24!==r&&26!==r,i),s)return s;27===r&&(this._parseStack.transition|=1),this._params.reset(),this._params.addParam(0),this._collect=0}this._parseStack.state=0,o=this._parseStack.chunkPos+1,this.precedingJoinState=0,this.currentState=15&this._parseStack.transition}for(let i=o;i<t;++i){switch(r=e[i],n=this._transitions.table[this.currentState<<8|(r<160?r:l)],n>>4){case 2:for(let s=i+1;;++s){if(s>=t||(r=e[s])<32||r>126&&r<l){this._printHandler(e,i,s),i=s-1;break}if(++s>=t||(r=e[s])<32||r>126&&r<l){this._printHandler(e,i,s),i=s-1;break}if(++s>=t||(r=e[s])<32||r>126&&r<l){this._printHandler(e,i,s),i=s-1;break}if(++s>=t||(r=e[s])<32||r>126&&r<l){this._printHandler(e,i,s),i=s-1;break}}break;case 3:this._executeHandlers[r]?this._executeHandlers[r]():this._executeHandlerFb(r),this.precedingJoinState=0;break;case 0:break;case 1:if(this._errorHandler({position:i,code:r,currentState:this.currentState,collect:this._collect,params:this._params,abort:!1}).abort)return;break;case 7:const o=this._csiHandlers[this._collect<<8|r];let a=o?o.length-1:-1;for(;a>=0&&(s=o[a](this._params),!0!==s);a--)if(s instanceof Promise)return this._preserveStack(3,o,a,n,i),s;a<0&&this._csiHandlerFb(this._collect<<8|r,this._params),this.precedingJoinState=0;break;case 8:do{switch(r){case 59:this._params.addParam(0);break;case 58:this._params.addSubParam(-1);break;default:this._params.addDigit(r-48)}}while(++i<t&&(r=e[i])>47&&r<60);i--;break;case 9:this._collect<<=8,this._collect|=r;break;case 10:const h=this._escHandlers[this._collect<<8|r];let c=h?h.length-1:-1;for(;c>=0&&(s=h[c](),!0!==s);c--)if(s instanceof Promise)return this._preserveStack(4,h,c,n,i),s;c<0&&this._escHandlerFb(this._collect<<8|r),this.precedingJoinState=0;break;case 11:this._params.reset(),this._params.addParam(0),this._collect=0;break;case 12:this._dcsParser.hook(this._collect<<8|r,this._params);break;case 13:for(let s=i+1;;++s)if(s>=t||24===(r=e[s])||26===r||27===r||r>127&&r<l){this._dcsParser.put(e,i,s),i=s-1;break}break;case 14:if(s=this._dcsParser.unhook(24!==r&&26!==r),s)return this._preserveStack(6,[],0,n,i),s;27===r&&(n|=1),this._params.reset(),this._params.addParam(0),this._collect=0,this.precedingJoinState=0;break;case 4:this._oscParser.start();break;case 5:for(let s=i+1;;s++)if(s>=t||(r=e[s])<32||r>127&&r<l){this._oscParser.put(e,i,s),i=s-1;break}break;case 6:if(s=this._oscParser.end(24!==r&&26!==r),s)return this._preserveStack(5,[],0,n,i),s;27===r&&(n|=1),this._params.reset(),this._params.addParam(0),this._collect=0,this.precedingJoinState=0}this.currentState=15&n}}}t.EscapeSequenceParser=h},1346:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.OscHandler=t.OscParser=void 0;const s=i(1263),r=i(726),n=[];t.OscParser=class{constructor(){this._state=0,this._active=n,this._id=-1,this._handlers=Object.create(null),this._handlerFb=()=>{},this._stack={paused:!1,loopPosition:0,fallThrough:!1}}registerHandler(e,t){void 0===this._handlers[e]&&(this._handlers[e]=[]);const i=this._handlers[e];return i.push(t),{dispose:()=>{const e=i.indexOf(t);-1!==e&&i.splice(e,1)}}}clearHandler(e){this._handlers[e]&&delete this._handlers[e]}setHandlerFallback(e){this._handlerFb=e}dispose(){this._handlers=Object.create(null),this._handlerFb=()=>{},this._active=n}reset(){if(2===this._state)for(let e=this._stack.paused?this._stack.loopPosition-1:this._active.length-1;e>=0;--e)this._active[e].end(!1);this._stack.paused=!1,this._active=n,this._id=-1,this._state=0}_start(){if(this._active=this._handlers[this._id]||n,this._active.length)for(let e=this._active.length-1;e>=0;e--)this._active[e].start();else this._handlerFb(this._id,"START")}_put(e,t,i){if(this._active.length)for(let s=this._active.length-1;s>=0;s--)this._active[s].put(e,t,i);else this._handlerFb(this._id,"PUT",(0,r.utf32ToString)(e,t,i))}start(){this.reset(),this._state=1}put(e,t,i){if(3!==this._state){if(1===this._state)for(;t<i;){const i=e[t++];if(59===i){this._state=2,this._start();break}if(i<48||57<i)return void(this._state=3);-1===this._id&&(this._id=0),this._id=10*this._id+i-48}2===this._state&&i-t>0&&this._put(e,t,i)}}end(e,t=!0){if(0!==this._state){if(3!==this._state)if(1===this._state&&this._start(),this._active.length){let i=!1,s=this._active.length-1,r=!1;if(this._stack.paused&&(s=this._stack.loopPosition-1,i=t,r=this._stack.fallThrough,this._stack.paused=!1),!r&&!1===i){for(;s>=0&&(i=this._active[s].end(e),!0!==i);s--)if(i instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=s,this._stack.fallThrough=!1,i;s--}for(;s>=0;s--)if(i=this._active[s].end(!1),i instanceof Promise)return this._stack.paused=!0,this._stack.loopPosition=s,this._stack.fallThrough=!0,i}else this._handlerFb(this._id,"END",e);this._active=n,this._id=-1,this._state=0}}},t.OscHandler=class{constructor(e){this._handler=e,this._data="",this._hitLimit=!1}start(){this._data="",this._hitLimit=!1}put(e,t,i){this._hitLimit||(this._data+=(0,r.utf32ToString)(e,t,i),this._data.length>s.PAYLOAD_LIMIT&&(this._data="",this._hitLimit=!0))}end(e){let t=!1;if(this._hitLimit)t=!1;else if(e&&(t=this._handler(this._data),t instanceof Promise))return t.then((e=>(this._data="",this._hitLimit=!1,e)));return this._data="",this._hitLimit=!1,t}}},7262:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Params=void 0;const i=2147483647;class s{static fromArray(e){const t=new s;if(!e.length)return t;for(let i=Array.isArray(e[0])?1:0;i<e.length;++i){const s=e[i];if(Array.isArray(s))for(let e=0;e<s.length;++e)t.addSubParam(s[e]);else t.addParam(s)}return t}constructor(e=32,t=32){if(this.maxLength=e,this.maxSubParamsLength=t,t>256)throw new Error("maxSubParamsLength must not be greater than 256");this.params=new Int32Array(e),this.length=0,this._subParams=new Int32Array(t),this._subParamsLength=0,this._subParamsIdx=new Uint16Array(e),this._rejectDigits=!1,this._rejectSubDigits=!1,this._digitIsSub=!1}clone(){const e=new s(this.maxLength,this.maxSubParamsLength);return e.params.set(this.params),e.length=this.length,e._subParams.set(this._subParams),e._subParamsLength=this._subParamsLength,e._subParamsIdx.set(this._subParamsIdx),e._rejectDigits=this._rejectDigits,e._rejectSubDigits=this._rejectSubDigits,e._digitIsSub=this._digitIsSub,e}toArray(){const e=[];for(let t=0;t<this.length;++t){e.push(this.params[t]);const i=this._subParamsIdx[t]>>8,s=255&this._subParamsIdx[t];s-i>0&&e.push(Array.prototype.slice.call(this._subParams,i,s))}return e}reset(){this.length=0,this._subParamsLength=0,this._rejectDigits=!1,this._rejectSubDigits=!1,this._digitIsSub=!1}addParam(e){if(this._digitIsSub=!1,this.length>=this.maxLength)this._rejectDigits=!0;else{if(e<-1)throw new Error("values lesser than -1 are not allowed");this._subParamsIdx[this.length]=this._subParamsLength<<8|this._subParamsLength,this.params[this.length++]=e>i?i:e}}addSubParam(e){if(this._digitIsSub=!0,this.length)if(this._rejectDigits||this._subParamsLength>=this.maxSubParamsLength)this._rejectSubDigits=!0;else{if(e<-1)throw new Error("values lesser than -1 are not allowed");this._subParams[this._subParamsLength++]=e>i?i:e,this._subParamsIdx[this.length-1]++}}hasSubParams(e){return(255&this._subParamsIdx[e])-(this._subParamsIdx[e]>>8)>0}getSubParams(e){const t=this._subParamsIdx[e]>>8,i=255&this._subParamsIdx[e];return i-t>0?this._subParams.subarray(t,i):null}getSubParamsAll(){const e={};for(let t=0;t<this.length;++t){const i=this._subParamsIdx[t]>>8,s=255&this._subParamsIdx[t];s-i>0&&(e[t]=this._subParams.slice(i,s))}return e}addDigit(e){let t;if(this._rejectDigits||!(t=this._digitIsSub?this._subParamsLength:this.length)||this._digitIsSub&&this._rejectSubDigits)return;const s=this._digitIsSub?this._subParams:this.params,r=s[t-1];s[t-1]=~r?Math.min(10*r+e,i):e}}t.Params=s},3027:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.AddonManager=void 0,t.AddonManager=class{constructor(){this._addons=[]}dispose(){for(let e=this._addons.length-1;e>=0;e--)this._addons[e].instance.dispose()}loadAddon(e,t){const i={instance:t,dispose:t.dispose,isDisposed:!1};this._addons.push(i),t.dispose=()=>this._wrappedAddonDispose(i),t.activate(e)}_wrappedAddonDispose(e){if(e.isDisposed)return;let t=-1;for(let i=0;i<this._addons.length;i++)if(this._addons[i]===e){t=i;break}if(-1===t)throw new Error("Could not dispose an addon that has not been loaded");e.isDisposed=!0,e.dispose.apply(e.instance),this._addons.splice(t,1)}}},3235:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferApiView=void 0;const s=i(793),r=i(3055);t.BufferApiView=class{constructor(e,t){this._buffer=e,this.type=t}init(e){return this._buffer=e,this}get cursorY(){return this._buffer.y}get cursorX(){return this._buffer.x}get viewportY(){return this._buffer.ydisp}get baseY(){return this._buffer.ybase}get length(){return this._buffer.lines.length}getLine(e){const t=this._buffer.lines.get(e);if(t)return new s.BufferLineApiView(t)}getNullCell(){return new r.CellData}}},793:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferLineApiView=void 0;const s=i(3055);t.BufferLineApiView=class{constructor(e){this._line=e}get isWrapped(){return this._line.isWrapped}get length(){return this._line.length}getCell(e,t){if(!(e<0||e>=this._line.length))return t?(this._line.loadCell(e,t),t):this._line.loadCell(e,new s.CellData)}translateToString(e,t,i){return this._line.translateToString(e,t,i)}}},5101:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BufferNamespaceApi=void 0;const s=i(3235),r=i(7150),n=i(802);class o extends r.Disposable{constructor(e){super(),this._core=e,this._onBufferChange=this._register(new n.Emitter),this.onBufferChange=this._onBufferChange.event,this._normal=new s.BufferApiView(this._core.buffers.normal,"normal"),this._alternate=new s.BufferApiView(this._core.buffers.alt,"alternate"),this._core.buffers.onBufferActivate((()=>this._onBufferChange.fire(this.active)))}get active(){if(this._core.buffers.active===this._core.buffers.normal)return this.normal;if(this._core.buffers.active===this._core.buffers.alt)return this.alternate;throw new Error("Active buffer is neither normal nor alternate")}get normal(){return this._normal.init(this._core.buffers.normal)}get alternate(){return this._alternate.init(this._core.buffers.alt)}}t.BufferNamespaceApi=o},6097:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ParserApi=void 0,t.ParserApi=class{constructor(e){this._core=e}registerCsiHandler(e,t){return this._core.registerCsiHandler(e,(e=>t(e.toArray())))}addCsiHandler(e,t){return this.registerCsiHandler(e,t)}registerDcsHandler(e,t){return this._core.registerDcsHandler(e,((e,i)=>t(e,i.toArray())))}addDcsHandler(e,t){return this.registerDcsHandler(e,t)}registerEscHandler(e,t){return this._core.registerEscHandler(e,t)}addEscHandler(e,t){return this.registerEscHandler(e,t)}registerOscHandler(e,t){return this._core.registerOscHandler(e,t)}addOscHandler(e,t){return this.registerOscHandler(e,t)}}},4335:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.UnicodeApi=void 0,t.UnicodeApi=class{constructor(e){this._core=e}register(e){this._core.unicodeService.register(e)}get versions(){return this._core.unicodeService.versions}get activeVersion(){return this._core.unicodeService.activeVersion}set activeVersion(e){this._core.unicodeService.activeVersion=e}}},9640:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.BufferService=t.MINIMUM_ROWS=t.MINIMUM_COLS=void 0;const n=i(7150),o=i(4097),a=i(6501),l=i(802);t.MINIMUM_COLS=2,t.MINIMUM_ROWS=1;let h=class extends n.Disposable{get buffer(){return this.buffers.active}constructor(e){super(),this.isUserScrolling=!1,this._onResize=this._register(new l.Emitter),this.onResize=this._onResize.event,this._onScroll=this._register(new l.Emitter),this.onScroll=this._onScroll.event,this.cols=Math.max(e.rawOptions.cols||0,t.MINIMUM_COLS),this.rows=Math.max(e.rawOptions.rows||0,t.MINIMUM_ROWS),this.buffers=this._register(new o.BufferSet(e,this)),this._register(this.buffers.onBufferActivate((e=>{this._onScroll.fire(e.activeBuffer.ydisp)})))}resize(e,t){const i=this.cols!==e,s=this.rows!==t;this.cols=e,this.rows=t,this.buffers.resize(e,t),this._onResize.fire({cols:e,rows:t,colsChanged:i,rowsChanged:s})}reset(){this.buffers.reset(),this.isUserScrolling=!1}scroll(e,t=!1){const i=this.buffer;let s;s=this._cachedBlankLine,s&&s.length===this.cols&&s.getFg(0)===e.fg&&s.getBg(0)===e.bg||(s=i.getBlankLine(e,t),this._cachedBlankLine=s),s.isWrapped=t;const r=i.ybase+i.scrollTop,n=i.ybase+i.scrollBottom;if(0===i.scrollTop){const e=i.lines.isFull;n===i.lines.length-1?e?i.lines.recycle().copyFrom(s):i.lines.push(s.clone()):i.lines.splice(n+1,0,s.clone()),e?this.isUserScrolling&&(i.ydisp=Math.max(i.ydisp-1,0)):(i.ybase++,this.isUserScrolling||i.ydisp++)}else{const e=n-r+1;i.lines.shiftElements(r+1,e-1,-1),i.lines.set(n,s.clone())}this.isUserScrolling||(i.ydisp=i.ybase),this._onScroll.fire(i.ydisp)}scrollLines(e,t){const i=this.buffer;if(e<0){if(0===i.ydisp)return;this.isUserScrolling=!0}else e+i.ydisp>=i.ybase&&(this.isUserScrolling=!1);const s=i.ydisp;i.ydisp=Math.max(Math.min(i.ydisp+e,i.ybase),0),s!==i.ydisp&&(t||this._onScroll.fire(i.ydisp))}};t.BufferService=h,t.BufferService=h=s([r(0,a.IOptionsService)],h)},5746:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.CharsetService=void 0,t.CharsetService=class{constructor(){this.glevel=0,this._charsets=[]}reset(){this.charset=void 0,this._charsets=[],this.glevel=0}setgLevel(e){this.glevel=e,this.charset=this._charsets[e]}setgCharset(e,t){this._charsets[e]=t,this.glevel===e&&(this.charset=t)}}},7792:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CoreMouseService=void 0;const n=i(6501),o=i(7150),a=i(802),l={NONE:{events:0,restrict:()=>!1},X10:{events:1,restrict:e=>4!==e.button&&1===e.action&&(e.ctrl=!1,e.alt=!1,e.shift=!1,!0)},VT200:{events:19,restrict:e=>32!==e.action},DRAG:{events:23,restrict:e=>32!==e.action||3!==e.button},ANY:{events:31,restrict:e=>!0}};function h(e,t){let i=(e.ctrl?16:0)|(e.shift?4:0)|(e.alt?8:0);return 4===e.button?(i|=64,i|=e.action):(i|=3&e.button,4&e.button&&(i|=64),8&e.button&&(i|=128),32===e.action?i|=32:0!==e.action||t||(i|=3)),i}const c=String.fromCharCode,d={DEFAULT:e=>{const t=[h(e,!1)+32,e.col+32,e.row+32];return t[0]>255||t[1]>255||t[2]>255?"":`${c(t[0])}${c(t[1])}${c(t[2])}`},SGR:e=>{const t=0===e.action&&4!==e.button?"m":"M";return`[<${h(e,!0)};${e.col};${e.row}${t}`},SGR_PIXELS:e=>{const t=0===e.action&&4!==e.button?"m":"M";return`[<${h(e,!0)};${e.x};${e.y}${t}`}};let u=class extends o.Disposable{constructor(e,t,i){super(),this._bufferService=e,this._coreService=t,this._optionsService=i,this._protocols={},this._encodings={},this._activeProtocol="",this._activeEncoding="",this._lastEvent=null,this._wheelPartialScroll=0,this._onProtocolChange=this._register(new a.Emitter),this.onProtocolChange=this._onProtocolChange.event;for(const e of Object.keys(l))this.addProtocol(e,l[e]);for(const e of Object.keys(d))this.addEncoding(e,d[e]);this.reset()}addProtocol(e,t){this._protocols[e]=t}addEncoding(e,t){this._encodings[e]=t}get activeProtocol(){return this._activeProtocol}get areMouseEventsActive(){return 0!==this._protocols[this._activeProtocol].events}set activeProtocol(e){if(!this._protocols[e])throw new Error(`unknown protocol "${e}"`);this._activeProtocol=e,this._onProtocolChange.fire(this._protocols[e].events)}get activeEncoding(){return this._activeEncoding}set activeEncoding(e){if(!this._encodings[e])throw new Error(`unknown encoding "${e}"`);this._activeEncoding=e}reset(){this.activeProtocol="NONE",this.activeEncoding="DEFAULT",this._lastEvent=null,this._wheelPartialScroll=0}consumeWheelEvent(e,t,i){if(0===e.deltaY||e.shiftKey)return 0;if(void 0===t||void 0===i)return 0;const s=t/i;let r=this._applyScrollModifier(e.deltaY,e);return e.deltaMode===WheelEvent.DOM_DELTA_PIXEL?(r/=s+0,Math.abs(e.deltaY)<50&&(r*=.3),this._wheelPartialScroll+=r,r=Math.floor(Math.abs(this._wheelPartialScroll))*(this._wheelPartialScroll>0?1:-1),this._wheelPartialScroll%=1):e.deltaMode===WheelEvent.DOM_DELTA_PAGE&&(r*=this._bufferService.rows),r}_applyScrollModifier(e,t){return t.altKey||t.ctrlKey||t.shiftKey?e*this._optionsService.rawOptions.fastScrollSensitivity*this._optionsService.rawOptions.scrollSensitivity:e*this._optionsService.rawOptions.scrollSensitivity}triggerMouseEvent(e){if(e.col<0||e.col>=this._bufferService.cols||e.row<0||e.row>=this._bufferService.rows)return!1;if(4===e.button&&32===e.action)return!1;if(3===e.button&&32!==e.action)return!1;if(4!==e.button&&(2===e.action||3===e.action))return!1;if(e.col++,e.row++,32===e.action&&this._lastEvent&&this._equalEvents(this._lastEvent,e,"SGR_PIXELS"===this._activeEncoding))return!1;if(!this._protocols[this._activeProtocol].restrict(e))return!1;const t=this._encodings[this._activeEncoding](e);return t&&("DEFAULT"===this._activeEncoding?this._coreService.triggerBinaryEvent(t):this._coreService.triggerDataEvent(t,!0)),this._lastEvent=e,!0}explainEvents(e){return{down:!!(1&e),up:!!(2&e),drag:!!(4&e),move:!!(8&e),wheel:!!(16&e)}}_equalEvents(e,t,i){if(i){if(e.x!==t.x)return!1;if(e.y!==t.y)return!1}else{if(e.col!==t.col)return!1;if(e.row!==t.row)return!1}return e.button===t.button&&e.action===t.action&&e.ctrl===t.ctrl&&e.alt===t.alt&&e.shift===t.shift}};t.CoreMouseService=u,t.CoreMouseService=u=s([r(0,n.IBufferService),r(1,n.ICoreService),r(2,n.IOptionsService)],u)},4071:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.CoreService=void 0;const n=i(7453),o=i(7150),a=i(6501),l=i(802),h=Object.freeze({insertMode:!1}),c=Object.freeze({applicationCursorKeys:!1,applicationKeypad:!1,bracketedPasteMode:!1,cursorBlink:void 0,cursorStyle:void 0,origin:!1,reverseWraparound:!1,sendFocus:!1,synchronizedOutput:!1,wraparound:!0});let d=class extends o.Disposable{constructor(e,t,i){super(),this._bufferService=e,this._logService=t,this._optionsService=i,this.isCursorInitialized=!1,this.isCursorHidden=!1,this._onData=this._register(new l.Emitter),this.onData=this._onData.event,this._onUserInput=this._register(new l.Emitter),this.onUserInput=this._onUserInput.event,this._onBinary=this._register(new l.Emitter),this.onBinary=this._onBinary.event,this._onRequestScrollToBottom=this._register(new l.Emitter),this.onRequestScrollToBottom=this._onRequestScrollToBottom.event,this.modes=(0,n.clone)(h),this.decPrivateModes=(0,n.clone)(c)}reset(){this.modes=(0,n.clone)(h),this.decPrivateModes=(0,n.clone)(c)}triggerDataEvent(e,t=!1){if(this._optionsService.rawOptions.disableStdin)return;const i=this._bufferService.buffer;t&&this._optionsService.rawOptions.scrollOnUserInput&&i.ybase!==i.ydisp&&this._onRequestScrollToBottom.fire(),t&&this._onUserInput.fire(),this._logService.debug(`sending data "${e}"`),this._logService.trace("sending data (codes)",(()=>e.split("").map((e=>e.charCodeAt(0))))),this._onData.fire(e)}triggerBinaryEvent(e){this._optionsService.rawOptions.disableStdin||(this._logService.debug(`sending binary "${e}"`),this._logService.trace("sending binary (codes)",(()=>e.split("").map((e=>e.charCodeAt(0))))),this._onBinary.fire(e))}};t.CoreService=d,t.CoreService=d=s([r(0,a.IBufferService),r(1,a.ILogService),r(2,a.IOptionsService)],d)},4720:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DecorationService=void 0;const s=i(4103),r=i(7150),n=i(3087),o=i(802);let a=0,l=0;class h extends r.Disposable{get decorations(){return this._decorations.values()}constructor(){super(),this._decorations=new n.SortedList((e=>e?.marker.line)),this._onDecorationRegistered=this._register(new o.Emitter),this.onDecorationRegistered=this._onDecorationRegistered.event,this._onDecorationRemoved=this._register(new o.Emitter),this.onDecorationRemoved=this._onDecorationRemoved.event,this._register((0,r.toDisposable)((()=>this.reset())))}registerDecoration(e){if(e.marker.isDisposed)return;const t=new c(e);if(t){const e=t.marker.onDispose((()=>t.dispose())),i=t.onDispose((()=>{i.dispose(),t&&(this._decorations.delete(t)&&this._onDecorationRemoved.fire(t),e.dispose())}));this._decorations.insert(t),this._onDecorationRegistered.fire(t)}return t}reset(){for(const e of this._decorations.values())e.dispose();this._decorations.clear()}*getDecorationsAtCell(e,t,i){let s=0,r=0;for(const n of this._decorations.getKeyIterator(t))s=n.options.x??0,r=s+(n.options.width??1),e>=s&&e<r&&(!i||(n.options.layer??"bottom")===i)&&(yield n)}forEachDecorationAtCell(e,t,i,s){this._decorations.forEachByKey(t,(t=>{a=t.options.x??0,l=a+(t.options.width??1),e>=a&&e<l&&(!i||(t.options.layer??"bottom")===i)&&s(t)}))}}t.DecorationService=h;class c extends r.DisposableStore{get backgroundColorRGB(){return null===this._cachedBg&&(this.options.backgroundColor?this._cachedBg=s.css.toColor(this.options.backgroundColor):this._cachedBg=void 0),this._cachedBg}get foregroundColorRGB(){return null===this._cachedFg&&(this.options.foregroundColor?this._cachedFg=s.css.toColor(this.options.foregroundColor):this._cachedFg=void 0),this._cachedFg}constructor(e){super(),this.options=e,this.onRenderEmitter=this.add(new o.Emitter),this.onRender=this.onRenderEmitter.event,this._onDispose=this.add(new o.Emitter),this.onDispose=this._onDispose.event,this._cachedBg=null,this._cachedFg=null,this.marker=e.marker,this.options.overviewRulerOptions&&!this.options.overviewRulerOptions.position&&(this.options.overviewRulerOptions.position="full")}dispose(){this._onDispose.fire(),super.dispose()}}},6025:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.InstantiationService=t.ServiceCollection=void 0;const s=i(6501),r=i(6201);class n{constructor(...e){this._entries=new Map;for(const[t,i]of e)this.set(t,i)}set(e,t){const i=this._entries.get(e);return this._entries.set(e,t),i}forEach(e){for(const[t,i]of this._entries.entries())e(t,i)}has(e){return this._entries.has(e)}get(e){return this._entries.get(e)}}t.ServiceCollection=n,t.InstantiationService=class{constructor(){this._services=new n,this._services.set(s.IInstantiationService,this)}setService(e,t){this._services.set(e,t)}getService(e){return this._services.get(e)}createInstance(e,...t){const i=(0,r.getServiceDependencies)(e).sort(((e,t)=>e.index-t.index)),s=[];for(const t of i){const i=this._services.get(t.id);if(!i)throw new Error(`[createInstance] ${e.name} depends on UNKNOWN service ${t.id._id}.`);s.push(i)}const n=i.length>0?i[0].index:t.length;if(t.length!==n)throw new Error(`[createInstance] First service dependency of ${e.name} at position ${n+1} conflicts with ${t.length} static arguments`);return new e(...[...t,...s])}}},7276:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.LogService=void 0,t.setTraceLogger=function(e){l=e},t.traceCall=function(e,t,i){if("function"!=typeof i.value)throw new Error("not supported");const s=i.value;i.value=function(...e){if(l.logLevel!==o.LogLevelEnum.TRACE)return s.apply(this,e);l.trace(`GlyphRenderer#${s.name}(${e.map((e=>JSON.stringify(e))).join(", ")})`);const t=s.apply(this,e);return l.trace(`GlyphRenderer#${s.name} return`,t),t}};const n=i(7150),o=i(6501),a={trace:o.LogLevelEnum.TRACE,debug:o.LogLevelEnum.DEBUG,info:o.LogLevelEnum.INFO,warn:o.LogLevelEnum.WARN,error:o.LogLevelEnum.ERROR,off:o.LogLevelEnum.OFF};let l,h=class extends n.Disposable{get logLevel(){return this._logLevel}constructor(e){super(),this._optionsService=e,this._logLevel=o.LogLevelEnum.OFF,this._updateLogLevel(),this._register(this._optionsService.onSpecificOptionChange("logLevel",(()=>this._updateLogLevel()))),l=this}_updateLogLevel(){this._logLevel=a[this._optionsService.rawOptions.logLevel]}_evalLazyOptionalParams(e){for(let t=0;t<e.length;t++)"function"==typeof e[t]&&(e[t]=e[t]())}_log(e,t,i){this._evalLazyOptionalParams(i),e.call(console,(this._optionsService.options.logger?"":"xterm.js: ")+t,...i)}trace(e,...t){this._logLevel<=o.LogLevelEnum.TRACE&&this._log(this._optionsService.options.logger?.trace.bind(this._optionsService.options.logger)??console.log,e,t)}debug(e,...t){this._logLevel<=o.LogLevelEnum.DEBUG&&this._log(this._optionsService.options.logger?.debug.bind(this._optionsService.options.logger)??console.log,e,t)}info(e,...t){this._logLevel<=o.LogLevelEnum.INFO&&this._log(this._optionsService.options.logger?.info.bind(this._optionsService.options.logger)??console.info,e,t)}warn(e,...t){this._logLevel<=o.LogLevelEnum.WARN&&this._log(this._optionsService.options.logger?.warn.bind(this._optionsService.options.logger)??console.warn,e,t)}error(e,...t){this._logLevel<=o.LogLevelEnum.ERROR&&this._log(this._optionsService.options.logger?.error.bind(this._optionsService.options.logger)??console.error,e,t)}};t.LogService=h,t.LogService=h=s([r(0,o.IOptionsService)],h)},56:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.OptionsService=t.DEFAULT_OPTIONS=void 0;const s=i(7150),r=i(701),n=i(802);t.DEFAULT_OPTIONS={cols:80,rows:24,cursorBlink:!1,cursorStyle:"block",cursorWidth:1,cursorInactiveStyle:"outline",customGlyphs:!0,drawBoldTextInBrightColors:!0,documentOverride:null,fastScrollModifier:"alt",fastScrollSensitivity:5,fontFamily:"monospace",fontSize:15,fontWeight:"normal",fontWeightBold:"bold",ignoreBracketedPasteMode:!1,lineHeight:1,letterSpacing:0,linkHandler:null,logLevel:"info",logger:null,scrollback:1e3,scrollOnEraseInDisplay:!1,scrollOnUserInput:!0,scrollSensitivity:1,screenReaderMode:!1,smoothScrollDuration:0,macOptionIsMeta:!1,macOptionClickForcesSelection:!1,minimumContrastRatio:1,disableStdin:!1,allowProposedApi:!1,allowTransparency:!1,tabStopWidth:8,theme:{},reflowCursorLine:!1,rescaleOverlappingGlyphs:!1,rightClickSelectsWord:r.isMac,windowOptions:{},windowsMode:!1,windowsPty:{},wordSeparator:" ()[]{}',\"`",altClickMovesCursor:!0,convertEol:!1,termName:"xterm",cancelEvents:!1,overviewRuler:{}};const o=["normal","bold","100","200","300","400","500","600","700","800","900"];class a extends s.Disposable{constructor(e){super(),this._onOptionChange=this._register(new n.Emitter),this.onOptionChange=this._onOptionChange.event;const i={...t.DEFAULT_OPTIONS};for(const t in e)if(t in i)try{const s=e[t];i[t]=this._sanitizeAndValidateOption(t,s)}catch(e){console.error(e)}this.rawOptions=i,this.options={...i},this._setupOptions(),this._register((0,s.toDisposable)((()=>{this.rawOptions.linkHandler=null,this.rawOptions.documentOverride=null})))}onSpecificOptionChange(e,t){return this.onOptionChange((i=>{i===e&&t(this.rawOptions[e])}))}onMultipleOptionChange(e,t){return this.onOptionChange((i=>{-1!==e.indexOf(i)&&t()}))}_setupOptions(){const e=e=>{if(!(e in t.DEFAULT_OPTIONS))throw new Error(`No option with key "${e}"`);return this.rawOptions[e]},i=(e,i)=>{if(!(e in t.DEFAULT_OPTIONS))throw new Error(`No option with key "${e}"`);i=this._sanitizeAndValidateOption(e,i),this.rawOptions[e]!==i&&(this.rawOptions[e]=i,this._onOptionChange.fire(e))};for(const t in this.rawOptions){const s={get:e.bind(this,t),set:i.bind(this,t)};Object.defineProperty(this.options,t,s)}}_sanitizeAndValidateOption(e,i){switch(e){case"cursorStyle":if(i||(i=t.DEFAULT_OPTIONS[e]),!function(e){return"block"===e||"underline"===e||"bar"===e}(i))throw new Error(`"${i}" is not a valid value for ${e}`);break;case"wordSeparator":i||(i=t.DEFAULT_OPTIONS[e]);break;case"fontWeight":case"fontWeightBold":if("number"==typeof i&&1<=i&&i<=1e3)break;i=o.includes(i)?i:t.DEFAULT_OPTIONS[e];break;case"cursorWidth":i=Math.floor(i);case"lineHeight":case"tabStopWidth":if(i<1)throw new Error(`${e} cannot be less than 1, value: ${i}`);break;case"minimumContrastRatio":i=Math.max(1,Math.min(21,Math.round(10*i)/10));break;case"scrollback":if((i=Math.min(i,4294967295))<0)throw new Error(`${e} cannot be less than 0, value: ${i}`);break;case"fastScrollSensitivity":case"scrollSensitivity":if(i<=0)throw new Error(`${e} cannot be less than or equal to 0, value: ${i}`);break;case"rows":case"cols":if(!i&&0!==i)throw new Error(`${e} must be numeric, value: ${i}`);break;case"windowsPty":i=i??{}}return i}}t.OptionsService=a},8811:function(e,t,i){var s=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},r=this&&this.__param||function(e,t){return function(i,s){t(i,s,e)}};Object.defineProperty(t,"__esModule",{value:!0}),t.OscLinkService=void 0;const n=i(6501);let o=class{constructor(e){this._bufferService=e,this._nextId=1,this._entriesWithId=new Map,this._dataByLinkId=new Map}registerLink(e){const t=this._bufferService.buffer;if(void 0===e.id){const i=t.addMarker(t.ybase+t.y),s={data:e,id:this._nextId++,lines:[i]};return i.onDispose((()=>this._removeMarkerFromLink(s,i))),this._dataByLinkId.set(s.id,s),s.id}const i=e,s=this._getEntryIdKey(i),r=this._entriesWithId.get(s);if(r)return this.addLineToLink(r.id,t.ybase+t.y),r.id;const n=t.addMarker(t.ybase+t.y),o={id:this._nextId++,key:this._getEntryIdKey(i),data:i,lines:[n]};return n.onDispose((()=>this._removeMarkerFromLink(o,n))),this._entriesWithId.set(o.key,o),this._dataByLinkId.set(o.id,o),o.id}addLineToLink(e,t){const i=this._dataByLinkId.get(e);if(i&&i.lines.every((e=>e.line!==t))){const e=this._bufferService.buffer.addMarker(t);i.lines.push(e),e.onDispose((()=>this._removeMarkerFromLink(i,e)))}}getLinkData(e){return this._dataByLinkId.get(e)?.data}_getEntryIdKey(e){return`${e.id};;${e.uri}`}_removeMarkerFromLink(e,t){const i=e.lines.indexOf(t);-1!==i&&(e.lines.splice(i,1),0===e.lines.length&&(void 0!==e.data.id&&this._entriesWithId.delete(e.key),this._dataByLinkId.delete(e.id)))}};t.OscLinkService=o,t.OscLinkService=o=s([r(0,n.IBufferService)],o)},6201:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.serviceRegistry=void 0,t.getServiceDependencies=function(e){return e[s]||[]},t.createDecorator=function(e){if(t.serviceRegistry.has(e))return t.serviceRegistry.get(e);const r=function(e,t,n){if(3!==arguments.length)throw new Error("@IServiceName-decorator can only be used to decorate a parameter");!function(e,t,r){t[i]===t?t[s].push({id:e,index:r}):(t[s]=[{id:e,index:r}],t[i]=t)}(r,e,n)};return r._id=e,t.serviceRegistry.set(e,r),r};const i="di$target",s="di$dependencies";t.serviceRegistry=new Map},6501:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.IDecorationService=t.IUnicodeService=t.IOscLinkService=t.IOptionsService=t.ILogService=t.LogLevelEnum=t.IInstantiationService=t.ICharsetService=t.ICoreService=t.ICoreMouseService=t.IBufferService=void 0;const s=i(6201);var r;t.IBufferService=(0,s.createDecorator)("BufferService"),t.ICoreMouseService=(0,s.createDecorator)("CoreMouseService"),t.ICoreService=(0,s.createDecorator)("CoreService"),t.ICharsetService=(0,s.createDecorator)("CharsetService"),t.IInstantiationService=(0,s.createDecorator)("InstantiationService"),function(e){e[e.TRACE=0]="TRACE",e[e.DEBUG=1]="DEBUG",e[e.INFO=2]="INFO",e[e.WARN=3]="WARN",e[e.ERROR=4]="ERROR",e[e.OFF=5]="OFF"}(r||(t.LogLevelEnum=r={})),t.ILogService=(0,s.createDecorator)("LogService"),t.IOptionsService=(0,s.createDecorator)("OptionsService"),t.IOscLinkService=(0,s.createDecorator)("OscLinkService"),t.IUnicodeService=(0,s.createDecorator)("UnicodeService"),t.IDecorationService=(0,s.createDecorator)("DecorationService")},6415:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.UnicodeService=void 0;const s=i(7428),r=i(802);class n{static extractShouldJoin(e){return!!(1&e)}static extractWidth(e){return e>>1&3}static extractCharKind(e){return e>>3}static createPropertyValue(e,t,i=!1){return(16777215&e)<<3|(3&t)<<1|(i?1:0)}constructor(){this._providers=Object.create(null),this._active="",this._onChange=new r.Emitter,this.onChange=this._onChange.event;const e=new s.UnicodeV6;this.register(e),this._active=e.version,this._activeProvider=e}dispose(){this._onChange.dispose()}get versions(){return Object.keys(this._providers)}get activeVersion(){return this._active}set activeVersion(e){if(!this._providers[e])throw new Error(`unknown Unicode version "${e}"`);this._active=e,this._activeProvider=this._providers[e],this._onChange.fire(e)}register(e){this._providers[e.version]=e}wcwidth(e){return this._activeProvider.wcwidth(e)}getStringCellWidth(e){let t=0,i=0;const s=e.length;for(let r=0;r<s;++r){let o=e.charCodeAt(r);if(55296<=o&&o<=56319){if(++r>=s)return t+this.wcwidth(o);const i=e.charCodeAt(r);56320<=i&&i<=57343?o=1024*(o-55296)+i-56320+65536:t+=this.wcwidth(i)}const a=this.charProperties(o,i);let l=n.extractWidth(a);n.extractShouldJoin(a)&&(l-=n.extractWidth(i)),t+=l,i=a}return t}charProperties(e,t){return this._activeProvider.charProperties(e,t)}}t.UnicodeService=n},4333:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.isAndroid=t.isElectron=t.isWebkitWebView=t.isSafari=t.isChrome=t.isWebKit=t.isFirefox=t.onDidChangeFullscreen=t.onDidChangeZoomLevel=void 0,t.addMatchMediaChangeListener=o,t.setZoomLevel=function(e,t){n.INSTANCE.setZoomLevel(e,t)},t.getZoomLevel=function(e){return n.INSTANCE.getZoomLevel(e)},t.getZoomFactor=function(e){return n.INSTANCE.getZoomFactor(e)},t.setZoomFactor=function(e,t){n.INSTANCE.setZoomFactor(e,t)},t.setFullscreen=function(e,t){n.INSTANCE.setFullscreen(e,t)},t.isFullscreen=function(e){return n.INSTANCE.isFullscreen(e)},t.isStandalone=function(){return l},t.isWCOEnabled=function(){return navigator?.windowControlsOverlay?.visible},t.getWCOBoundingRect=function(){return navigator?.windowControlsOverlay?.getTitlebarAreaRect()};const s=i(4693),r=i(802);class n{constructor(){this.mapWindowIdToZoomLevel=new Map,this._onDidChangeZoomLevel=new r.Emitter,this.onDidChangeZoomLevel=this._onDidChangeZoomLevel.event,this.mapWindowIdToZoomFactor=new Map,this._onDidChangeFullscreen=new r.Emitter,this.onDidChangeFullscreen=this._onDidChangeFullscreen.event,this.mapWindowIdToFullScreen=new Map}static{this.INSTANCE=new n}getZoomLevel(e){return this.mapWindowIdToZoomLevel.get(this.getWindowId(e))??0}setZoomLevel(e,t){if(this.getZoomLevel(t)===e)return;const i=this.getWindowId(t);this.mapWindowIdToZoomLevel.set(i,e),this._onDidChangeZoomLevel.fire(i)}getZoomFactor(e){return this.mapWindowIdToZoomFactor.get(this.getWindowId(e))??1}setZoomFactor(e,t){this.mapWindowIdToZoomFactor.set(this.getWindowId(t),e)}setFullscreen(e,t){if(this.isFullscreen(t)===e)return;const i=this.getWindowId(t);this.mapWindowIdToFullScreen.set(i,e),this._onDidChangeFullscreen.fire(i)}isFullscreen(e){return!!this.mapWindowIdToFullScreen.get(this.getWindowId(e))}getWindowId(e){return e.vscodeWindowId}}function o(e,t,i){"string"==typeof t&&(t=e.matchMedia(t)),t.addEventListener("change",i)}t.onDidChangeZoomLevel=n.INSTANCE.onDidChangeZoomLevel,t.onDidChangeFullscreen=n.INSTANCE.onDidChangeFullscreen;const a="object"==typeof navigator?navigator.userAgent:"";t.isFirefox=a.indexOf("Firefox")>=0,t.isWebKit=a.indexOf("AppleWebKit")>=0,t.isChrome=a.indexOf("Chrome")>=0,t.isSafari=!t.isChrome&&a.indexOf("Safari")>=0,t.isWebkitWebView=!t.isChrome&&!t.isSafari&&t.isWebKit,t.isElectron=a.indexOf("Electron/")>=0,t.isAndroid=a.indexOf("Android")>=0;let l=!1;if("function"==typeof s.mainWindow.matchMedia){const e=s.mainWindow.matchMedia("(display-mode: standalone) or (display-mode: window-controls-overlay)"),t=s.mainWindow.matchMedia("(display-mode: fullscreen)");l=e.matches,o(s.mainWindow,e,(({matches:e})=>{l&&t.matches||(l=e)}))}},7745:function(e,t,i){var s=this&&this.__createBinding||(Object.create?function(e,t,i,s){void 0===s&&(s=i);var r=Object.getOwnPropertyDescriptor(t,i);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,s,r)}:function(e,t,i,s){void 0===s&&(s=i),e[s]=t[i]}),r=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)"default"!==i&&Object.prototype.hasOwnProperty.call(e,i)&&s(t,e,i);return r(t,e),t};Object.defineProperty(t,"__esModule",{value:!0}),t.BrowserFeatures=t.KeyboardSupport=void 0;const o=n(i(4333)),a=i(4693),l=n(i(8163));var h;!function(e){e[e.Always=0]="Always",e[e.FullScreen=1]="FullScreen",e[e.None=2]="None"}(h||(t.KeyboardSupport=h={}));const c="object"==typeof navigator?navigator:{};t.BrowserFeatures={clipboard:{writeText:l.isNative||document.queryCommandSupported&&document.queryCommandSupported("copy")||!!(c&&c.clipboard&&c.clipboard.writeText),readText:l.isNative||!!(c&&c.clipboard&&c.clipboard.readText)},keyboard:l.isNative||o.isStandalone()?h.Always:c.keyboard||o.isSafari?h.FullScreen:h.None,touch:"ontouchstart"in a.mainWindow||c.maxTouchPoints>0,pointerEvents:a.mainWindow.PointerEvent&&("ontouchstart"in a.mainWindow||navigator.maxTouchPoints>0)}},7093:function(e,t,i){var s,r=this&&this.__createBinding||(Object.create?function(e,t,i,s){void 0===s&&(s=i);var r=Object.getOwnPropertyDescriptor(t,i);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,s,r)}:function(e,t,i,s){void 0===s&&(s=i),e[s]=t[i]}),n=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)"default"!==i&&Object.prototype.hasOwnProperty.call(e,i)&&r(t,e,i);return n(t,e),t};Object.defineProperty(t,"__esModule",{value:!0}),t.SafeTriangle=t.DragAndDropObserver=t.ModifierKeyEmitter=t.DetectedFullscreenMode=t.Namespace=t.EventHelper=t.EventType=t.sharedMutationObserver=t.Dimension=t.WindowIntervalTimer=t.scheduleAtNextAnimationFrame=t.runAtThisOrScheduleAtNextAnimationFrame=t.WindowIdleValue=t.addStandardDisposableGenericMouseUpListener=t.addStandardDisposableGenericMouseDownListener=t.addStandardDisposableListener=t.onDidUnregisterWindow=t.onWillUnregisterWindow=t.onDidRegisterWindow=t.hasWindow=t.getWindowById=t.getWindowId=t.getWindowsCount=t.getWindows=t.getDocument=t.getWindow=t.registerWindow=void 0,t.clearNode=function(e){for(;e.firstChild;)e.firstChild.remove()},t.clearNodeRecursively=function e(t){for(;t.firstChild;){const i=t.firstChild;i.remove(),e(i)}},t.addDisposableListener=C,t.addDisposableGenericMouseDownListener=w,t.addDisposableGenericMouseMoveListener=function(e,i,s){return C(e,g.isIOS&&l.BrowserFeatures.pointerEvents?t.EventType.POINTER_MOVE:t.EventType.MOUSE_MOVE,i,s)},t.addDisposableGenericMouseUpListener=E,t.runWhenWindowIdle=function(e,t,i){return(0,d._runWhenIdle)(e,t,i)},t.disposableWindowInterval=function(e,t,i,s){let r=0;const n=e.setInterval((()=>{r++,("number"==typeof s&&r>=s||!0===t())&&o.dispose()}),i),o=(0,p.toDisposable)((()=>{e.clearInterval(n)}));return o},t.measure=function(e,i){return(0,t.scheduleAtNextAnimationFrame)(e,i,1e4)},t.modify=function(e,i){return(0,t.scheduleAtNextAnimationFrame)(e,i,-1e4)},t.addDisposableThrottledListener=function(e,t,i,s,r){return new T(e,t,i,s,r)},t.getComputedStyle=k,t.getClientArea=function e(i,s){const r=(0,t.getWindow)(i),n=r.document;if(i!==n.body)return new O(i.clientWidth,i.clientHeight);if(g.isIOS&&r?.visualViewport)return new O(r.visualViewport.width,r.visualViewport.height);if(r?.innerWidth&&r.innerHeight)return new O(r.innerWidth,r.innerHeight);if(n.body&&n.body.clientWidth&&n.body.clientHeight)return new O(n.body.clientWidth,n.body.clientHeight);if(n.documentElement&&n.documentElement.clientWidth&&n.documentElement.clientHeight)return new O(n.documentElement.clientWidth,n.documentElement.clientHeight);if(s)return e(s);throw new Error("Unable to figure out browser width and height")},t.getTopLeftOffset=I,t.size=function(e,t,i){"number"==typeof t&&(e.style.width=`${t}px`),"number"==typeof i&&(e.style.height=`${i}px`)},t.position=function(e,t,i,s,r,n="absolute"){"number"==typeof t&&(e.style.top=`${t}px`),"number"==typeof i&&(e.style.right=`${i}px`),"number"==typeof s&&(e.style.bottom=`${s}px`),"number"==typeof r&&(e.style.left=`${r}px`),e.style.position=n},t.getDomNodePagePosition=function(e){const i=e.getBoundingClientRect(),s=(0,t.getWindow)(e);return{left:i.left+s.scrollX,top:i.top+s.scrollY,width:i.width,height:i.height}},t.getDomNodeZoomLevel=function(e){let t=e,i=1;do{const e=k(t).zoom;null!=e&&"1"!==e&&(i*=e),t=t.parentElement}while(null!==t&&t!==t.ownerDocument.documentElement);return i},t.getTotalWidth=P,t.getContentWidth=function(e){const t=M.getBorderLeftWidth(e)+M.getBorderRightWidth(e),i=M.getPaddingLeft(e)+M.getPaddingRight(e);return e.offsetWidth-t-i},t.getTotalScrollWidth=x,t.getContentHeight=function(e){const t=M.getBorderTopWidth(e)+M.getBorderBottomWidth(e),i=M.getPaddingTop(e)+M.getPaddingBottom(e);return e.offsetHeight-t-i},t.getTotalHeight=function(e){const t=M.getMarginTop(e)+M.getMarginBottom(e);return e.offsetHeight+t},t.getLargestChildWidth=function(e,t){const i=t.map((t=>Math.max(x(t),P(t))+function(e,t){if(null===e)return 0;const i=I(e),s=I(t);return i.left-s.left}(t,e)||0));return Math.max(...i)},t.isAncestor=B,t.setParentFlowTo=function(e,t){e.dataset[N]=t.id},t.isAncestorUsingFlowTo=function(e,t){let i=e;for(;i;){if(i===t)return!0;if(Q(i)){const e=U(i);if(e){i=e;continue}}i=i.parentNode}return!1},t.findParentWithClass=F,t.hasParentWithClass=function(e,t,i){return!!F(e,t,i)},t.isShadowRoot=W,t.isInShadowDOM=function(e){return!!H(e)},t.getShadowRoot=H,t.getActiveElement=K,t.isActiveElement=function(e){return K()===e},t.isAncestorOfActiveElement=function(e){return B(K(),e)},t.isActiveDocument=function(e){return e.ownerDocument===z()},t.getActiveDocument=z,t.getActiveWindow=function(){const e=z();return e.defaultView?.window??v.mainWindow},t.isGlobalStylesheet=function(e){return j.has(e)},t.createStyleSheet2=function(){return new $},t.createStyleSheet=V,t.cloneGlobalStylesheets=function(e){const t=new p.DisposableStore;for(const[i,s]of j)t.add(G(i,s,e));return t},t.createMetaElement=function(e=v.mainWindow.document.head){return q("meta",e)},t.createLinkElement=function(e=v.mainWindow.document.head){return q("link",e)},t.createCSSRule=function e(t,i,s=Y()){if(s&&i){s.sheet?.insertRule(`${t} {${i}}`,0);for(const r of j.get(s)??[])e(t,i,r)}},t.removeCSSRulesContainingSelector=function e(t,i=Y()){if(!i)return;const s=Z(i),r=[];for(let e=0;e<s.length;e++){const i=s[e];J(i)&&-1!==i.selectorText.indexOf(t)&&r.push(e)}for(let e=r.length-1;e>=0;e--)i.sheet?.deleteRule(r[e]);for(const s of j.get(i)??[])e(t,s)},t.isHTMLElement=Q,t.isHTMLAnchorElement=function(e){return e instanceof HTMLAnchorElement||e instanceof(0,t.getWindow)(e).HTMLAnchorElement},t.isHTMLSpanElement=function(e){return e instanceof HTMLSpanElement||e instanceof(0,t.getWindow)(e).HTMLSpanElement},t.isHTMLTextAreaElement=function(e){return e instanceof HTMLTextAreaElement||e instanceof(0,t.getWindow)(e).HTMLTextAreaElement},t.isHTMLInputElement=function(e){return e instanceof HTMLInputElement||e instanceof(0,t.getWindow)(e).HTMLInputElement},t.isHTMLButtonElement=function(e){return e instanceof HTMLButtonElement||e instanceof(0,t.getWindow)(e).HTMLButtonElement},t.isHTMLDivElement=function(e){return e instanceof HTMLDivElement||e instanceof(0,t.getWindow)(e).HTMLDivElement},t.isSVGElement=function(e){return e instanceof SVGElement||e instanceof(0,t.getWindow)(e).SVGElement},t.isMouseEvent=function(e){return e instanceof MouseEvent||e instanceof(0,t.getWindow)(e).MouseEvent},t.isKeyboardEvent=function(e){return e instanceof KeyboardEvent||e instanceof(0,t.getWindow)(e).KeyboardEvent},t.isPointerEvent=function(e){return e instanceof PointerEvent||e instanceof(0,t.getWindow)(e).PointerEvent},t.isDragEvent=function(e){return e instanceof DragEvent||e instanceof(0,t.getWindow)(e).DragEvent},t.isEventLike=function(e){const t=e;return!(!t||"function"!=typeof t.preventDefault||"function"!=typeof t.stopPropagation)},t.saveParentsScrollTop=function(e){const t=[];for(let i=0;e&&e.nodeType===e.ELEMENT_NODE;i++)t[i]=e.scrollTop,e=e.parentNode;return t},t.restoreParentsScrollTop=function(e,t){for(let i=0;e&&e.nodeType===e.ELEMENT_NODE;i++)e.scrollTop!==t[i]&&(e.scrollTop=t[i]),e=e.parentNode},t.trackFocus=function(e){return new ee(e)},t.after=function(e,t){return e.after(t),t},t.append=te,t.prepend=function(e,t){return e.insertBefore(t,e.firstChild),t},t.reset=function(e,...t){e.innerText="",te(e,...t)},t.$=ne,t.join=function(e,t){const i=[];return e.forEach(((e,s)=>{s>0&&(t instanceof Node?i.push(t.cloneNode()):i.push(document.createTextNode(t))),i.push(e)})),i},t.setVisibility=function(e,...t){e?oe(...t):ae(...t)},t.show=oe,t.hide=ae,t.removeTabIndexAndUpdateFocus=function(e){if(e&&e.hasAttribute("tabIndex")){if(e.ownerDocument.activeElement===e){const t=function(e){for(;e&&e.nodeType===e.ELEMENT_NODE;){if(Q(e)&&e.hasAttribute("tabIndex"))return e;e=e.parentNode}return null}(e.parentElement);t?.focus()}e.removeAttribute("tabindex")}},t.finalHandler=function(e){return t=>{t.preventDefault(),t.stopPropagation(),e(t)}},t.domContentLoaded=function(e){return new Promise((t=>{if("complete"===e.document.readyState||e.document&&null!==e.document.body)t(void 0);else{const i=()=>{e.window.removeEventListener("DOMContentLoaded",i,!1),t()};e.window.addEventListener("DOMContentLoaded",i,!1)}}))},t.computeScreenAwareSize=function(e,t){const i=e.devicePixelRatio*t;return Math.max(1,Math.floor(i))/e.devicePixelRatio},t.windowOpenNoOpener=function(e){v.mainWindow.open(e,"_blank","noopener")},t.windowOpenPopup=function(e){const t=Math.floor(v.mainWindow.screenLeft+v.mainWindow.innerWidth/2-le/2),i=Math.floor(v.mainWindow.screenTop+v.mainWindow.innerHeight/2-he/2);v.mainWindow.open(e,"_blank",`width=${le},height=${he},top=${i},left=${t}`)},t.windowOpenWithSuccess=function(e,t=!0){const i=v.mainWindow.open();return!!i&&(t&&(i.opener=null),i.location.href=e,!0)},t.animate=function(e,i){const s=()=>{i(),r=(0,t.scheduleAtNextAnimationFrame)(e,s)};let r=(0,t.scheduleAtNextAnimationFrame)(e,s);return(0,p.toDisposable)((()=>r.dispose()))},t.asCSSPropertyValue=function(e){return`'${e.replace(/'/g,"%27")}'`},t.asCssValueWithDefault=function e(t,i){if(void 0!==t){const s=t.match(/^\s*var\((.+)\)$/);if(s){const t=s[1].split(",",2);return 2===t.length&&(i=e(t[1].trim(),i)),`var(${t[0]}, ${i})`}return t}return i},t.detectFullscreen=function(e){return e.document.fullscreenElement||e.document.webkitFullscreenElement||e.document.webkitIsFullScreen?{mode:ce.DOCUMENT,guess:!1}:e.innerHeight===e.screen.height?{mode:ce.BROWSER,guess:!1}:(g.isMacintosh||g.isLinux)&&e.outerHeight===e.screen.height&&e.outerWidth===e.screen.width?{mode:ce.BROWSER,guess:!0}:null},t.multibyteAwareBtoa=function(e){return btoa(function(e){const t=new Uint16Array(e.length);for(let i=0;i<t.length;i++)t[i]=e.charCodeAt(i);let i="";const s=new Uint8Array(t.buffer);for(let e=0;e<s.length;e++)i+=String.fromCharCode(s[e]);return i}(e))},t.getCookieValue=function(e){const t=document.cookie.match("(^|[^;]+)\\s*"+e+"\\s*=\\s*([^;]+)");return t?t.pop():void 0},t.h=function(e,...t){let i,s;Array.isArray(t[0])?(i={},s=t[0]):(i=t[0]||{},s=t[1]);const r=_e.exec(e);if(!r||!r.groups)throw new Error("Bad use of h");const n=r.groups.tag||"div",o=document.createElement(n);r.groups.id&&(o.id=r.groups.id);const a=[];if(r.groups.class)for(const e of r.groups.class.split("."))""!==e&&a.push(e);if(void 0!==i.className)for(const e of i.className.split("."))""!==e&&a.push(e);a.length>0&&(o.className=a.join(" "));const l={};if(r.groups.name&&(l[r.groups.name]=o),s)for(const e of s)Q(e)?o.appendChild(e):"string"==typeof e?o.append(e):"root"in e&&(Object.assign(l,e),o.appendChild(e.root));for(const[e,t]of Object.entries(i))if("className"!==e)if("style"===e)for(const[e,i]of Object.entries(t))o.style.setProperty(fe(e),"number"==typeof i?i+"px":""+i);else"tabIndex"===e?o.tabIndex=t:o.setAttribute(fe(e),t.toString());return l.root=o,l},t.svgElem=function(e,...t){let i,s;Array.isArray(t[0])?(i={},s=t[0]):(i=t[0]||{},s=t[1]);const r=_e.exec(e);if(!r||!r.groups)throw new Error("Bad use of h");const n=r.groups.tag||"div",o=document.createElementNS("http://www.w3.org/2000/svg",n);r.groups.id&&(o.id=r.groups.id);const a=[];if(r.groups.class)for(const e of r.groups.class.split("."))""!==e&&a.push(e);if(void 0!==i.className)for(const e of i.className.split("."))""!==e&&a.push(e);a.length>0&&(o.className=a.join(" "));const l={};if(r.groups.name&&(l[r.groups.name]=o),s)for(const e of s)Q(e)?o.appendChild(e):"string"==typeof e?o.append(e):"root"in e&&(Object.assign(l,e),o.appendChild(e.root));for(const[e,t]of Object.entries(i))if("className"!==e)if("style"===e)for(const[e,i]of Object.entries(t))o.style.setProperty(fe(e),"number"==typeof i?i+"px":""+i);else"tabIndex"===e?o.tabIndex=t:o.setAttribute(fe(e),t.toString());return l.root=o,l},t.copyAttributes=pe,t.trackAttributes=function(e,i,s){pe(e,i,s);const r=new p.DisposableStore;return r.add(t.sharedMutationObserver.observe(e,r,{attributes:!0,attributeFilter:s})((t=>{for(const s of t)"attributes"===s.type&&s.attributeName&&ge(e,i,s.attributeName)}))),r};const a=o(i(4333)),l=i(7745),h=i(5394),c=i(5964),d=i(1758),u=i(9807),_=o(i(802)),f=i(7883),p=i(7150),g=o(i(8163)),m=i(6304),v=i(4693),S=i(7704);s=function(){const e=new Map;(0,v.ensureCodeWindow)(v.mainWindow,1);const i={window:v.mainWindow,disposables:new p.DisposableStore};e.set(v.mainWindow.vscodeWindowId,i);const s=new _.Emitter,r=new _.Emitter,n=new _.Emitter;return{onDidRegisterWindow:s.event,onWillUnregisterWindow:n.event,onDidUnregisterWindow:r.event,registerWindow(i){if(e.has(i.vscodeWindowId))return p.Disposable.None;const o=new p.DisposableStore,a={window:i,disposables:o.add(new p.DisposableStore)};return e.set(i.vscodeWindowId,a),o.add((0,p.toDisposable)((()=>{e.delete(i.vscodeWindowId),r.fire(i)}))),o.add(C(i,t.EventType.BEFORE_UNLOAD,(()=>{n.fire(i)}))),s.fire(a),o},getWindows:()=>e.values(),getWindowsCount:()=>e.size,getWindowId:e=>e.vscodeWindowId,hasWindow:t=>e.has(t),getWindowById:function(t,s){return("number"==typeof t?e.get(t):void 0)??(s?i:void 0)},getWindow(e){const t=e;if(t?.ownerDocument?.defaultView)return t.ownerDocument.defaultView.window;const i=e;return i?.view?i.view.window:v.mainWindow},getDocument(e){const i=e;return(0,t.getWindow)(i).document}}}(),t.registerWindow=s.registerWindow,t.getWindow=s.getWindow,t.getDocument=s.getDocument,t.getWindows=s.getWindows,t.getWindowsCount=s.getWindowsCount,t.getWindowId=s.getWindowId,t.getWindowById=s.getWindowById,t.hasWindow=s.hasWindow,t.onDidRegisterWindow=s.onDidRegisterWindow,t.onWillUnregisterWindow=s.onWillUnregisterWindow,t.onDidUnregisterWindow=s.onDidUnregisterWindow;class b{constructor(e,t,i,s){this._node=e,this._type=t,this._handler=i,this._options=s||!1,this._node.addEventListener(this._type,this._handler,this._options)}dispose(){this._handler&&(this._node.removeEventListener(this._type,this._handler,this._options),this._node=null,this._handler=null)}}function C(e,t,i,s){return new b(e,t,i,s)}function y(e,t){return function(i){return t(new c.StandardMouseEvent(e,i))}}function w(e,i,s){return C(e,g.isIOS&&l.BrowserFeatures.pointerEvents?t.EventType.POINTER_DOWN:t.EventType.MOUSE_DOWN,i,s)}function E(e,i,s){return C(e,g.isIOS&&l.BrowserFeatures.pointerEvents?t.EventType.POINTER_UP:t.EventType.MOUSE_UP,i,s)}t.addStandardDisposableListener=function(e,i,s,r){let n=s;return"click"===i||"mousedown"===i||"contextmenu"===i?n=y((0,t.getWindow)(e),s):"keydown"!==i&&"keypress"!==i&&"keyup"!==i||(n=function(e){return function(t){return e(new h.StandardKeyboardEvent(t))}}(s)),C(e,i,n,r)},t.addStandardDisposableGenericMouseDownListener=function(e,i,s){return w(e,y((0,t.getWindow)(e),i),s)},t.addStandardDisposableGenericMouseUpListener=function(e,i,s){return E(e,y((0,t.getWindow)(e),i),s)};class D extends d.AbstractIdleValue{constructor(e,t){super(e,t)}}t.WindowIdleValue=D;class L extends d.IntervalTimer{constructor(e){super(),this.defaultTarget=e&&(0,t.getWindow)(e)}cancelAndSet(e,t,i){return super.cancelAndSet(e,t,i??this.defaultTarget)}}t.WindowIntervalTimer=L;class R{constructor(e,t=0){this._runner=e,this.priority=t,this._canceled=!1}dispose(){this._canceled=!0}execute(){if(!this._canceled)try{this._runner()}catch(e){(0,u.onUnexpectedError)(e)}}static sort(e,t){return t.priority-e.priority}}!function(){const e=new Map,i=new Map,s=new Map,r=new Map;t.scheduleAtNextAnimationFrame=(n,o,a=0)=>{const l=(0,t.getWindowId)(n),h=new R(o,a);let c=e.get(l);return c||(c=[],e.set(l,c)),c.push(h),s.get(l)||(s.set(l,!0),n.requestAnimationFrame((()=>(t=>{s.set(t,!1);const n=e.get(t)??[];for(i.set(t,n),e.set(t,[]),r.set(t,!0);n.length>0;)n.sort(R.sort),n.shift().execute();r.set(t,!1)})(l)))),h},t.runAtThisOrScheduleAtNextAnimationFrame=(e,s,n)=>{const o=(0,t.getWindowId)(e);if(r.get(o)){const e=new R(s,n);let t=i.get(o);return t||(t=[],i.set(o,t)),t.push(e),e}return(0,t.scheduleAtNextAnimationFrame)(e,s,n)}}();const A=function(e,t){return t};class T extends p.Disposable{constructor(e,t,i,s=A,r=8){super();let n=null,o=0;const a=this._register(new d.TimeoutTimer),l=()=>{o=(new Date).getTime(),i(n),n=null};this._register(C(e,t,(e=>{n=s(n,e);const t=(new Date).getTime()-o;t>=r?(a.cancel(),l()):a.setIfNotSet(l,r-t)})))}}function k(e){return(0,t.getWindow)(e).getComputedStyle(e,null)}class M{static convertToPixels(e,t){return parseFloat(t)||0}static getDimension(e,t,i){const s=k(e),r=s?s.getPropertyValue(t):"0";return M.convertToPixels(e,r)}static getBorderLeftWidth(e){return M.getDimension(e,"border-left-width","borderLeftWidth")}static getBorderRightWidth(e){return M.getDimension(e,"border-right-width","borderRightWidth")}static getBorderTopWidth(e){return M.getDimension(e,"border-top-width","borderTopWidth")}static getBorderBottomWidth(e){return M.getDimension(e,"border-bottom-width","borderBottomWidth")}static getPaddingLeft(e){return M.getDimension(e,"padding-left","paddingLeft")}static getPaddingRight(e){return M.getDimension(e,"padding-right","paddingRight")}static getPaddingTop(e){return M.getDimension(e,"padding-top","paddingTop")}static getPaddingBottom(e){return M.getDimension(e,"padding-bottom","paddingBottom")}static getMarginLeft(e){return M.getDimension(e,"margin-left","marginLeft")}static getMarginTop(e){return M.getDimension(e,"margin-top","marginTop")}static getMarginRight(e){return M.getDimension(e,"margin-right","marginRight")}static getMarginBottom(e){return M.getDimension(e,"margin-bottom","marginBottom")}}class O{static{this.None=new O(0,0)}constructor(e,t){this.width=e,this.height=t}with(e=this.width,t=this.height){return e!==this.width||t!==this.height?new O(e,t):this}static is(e){return"object"==typeof e&&"number"==typeof e.height&&"number"==typeof e.width}static lift(e){return e instanceof O?e:new O(e.width,e.height)}static equals(e,t){return e===t||!(!e||!t)&&e.width===t.width&&e.height===t.height}}function I(e){let t=e.offsetParent,i=e.offsetTop,s=e.offsetLeft;for(;null!==(e=e.parentNode)&&e!==e.ownerDocument.body&&e!==e.ownerDocument.documentElement;){i-=e.scrollTop;const r=W(e)?null:k(e);r&&(s-="rtl"!==r.direction?e.scrollLeft:-e.scrollLeft),e===t&&(s+=M.getBorderLeftWidth(e),i+=M.getBorderTopWidth(e),i+=e.offsetTop,s+=e.offsetLeft,t=e.offsetParent)}return{left:s,top:i}}function P(e){const t=M.getMarginLeft(e)+M.getMarginRight(e);return e.offsetWidth+t}function x(e){const t=M.getMarginLeft(e)+M.getMarginRight(e);return e.scrollWidth+t}function B(e,t){return Boolean(t?.contains(e))}t.Dimension=O;const N="parentFlowToElementId";function U(e){const t=e.dataset[N];return"string"==typeof t?e.ownerDocument.getElementById(t):null}function F(e,t,i){for(;e&&e.nodeType===e.ELEMENT_NODE;){if(e.classList.contains(t))return e;if(i)if("string"==typeof i){if(e.classList.contains(i))return null}else if(e===i)return null;e=e.parentNode}return null}function W(e){return e&&!!e.host&&!!e.mode}function H(e){for(;e.parentNode;){if(e===e.ownerDocument?.body)return null;e=e.parentNode}return W(e)?e:null}function K(){let e=z().activeElement;for(;e?.shadowRoot;)e=e.shadowRoot.activeElement;return e}function z(){return(0,t.getWindowsCount)()<=1?v.mainWindow.document:Array.from((0,t.getWindows)()).map((({window:e})=>e.document)).find((e=>e.hasFocus()))??v.mainWindow.document}const j=new Map;class ${constructor(){this._currentCssStyle="",this._styleSheet=void 0}setStyle(e){e!==this._currentCssStyle&&(this._currentCssStyle=e,this._styleSheet?this._styleSheet.innerText=e:this._styleSheet=V(v.mainWindow.document.head,(t=>t.innerText=e)))}dispose(){this._styleSheet&&(this._styleSheet.remove(),this._styleSheet=void 0)}}function V(e=v.mainWindow.document.head,i,s){const r=document.createElement("style");if(r.type="text/css",r.media="screen",i?.(r),e.appendChild(r),s&&s.add((0,p.toDisposable)((()=>r.remove()))),e===v.mainWindow.document.head){const e=new Set;j.set(r,e);for(const{window:i,disposables:n}of(0,t.getWindows)()){if(i===v.mainWindow)continue;const t=n.add(G(r,e,i));s?.add(t)}}return r}function G(e,i,s){const r=new p.DisposableStore,n=e.cloneNode(!0);s.document.head.appendChild(n),r.add((0,p.toDisposable)((()=>n.remove())));for(const t of Z(e))n.sheet?.insertRule(t.cssText,n.sheet?.cssRules.length);return r.add(t.sharedMutationObserver.observe(e,r,{childList:!0})((()=>{n.textContent=e.textContent}))),i.add(n),r.add((0,p.toDisposable)((()=>i.delete(n)))),r}function q(e,t=v.mainWindow.document.head){const i=document.createElement(e);return t.appendChild(i),i}t.sharedMutationObserver=new class{constructor(){this.mutationObservers=new Map}observe(e,t,i){let s=this.mutationObservers.get(e);s||(s=new Map,this.mutationObservers.set(e,s));const r=(0,m.hash)(i);let n=s.get(r);if(n)n.users+=1;else{const o=new _.Emitter,a=new MutationObserver((e=>o.fire(e)));a.observe(e,i);const l=n={users:1,observer:a,onDidMutate:o.event};t.add((0,p.toDisposable)((()=>{l.users-=1,0===l.users&&(o.dispose(),a.disconnect(),s?.delete(r),0===s?.size&&this.mutationObservers.delete(e))}))),s.set(r,n)}return n.onDidMutate}};let X=null;function Y(){return X||(X=V()),X}function Z(e){return e?.sheet?.rules?e.sheet.rules:e?.sheet?.cssRules?e.sheet.cssRules:[]}function J(e){return"string"==typeof e.selectorText}function Q(e){return e instanceof HTMLElement||e instanceof(0,t.getWindow)(e).HTMLElement}t.EventType={CLICK:"click",AUXCLICK:"auxclick",DBLCLICK:"dblclick",MOUSE_UP:"mouseup",MOUSE_DOWN:"mousedown",MOUSE_OVER:"mouseover",MOUSE_MOVE:"mousemove",MOUSE_OUT:"mouseout",MOUSE_ENTER:"mouseenter",MOUSE_LEAVE:"mouseleave",MOUSE_WHEEL:"wheel",POINTER_UP:"pointerup",POINTER_DOWN:"pointerdown",POINTER_MOVE:"pointermove",POINTER_LEAVE:"pointerleave",CONTEXT_MENU:"contextmenu",WHEEL:"wheel",KEY_DOWN:"keydown",KEY_PRESS:"keypress",KEY_UP:"keyup",LOAD:"load",BEFORE_UNLOAD:"beforeunload",UNLOAD:"unload",PAGE_SHOW:"pageshow",PAGE_HIDE:"pagehide",PASTE:"paste",ABORT:"abort",ERROR:"error",RESIZE:"resize",SCROLL:"scroll",FULLSCREEN_CHANGE:"fullscreenchange",WK_FULLSCREEN_CHANGE:"webkitfullscreenchange",SELECT:"select",CHANGE:"change",SUBMIT:"submit",RESET:"reset",FOCUS:"focus",FOCUS_IN:"focusin",FOCUS_OUT:"focusout",BLUR:"blur",INPUT:"input",STORAGE:"storage",DRAG_START:"dragstart",DRAG:"drag",DRAG_ENTER:"dragenter",DRAG_LEAVE:"dragleave",DRAG_OVER:"dragover",DROP:"drop",DRAG_END:"dragend",ANIMATION_START:a.isWebKit?"webkitAnimationStart":"animationstart",ANIMATION_END:a.isWebKit?"webkitAnimationEnd":"animationend",ANIMATION_ITERATION:a.isWebKit?"webkitAnimationIteration":"animationiteration"},t.EventHelper={stop:(e,t)=>(e.preventDefault(),t&&e.stopPropagation(),e)};class ee extends p.Disposable{static hasFocusWithin(e){if(Q(e)){const t=H(e);return B(t?t.activeElement:e.ownerDocument.activeElement,e)}{const t=e;return B(t.document.activeElement,t.document)}}constructor(e){super(),this._onDidFocus=this._register(new _.Emitter),this.onDidFocus=this._onDidFocus.event,this._onDidBlur=this._register(new _.Emitter),this.onDidBlur=this._onDidBlur.event;let i=ee.hasFocusWithin(e),s=!1;const r=()=>{s=!1,i||(i=!0,this._onDidFocus.fire())},n=()=>{i&&(s=!0,(Q(e)?(0,t.getWindow)(e):e).setTimeout((()=>{s&&(s=!1,i=!1,this._onDidBlur.fire())}),0))};this._refreshStateHandler=()=>{ee.hasFocusWithin(e)!==i&&(i?n():r())},this._register(C(e,t.EventType.FOCUS,r,!0)),this._register(C(e,t.EventType.BLUR,n,!0)),Q(e)&&(this._register(C(e,t.EventType.FOCUS_IN,(()=>this._refreshStateHandler()))),this._register(C(e,t.EventType.FOCUS_OUT,(()=>this._refreshStateHandler()))))}refreshState(){this._refreshStateHandler()}}function te(e,...t){if(e.append(...t),1===t.length&&"string"!=typeof t[0])return t[0]}const ie=/([\w\-]+)?(#([\w\-]+))?((\.([\w\-]+))*)/;var se;function re(e,t,i,...s){const r=ie.exec(t);if(!r)throw new Error("Bad use of emmet");const n=r[1]||"div";let o;return o=e!==se.HTML?document.createElementNS(e,n):document.createElement(n),r[3]&&(o.id=r[3]),r[4]&&(o.className=r[4].replace(/\./g," ").trim()),i&&Object.entries(i).forEach((([e,t])=>{void 0!==t&&(/^on\w+$/.test(e)?o[e]=t:"selected"===e?t&&o.setAttribute(e,"true"):o.setAttribute(e,t))})),o.append(...s),o}function ne(e,t,...i){return re(se.HTML,e,t,...i)}function oe(...e){for(const t of e)t.style.display="",t.removeAttribute("aria-hidden")}function ae(...e){for(const t of e)t.style.display="none",t.setAttribute("aria-hidden","true")}!function(e){e.HTML="http://www.w3.org/1999/xhtml",e.SVG="http://www.w3.org/2000/svg"}(se||(t.Namespace=se={})),ne.SVG=function(e,t,...i){return re(se.SVG,e,t,...i)};const le=780,he=640;var ce;!function(e){e[e.DOCUMENT=1]="DOCUMENT",e[e.BROWSER=2]="BROWSER"}(ce||(t.DetectedFullscreenMode=ce={}));class de extends _.Emitter{constructor(){super(),this._subscriptions=new p.DisposableStore,this._keyStatus={altKey:!1,shiftKey:!1,ctrlKey:!1,metaKey:!1},this._subscriptions.add(_.Event.runAndSubscribe(t.onDidRegisterWindow,(({window:e,disposables:t})=>this.registerListeners(e,t)),{window:v.mainWindow,disposables:this._subscriptions}))}registerListeners(e,t){t.add(C(e,"keydown",(e=>{if(e.defaultPrevented)return;const t=new h.StandardKeyboardEvent(e);if(t.keyCode!==f.KeyCode.Alt||!e.repeat){if(e.altKey&&!this._keyStatus.altKey)this._keyStatus.lastKeyPressed="alt";else if(e.ctrlKey&&!this._keyStatus.ctrlKey)this._keyStatus.lastKeyPressed="ctrl";else if(e.metaKey&&!this._keyStatus.metaKey)this._keyStatus.lastKeyPressed="meta";else if(e.shiftKey&&!this._keyStatus.shiftKey)this._keyStatus.lastKeyPressed="shift";else{if(t.keyCode===f.KeyCode.Alt)return;this._keyStatus.lastKeyPressed=void 0}this._keyStatus.altKey=e.altKey,this._keyStatus.ctrlKey=e.ctrlKey,this._keyStatus.metaKey=e.metaKey,this._keyStatus.shiftKey=e.shiftKey,this._keyStatus.lastKeyPressed&&(this._keyStatus.event=e,this.fire(this._keyStatus))}}),!0)),t.add(C(e,"keyup",(e=>{e.defaultPrevented||(!e.altKey&&this._keyStatus.altKey?this._keyStatus.lastKeyReleased="alt":!e.ctrlKey&&this._keyStatus.ctrlKey?this._keyStatus.lastKeyReleased="ctrl":!e.metaKey&&this._keyStatus.metaKey?this._keyStatus.lastKeyReleased="meta":!e.shiftKey&&this._keyStatus.shiftKey?this._keyStatus.lastKeyReleased="shift":this._keyStatus.lastKeyReleased=void 0,this._keyStatus.lastKeyPressed!==this._keyStatus.lastKeyReleased&&(this._keyStatus.lastKeyPressed=void 0),this._keyStatus.altKey=e.altKey,this._keyStatus.ctrlKey=e.ctrlKey,this._keyStatus.metaKey=e.metaKey,this._keyStatus.shiftKey=e.shiftKey,this._keyStatus.lastKeyReleased&&(this._keyStatus.event=e,this.fire(this._keyStatus)))}),!0)),t.add(C(e.document.body,"mousedown",(()=>{this._keyStatus.lastKeyPressed=void 0}),!0)),t.add(C(e.document.body,"mouseup",(()=>{this._keyStatus.lastKeyPressed=void 0}),!0)),t.add(C(e.document.body,"mousemove",(e=>{e.buttons&&(this._keyStatus.lastKeyPressed=void 0)}),!0)),t.add(C(e,"blur",(()=>{this.resetKeyStatus()})))}get keyStatus(){return this._keyStatus}get isModifierPressed(){return this._keyStatus.altKey||this._keyStatus.ctrlKey||this._keyStatus.metaKey||this._keyStatus.shiftKey}resetKeyStatus(){this.doResetKeyStatus(),this.fire(this._keyStatus)}doResetKeyStatus(){this._keyStatus={altKey:!1,shiftKey:!1,ctrlKey:!1,metaKey:!1}}static getInstance(){return de.instance||(de.instance=new de),de.instance}dispose(){super.dispose(),this._subscriptions.dispose()}}t.ModifierKeyEmitter=de;class ue extends p.Disposable{constructor(e,t){super(),this.element=e,this.callbacks=t,this.counter=0,this.dragStartTime=0,this.registerListeners()}registerListeners(){this.callbacks.onDragStart&&this._register(C(this.element,t.EventType.DRAG_START,(e=>{this.callbacks.onDragStart?.(e)}))),this.callbacks.onDrag&&this._register(C(this.element,t.EventType.DRAG,(e=>{this.callbacks.onDrag?.(e)}))),this._register(C(this.element,t.EventType.DRAG_ENTER,(e=>{this.counter++,this.dragStartTime=e.timeStamp,this.callbacks.onDragEnter?.(e)}))),this._register(C(this.element,t.EventType.DRAG_OVER,(e=>{e.preventDefault(),this.callbacks.onDragOver?.(e,e.timeStamp-this.dragStartTime)}))),this._register(C(this.element,t.EventType.DRAG_LEAVE,(e=>{this.counter--,0===this.counter&&(this.dragStartTime=0,this.callbacks.onDragLeave?.(e))}))),this._register(C(this.element,t.EventType.DRAG_END,(e=>{this.counter=0,this.dragStartTime=0,this.callbacks.onDragEnd?.(e)}))),this._register(C(this.element,t.EventType.DROP,(e=>{this.counter=0,this.dragStartTime=0,this.callbacks.onDrop?.(e)})))}}t.DragAndDropObserver=ue;const _e=/(?<tag>[\w\-]+)?(?:#(?<id>[\w\-]+))?(?<class>(?:\.(?:[\w\-]+))*)(?:@(?<name>(?:[\w\_])+))?/;function fe(e){return e.replace(/([a-z])([A-Z])/g,"$1-$2").toLowerCase()}function pe(e,t,i){for(const{name:s,value:r}of e.attributes)i&&!i.includes(s)||t.setAttribute(s,r)}function ge(e,t,i){const s=e.getAttribute(i);s?t.setAttribute(i,s):t.removeAttribute(i)}t.SafeTriangle=class{constructor(e,t,i){this.originX=e,this.originY=t,this.triangles=[];const{top:s,left:r,right:n,bottom:o}=i.getBoundingClientRect(),a=this.triangles;let l=0;a[l++]=r,a[l++]=s,a[l++]=n,a[l++]=s,a[l++]=r,a[l++]=s,a[l++]=r,a[l++]=o,a[l++]=n,a[l++]=s,a[l++]=n,a[l++]=o,a[l++]=r,a[l++]=o,a[l++]=n,a[l++]=o}contains(e,t){const{triangles:i,originX:s,originY:r}=this;for(let n=0;n<4;n++)if((0,S.isPointWithinTriangle)(e,t,s,r,i[2*n],i[2*n+1],i[2*n+2],i[2*n+3]))return!0;return!1}}},9675:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.FastDomNode=void 0,t.createFastDomNode=function(e){return new i(e)};class i{constructor(e){this.domNode=e,this._maxWidth="",this._width="",this._height="",this._top="",this._left="",this._bottom="",this._right="",this._paddingTop="",this._paddingLeft="",this._paddingBottom="",this._paddingRight="",this._fontFamily="",this._fontWeight="",this._fontSize="",this._fontStyle="",this._fontFeatureSettings="",this._fontVariationSettings="",this._textDecoration="",this._lineHeight="",this._letterSpacing="",this._className="",this._display="",this._position="",this._visibility="",this._color="",this._backgroundColor="",this._layerHint=!1,this._contain="none",this._boxShadow=""}setMaxWidth(e){const t=s(e);this._maxWidth!==t&&(this._maxWidth=t,this.domNode.style.maxWidth=this._maxWidth)}setWidth(e){const t=s(e);this._width!==t&&(this._width=t,this.domNode.style.width=this._width)}setHeight(e){const t=s(e);this._height!==t&&(this._height=t,this.domNode.style.height=this._height)}setTop(e){const t=s(e);this._top!==t&&(this._top=t,this.domNode.style.top=this._top)}setLeft(e){const t=s(e);this._left!==t&&(this._left=t,this.domNode.style.left=this._left)}setBottom(e){const t=s(e);this._bottom!==t&&(this._bottom=t,this.domNode.style.bottom=this._bottom)}setRight(e){const t=s(e);this._right!==t&&(this._right=t,this.domNode.style.right=this._right)}setPaddingTop(e){const t=s(e);this._paddingTop!==t&&(this._paddingTop=t,this.domNode.style.paddingTop=this._paddingTop)}setPaddingLeft(e){const t=s(e);this._paddingLeft!==t&&(this._paddingLeft=t,this.domNode.style.paddingLeft=this._paddingLeft)}setPaddingBottom(e){const t=s(e);this._paddingBottom!==t&&(this._paddingBottom=t,this.domNode.style.paddingBottom=this._paddingBottom)}setPaddingRight(e){const t=s(e);this._paddingRight!==t&&(this._paddingRight=t,this.domNode.style.paddingRight=this._paddingRight)}setFontFamily(e){this._fontFamily!==e&&(this._fontFamily=e,this.domNode.style.fontFamily=this._fontFamily)}setFontWeight(e){this._fontWeight!==e&&(this._fontWeight=e,this.domNode.style.fontWeight=this._fontWeight)}setFontSize(e){const t=s(e);this._fontSize!==t&&(this._fontSize=t,this.domNode.style.fontSize=this._fontSize)}setFontStyle(e){this._fontStyle!==e&&(this._fontStyle=e,this.domNode.style.fontStyle=this._fontStyle)}setFontFeatureSettings(e){this._fontFeatureSettings!==e&&(this._fontFeatureSettings=e,this.domNode.style.fontFeatureSettings=this._fontFeatureSettings)}setFontVariationSettings(e){this._fontVariationSettings!==e&&(this._fontVariationSettings=e,this.domNode.style.fontVariationSettings=this._fontVariationSettings)}setTextDecoration(e){this._textDecoration!==e&&(this._textDecoration=e,this.domNode.style.textDecoration=this._textDecoration)}setLineHeight(e){const t=s(e);this._lineHeight!==t&&(this._lineHeight=t,this.domNode.style.lineHeight=this._lineHeight)}setLetterSpacing(e){const t=s(e);this._letterSpacing!==t&&(this._letterSpacing=t,this.domNode.style.letterSpacing=this._letterSpacing)}setClassName(e){this._className!==e&&(this._className=e,this.domNode.className=this._className)}toggleClassName(e,t){this.domNode.classList.toggle(e,t),this._className=this.domNode.className}setDisplay(e){this._display!==e&&(this._display=e,this.domNode.style.display=this._display)}setPosition(e){this._position!==e&&(this._position=e,this.domNode.style.position=this._position)}setVisibility(e){this._visibility!==e&&(this._visibility=e,this.domNode.style.visibility=this._visibility)}setColor(e){this._color!==e&&(this._color=e,this.domNode.style.color=this._color)}setBackgroundColor(e){this._backgroundColor!==e&&(this._backgroundColor=e,this.domNode.style.backgroundColor=this._backgroundColor)}setLayerHinting(e){this._layerHint!==e&&(this._layerHint=e,this.domNode.style.transform=this._layerHint?"translate3d(0px, 0px, 0px)":"")}setBoxShadow(e){this._boxShadow!==e&&(this._boxShadow=e,this.domNode.style.boxShadow=e)}setContain(e){this._contain!==e&&(this._contain=e,this.domNode.style.contain=this._contain)}setAttribute(e,t){this.domNode.setAttribute(e,t)}removeAttribute(e){this.domNode.removeAttribute(e)}appendChild(e){this.domNode.appendChild(e.domNode)}removeChild(e){this.domNode.removeChild(e.domNode)}}function s(e){return"number"==typeof e?`${e}px`:e}t.FastDomNode=i},8328:function(e,t,i){var s=this&&this.__createBinding||(Object.create?function(e,t,i,s){void 0===s&&(s=i);var r=Object.getOwnPropertyDescriptor(t,i);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,s,r)}:function(e,t,i,s){void 0===s&&(s=i),e[s]=t[i]}),r=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)"default"!==i&&Object.prototype.hasOwnProperty.call(e,i)&&s(t,e,i);return r(t,e),t};Object.defineProperty(t,"__esModule",{value:!0}),t.GlobalPointerMoveMonitor=void 0;const o=n(i(7093)),a=i(7150);t.GlobalPointerMoveMonitor=class{constructor(){this._hooks=new a.DisposableStore,this._pointerMoveCallback=null,this._onStopCallback=null}dispose(){this.stopMonitoring(!1),this._hooks.dispose()}stopMonitoring(e,t){if(!this.isMonitoring())return;this._hooks.clear(),this._pointerMoveCallback=null;const i=this._onStopCallback;this._onStopCallback=null,e&&i&&i(t)}isMonitoring(){return!!this._pointerMoveCallback}startMonitoring(e,t,i,s,r){this.isMonitoring()&&this.stopMonitoring(!1),this._pointerMoveCallback=s,this._onStopCallback=r;let n=e;try{e.setPointerCapture(t),this._hooks.add((0,a.toDisposable)((()=>{try{e.releasePointerCapture(t)}catch(e){}})))}catch(t){n=o.getWindow(e)}this._hooks.add(o.addDisposableListener(n,o.EventType.POINTER_MOVE,(e=>{e.buttons===i?(e.preventDefault(),this._pointerMoveCallback(e)):this.stopMonitoring(!0)}))),this._hooks.add(o.addDisposableListener(n,o.EventType.POINTER_UP,(e=>this.stopMonitoring(!0))))}}},6609:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.IframeUtils=void 0,t.parentOriginHash=async function(e,t){if(!crypto.subtle)throw new Error("'crypto.subtle' is not available so webviews will not work. This is likely because the editor is not running in a secure context (https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts).");const i=JSON.stringify({parentOrigin:e,salt:t}),s=(new TextEncoder).encode(i);return function(e){const t=Array.from(new Uint8Array(e)).map((e=>e.toString(16).padStart(2,"0"))).join("");return BigInt(`0x${t}`).toString(32).padStart(52,"0")}(await crypto.subtle.digest("sha-256",s))};const i=new WeakMap;function s(e){if(!e.parent||e.parent===e)return null;try{const t=e.location,i=e.parent.location;if("null"!==t.origin&&"null"!==i.origin&&t.origin!==i.origin)return null}catch(e){return null}return e.parent}t.IframeUtils=class{static getSameOriginWindowChain(e){let t=i.get(e);if(!t){t=[],i.set(e,t);let r,n=e;do{r=s(n),r?t.push({window:new WeakRef(n),iframeElement:n.frameElement||null}):t.push({window:new WeakRef(n),iframeElement:null}),n=r}while(n)}return t.slice(0)}static getPositionOfChildWindowRelativeToAncestorWindow(e,t){if(!t||e===t)return{top:0,left:0};let i=0,s=0;const r=this.getSameOriginWindowChain(e);for(const e of r){const r=e.window.deref();if(i+=r?.scrollY??0,s+=r?.scrollX??0,r===t)break;if(!e.iframeElement)break;const n=e.iframeElement.getBoundingClientRect();i+=n.top,s+=n.left}return{top:i,left:s}}}},5394:function(e,t,i){var s=this&&this.__createBinding||(Object.create?function(e,t,i,s){void 0===s&&(s=i);var r=Object.getOwnPropertyDescriptor(t,i);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,s,r)}:function(e,t,i,s){void 0===s&&(s=i),e[s]=t[i]}),r=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)"default"!==i&&Object.prototype.hasOwnProperty.call(e,i)&&s(t,e,i);return r(t,e),t};Object.defineProperty(t,"__esModule",{value:!0}),t.StandardKeyboardEvent=void 0,t.printKeyboardEvent=function(e){const t=[];return e.ctrlKey&&t.push("ctrl"),e.shiftKey&&t.push("shift"),e.altKey&&t.push("alt"),e.metaKey&&t.push("meta"),`modifiers: [${t.join(",")}], code: ${e.code}, keyCode: ${e.keyCode}, key: ${e.key}`},t.printStandardKeyboardEvent=function(e){const t=[];return e.ctrlKey&&t.push("ctrl"),e.shiftKey&&t.push("shift"),e.altKey&&t.push("alt"),e.metaKey&&t.push("meta"),`modifiers: [${t.join(",")}], code: ${e.code}, keyCode: ${e.keyCode} ('${a.KeyCodeUtils.toString(e.keyCode)}')`};const o=n(i(4333)),a=i(7883),l=i(2811),h=n(i(8163)),c=h.isMacintosh?a.KeyMod.WinCtrl:a.KeyMod.CtrlCmd,d=a.KeyMod.Alt,u=a.KeyMod.Shift,_=h.isMacintosh?a.KeyMod.CtrlCmd:a.KeyMod.WinCtrl;t.StandardKeyboardEvent=class{constructor(e){this._standardKeyboardEventBrand=!0;const t=e;this.browserEvent=t,this.target=t.target,this.ctrlKey=t.ctrlKey,this.shiftKey=t.shiftKey,this.altKey=t.altKey,this.metaKey=t.metaKey,this.altGraphKey=t.getModifierState?.("AltGraph"),this.keyCode=function(e){if(e.charCode){const t=String.fromCharCode(e.charCode).toUpperCase();return a.KeyCodeUtils.fromString(t)}const t=e.keyCode;if(3===t)return a.KeyCode.PauseBreak;if(o.isFirefox)switch(t){case 59:return a.KeyCode.Semicolon;case 60:if(h.isLinux)return a.KeyCode.IntlBackslash;break;case 61:return a.KeyCode.Equal;case 107:return a.KeyCode.NumpadAdd;case 109:return a.KeyCode.NumpadSubtract;case 173:return a.KeyCode.Minus;case 224:if(h.isMacintosh)return a.KeyCode.Meta}else if(o.isWebKit){if(h.isMacintosh&&93===t)return a.KeyCode.Meta;if(!h.isMacintosh&&92===t)return a.KeyCode.Meta}return a.EVENT_KEY_CODE_MAP[t]||a.KeyCode.Unknown}(t),this.code=t.code,this.ctrlKey=this.ctrlKey||this.keyCode===a.KeyCode.Ctrl,this.altKey=this.altKey||this.keyCode===a.KeyCode.Alt,this.shiftKey=this.shiftKey||this.keyCode===a.KeyCode.Shift,this.metaKey=this.metaKey||this.keyCode===a.KeyCode.Meta,this._asKeybinding=this._computeKeybinding(),this._asKeyCodeChord=this._computeKeyCodeChord()}preventDefault(){this.browserEvent&&this.browserEvent.preventDefault&&this.browserEvent.preventDefault()}stopPropagation(){this.browserEvent&&this.browserEvent.stopPropagation&&this.browserEvent.stopPropagation()}toKeyCodeChord(){return this._asKeyCodeChord}equals(e){return this._asKeybinding===e}_computeKeybinding(){let e=a.KeyCode.Unknown;this.keyCode!==a.KeyCode.Ctrl&&this.keyCode!==a.KeyCode.Shift&&this.keyCode!==a.KeyCode.Alt&&this.keyCode!==a.KeyCode.Meta&&(e=this.keyCode);let t=0;return this.ctrlKey&&(t|=c),this.altKey&&(t|=d),this.shiftKey&&(t|=u),this.metaKey&&(t|=_),t|=e,t}_computeKeyCodeChord(){let e=a.KeyCode.Unknown;return this.keyCode!==a.KeyCode.Ctrl&&this.keyCode!==a.KeyCode.Shift&&this.keyCode!==a.KeyCode.Alt&&this.keyCode!==a.KeyCode.Meta&&(e=this.keyCode),new l.KeyCodeChord(this.ctrlKey,this.shiftKey,this.altKey,this.metaKey,e)}}},5964:function(e,t,i){var s=this&&this.__createBinding||(Object.create?function(e,t,i,s){void 0===s&&(s=i);var r=Object.getOwnPropertyDescriptor(t,i);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,s,r)}:function(e,t,i,s){void 0===s&&(s=i),e[s]=t[i]}),r=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)"default"!==i&&Object.prototype.hasOwnProperty.call(e,i)&&s(t,e,i);return r(t,e),t};Object.defineProperty(t,"__esModule",{value:!0}),t.StandardWheelEvent=t.DragMouseEvent=t.StandardMouseEvent=void 0;const o=n(i(4333)),a=i(6609),l=n(i(8163));class h{constructor(e,t){this.timestamp=Date.now(),this.browserEvent=t,this.leftButton=0===t.button,this.middleButton=1===t.button,this.rightButton=2===t.button,this.buttons=t.buttons,this.target=t.target,this.detail=t.detail||1,"dblclick"===t.type&&(this.detail=2),this.ctrlKey=t.ctrlKey,this.shiftKey=t.shiftKey,this.altKey=t.altKey,this.metaKey=t.metaKey,"number"==typeof t.pageX?(this.posx=t.pageX,this.posy=t.pageY):(this.posx=t.clientX+this.target.ownerDocument.body.scrollLeft+this.target.ownerDocument.documentElement.scrollLeft,this.posy=t.clientY+this.target.ownerDocument.body.scrollTop+this.target.ownerDocument.documentElement.scrollTop);const i=a.IframeUtils.getPositionOfChildWindowRelativeToAncestorWindow(e,t.view);this.posx-=i.left,this.posy-=i.top}preventDefault(){this.browserEvent.preventDefault()}stopPropagation(){this.browserEvent.stopPropagation()}}t.StandardMouseEvent=h,t.DragMouseEvent=class extends h{constructor(e,t){super(e,t),this.dataTransfer=t.dataTransfer}},t.StandardWheelEvent=class{constructor(e,t=0,i=0){this.browserEvent=e||null,this.target=e?e.target||e.targetNode||e.srcElement:null,this.deltaY=i,this.deltaX=t;let s=!1;if(o.isChrome){const e=navigator.userAgent.match(/Chrome\/(\d+)/);s=(e?parseInt(e[1]):123)<=122}if(e){const t=e,i=e,r=e.view?.devicePixelRatio||1;if(void 0!==t.wheelDeltaY)this.deltaY=s?t.wheelDeltaY/(120*r):t.wheelDeltaY/120;else if(void 0!==i.VERTICAL_AXIS&&i.axis===i.VERTICAL_AXIS)this.deltaY=-i.detail/3;else if("wheel"===e.type){const t=e;t.deltaMode===t.DOM_DELTA_LINE?o.isFirefox&&!l.isMacintosh?this.deltaY=-e.deltaY/3:this.deltaY=-e.deltaY:this.deltaY=-e.deltaY/40}if(void 0!==t.wheelDeltaX)o.isSafari&&l.isWindows?this.deltaX=-t.wheelDeltaX/120:this.deltaX=s?t.wheelDeltaX/(120*r):t.wheelDeltaX/120;else if(void 0!==i.HORIZONTAL_AXIS&&i.axis===i.HORIZONTAL_AXIS)this.deltaX=-e.detail/3;else if("wheel"===e.type){const t=e;t.deltaMode===t.DOM_DELTA_LINE?o.isFirefox&&!l.isMacintosh?this.deltaX=-e.deltaX/3:this.deltaX=-e.deltaX:this.deltaX=-e.deltaX/40}0===this.deltaY&&0===this.deltaX&&e.wheelDelta&&(this.deltaY=s?e.wheelDelta/(120*r):e.wheelDelta/120)}}preventDefault(){this.browserEvent?.preventDefault()}stopPropagation(){this.browserEvent?.stopPropagation()}}},8594:function(e,t,i){var s=this&&this.__createBinding||(Object.create?function(e,t,i,s){void 0===s&&(s=i);var r=Object.getOwnPropertyDescriptor(t,i);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,s,r)}:function(e,t,i,s){void 0===s&&(s=i),e[s]=t[i]}),r=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__decorate||function(e,t,i,s){var r,n=arguments.length,o=n<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(r=e[a])&&(o=(n<3?r(o):n>3?r(t,i,o):r(t,i))||o);return n>3&&o&&Object.defineProperty(t,i,o),o},o=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)"default"!==i&&Object.prototype.hasOwnProperty.call(e,i)&&s(t,e,i);return r(t,e),t};Object.defineProperty(t,"__esModule",{value:!0}),t.Gesture=t.EventType=void 0;const a=o(i(7093)),l=i(4693),h=o(i(3058)),c=i(4838),d=i(802),u=i(7150),_=i(6317);var f;!function(e){e.Tap="-xterm-gesturetap",e.Change="-xterm-gesturechange",e.Start="-xterm-gesturestart",e.End="-xterm-gesturesend",e.Contextmenu="-xterm-gesturecontextmenu"}(f||(t.EventType=f={}));class p extends u.Disposable{static{this.SCROLL_FRICTION=-.005}static{this.HOLD_DELAY=700}static{this.CLEAR_TAP_COUNT_TIME=400}constructor(){super(),this.dispatched=!1,this.targets=new _.LinkedList,this.ignoreTargets=new _.LinkedList,this.activeTouches={},this.handle=null,this._lastSetTapCountTime=0,this._register(d.Event.runAndSubscribe(a.onDidRegisterWindow,(({window:e,disposables:t})=>{t.add(a.addDisposableListener(e.document,"touchstart",(e=>this.onTouchStart(e)),{passive:!1})),t.add(a.addDisposableListener(e.document,"touchend",(t=>this.onTouchEnd(e,t)))),t.add(a.addDisposableListener(e.document,"touchmove",(e=>this.onTouchMove(e)),{passive:!1}))}),{window:l.mainWindow,disposables:this._store}))}static addTarget(e){if(!p.isTouchDevice())return u.Disposable.None;p.INSTANCE||(p.INSTANCE=(0,u.markAsSingleton)(new p));const t=p.INSTANCE.targets.push(e);return(0,u.toDisposable)(t)}static ignoreTarget(e){if(!p.isTouchDevice())return u.Disposable.None;p.INSTANCE||(p.INSTANCE=(0,u.markAsSingleton)(new p));const t=p.INSTANCE.ignoreTargets.push(e);return(0,u.toDisposable)(t)}static isTouchDevice(){return"ontouchstart"in l.mainWindow||navigator.maxTouchPoints>0}dispose(){this.handle&&(this.handle.dispose(),this.handle=null),super.dispose()}onTouchStart(e){const t=Date.now();this.handle&&(this.handle.dispose(),this.handle=null);for(let i=0,s=e.targetTouches.length;i<s;i++){const s=e.targetTouches.item(i);this.activeTouches[s.identifier]={id:s.identifier,initialTarget:s.target,initialTimeStamp:t,initialPageX:s.pageX,initialPageY:s.pageY,rollingTimestamps:[t],rollingPageX:[s.pageX],rollingPageY:[s.pageY]};const r=this.newGestureEvent(f.Start,s.target);r.pageX=s.pageX,r.pageY=s.pageY,this.dispatchEvent(r)}this.dispatched&&(e.preventDefault(),e.stopPropagation(),this.dispatched=!1)}onTouchEnd(e,t){const i=Date.now(),s=Object.keys(this.activeTouches).length;for(let r=0,n=t.changedTouches.length;r<n;r++){const n=t.changedTouches.item(r);if(!this.activeTouches.hasOwnProperty(String(n.identifier))){console.warn("move of an UNKNOWN touch",n);continue}const o=this.activeTouches[n.identifier],a=Date.now()-o.initialTimeStamp;if(a<p.HOLD_DELAY&&Math.abs(o.initialPageX-h.tail(o.rollingPageX))<30&&Math.abs(o.initialPageY-h.tail(o.rollingPageY))<30){const e=this.newGestureEvent(f.Tap,o.initialTarget);e.pageX=h.tail(o.rollingPageX),e.pageY=h.tail(o.rollingPageY),this.dispatchEvent(e)}else if(a>=p.HOLD_DELAY&&Math.abs(o.initialPageX-h.tail(o.rollingPageX))<30&&Math.abs(o.initialPageY-h.tail(o.rollingPageY))<30){const e=this.newGestureEvent(f.Contextmenu,o.initialTarget);e.pageX=h.tail(o.rollingPageX),e.pageY=h.tail(o.rollingPageY),this.dispatchEvent(e)}else if(1===s){const t=h.tail(o.rollingPageX),s=h.tail(o.rollingPageY),r=h.tail(o.rollingTimestamps)-o.rollingTimestamps[0],n=t-o.rollingPageX[0],a=s-o.rollingPageY[0],l=[...this.targets].filter((e=>o.initialTarget instanceof Node&&e.contains(o.initialTarget)));this.inertia(e,l,i,Math.abs(n)/r,n>0?1:-1,t,Math.abs(a)/r,a>0?1:-1,s)}this.dispatchEvent(this.newGestureEvent(f.End,o.initialTarget)),delete this.activeTouches[n.identifier]}this.dispatched&&(t.preventDefault(),t.stopPropagation(),this.dispatched=!1)}newGestureEvent(e,t){const i=document.createEvent("CustomEvent");return i.initEvent(e,!1,!0),i.initialTarget=t,i.tapCount=0,i}dispatchEvent(e){if(e.type===f.Tap){const t=(new Date).getTime();let i=0;i=t-this._lastSetTapCountTime>p.CLEAR_TAP_COUNT_TIME?1:2,this._lastSetTapCountTime=t,e.tapCount=i}else e.type!==f.Change&&e.type!==f.Contextmenu||(this._lastSetTapCountTime=0);if(e.initialTarget instanceof Node){for(const t of this.ignoreTargets)if(t.contains(e.initialTarget))return;const t=[];for(const i of this.targets)if(i.contains(e.initialTarget)){let s=0,r=e.initialTarget;for(;r&&r!==i;)s++,r=r.parentElement;t.push([s,i])}t.sort(((e,t)=>e[0]-t[0]));for(const[i,s]of t)s.dispatchEvent(e),this.dispatched=!0}}inertia(e,t,i,s,r,n,o,l,h){this.handle=a.scheduleAtNextAnimationFrame(e,(()=>{const a=Date.now(),c=a-i;let d=0,u=0,_=!0;s+=p.SCROLL_FRICTION*c,o+=p.SCROLL_FRICTION*c,s>0&&(_=!1,d=r*s*c),o>0&&(_=!1,u=l*o*c);const g=this.newGestureEvent(f.Change);g.translationX=d,g.translationY=u,t.forEach((e=>e.dispatchEvent(g))),_||this.inertia(e,t,a,s,r,n+d,o,l,h+u)}))}onTouchMove(e){const t=Date.now();for(let i=0,s=e.changedTouches.length;i<s;i++){const s=e.changedTouches.item(i);if(!this.activeTouches.hasOwnProperty(String(s.identifier))){console.warn("end of an UNKNOWN touch",s);continue}const r=this.activeTouches[s.identifier],n=this.newGestureEvent(f.Change,r.initialTarget);n.translationX=s.pageX-h.tail(r.rollingPageX),n.translationY=s.pageY-h.tail(r.rollingPageY),n.pageX=s.pageX,n.pageY=s.pageY,this.dispatchEvent(n),r.rollingPageX.length>3&&(r.rollingPageX.shift(),r.rollingPageY.shift(),r.rollingTimestamps.shift()),r.rollingPageX.push(s.pageX),r.rollingPageY.push(s.pageY),r.rollingTimestamps.push(t)}this.dispatched&&(e.preventDefault(),e.stopPropagation(),this.dispatched=!1)}}t.Gesture=p,n([c.memoize],p,"isTouchDevice",null)},8801:function(e,t,i){var s=this&&this.__createBinding||(Object.create?function(e,t,i,s){void 0===s&&(s=i);var r=Object.getOwnPropertyDescriptor(t,i);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,s,r)}:function(e,t,i,s){void 0===s&&(s=i),e[s]=t[i]}),r=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)"default"!==i&&Object.prototype.hasOwnProperty.call(e,i)&&s(t,e,i);return r(t,e),t};Object.defineProperty(t,"__esModule",{value:!0}),t.AbstractScrollbar=void 0;const o=n(i(7093)),a=i(9675),l=i(8328),h=i(8974),c=i(79),d=i(8286),u=n(i(8163));class _ extends d.Widget{constructor(e){super(),this._lazyRender=e.lazyRender,this._host=e.host,this._scrollable=e.scrollable,this._scrollByPage=e.scrollByPage,this._scrollbarState=e.scrollbarState,this._visibilityController=this._register(new c.ScrollbarVisibilityController(e.visibility,"visible scrollbar "+e.extraScrollbarClassName,"invisible scrollbar "+e.extraScrollbarClassName)),this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded()),this._pointerMoveMonitor=this._register(new l.GlobalPointerMoveMonitor),this._shouldRender=!0,this.domNode=(0,a.createFastDomNode)(document.createElement("div")),this.domNode.setAttribute("role","presentation"),this.domNode.setAttribute("aria-hidden","true"),this._visibilityController.setDomNode(this.domNode),this.domNode.setPosition("absolute"),this._register(o.addDisposableListener(this.domNode.domNode,o.EventType.POINTER_DOWN,(e=>this._domNodePointerDown(e))))}_createArrow(e){const t=this._register(new h.ScrollbarArrow(e));this.domNode.domNode.appendChild(t.bgDomNode),this.domNode.domNode.appendChild(t.domNode)}_createSlider(e,t,i,s){this.slider=(0,a.createFastDomNode)(document.createElement("div")),this.slider.setClassName("slider"),this.slider.setPosition("absolute"),this.slider.setTop(e),this.slider.setLeft(t),"number"==typeof i&&this.slider.setWidth(i),"number"==typeof s&&this.slider.setHeight(s),this.slider.setLayerHinting(!0),this.slider.setContain("strict"),this.domNode.domNode.appendChild(this.slider.domNode),this._register(o.addDisposableListener(this.slider.domNode,o.EventType.POINTER_DOWN,(e=>{0===e.button&&(e.preventDefault(),this._sliderPointerDown(e))}))),this.onclick(this.slider.domNode,(e=>{e.leftButton&&e.stopPropagation()}))}_onElementSize(e){return this._scrollbarState.setVisibleSize(e)&&(this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded()),this._shouldRender=!0,this._lazyRender||this.render()),this._shouldRender}_onElementScrollSize(e){return this._scrollbarState.setScrollSize(e)&&(this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded()),this._shouldRender=!0,this._lazyRender||this.render()),this._shouldRender}_onElementScrollPosition(e){return this._scrollbarState.setScrollPosition(e)&&(this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded()),this._shouldRender=!0,this._lazyRender||this.render()),this._shouldRender}beginReveal(){this._visibilityController.setShouldBeVisible(!0)}beginHide(){this._visibilityController.setShouldBeVisible(!1)}render(){this._shouldRender&&(this._shouldRender=!1,this._renderDomNode(this._scrollbarState.getRectangleLargeSize(),this._scrollbarState.getRectangleSmallSize()),this._updateSlider(this._scrollbarState.getSliderSize(),this._scrollbarState.getArrowSize()+this._scrollbarState.getSliderPosition()))}_domNodePointerDown(e){e.target===this.domNode.domNode&&this._onPointerDown(e)}delegatePointerDown(e){const t=this.domNode.domNode.getClientRects()[0].top,i=t+this._scrollbarState.getSliderPosition(),s=t+this._scrollbarState.getSliderPosition()+this._scrollbarState.getSliderSize(),r=this._sliderPointerPosition(e);i<=r&&r<=s?0===e.button&&(e.preventDefault(),this._sliderPointerDown(e)):this._onPointerDown(e)}_onPointerDown(e){let t,i;if(e.target===this.domNode.domNode&&"number"==typeof e.offsetX&&"number"==typeof e.offsetY)t=e.offsetX,i=e.offsetY;else{const s=o.getDomNodePagePosition(this.domNode.domNode);t=e.pageX-s.left,i=e.pageY-s.top}const s=this._pointerDownRelativePosition(t,i);this._setDesiredScrollPositionNow(this._scrollByPage?this._scrollbarState.getDesiredScrollPositionFromOffsetPaged(s):this._scrollbarState.getDesiredScrollPositionFromOffset(s)),0===e.button&&(e.preventDefault(),this._sliderPointerDown(e))}_sliderPointerDown(e){if(!(e.target&&e.target instanceof Element))return;const t=this._sliderPointerPosition(e),i=this._sliderOrthogonalPointerPosition(e),s=this._scrollbarState.clone();this.slider.toggleClassName("active",!0),this._pointerMoveMonitor.startMonitoring(e.target,e.pointerId,e.buttons,(e=>{const r=this._sliderOrthogonalPointerPosition(e),n=Math.abs(r-i);if(u.isWindows&&n>140)return void this._setDesiredScrollPositionNow(s.getScrollPosition());const o=this._sliderPointerPosition(e)-t;this._setDesiredScrollPositionNow(s.getDesiredScrollPositionFromDelta(o))}),(()=>{this.slider.toggleClassName("active",!1),this._host.onDragEnd()})),this._host.onDragStart()}_setDesiredScrollPositionNow(e){const t={};this.writeScrollPosition(t,e),this._scrollable.setScrollPositionNow(t)}updateScrollbarSize(e){this._updateScrollbarSize(e),this._scrollbarState.setScrollbarSize(e),this._shouldRender=!0,this._lazyRender||this.render()}isNeeded(){return this._scrollbarState.isNeeded()}}t.AbstractScrollbar=_},151:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.HorizontalScrollbar=void 0;const s=i(8801),r=i(8245),n=i(9881);class o extends s.AbstractScrollbar{constructor(e,t,i){const s=e.getScrollDimensions(),o=e.getCurrentScrollPosition();if(super({lazyRender:t.lazyRender,host:i,scrollbarState:new r.ScrollbarState(t.horizontalHasArrows?t.arrowSize:0,t.horizontal===n.ScrollbarVisibility.Hidden?0:t.horizontalScrollbarSize,t.vertical===n.ScrollbarVisibility.Hidden?0:t.verticalScrollbarSize,s.width,s.scrollWidth,o.scrollLeft),visibility:t.horizontal,extraScrollbarClassName:"horizontal",scrollable:e,scrollByPage:t.scrollByPage}),t.horizontalHasArrows)throw new Error("horizontalHasArrows is not supported in xterm.js");this._createSlider(Math.floor((t.horizontalScrollbarSize-t.horizontalSliderSize)/2),0,void 0,t.horizontalSliderSize)}_updateSlider(e,t){this.slider.setWidth(e),this.slider.setLeft(t)}_renderDomNode(e,t){this.domNode.setWidth(e),this.domNode.setHeight(t),this.domNode.setLeft(0),this.domNode.setBottom(0)}onDidScroll(e){return this._shouldRender=this._onElementScrollSize(e.scrollWidth)||this._shouldRender,this._shouldRender=this._onElementScrollPosition(e.scrollLeft)||this._shouldRender,this._shouldRender=this._onElementSize(e.width)||this._shouldRender,this._shouldRender}_pointerDownRelativePosition(e,t){return e}_sliderPointerPosition(e){return e.pageX}_sliderOrthogonalPointerPosition(e){return e.pageY}_updateScrollbarSize(e){this.slider.setHeight(e)}writeScrollPosition(e,t){e.scrollLeft=t}updateOptions(e){this.updateScrollbarSize(e.horizontal===n.ScrollbarVisibility.Hidden?0:e.horizontalScrollbarSize),this._scrollbarState.setOppositeScrollbarSize(e.vertical===n.ScrollbarVisibility.Hidden?0:e.verticalScrollbarSize),this._visibilityController.setVisibility(e.horizontal),this._scrollByPage=e.scrollByPage}}t.HorizontalScrollbar=o},8234:function(e,t,i){var s=this&&this.__createBinding||(Object.create?function(e,t,i,s){void 0===s&&(s=i);var r=Object.getOwnPropertyDescriptor(t,i);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,s,r)}:function(e,t,i,s){void 0===s&&(s=i),e[s]=t[i]}),r=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)"default"!==i&&Object.prototype.hasOwnProperty.call(e,i)&&s(t,e,i);return r(t,e),t};Object.defineProperty(t,"__esModule",{value:!0}),t.DomScrollableElement=t.SmoothScrollableElement=t.ScrollableElement=t.AbstractScrollableElement=t.MouseWheelClassifier=void 0;const o=i(4333),a=n(i(7093)),l=i(9675),h=i(5964),c=i(151),d=i(5473),u=i(8286),_=i(1758),f=i(802),p=i(7150),g=n(i(8163)),m=i(9881);class v{constructor(e,t,i){this.timestamp=e,this.deltaX=t,this.deltaY=i,this.score=0}}class S{static{this.INSTANCE=new S}constructor(){this._capacity=5,this._memory=[],this._front=-1,this._rear=-1}isPhysicalMouseWheel(){if(-1===this._front&&-1===this._rear)return!1;let e=1,t=0,i=1,s=this._rear;for(;;){const r=s===this._front?e:Math.pow(2,-i);if(e-=r,t+=this._memory[s].score*r,s===this._front)break;s=(this._capacity+s-1)%this._capacity,i++}return t<=.5}acceptStandardWheelEvent(e){if(o.isChrome){const t=a.getWindow(e.browserEvent),i=(0,o.getZoomFactor)(t);this.accept(Date.now(),e.deltaX*i,e.deltaY*i)}else this.accept(Date.now(),e.deltaX,e.deltaY)}accept(e,t,i){let s=null;const r=new v(e,t,i);-1===this._front&&-1===this._rear?(this._memory[0]=r,this._front=0,this._rear=0):(s=this._memory[this._rear],this._rear=(this._rear+1)%this._capacity,this._rear===this._front&&(this._front=(this._front+1)%this._capacity),this._memory[this._rear]=r),r.score=this._computeScore(r,s)}_computeScore(e,t){if(Math.abs(e.deltaX)>0&&Math.abs(e.deltaY)>0)return 1;let i=.5;if(this._isAlmostInt(e.deltaX)&&this._isAlmostInt(e.deltaY)||(i+=.25),t){const s=Math.abs(e.deltaX),r=Math.abs(e.deltaY),n=Math.abs(t.deltaX),o=Math.abs(t.deltaY),a=Math.max(Math.min(s,n),1),l=Math.max(Math.min(r,o),1),h=Math.max(s,n),c=Math.max(r,o);h%a==0&&c%l==0&&(i-=.5)}return Math.min(Math.max(i,0),1)}_isAlmostInt(e){return Math.abs(Math.round(e)-e)<.01}}t.MouseWheelClassifier=S;class b extends u.Widget{get options(){return this._options}constructor(e,t,i){super(),this._onScroll=this._register(new f.Emitter),this.onScroll=this._onScroll.event,this._onWillScroll=this._register(new f.Emitter),this.onWillScroll=this._onWillScroll.event,this._options=function(e){const t={lazyRender:void 0!==e.lazyRender&&e.lazyRender,className:void 0!==e.className?e.className:"",useShadows:void 0===e.useShadows||e.useShadows,handleMouseWheel:void 0===e.handleMouseWheel||e.handleMouseWheel,flipAxes:void 0!==e.flipAxes&&e.flipAxes,consumeMouseWheelIfScrollbarIsNeeded:void 0!==e.consumeMouseWheelIfScrollbarIsNeeded&&e.consumeMouseWheelIfScrollbarIsNeeded,alwaysConsumeMouseWheel:void 0!==e.alwaysConsumeMouseWheel&&e.alwaysConsumeMouseWheel,scrollYToX:void 0!==e.scrollYToX&&e.scrollYToX,mouseWheelScrollSensitivity:void 0!==e.mouseWheelScrollSensitivity?e.mouseWheelScrollSensitivity:1,fastScrollSensitivity:void 0!==e.fastScrollSensitivity?e.fastScrollSensitivity:5,scrollPredominantAxis:void 0===e.scrollPredominantAxis||e.scrollPredominantAxis,mouseWheelSmoothScroll:void 0===e.mouseWheelSmoothScroll||e.mouseWheelSmoothScroll,arrowSize:void 0!==e.arrowSize?e.arrowSize:11,listenOnDomNode:void 0!==e.listenOnDomNode?e.listenOnDomNode:null,horizontal:void 0!==e.horizontal?e.horizontal:m.ScrollbarVisibility.Auto,horizontalScrollbarSize:void 0!==e.horizontalScrollbarSize?e.horizontalScrollbarSize:10,horizontalSliderSize:void 0!==e.horizontalSliderSize?e.horizontalSliderSize:0,horizontalHasArrows:void 0!==e.horizontalHasArrows&&e.horizontalHasArrows,vertical:void 0!==e.vertical?e.vertical:m.ScrollbarVisibility.Auto,verticalScrollbarSize:void 0!==e.verticalScrollbarSize?e.verticalScrollbarSize:10,verticalHasArrows:void 0!==e.verticalHasArrows&&e.verticalHasArrows,verticalSliderSize:void 0!==e.verticalSliderSize?e.verticalSliderSize:0,scrollByPage:void 0!==e.scrollByPage&&e.scrollByPage};return t.horizontalSliderSize=void 0!==e.horizontalSliderSize?e.horizontalSliderSize:t.horizontalScrollbarSize,t.verticalSliderSize=void 0!==e.verticalSliderSize?e.verticalSliderSize:t.verticalScrollbarSize,g.isMacintosh&&(t.className+=" mac"),t}(t),this._scrollable=i,this._register(this._scrollable.onScroll((e=>{this._onWillScroll.fire(e),this._onDidScroll(e),this._onScroll.fire(e)})));const s={onMouseWheel:e=>this._onMouseWheel(e),onDragStart:()=>this._onDragStart(),onDragEnd:()=>this._onDragEnd()};this._verticalScrollbar=this._register(new d.VerticalScrollbar(this._scrollable,this._options,s)),this._horizontalScrollbar=this._register(new c.HorizontalScrollbar(this._scrollable,this._options,s)),this._domNode=document.createElement("div"),this._domNode.className="xterm-scrollable-element "+this._options.className,this._domNode.setAttribute("role","presentation"),this._domNode.style.position="relative",this._domNode.appendChild(e),this._domNode.appendChild(this._horizontalScrollbar.domNode.domNode),this._domNode.appendChild(this._verticalScrollbar.domNode.domNode),this._options.useShadows?(this._leftShadowDomNode=(0,l.createFastDomNode)(document.createElement("div")),this._leftShadowDomNode.setClassName("shadow"),this._domNode.appendChild(this._leftShadowDomNode.domNode),this._topShadowDomNode=(0,l.createFastDomNode)(document.createElement("div")),this._topShadowDomNode.setClassName("shadow"),this._domNode.appendChild(this._topShadowDomNode.domNode),this._topLeftShadowDomNode=(0,l.createFastDomNode)(document.createElement("div")),this._topLeftShadowDomNode.setClassName("shadow"),this._domNode.appendChild(this._topLeftShadowDomNode.domNode)):(this._leftShadowDomNode=null,this._topShadowDomNode=null,this._topLeftShadowDomNode=null),this._listenOnDomNode=this._options.listenOnDomNode||this._domNode,this._mouseWheelToDispose=[],this._setListeningToMouseWheel(this._options.handleMouseWheel),this.onmouseover(this._listenOnDomNode,(e=>this._onMouseOver(e))),this.onmouseleave(this._listenOnDomNode,(e=>this._onMouseLeave(e))),this._hideTimeout=this._register(new _.TimeoutTimer),this._isDragging=!1,this._mouseIsOver=!1,this._shouldRender=!0,this._revealOnScroll=!0}dispose(){this._mouseWheelToDispose=(0,p.dispose)(this._mouseWheelToDispose),super.dispose()}getDomNode(){return this._domNode}getOverviewRulerLayoutInfo(){return{parent:this._domNode,insertBefore:this._verticalScrollbar.domNode.domNode}}delegateVerticalScrollbarPointerDown(e){this._verticalScrollbar.delegatePointerDown(e)}getScrollDimensions(){return this._scrollable.getScrollDimensions()}setScrollDimensions(e){this._scrollable.setScrollDimensions(e,!1)}updateClassName(e){this._options.className=e,g.isMacintosh&&(this._options.className+=" mac"),this._domNode.className="xterm-scrollable-element "+this._options.className}updateOptions(e){void 0!==e.handleMouseWheel&&(this._options.handleMouseWheel=e.handleMouseWheel,this._setListeningToMouseWheel(this._options.handleMouseWheel)),void 0!==e.mouseWheelScrollSensitivity&&(this._options.mouseWheelScrollSensitivity=e.mouseWheelScrollSensitivity),void 0!==e.fastScrollSensitivity&&(this._options.fastScrollSensitivity=e.fastScrollSensitivity),void 0!==e.scrollPredominantAxis&&(this._options.scrollPredominantAxis=e.scrollPredominantAxis),void 0!==e.horizontal&&(this._options.horizontal=e.horizontal),void 0!==e.vertical&&(this._options.vertical=e.vertical),void 0!==e.horizontalScrollbarSize&&(this._options.horizontalScrollbarSize=e.horizontalScrollbarSize),void 0!==e.verticalScrollbarSize&&(this._options.verticalScrollbarSize=e.verticalScrollbarSize),void 0!==e.scrollByPage&&(this._options.scrollByPage=e.scrollByPage),this._horizontalScrollbar.updateOptions(this._options),this._verticalScrollbar.updateOptions(this._options),this._options.lazyRender||this._render()}setRevealOnScroll(e){this._revealOnScroll=e}delegateScrollFromMouseWheelEvent(e){this._onMouseWheel(new h.StandardWheelEvent(e))}_setListeningToMouseWheel(e){if(this._mouseWheelToDispose.length>0!==e&&(this._mouseWheelToDispose=(0,p.dispose)(this._mouseWheelToDispose),e)){const e=e=>{this._onMouseWheel(new h.StandardWheelEvent(e))};this._mouseWheelToDispose.push(a.addDisposableListener(this._listenOnDomNode,a.EventType.MOUSE_WHEEL,e,{passive:!1}))}}_onMouseWheel(e){if(e.browserEvent?.defaultPrevented)return;const t=S.INSTANCE;t.acceptStandardWheelEvent(e);let i=!1;if(e.deltaY||e.deltaX){let s=e.deltaY*this._options.mouseWheelScrollSensitivity,r=e.deltaX*this._options.mouseWheelScrollSensitivity;this._options.scrollPredominantAxis&&(this._options.scrollYToX&&r+s===0?r=s=0:Math.abs(s)>=Math.abs(r)?r=0:s=0),this._options.flipAxes&&([s,r]=[r,s]);const n=!g.isMacintosh&&e.browserEvent&&e.browserEvent.shiftKey;!this._options.scrollYToX&&!n||r||(r=s,s=0),e.browserEvent&&e.browserEvent.altKey&&(r*=this._options.fastScrollSensitivity,s*=this._options.fastScrollSensitivity);const o=this._scrollable.getFutureScrollPosition();let a={};if(s){const e=50*s,t=o.scrollTop-(e<0?Math.floor(e):Math.ceil(e));this._verticalScrollbar.writeScrollPosition(a,t)}if(r){const e=50*r,t=o.scrollLeft-(e<0?Math.floor(e):Math.ceil(e));this._horizontalScrollbar.writeScrollPosition(a,t)}a=this._scrollable.validateScrollPosition(a),(o.scrollLeft!==a.scrollLeft||o.scrollTop!==a.scrollTop)&&(this._options.mouseWheelSmoothScroll&&t.isPhysicalMouseWheel()?this._scrollable.setScrollPositionSmooth(a):this._scrollable.setScrollPositionNow(a),i=!0)}let s=i;!s&&this._options.alwaysConsumeMouseWheel&&(s=!0),!s&&this._options.consumeMouseWheelIfScrollbarIsNeeded&&(this._verticalScrollbar.isNeeded()||this._horizontalScrollbar.isNeeded())&&(s=!0),s&&(e.preventDefault(),e.stopPropagation())}_onDidScroll(e){this._shouldRender=this._horizontalScrollbar.onDidScroll(e)||this._shouldRender,this._shouldRender=this._verticalScrollbar.onDidScroll(e)||this._shouldRender,this._options.useShadows&&(this._shouldRender=!0),this._revealOnScroll&&this._reveal(),this._options.lazyRender||this._render()}renderNow(){if(!this._options.lazyRender)throw new Error("Please use `lazyRender` together with `renderNow`!");this._render()}_render(){if(this._shouldRender&&(this._shouldRender=!1,this._horizontalScrollbar.render(),this._verticalScrollbar.render(),this._options.useShadows)){const e=this._scrollable.getCurrentScrollPosition(),t=e.scrollTop>0,i=e.scrollLeft>0,s=i?" left":"",r=t?" top":"",n=i||t?" top-left-corner":"";this._leftShadowDomNode.setClassName(`shadow${s}`),this._topShadowDomNode.setClassName(`shadow${r}`),this._topLeftShadowDomNode.setClassName(`shadow${n}${r}${s}`)}}_onDragStart(){this._isDragging=!0,this._reveal()}_onDragEnd(){this._isDragging=!1,this._hide()}_onMouseLeave(e){this._mouseIsOver=!1,this._hide()}_onMouseOver(e){this._mouseIsOver=!0,this._reveal()}_reveal(){this._verticalScrollbar.beginReveal(),this._horizontalScrollbar.beginReveal(),this._scheduleHide()}_hide(){this._mouseIsOver||this._isDragging||(this._verticalScrollbar.beginHide(),this._horizontalScrollbar.beginHide())}_scheduleHide(){this._mouseIsOver||this._isDragging||this._hideTimeout.cancelAndSet((()=>this._hide()),500)}}t.AbstractScrollableElement=b,t.ScrollableElement=class extends b{constructor(e,t){(t=t||{}).mouseWheelSmoothScroll=!1;const i=new m.Scrollable({forceIntegerValues:!0,smoothScrollDuration:0,scheduleAtNextAnimationFrame:t=>a.scheduleAtNextAnimationFrame(a.getWindow(e),t)});super(e,t,i),this._register(i)}setScrollPosition(e){this._scrollable.setScrollPositionNow(e)}getScrollPosition(){return this._scrollable.getCurrentScrollPosition()}},t.SmoothScrollableElement=class extends b{constructor(e,t,i){super(e,t,i)}setScrollPosition(e){e.reuseAnimation?this._scrollable.setScrollPositionSmooth(e,e.reuseAnimation):this._scrollable.setScrollPositionNow(e)}getScrollPosition(){return this._scrollable.getCurrentScrollPosition()}},t.DomScrollableElement=class extends b{constructor(e,t){(t=t||{}).mouseWheelSmoothScroll=!1;const i=new m.Scrollable({forceIntegerValues:!1,smoothScrollDuration:0,scheduleAtNextAnimationFrame:t=>a.scheduleAtNextAnimationFrame(a.getWindow(e),t)});super(e,t,i),this._register(i),this._element=e,this._register(this.onScroll((e=>{e.scrollTopChanged&&(this._element.scrollTop=e.scrollTop),e.scrollLeftChanged&&(this._element.scrollLeft=e.scrollLeft)}))),this.scanDomNode()}setScrollPosition(e){this._scrollable.setScrollPositionNow(e)}getScrollPosition(){return this._scrollable.getCurrentScrollPosition()}scanDomNode(){this.setScrollDimensions({width:this._element.clientWidth,scrollWidth:this._element.scrollWidth,height:this._element.clientHeight,scrollHeight:this._element.scrollHeight}),this.setScrollPosition({scrollLeft:this._element.scrollLeft,scrollTop:this._element.scrollTop})}}},8974:function(e,t,i){var s=this&&this.__createBinding||(Object.create?function(e,t,i,s){void 0===s&&(s=i);var r=Object.getOwnPropertyDescriptor(t,i);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,s,r)}:function(e,t,i,s){void 0===s&&(s=i),e[s]=t[i]}),r=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)"default"!==i&&Object.prototype.hasOwnProperty.call(e,i)&&s(t,e,i);return r(t,e),t};Object.defineProperty(t,"__esModule",{value:!0}),t.ScrollbarArrow=t.ARROW_IMG_SIZE=void 0;const o=i(8328),a=i(8286),l=i(1758),h=n(i(7093));t.ARROW_IMG_SIZE=11;class c extends a.Widget{constructor(e){super(),this._onActivate=e.onActivate,this.bgDomNode=document.createElement("div"),this.bgDomNode.className="arrow-background",this.bgDomNode.style.position="absolute",this.bgDomNode.style.width=e.bgWidth+"px",this.bgDomNode.style.height=e.bgHeight+"px",void 0!==e.top&&(this.bgDomNode.style.top="0px"),void 0!==e.left&&(this.bgDomNode.style.left="0px"),void 0!==e.bottom&&(this.bgDomNode.style.bottom="0px"),void 0!==e.right&&(this.bgDomNode.style.right="0px"),this.domNode=document.createElement("div"),this.domNode.className=e.className,this.domNode.style.position="absolute",this.domNode.style.width=t.ARROW_IMG_SIZE+"px",this.domNode.style.height=t.ARROW_IMG_SIZE+"px",void 0!==e.top&&(this.domNode.style.top=e.top+"px"),void 0!==e.left&&(this.domNode.style.left=e.left+"px"),void 0!==e.bottom&&(this.domNode.style.bottom=e.bottom+"px"),void 0!==e.right&&(this.domNode.style.right=e.right+"px"),this._pointerMoveMonitor=this._register(new o.GlobalPointerMoveMonitor),this._register(h.addStandardDisposableListener(this.bgDomNode,h.EventType.POINTER_DOWN,(e=>this._arrowPointerDown(e)))),this._register(h.addStandardDisposableListener(this.domNode,h.EventType.POINTER_DOWN,(e=>this._arrowPointerDown(e)))),this._pointerdownRepeatTimer=this._register(new h.WindowIntervalTimer),this._pointerdownScheduleRepeatTimer=this._register(new l.TimeoutTimer)}_arrowPointerDown(e){e.target&&e.target instanceof Element&&(this._onActivate(),this._pointerdownRepeatTimer.cancel(),this._pointerdownScheduleRepeatTimer.cancelAndSet((()=>{this._pointerdownRepeatTimer.cancelAndSet((()=>this._onActivate()),1e3/24,h.getWindow(e))}),200),this._pointerMoveMonitor.startMonitoring(e.target,e.pointerId,e.buttons,(e=>{}),(()=>{this._pointerdownRepeatTimer.cancel(),this._pointerdownScheduleRepeatTimer.cancel()})),e.preventDefault())}}t.ScrollbarArrow=c},8245:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ScrollbarState=void 0;class i{constructor(e,t,i,s,r,n){this._scrollbarSize=Math.round(t),this._oppositeScrollbarSize=Math.round(i),this._arrowSize=Math.round(e),this._visibleSize=s,this._scrollSize=r,this._scrollPosition=n,this._computedAvailableSize=0,this._computedIsNeeded=!1,this._computedSliderSize=0,this._computedSliderRatio=0,this._computedSliderPosition=0,this._refreshComputedValues()}clone(){return new i(this._arrowSize,this._scrollbarSize,this._oppositeScrollbarSize,this._visibleSize,this._scrollSize,this._scrollPosition)}setVisibleSize(e){const t=Math.round(e);return this._visibleSize!==t&&(this._visibleSize=t,this._refreshComputedValues(),!0)}setScrollSize(e){const t=Math.round(e);return this._scrollSize!==t&&(this._scrollSize=t,this._refreshComputedValues(),!0)}setScrollPosition(e){const t=Math.round(e);return this._scrollPosition!==t&&(this._scrollPosition=t,this._refreshComputedValues(),!0)}setScrollbarSize(e){this._scrollbarSize=Math.round(e)}setOppositeScrollbarSize(e){this._oppositeScrollbarSize=Math.round(e)}static _computeValues(e,t,i,s,r){const n=Math.max(0,i-e),o=Math.max(0,n-2*t),a=s>0&&s>i;if(!a)return{computedAvailableSize:Math.round(n),computedIsNeeded:a,computedSliderSize:Math.round(o),computedSliderRatio:0,computedSliderPosition:0};const l=Math.round(Math.max(20,Math.floor(i*o/s))),h=(o-l)/(s-i),c=r*h;return{computedAvailableSize:Math.round(n),computedIsNeeded:a,computedSliderSize:Math.round(l),computedSliderRatio:h,computedSliderPosition:Math.round(c)}}_refreshComputedValues(){const e=i._computeValues(this._oppositeScrollbarSize,this._arrowSize,this._visibleSize,this._scrollSize,this._scrollPosition);this._computedAvailableSize=e.computedAvailableSize,this._computedIsNeeded=e.computedIsNeeded,this._computedSliderSize=e.computedSliderSize,this._computedSliderRatio=e.computedSliderRatio,this._computedSliderPosition=e.computedSliderPosition}getArrowSize(){return this._arrowSize}getScrollPosition(){return this._scrollPosition}getRectangleLargeSize(){return this._computedAvailableSize}getRectangleSmallSize(){return this._scrollbarSize}isNeeded(){return this._computedIsNeeded}getSliderSize(){return this._computedSliderSize}getSliderPosition(){return this._computedSliderPosition}getDesiredScrollPositionFromOffset(e){if(!this._computedIsNeeded)return 0;const t=e-this._arrowSize-this._computedSliderSize/2;return Math.round(t/this._computedSliderRatio)}getDesiredScrollPositionFromOffsetPaged(e){if(!this._computedIsNeeded)return 0;const t=e-this._arrowSize;let i=this._scrollPosition;return t<this._computedSliderPosition?i-=this._visibleSize:i+=this._visibleSize,i}getDesiredScrollPositionFromDelta(e){if(!this._computedIsNeeded)return 0;const t=this._computedSliderPosition+e;return Math.round(t/this._computedSliderRatio)}}t.ScrollbarState=i},79:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ScrollbarVisibilityController=void 0;const s=i(1758),r=i(7150),n=i(9881);class o extends r.Disposable{constructor(e,t,i){super(),this._visibility=e,this._visibleClassName=t,this._invisibleClassName=i,this._domNode=null,this._isVisible=!1,this._isNeeded=!1,this._rawShouldBeVisible=!1,this._shouldBeVisible=!1,this._revealTimer=this._register(new s.TimeoutTimer)}setVisibility(e){this._visibility!==e&&(this._visibility=e,this._updateShouldBeVisible())}setShouldBeVisible(e){this._rawShouldBeVisible=e,this._updateShouldBeVisible()}_applyVisibilitySetting(){return this._visibility!==n.ScrollbarVisibility.Hidden&&(this._visibility===n.ScrollbarVisibility.Visible||this._rawShouldBeVisible)}_updateShouldBeVisible(){const e=this._applyVisibilitySetting();this._shouldBeVisible!==e&&(this._shouldBeVisible=e,this.ensureVisibility())}setIsNeeded(e){this._isNeeded!==e&&(this._isNeeded=e,this.ensureVisibility())}setDomNode(e){this._domNode=e,this._domNode.setClassName(this._invisibleClassName),this.setShouldBeVisible(!1)}ensureVisibility(){this._isNeeded?this._shouldBeVisible?this._reveal():this._hide(!0):this._hide(!1)}_reveal(){this._isVisible||(this._isVisible=!0,this._revealTimer.setIfNotSet((()=>{this._domNode?.setClassName(this._visibleClassName)}),0))}_hide(e){this._revealTimer.cancel(),this._isVisible&&(this._isVisible=!1,this._domNode?.setClassName(this._invisibleClassName+(e?" fade":"")))}}t.ScrollbarVisibilityController=o},5473:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.VerticalScrollbar=void 0;const s=i(8801),r=i(8245),n=i(9881);class o extends s.AbstractScrollbar{constructor(e,t,i){const s=e.getScrollDimensions(),o=e.getCurrentScrollPosition();if(super({lazyRender:t.lazyRender,host:i,scrollbarState:new r.ScrollbarState(t.verticalHasArrows?t.arrowSize:0,t.vertical===n.ScrollbarVisibility.Hidden?0:t.verticalScrollbarSize,0,s.height,s.scrollHeight,o.scrollTop),visibility:t.vertical,extraScrollbarClassName:"vertical",scrollable:e,scrollByPage:t.scrollByPage}),t.verticalHasArrows)throw new Error("horizontalHasArrows is not supported in xterm.js");this._createSlider(0,Math.floor((t.verticalScrollbarSize-t.verticalSliderSize)/2),t.verticalSliderSize,void 0)}_updateSlider(e,t){this.slider.setHeight(e),this.slider.setTop(t)}_renderDomNode(e,t){this.domNode.setWidth(t),this.domNode.setHeight(e),this.domNode.setRight(0),this.domNode.setTop(0)}onDidScroll(e){return this._shouldRender=this._onElementScrollSize(e.scrollHeight)||this._shouldRender,this._shouldRender=this._onElementScrollPosition(e.scrollTop)||this._shouldRender,this._shouldRender=this._onElementSize(e.height)||this._shouldRender,this._shouldRender}_pointerDownRelativePosition(e,t){return t}_sliderPointerPosition(e){return e.pageY}_sliderOrthogonalPointerPosition(e){return e.pageX}_updateScrollbarSize(e){this.slider.setWidth(e)}writeScrollPosition(e,t){e.scrollTop=t}updateOptions(e){this.updateScrollbarSize(e.vertical===n.ScrollbarVisibility.Hidden?0:e.verticalScrollbarSize),this._scrollbarState.setOppositeScrollbarSize(0),this._visibilityController.setVisibility(e.vertical),this._scrollByPage=e.scrollByPage}}t.VerticalScrollbar=o},8286:function(e,t,i){var s=this&&this.__createBinding||(Object.create?function(e,t,i,s){void 0===s&&(s=i);var r=Object.getOwnPropertyDescriptor(t,i);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,s,r)}:function(e,t,i,s){void 0===s&&(s=i),e[s]=t[i]}),r=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)"default"!==i&&Object.prototype.hasOwnProperty.call(e,i)&&s(t,e,i);return r(t,e),t};Object.defineProperty(t,"__esModule",{value:!0}),t.Widget=void 0;const o=n(i(7093)),a=i(5394),l=i(5964),h=i(8594),c=i(7150);class d extends c.Disposable{onclick(e,t){this._register(o.addDisposableListener(e,o.EventType.CLICK,(i=>t(new l.StandardMouseEvent(o.getWindow(e),i)))))}onmousedown(e,t){this._register(o.addDisposableListener(e,o.EventType.MOUSE_DOWN,(i=>t(new l.StandardMouseEvent(o.getWindow(e),i)))))}onmouseover(e,t){this._register(o.addDisposableListener(e,o.EventType.MOUSE_OVER,(i=>t(new l.StandardMouseEvent(o.getWindow(e),i)))))}onmouseleave(e,t){this._register(o.addDisposableListener(e,o.EventType.MOUSE_LEAVE,(i=>t(new l.StandardMouseEvent(o.getWindow(e),i)))))}onkeydown(e,t){this._register(o.addDisposableListener(e,o.EventType.KEY_DOWN,(e=>t(new a.StandardKeyboardEvent(e)))))}onkeyup(e,t){this._register(o.addDisposableListener(e,o.EventType.KEY_UP,(e=>t(new a.StandardKeyboardEvent(e)))))}oninput(e,t){this._register(o.addDisposableListener(e,o.EventType.INPUT,t))}onblur(e,t){this._register(o.addDisposableListener(e,o.EventType.BLUR,t))}onfocus(e,t){this._register(o.addDisposableListener(e,o.EventType.FOCUS,t))}onchange(e,t){this._register(o.addDisposableListener(e,o.EventType.CHANGE,t))}ignoreGesture(e){return h.Gesture.ignoreTarget(e)}}t.Widget=d},4693:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.mainWindow=void 0,t.ensureCodeWindow=function(e,t){},t.mainWindow="object"==typeof window?window:globalThis},3058:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Permutation=t.CallbackIterable=t.ArrayQueue=t.booleanComparator=t.numberComparator=t.CompareResult=void 0,t.tail=function(e,t=0){return e[e.length-(1+t)]},t.tail2=function(e){if(0===e.length)throw new Error("Invalid tail call");return[e.slice(0,e.length-1),e[e.length-1]]},t.equals=function(e,t,i=(e,t)=>e===t){if(e===t)return!0;if(!e||!t)return!1;if(e.length!==t.length)return!1;for(let s=0,r=e.length;s<r;s++)if(!i(e[s],t[s]))return!1;return!0},t.removeFastWithoutKeepingOrder=function(e,t){const i=e.length-1;t<i&&(e[t]=e[i]),e.pop()},t.binarySearch=function(e,t,i){return n(e.length,(s=>i(e[s],t)))},t.binarySearch2=n,t.quickSelect=function e(t,i,s){if((t|=0)>=i.length)throw new TypeError("invalid index");const r=i[Math.floor(i.length*Math.random())],n=[],o=[],a=[];for(const e of i){const t=s(e,r);t<0?n.push(e):t>0?o.push(e):a.push(e)}return t<n.length?e(t,n,s):t<n.length+a.length?a[0]:e(t-(n.length+a.length),o,s)},t.groupBy=function(e,t){const i=[];let s;for(const r of e.slice(0).sort(t))s&&0===t(s[0],r)?s.push(r):(s=[r],i.push(s));return i},t.groupAdjacentBy=function*(e,t){let i,s;for(const r of e)void 0!==s&&t(s,r)?i.push(r):(i&&(yield i),i=[r]),s=r;i&&(yield i)},t.forEachAdjacent=function(e,t){for(let i=0;i<=e.length;i++)t(0===i?void 0:e[i-1],i===e.length?void 0:e[i])},t.forEachWithNeighbors=function(e,t){for(let i=0;i<e.length;i++)t(0===i?void 0:e[i-1],e[i],i+1===e.length?void 0:e[i+1])},t.sortedDiff=o,t.delta=function(e,t,i){const s=o(e,t,i),r=[],n=[];for(const t of s)r.push(...e.slice(t.start,t.start+t.deleteCount)),n.push(...t.toInsert);return{removed:r,added:n}},t.top=function(e,t,i){if(0===i)return[];const s=e.slice(0,i).sort(t);return a(e,t,s,i,e.length),s},t.topAsync=function(e,t,i,r,n){return 0===i?Promise.resolve([]):new Promise(((o,l)=>{(async()=>{const o=e.length,l=e.slice(0,i).sort(t);for(let h=i,c=Math.min(i+r,o);h<o;h=c,c=Math.min(c+r,o)){if(h>i&&await new Promise((e=>setTimeout(e))),n&&n.isCancellationRequested)throw new s.CancellationError;a(e,t,l,h,c)}return l})().then(o,l)}))},t.coalesce=function(e){return e.filter((e=>!!e))},t.coalesceInPlace=function(e){let t=0;for(let i=0;i<e.length;i++)e[i]&&(e[t]=e[i],t+=1);e.length=t},t.move=function(e,t,i){e.splice(i,0,e.splice(t,1)[0])},t.isFalsyOrEmpty=function(e){return!Array.isArray(e)||0===e.length},t.isNonEmptyArray=function(e){return Array.isArray(e)&&e.length>0},t.distinct=function(e,t=e=>e){const i=new Set;return e.filter((e=>{const s=t(e);return!i.has(s)&&(i.add(s),!0)}))},t.uniqueFilter=function(e){const t=new Set;return i=>{const s=e(i);return!t.has(s)&&(t.add(s),!0)}},t.firstOrDefault=function(e,t){return e.length>0?e[0]:t},t.lastOrDefault=function(e,t){return e.length>0?e[e.length-1]:t},t.commonPrefixLength=function(e,t,i=(e,t)=>e===t){let s=0;for(let r=0,n=Math.min(e.length,t.length);r<n&&i(e[r],t[r]);r++)s++;return s},t.range=function(e,t){let i="number"==typeof t?e:0;"number"==typeof t?i=e:(i=0,t=e);const s=[];if(i<=t)for(let e=i;e<t;e++)s.push(e);else for(let e=i;e>t;e--)s.push(e);return s},t.index=function(e,t,i){return e.reduce(((e,s)=>(e[t(s)]=i?i(s):s,e)),Object.create(null))},t.insert=function(e,t){return e.push(t),()=>l(e,t)},t.remove=l,t.arrayInsert=function(e,t,i){const s=e.slice(0,t),r=e.slice(t);return s.concat(i,r)},t.shuffle=function(e,t){let i;if("number"==typeof t){let e=t;i=()=>{const t=179426549*Math.sin(e++);return t-Math.floor(t)}}else i=Math.random;for(let t=e.length-1;t>0;t-=1){const s=Math.floor(i()*(t+1)),r=e[t];e[t]=e[s],e[s]=r}},t.pushToStart=function(e,t){const i=e.indexOf(t);i>-1&&(e.splice(i,1),e.unshift(t))},t.pushToEnd=function(e,t){const i=e.indexOf(t);i>-1&&(e.splice(i,1),e.push(t))},t.pushMany=function(e,t){for(const i of t)e.push(i)},t.mapArrayOrNot=function(e,t){return Array.isArray(e)?e.map(t):t(e)},t.asArray=function(e){return Array.isArray(e)?e:[e]},t.getRandomElement=function(e){return e[Math.floor(Math.random()*e.length)]},t.insertInto=h,t.splice=function(e,t,i,s){const r=c(e,t);let n=e.splice(r,i);return void 0===n&&(n=[]),h(e,r,s),n},t.compareBy=function(e,t){return(i,s)=>t(e(i),e(s))},t.tieBreakComparators=function(...e){return(t,i)=>{for(const s of e){const e=s(t,i);if(!d.isNeitherLessOrGreaterThan(e))return e}return d.neitherLessOrGreaterThan}},t.reverseOrder=function(e){return(t,i)=>-e(t,i)};const s=i(9807),r=i(8297);function n(e,t){let i=0,s=e-1;for(;i<=s;){const e=(i+s)/2|0,r=t(e);if(r<0)i=e+1;else{if(!(r>0))return e;s=e-1}}return-(i+1)}function o(e,t,i){const s=[];function r(e,t,i){if(0===t&&0===i.length)return;const r=s[s.length-1];r&&r.start+r.deleteCount===e?(r.deleteCount+=t,r.toInsert.push(...i)):s.push({start:e,deleteCount:t,toInsert:i})}let n=0,o=0;for(;;){if(n===e.length){r(n,0,t.slice(o));break}if(o===t.length){r(n,e.length-n,[]);break}const s=e[n],a=t[o],l=i(s,a);0===l?(n+=1,o+=1):l<0?(r(n,1,[]),n+=1):l>0&&(r(n,0,[a]),o+=1)}return s}function a(e,t,i,s,n){for(const o=i.length;s<n;s++){const n=e[s];if(t(n,i[o-1])<0){i.pop();const e=(0,r.findFirstIdxMonotonousOrArrLen)(i,(e=>t(n,e)<0));i.splice(e,0,n)}}}function l(e,t){const i=e.indexOf(t);if(i>-1)return e.splice(i,1),t}function h(e,t,i){const s=c(e,t),r=e.length,n=i.length;e.length=r+n;for(let t=r-1;t>=s;t--)e[t+n]=e[t];for(let t=0;t<n;t++)e[t+s]=i[t]}function c(e,t){return t<0?Math.max(t+e.length,0):Math.min(t,e.length)}var d;!function(e){e.isLessThan=function(e){return e<0},e.isLessThanOrEqual=function(e){return e<=0},e.isGreaterThan=function(e){return e>0},e.isNeitherLessOrGreaterThan=function(e){return 0===e},e.greaterThan=1,e.lessThan=-1,e.neitherLessOrGreaterThan=0}(d||(t.CompareResult=d={})),t.numberComparator=(e,t)=>e-t,t.booleanComparator=(e,i)=>(0,t.numberComparator)(e?1:0,i?1:0),t.ArrayQueue=class{constructor(e){this.items=e,this.firstIdx=0,this.lastIdx=this.items.length-1}get length(){return this.lastIdx-this.firstIdx+1}takeWhile(e){let t=this.firstIdx;for(;t<this.items.length&&e(this.items[t]);)t++;const i=t===this.firstIdx?null:this.items.slice(this.firstIdx,t);return this.firstIdx=t,i}takeFromEndWhile(e){let t=this.lastIdx;for(;t>=0&&e(this.items[t]);)t--;const i=t===this.lastIdx?null:this.items.slice(t+1,this.lastIdx+1);return this.lastIdx=t,i}peek(){if(0!==this.length)return this.items[this.firstIdx]}peekLast(){if(0!==this.length)return this.items[this.lastIdx]}dequeue(){const e=this.items[this.firstIdx];return this.firstIdx++,e}removeLast(){const e=this.items[this.lastIdx];return this.lastIdx--,e}takeCount(e){const t=this.items.slice(this.firstIdx,this.firstIdx+e);return this.firstIdx+=e,t}};class u{static{this.empty=new u((e=>{}))}constructor(e){this.iterate=e}forEach(e){this.iterate((t=>(e(t),!0)))}toArray(){const e=[];return this.iterate((t=>(e.push(t),!0))),e}filter(e){return new u((t=>this.iterate((i=>!e(i)||t(i)))))}map(e){return new u((t=>this.iterate((i=>t(e(i))))))}some(e){let t=!1;return this.iterate((i=>(t=e(i),!t))),t}findFirst(e){let t;return this.iterate((i=>!e(i)||(t=i,!1))),t}findLast(e){let t;return this.iterate((i=>(e(i)&&(t=i),!0))),t}findLastMaxBy(e){let t,i=!0;return this.iterate((s=>((i||d.isGreaterThan(e(s,t)))&&(i=!1,t=s),!0))),t}}t.CallbackIterable=u;class _{constructor(e){this._indexMap=e}static createSortPermutation(e,t){const i=Array.from(e.keys()).sort(((i,s)=>t(e[i],e[s])));return new _(i)}apply(e){return e.map(((t,i)=>e[this._indexMap[i]]))}inverse(){const e=this._indexMap.slice();for(let t=0;t<this._indexMap.length;t++)e[this._indexMap[t]]=t;return new _(e)}}t.Permutation=_},8297:(e,t)=>{function i(e,t,i=e.length-1){for(let s=i;s>=0;s--)if(t(e[s]))return s;return-1}function s(e,t,i=0,s=e.length){let r=i,n=s;for(;r<n;){const i=Math.floor((r+n)/2);t(e[i])?r=i+1:n=i}return r-1}function r(e,t,i=0,s=e.length){let r=i,n=s;for(;r<n;){const i=Math.floor((r+n)/2);t(e[i])?n=i:r=i+1}return r}Object.defineProperty(t,"__esModule",{value:!0}),t.MonotonousArray=void 0,t.findLast=function(e,t){const s=i(e,t);if(-1!==s)return e[s]},t.findLastIdx=i,t.findLastMonotonous=function(e,t){const i=s(e,t);return-1===i?void 0:e[i]},t.findLastIdxMonotonous=s,t.findFirstMonotonous=function(e,t){const i=r(e,t);return i===e.length?void 0:e[i]},t.findFirstIdxMonotonousOrArrLen=r,t.findFirstIdxMonotonous=function(e,t,i=0,s=e.length){const n=r(e,t,i,s);return n===e.length?-1:n},t.findFirstMax=o,t.findLastMax=function(e,t){if(0===e.length)return;let i=e[0];for(let s=1;s<e.length;s++){const r=e[s];t(r,i)>=0&&(i=r)}return i},t.findFirstMin=function(e,t){return o(e,((e,i)=>-t(e,i)))},t.findMaxIdx=function(e,t){if(0===e.length)return-1;let i=0;for(let s=1;s<e.length;s++)t(e[s],e[i])>0&&(i=s);return i},t.mapFindFirst=function(e,t){for(const i of e){const e=t(i);if(void 0!==e)return e}};class n{static{this.assertInvariants=!1}constructor(e){this._array=e,this._findLastMonotonousLastIdx=0}findLastMonotonous(e){if(n.assertInvariants){if(this._prevFindLastPredicate)for(const t of this._array)if(this._prevFindLastPredicate(t)&&!e(t))throw new Error("MonotonousArray: current predicate must be weaker than (or equal to) the previous predicate.");this._prevFindLastPredicate=e}const t=s(this._array,e,this._findLastMonotonousLastIdx);return this._findLastMonotonousLastIdx=t+1,-1===t?void 0:this._array[t]}}function o(e,t){if(0===e.length)return;let i=e[0];for(let s=1;s<e.length;s++){const r=e[s];t(r,i)>0&&(i=r)}return i}t.MonotonousArray=n},1758:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.AsyncIterableSource=t.CancelableAsyncIterableObject=t.AsyncIterableObject=t.LazyStatefulPromise=t.StatefulPromise=t.Promises=t.DeferredPromise=t.IntervalCounter=t.TaskSequentializer=t.GlobalIdleValue=t.AbstractIdleValue=t._runWhenIdle=t.runWhenGlobalIdle=t.ThrottledWorker=t.RunOnceWorker=t.ProcessTimeRunOnceScheduler=t.RunOnceScheduler=t.IntervalTimer=t.TimeoutTimer=t.LimitedQueue=t.Queue=t.Limiter=t.AutoOpenBarrier=t.Barrier=t.ThrottledDelayer=t.Delayer=t.SequencerByKey=t.Sequencer=t.Throttler=void 0,t.isThenable=c,t.createCancelablePromise=d,t.raceCancellation=function(e,t,i){return new Promise(((s,r)=>{const n=t.onCancellationRequested((()=>{n.dispose(),s(i)}));e.then(s,r).finally((()=>n.dispose()))}))},t.raceCancellationError=function(e,t){return new Promise(((i,s)=>{const n=t.onCancellationRequested((()=>{n.dispose(),s(new r.CancellationError)}));e.then(i,s).finally((()=>n.dispose()))}))},t.raceCancellablePromises=async function(e){let t=-1;const i=e.map(((e,i)=>e.then((e=>(t=i,e)))));try{return await Promise.race(i)}finally{e.forEach(((e,i)=>{i!==t&&e.cancel()}))}},t.raceTimeout=function(e,t,i){let s;const r=setTimeout((()=>{s?.(void 0),i?.()}),t);return Promise.race([e.finally((()=>clearTimeout(r))),new Promise((e=>s=e))])},t.asPromise=function(e){return new Promise(((t,i)=>{const s=e();c(s)?s.then(t,i):t(s)}))},t.promiseWithResolvers=u,t.timeout=g,t.disposableTimeout=function(e,t=0,i){const s=setTimeout((()=>{e(),i&&r.dispose()}),t),r=(0,o.toDisposable)((()=>{clearTimeout(s),i?.deleteAndLeak(r)}));return i?.add(r),r},t.sequence=function(e){const t=[];let i=0;const s=e.length;return Promise.resolve(null).then((function r(n){null!=n&&t.push(n);const o=i<s?e[i++]():null;return o?o.then(r):Promise.resolve(t)}))},t.first=function(e,t=e=>!!e,i=null){let s=0;const r=e.length,n=()=>{if(s>=r)return Promise.resolve(i);const o=e[s++];return Promise.resolve(o()).then((e=>t(e)?Promise.resolve(e):n()))};return n()},t.firstParallel=function(e,t=e=>!!e,i=null){if(0===e.length)return Promise.resolve(i);let s=e.length;const r=()=>{s=-1;for(const t of e)t.cancel?.()};return new Promise(((n,o)=>{for(const a of e)a.then((e=>{--s>=0&&t(e)?(r(),n(e)):0===s&&n(i)})).catch((e=>{--s>=0&&(r(),o(e))}))}))},t.retry=async function(e,t,i){let s;for(let r=0;r<i;r++)try{return await e()}catch(e){s=e,await g(t)}throw s},t.createCancelableAsyncIterable=function(e){const t=new s.CancellationTokenSource,i=e(t.token);return new A(t,(async e=>{const s=t.token.onCancellationRequested((()=>{s.dispose(),t.dispose(),e.reject(new r.CancellationError)}));try{for await(const s of i){if(t.token.isCancellationRequested)return;e.emitOne(s)}s.dispose(),t.dispose()}catch(i){s.dispose(),t.dispose(),e.reject(i)}}))};const s=i(8447),r=i(9807),n=i(802),o=i(7150),a=i(8163),l=i(5015),h=i(626);function c(e){return!!e&&"function"==typeof e.then}function d(e){const t=new s.CancellationTokenSource,i=e(t.token),n=new Promise(((e,s)=>{const n=t.token.onCancellationRequested((()=>{n.dispose(),s(new r.CancellationError)}));Promise.resolve(i).then((i=>{n.dispose(),t.dispose(),e(i)}),(e=>{n.dispose(),t.dispose(),s(e)}))}));return new class{cancel(){t.cancel(),t.dispose()}then(e,t){return n.then(e,t)}catch(e){return this.then(void 0,e)}finally(e){return n.finally(e)}}}function u(){let e,t;return{promise:new Promise(((i,s)=>{e=i,t=s})),resolve:e,reject:t}}class _{constructor(){this.isDisposed=!1,this.activePromise=null,this.queuedPromise=null,this.queuedPromiseFactory=null}queue(e){if(this.isDisposed)return Promise.reject(new Error("Throttler is disposed"));if(this.activePromise){if(this.queuedPromiseFactory=e,!this.queuedPromise){const e=()=>{if(this.queuedPromise=null,this.isDisposed)return;const e=this.queue(this.queuedPromiseFactory);return this.queuedPromiseFactory=null,e};this.queuedPromise=new Promise((t=>{this.activePromise.then(e,e).then(t)}))}return new Promise(((e,t)=>{this.queuedPromise.then(e,t)}))}return this.activePromise=e(),new Promise(((e,t)=>{this.activePromise.then((t=>{this.activePromise=null,e(t)}),(e=>{this.activePromise=null,t(e)}))}))}dispose(){this.isDisposed=!0}}t.Throttler=_,t.Sequencer=class{constructor(){this.current=Promise.resolve(null)}queue(e){return this.current=this.current.then((()=>e()),(()=>e()))}},t.SequencerByKey=class{constructor(){this.promiseMap=new Map}queue(e,t){const i=(this.promiseMap.get(e)??Promise.resolve()).catch((()=>{})).then(t).finally((()=>{this.promiseMap.get(e)===i&&this.promiseMap.delete(e)}));return this.promiseMap.set(e,i),i}};class f{constructor(e){this.defaultDelay=e,this.deferred=null,this.completionPromise=null,this.doResolve=null,this.doReject=null,this.task=null}trigger(e,t=this.defaultDelay){this.task=e,this.cancelTimeout(),this.completionPromise||(this.completionPromise=new Promise(((e,t)=>{this.doResolve=e,this.doReject=t})).then((()=>{if(this.completionPromise=null,this.doResolve=null,this.task){const e=this.task;return this.task=null,e()}})));const i=()=>{this.deferred=null,this.doResolve?.(null)};return this.deferred=t===l.MicrotaskDelay?(e=>{let t=!0;return queueMicrotask((()=>{t&&(t=!1,e())})),{isTriggered:()=>t,dispose:()=>{t=!1}}})(i):((e,t)=>{let i=!0;const s=setTimeout((()=>{i=!1,t()}),e);return{isTriggered:()=>i,dispose:()=>{clearTimeout(s),i=!1}}})(t,i),this.completionPromise}isTriggered(){return!!this.deferred?.isTriggered()}cancel(){this.cancelTimeout(),this.completionPromise&&(this.doReject?.(new r.CancellationError),this.completionPromise=null)}cancelTimeout(){this.deferred?.dispose(),this.deferred=null}dispose(){this.cancel()}}t.Delayer=f,t.ThrottledDelayer=class{constructor(e){this.delayer=new f(e),this.throttler=new _}trigger(e,t){return this.delayer.trigger((()=>this.throttler.queue(e)),t)}isTriggered(){return this.delayer.isTriggered()}cancel(){this.delayer.cancel()}dispose(){this.delayer.dispose(),this.throttler.dispose()}};class p{constructor(){this._isOpen=!1,this._promise=new Promise(((e,t)=>{this._completePromise=e}))}isOpen(){return this._isOpen}open(){this._isOpen=!0,this._completePromise(!0)}wait(){return this._promise}}function g(e,t){return t?new Promise(((i,s)=>{const n=setTimeout((()=>{o.dispose(),i()}),e),o=t.onCancellationRequested((()=>{clearTimeout(n),o.dispose(),s(new r.CancellationError)}))})):d((t=>g(e,t)))}t.Barrier=p,t.AutoOpenBarrier=class extends p{constructor(e){super(),this._timeout=setTimeout((()=>this.open()),e)}open(){clearTimeout(this._timeout),super.open()}};class m{constructor(e){this._size=0,this._isDisposed=!1,this.maxDegreeOfParalellism=e,this.outstandingPromises=[],this.runningPromises=0,this._onDrained=new n.Emitter}whenIdle(){return this.size>0?n.Event.toPromise(this.onDrained):Promise.resolve()}get onDrained(){return this._onDrained.event}get size(){return this._size}queue(e){if(this._isDisposed)throw new Error("Object has been disposed");return this._size++,new Promise(((t,i)=>{this.outstandingPromises.push({factory:e,c:t,e:i}),this.consume()}))}consume(){for(;this.outstandingPromises.length&&this.runningPromises<this.maxDegreeOfParalellism;){const e=this.outstandingPromises.shift();this.runningPromises++;const t=e.factory();t.then(e.c,e.e),t.then((()=>this.consumed()),(()=>this.consumed()))}}consumed(){this._isDisposed||(this.runningPromises--,0==--this._size&&this._onDrained.fire(),this.outstandingPromises.length>0&&this.consume())}clear(){if(this._isDisposed)throw new Error("Object has been disposed");this.outstandingPromises.length=0,this._size=this.runningPromises}dispose(){this._isDisposed=!0,this.outstandingPromises.length=0,this._size=0,this._onDrained.dispose()}}t.Limiter=m,t.Queue=class extends m{constructor(){super(1)}},t.LimitedQueue=class{constructor(){this.sequentializer=new C,this.tasks=0}queue(e){return this.sequentializer.isRunning()?this.sequentializer.queue((()=>this.sequentializer.run(this.tasks++,e()))):this.sequentializer.run(this.tasks++,e())}},t.TimeoutTimer=class{constructor(e,t){this._isDisposed=!1,this._token=-1,"function"==typeof e&&"number"==typeof t&&this.setIfNotSet(e,t)}dispose(){this.cancel(),this._isDisposed=!0}cancel(){-1!==this._token&&(clearTimeout(this._token),this._token=-1)}cancelAndSet(e,t){if(this._isDisposed)throw new r.BugIndicatingError("Calling 'cancelAndSet' on a disposed TimeoutTimer");this.cancel(),this._token=setTimeout((()=>{this._token=-1,e()}),t)}setIfNotSet(e,t){if(this._isDisposed)throw new r.BugIndicatingError("Calling 'setIfNotSet' on a disposed TimeoutTimer");-1===this._token&&(this._token=setTimeout((()=>{this._token=-1,e()}),t))}},t.IntervalTimer=class{constructor(){this.disposable=void 0,this.isDisposed=!1}cancel(){this.disposable?.dispose(),this.disposable=void 0}cancelAndSet(e,t,i=globalThis){if(this.isDisposed)throw new r.BugIndicatingError("Calling 'cancelAndSet' on a disposed IntervalTimer");this.cancel();const s=i.setInterval((()=>{e()}),t);this.disposable=(0,o.toDisposable)((()=>{i.clearInterval(s),this.disposable=void 0}))}dispose(){this.cancel(),this.isDisposed=!0}};class v{constructor(e,t){this.timeoutToken=-1,this.runner=e,this.timeout=t,this.timeoutHandler=this.onTimeout.bind(this)}dispose(){this.cancel(),this.runner=null}cancel(){this.isScheduled()&&(clearTimeout(this.timeoutToken),this.timeoutToken=-1)}schedule(e=this.timeout){this.cancel(),this.timeoutToken=setTimeout(this.timeoutHandler,e)}get delay(){return this.timeout}set delay(e){this.timeout=e}isScheduled(){return-1!==this.timeoutToken}flush(){this.isScheduled()&&(this.cancel(),this.doRun())}onTimeout(){this.timeoutToken=-1,this.runner&&this.doRun()}doRun(){this.runner?.()}}t.RunOnceScheduler=v,t.ProcessTimeRunOnceScheduler=class{constructor(e,t){t%1e3!=0&&console.warn(`ProcessTimeRunOnceScheduler resolution is 1s, ${t}ms is not a multiple of 1000ms.`),this.runner=e,this.timeout=t,this.counter=0,this.intervalToken=-1,this.intervalHandler=this.onInterval.bind(this)}dispose(){this.cancel(),this.runner=null}cancel(){this.isScheduled()&&(clearInterval(this.intervalToken),this.intervalToken=-1)}schedule(e=this.timeout){e%1e3!=0&&console.warn(`ProcessTimeRunOnceScheduler resolution is 1s, ${e}ms is not a multiple of 1000ms.`),this.cancel(),this.counter=Math.ceil(e/1e3),this.intervalToken=setInterval(this.intervalHandler,1e3)}isScheduled(){return-1!==this.intervalToken}onInterval(){this.counter--,this.counter>0||(clearInterval(this.intervalToken),this.intervalToken=-1,this.runner?.())}},t.RunOnceWorker=class extends v{constructor(e,t){super(e,t),this.units=[]}work(e){this.units.push(e),this.isScheduled()||this.schedule()}doRun(){const e=this.units;this.units=[],this.runner?.(e)}dispose(){this.units=[],super.dispose()}};class S extends o.Disposable{constructor(e,t){super(),this.options=e,this.handler=t,this.pendingWork=[],this.throttler=this._register(new o.MutableDisposable),this.disposed=!1}get pending(){return this.pendingWork.length}work(e){if(this.disposed)return!1;if("number"==typeof this.options.maxBufferedWork)if(this.throttler.value){if(this.pending+e.length>this.options.maxBufferedWork)return!1}else if(this.pending+e.length-this.options.maxWorkChunkSize>this.options.maxBufferedWork)return!1;for(const t of e)this.pendingWork.push(t);return this.throttler.value||this.doWork(),!0}doWork(){this.handler(this.pendingWork.splice(0,this.options.maxWorkChunkSize)),this.pendingWork.length>0&&(this.throttler.value=new v((()=>{this.throttler.clear(),this.doWork()}),this.options.throttleDelay),this.throttler.value.schedule())}dispose(){super.dispose(),this.disposed=!0}}t.ThrottledWorker=S,"function"!=typeof globalThis.requestIdleCallback||"function"!=typeof globalThis.cancelIdleCallback?t._runWhenIdle=(e,t)=>{(0,a.setTimeout0)((()=>{if(i)return;const e=Date.now()+15,s={didTimeout:!0,timeRemaining:()=>Math.max(0,e-Date.now())};t(Object.freeze(s))}));let i=!1;return{dispose(){i||(i=!0)}}}:t._runWhenIdle=(e,t,i)=>{const s=e.requestIdleCallback(t,"number"==typeof i?{timeout:i}:void 0);let r=!1;return{dispose(){r||(r=!0,e.cancelIdleCallback(s))}}},t.runWhenGlobalIdle=e=>(0,t._runWhenIdle)(globalThis,e);class b{constructor(e,i){this._didRun=!1,this._executor=()=>{try{this._value=i()}catch(e){this._error=e}finally{this._didRun=!0}},this._handle=(0,t._runWhenIdle)(e,(()=>this._executor()))}dispose(){this._handle.dispose()}get value(){if(this._didRun||(this._handle.dispose(),this._executor()),this._error)throw this._error;return this._value}get isInitialized(){return this._didRun}}t.AbstractIdleValue=b,t.GlobalIdleValue=class extends b{constructor(e){super(globalThis,e)}};class C{isRunning(e){return"number"==typeof e?this._running?.taskId===e:!!this._running}get running(){return this._running?.promise}cancelRunning(){this._running?.cancel()}run(e,t,i){return this._running={taskId:e,cancel:()=>i?.(),promise:t},t.then((()=>this.doneRunning(e)),(()=>this.doneRunning(e))),t}doneRunning(e){this._running&&e===this._running.taskId&&(this._running=void 0,this.runQueued())}runQueued(){if(this._queued){const e=this._queued;this._queued=void 0,e.run().then(e.promiseResolve,e.promiseReject)}}queue(e){if(this._queued)this._queued.run=e;else{const{promise:t,resolve:i,reject:s}=u();this._queued={run:e,promise:t,promiseResolve:i,promiseReject:s}}return this._queued.promise}hasQueued(){return!!this._queued}async join(){return this._queued?.promise??this._running?.promise}}var y,w,E;t.TaskSequentializer=C,t.IntervalCounter=class{constructor(e,t=()=>Date.now()){this.interval=e,this.nowFn=t,this.lastIncrementTime=0,this.value=0}increment(){const e=this.nowFn();return e-this.lastIncrementTime>this.interval&&(this.lastIncrementTime=e,this.value=0),this.value++,this.value}},function(e){e[e.Resolved=0]="Resolved",e[e.Rejected=1]="Rejected"}(y||(y={}));class D{get isRejected(){return this.outcome?.outcome===y.Rejected}get isResolved(){return this.outcome?.outcome===y.Resolved}get isSettled(){return!!this.outcome}get value(){return this.outcome?.outcome===y.Resolved?this.outcome?.value:void 0}constructor(){this.p=new Promise(((e,t)=>{this.completeCallback=e,this.errorCallback=t}))}complete(e){return new Promise((t=>{this.completeCallback(e),this.outcome={outcome:y.Resolved,value:e},t()}))}error(e){return new Promise((t=>{this.errorCallback(e),this.outcome={outcome:y.Rejected,value:e},t()}))}cancel(){return this.error(new r.CancellationError)}}t.DeferredPromise=D,function(e){e.settled=async function(e){let t;const i=await Promise.all(e.map((e=>e.then((e=>e),(e=>{t||(t=e)})))));if(void 0!==t)throw t;return i},e.withAsyncBody=function(e){return new Promise((async(t,i)=>{try{await e(t,i)}catch(e){i(e)}}))}}(w||(t.Promises=w={}));class L{get value(){return this._value}get error(){return this._error}get isResolved(){return this._isResolved}constructor(e){this._value=void 0,this._error=void 0,this._isResolved=!1,this.promise=e.then((e=>(this._value=e,this._isResolved=!0,e)),(e=>{throw this._error=e,this._isResolved=!0,e}))}requireValue(){if(!this._isResolved)throw new r.BugIndicatingError("Promise is not resolved yet");if(this._error)throw this._error;return this._value}}t.StatefulPromise=L,t.LazyStatefulPromise=class{constructor(e){this._compute=e,this._promise=new h.Lazy((()=>new L(this._compute())))}requireValue(){return this._promise.value.requireValue()}getPromise(){return this._promise.value.promise}get currentValue(){return this._promise.rawValue?.value}},function(e){e[e.Initial=0]="Initial",e[e.DoneOK=1]="DoneOK",e[e.DoneError=2]="DoneError"}(E||(E={}));class R{static fromArray(e){return new R((t=>{t.emitMany(e)}))}static fromPromise(e){return new R((async t=>{t.emitMany(await e)}))}static fromPromises(e){return new R((async t=>{await Promise.all(e.map((async e=>t.emitOne(await e))))}))}static merge(e){return new R((async t=>{await Promise.all(e.map((async e=>{for await(const i of e)t.emitOne(i)})))}))}static{this.EMPTY=R.fromArray([])}constructor(e,t){this._state=E.Initial,this._results=[],this._error=null,this._onReturn=t,this._onStateChanged=new n.Emitter,queueMicrotask((async()=>{const t={emitOne:e=>this.emitOne(e),emitMany:e=>this.emitMany(e),reject:e=>this.reject(e)};try{await Promise.resolve(e(t)),this.resolve()}catch(e){this.reject(e)}finally{t.emitOne=void 0,t.emitMany=void 0,t.reject=void 0}}))}[Symbol.asyncIterator](){let e=0;return{next:async()=>{for(;;){if(this._state===E.DoneError)throw this._error;if(e<this._results.length)return{done:!1,value:this._results[e++]};if(this._state===E.DoneOK)return{done:!0,value:void 0};await n.Event.toPromise(this._onStateChanged.event)}},return:async()=>(this._onReturn?.(),{done:!0,value:void 0})}}static map(e,t){return new R((async i=>{for await(const s of e)i.emitOne(t(s))}))}map(e){return R.map(this,e)}static filter(e,t){return new R((async i=>{for await(const s of e)t(s)&&i.emitOne(s)}))}filter(e){return R.filter(this,e)}static coalesce(e){return R.filter(e,(e=>!!e))}coalesce(){return R.coalesce(this)}static async toPromise(e){const t=[];for await(const i of e)t.push(i);return t}toPromise(){return R.toPromise(this)}emitOne(e){this._state===E.Initial&&(this._results.push(e),this._onStateChanged.fire())}emitMany(e){this._state===E.Initial&&(this._results=this._results.concat(e),this._onStateChanged.fire())}resolve(){this._state===E.Initial&&(this._state=E.DoneOK,this._onStateChanged.fire())}reject(e){this._state===E.Initial&&(this._state=E.DoneError,this._error=e,this._onStateChanged.fire())}}t.AsyncIterableObject=R;class A extends R{constructor(e,t){super(t),this._source=e}cancel(){this._source.cancel()}}t.CancelableAsyncIterableObject=A,t.AsyncIterableSource=class{constructor(e){let t,i;this._deferred=new D,this._asyncIterable=new R((e=>{if(!t)return i&&e.emitMany(i),this._errorFn=t=>e.reject(t),this._emitFn=t=>e.emitOne(t),this._deferred.p;e.reject(t)}),e),this._emitFn=e=>{i||(i=[]),i.push(e)},this._errorFn=e=>{t||(t=e)}}get asyncIterable(){return this._asyncIterable}resolve(){this._deferred.complete()}reject(e){this._errorFn(e),this._deferred.complete()}emitOne(e){this._emitFn(e)}}},8447:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.CancellationTokenSource=t.CancellationToken=void 0,t.cancelOnDispose=function(e){const t=new a;return e.add({dispose(){t.cancel()}}),t.token};const s=i(802),r=Object.freeze((function(e,t){const i=setTimeout(e.bind(t),0);return{dispose(){clearTimeout(i)}}}));var n;!function(e){e.isCancellationToken=function(t){return t===e.None||t===e.Cancelled||t instanceof o||!(!t||"object"!=typeof t)&&"boolean"==typeof t.isCancellationRequested&&"function"==typeof t.onCancellationRequested},e.None=Object.freeze({isCancellationRequested:!1,onCancellationRequested:s.Event.None}),e.Cancelled=Object.freeze({isCancellationRequested:!0,onCancellationRequested:r})}(n||(t.CancellationToken=n={}));class o{constructor(){this._isCancelled=!1,this._emitter=null}cancel(){this._isCancelled||(this._isCancelled=!0,this._emitter&&(this._emitter.fire(void 0),this.dispose()))}get isCancellationRequested(){return this._isCancelled}get onCancellationRequested(){return this._isCancelled?r:(this._emitter||(this._emitter=new s.Emitter),this._emitter.event)}dispose(){this._emitter&&(this._emitter.dispose(),this._emitter=null)}}class a{constructor(e){this._token=void 0,this._parentListener=void 0,this._parentListener=e&&e.onCancellationRequested(this.cancel,this)}get token(){return this._token||(this._token=new o),this._token}cancel(){this._token?this._token instanceof o&&this._token.cancel():this._token=n.Cancelled}dispose(e=!1){e&&this.cancel(),this._parentListener?.dispose(),this._token?this._token instanceof o&&this._token.dispose():this._token=n.None}}t.CancellationTokenSource=a},4869:(e,t)=>{var i;Object.defineProperty(t,"__esModule",{value:!0}),t.CharCode=void 0,function(e){e[e.Null=0]="Null",e[e.Backspace=8]="Backspace",e[e.Tab=9]="Tab",e[e.LineFeed=10]="LineFeed",e[e.CarriageReturn=13]="CarriageReturn",e[e.Space=32]="Space",e[e.ExclamationMark=33]="ExclamationMark",e[e.DoubleQuote=34]="DoubleQuote",e[e.Hash=35]="Hash",e[e.DollarSign=36]="DollarSign",e[e.PercentSign=37]="PercentSign",e[e.Ampersand=38]="Ampersand",e[e.SingleQuote=39]="SingleQuote",e[e.OpenParen=40]="OpenParen",e[e.CloseParen=41]="CloseParen",e[e.Asterisk=42]="Asterisk",e[e.Plus=43]="Plus",e[e.Comma=44]="Comma",e[e.Dash=45]="Dash",e[e.Period=46]="Period",e[e.Slash=47]="Slash",e[e.Digit0=48]="Digit0",e[e.Digit1=49]="Digit1",e[e.Digit2=50]="Digit2",e[e.Digit3=51]="Digit3",e[e.Digit4=52]="Digit4",e[e.Digit5=53]="Digit5",e[e.Digit6=54]="Digit6",e[e.Digit7=55]="Digit7",e[e.Digit8=56]="Digit8",e[e.Digit9=57]="Digit9",e[e.Colon=58]="Colon",e[e.Semicolon=59]="Semicolon",e[e.LessThan=60]="LessThan",e[e.Equals=61]="Equals",e[e.GreaterThan=62]="GreaterThan",e[e.QuestionMark=63]="QuestionMark",e[e.AtSign=64]="AtSign",e[e.A=65]="A",e[e.B=66]="B",e[e.C=67]="C",e[e.D=68]="D",e[e.E=69]="E",e[e.F=70]="F",e[e.G=71]="G",e[e.H=72]="H",e[e.I=73]="I",e[e.J=74]="J",e[e.K=75]="K",e[e.L=76]="L",e[e.M=77]="M",e[e.N=78]="N",e[e.O=79]="O",e[e.P=80]="P",e[e.Q=81]="Q",e[e.R=82]="R",e[e.S=83]="S",e[e.T=84]="T",e[e.U=85]="U",e[e.V=86]="V",e[e.W=87]="W",e[e.X=88]="X",e[e.Y=89]="Y",e[e.Z=90]="Z",e[e.OpenSquareBracket=91]="OpenSquareBracket",e[e.Backslash=92]="Backslash",e[e.CloseSquareBracket=93]="CloseSquareBracket",e[e.Caret=94]="Caret",e[e.Underline=95]="Underline",e[e.BackTick=96]="BackTick",e[e.a=97]="a",e[e.b=98]="b",e[e.c=99]="c",e[e.d=100]="d",e[e.e=101]="e",e[e.f=102]="f",e[e.g=103]="g",e[e.h=104]="h",e[e.i=105]="i",e[e.j=106]="j",e[e.k=107]="k",e[e.l=108]="l",e[e.m=109]="m",e[e.n=110]="n",e[e.o=111]="o",e[e.p=112]="p",e[e.q=113]="q",e[e.r=114]="r",e[e.s=115]="s",e[e.t=116]="t",e[e.u=117]="u",e[e.v=118]="v",e[e.w=119]="w",e[e.x=120]="x",e[e.y=121]="y",e[e.z=122]="z",e[e.OpenCurlyBrace=123]="OpenCurlyBrace",e[e.Pipe=124]="Pipe",e[e.CloseCurlyBrace=125]="CloseCurlyBrace",e[e.Tilde=126]="Tilde",e[e.NoBreakSpace=160]="NoBreakSpace",e[e.U_Combining_Grave_Accent=768]="U_Combining_Grave_Accent",e[e.U_Combining_Acute_Accent=769]="U_Combining_Acute_Accent",e[e.U_Combining_Circumflex_Accent=770]="U_Combining_Circumflex_Accent",e[e.U_Combining_Tilde=771]="U_Combining_Tilde",e[e.U_Combining_Macron=772]="U_Combining_Macron",e[e.U_Combining_Overline=773]="U_Combining_Overline",e[e.U_Combining_Breve=774]="U_Combining_Breve",e[e.U_Combining_Dot_Above=775]="U_Combining_Dot_Above",e[e.U_Combining_Diaeresis=776]="U_Combining_Diaeresis",e[e.U_Combining_Hook_Above=777]="U_Combining_Hook_Above",e[e.U_Combining_Ring_Above=778]="U_Combining_Ring_Above",e[e.U_Combining_Double_Acute_Accent=779]="U_Combining_Double_Acute_Accent",e[e.U_Combining_Caron=780]="U_Combining_Caron",e[e.U_Combining_Vertical_Line_Above=781]="U_Combining_Vertical_Line_Above",e[e.U_Combining_Double_Vertical_Line_Above=782]="U_Combining_Double_Vertical_Line_Above",e[e.U_Combining_Double_Grave_Accent=783]="U_Combining_Double_Grave_Accent",e[e.U_Combining_Candrabindu=784]="U_Combining_Candrabindu",e[e.U_Combining_Inverted_Breve=785]="U_Combining_Inverted_Breve",e[e.U_Combining_Turned_Comma_Above=786]="U_Combining_Turned_Comma_Above",e[e.U_Combining_Comma_Above=787]="U_Combining_Comma_Above",e[e.U_Combining_Reversed_Comma_Above=788]="U_Combining_Reversed_Comma_Above",e[e.U_Combining_Comma_Above_Right=789]="U_Combining_Comma_Above_Right",e[e.U_Combining_Grave_Accent_Below=790]="U_Combining_Grave_Accent_Below",e[e.U_Combining_Acute_Accent_Below=791]="U_Combining_Acute_Accent_Below",e[e.U_Combining_Left_Tack_Below=792]="U_Combining_Left_Tack_Below",e[e.U_Combining_Right_Tack_Below=793]="U_Combining_Right_Tack_Below",e[e.U_Combining_Left_Angle_Above=794]="U_Combining_Left_Angle_Above",e[e.U_Combining_Horn=795]="U_Combining_Horn",e[e.U_Combining_Left_Half_Ring_Below=796]="U_Combining_Left_Half_Ring_Below",e[e.U_Combining_Up_Tack_Below=797]="U_Combining_Up_Tack_Below",e[e.U_Combining_Down_Tack_Below=798]="U_Combining_Down_Tack_Below",e[e.U_Combining_Plus_Sign_Below=799]="U_Combining_Plus_Sign_Below",e[e.U_Combining_Minus_Sign_Below=800]="U_Combining_Minus_Sign_Below",e[e.U_Combining_Palatalized_Hook_Below=801]="U_Combining_Palatalized_Hook_Below",e[e.U_Combining_Retroflex_Hook_Below=802]="U_Combining_Retroflex_Hook_Below",e[e.U_Combining_Dot_Below=803]="U_Combining_Dot_Below",e[e.U_Combining_Diaeresis_Below=804]="U_Combining_Diaeresis_Below",e[e.U_Combining_Ring_Below=805]="U_Combining_Ring_Below",e[e.U_Combining_Comma_Below=806]="U_Combining_Comma_Below",e[e.U_Combining_Cedilla=807]="U_Combining_Cedilla",e[e.U_Combining_Ogonek=808]="U_Combining_Ogonek",e[e.U_Combining_Vertical_Line_Below=809]="U_Combining_Vertical_Line_Below",e[e.U_Combining_Bridge_Below=810]="U_Combining_Bridge_Below",e[e.U_Combining_Inverted_Double_Arch_Below=811]="U_Combining_Inverted_Double_Arch_Below",e[e.U_Combining_Caron_Below=812]="U_Combining_Caron_Below",e[e.U_Combining_Circumflex_Accent_Below=813]="U_Combining_Circumflex_Accent_Below",e[e.U_Combining_Breve_Below=814]="U_Combining_Breve_Below",e[e.U_Combining_Inverted_Breve_Below=815]="U_Combining_Inverted_Breve_Below",e[e.U_Combining_Tilde_Below=816]="U_Combining_Tilde_Below",e[e.U_Combining_Macron_Below=817]="U_Combining_Macron_Below",e[e.U_Combining_Low_Line=818]="U_Combining_Low_Line",e[e.U_Combining_Double_Low_Line=819]="U_Combining_Double_Low_Line",e[e.U_Combining_Tilde_Overlay=820]="U_Combining_Tilde_Overlay",e[e.U_Combining_Short_Stroke_Overlay=821]="U_Combining_Short_Stroke_Overlay",e[e.U_Combining_Long_Stroke_Overlay=822]="U_Combining_Long_Stroke_Overlay",e[e.U_Combining_Short_Solidus_Overlay=823]="U_Combining_Short_Solidus_Overlay",e[e.U_Combining_Long_Solidus_Overlay=824]="U_Combining_Long_Solidus_Overlay",e[e.U_Combining_Right_Half_Ring_Below=825]="U_Combining_Right_Half_Ring_Below",e[e.U_Combining_Inverted_Bridge_Below=826]="U_Combining_Inverted_Bridge_Below",e[e.U_Combining_Square_Below=827]="U_Combining_Square_Below",e[e.U_Combining_Seagull_Below=828]="U_Combining_Seagull_Below",e[e.U_Combining_X_Above=829]="U_Combining_X_Above",e[e.U_Combining_Vertical_Tilde=830]="U_Combining_Vertical_Tilde",e[e.U_Combining_Double_Overline=831]="U_Combining_Double_Overline",e[e.U_Combining_Grave_Tone_Mark=832]="U_Combining_Grave_Tone_Mark",e[e.U_Combining_Acute_Tone_Mark=833]="U_Combining_Acute_Tone_Mark",e[e.U_Combining_Greek_Perispomeni=834]="U_Combining_Greek_Perispomeni",e[e.U_Combining_Greek_Koronis=835]="U_Combining_Greek_Koronis",e[e.U_Combining_Greek_Dialytika_Tonos=836]="U_Combining_Greek_Dialytika_Tonos",e[e.U_Combining_Greek_Ypogegrammeni=837]="U_Combining_Greek_Ypogegrammeni",e[e.U_Combining_Bridge_Above=838]="U_Combining_Bridge_Above",e[e.U_Combining_Equals_Sign_Below=839]="U_Combining_Equals_Sign_Below",e[e.U_Combining_Double_Vertical_Line_Below=840]="U_Combining_Double_Vertical_Line_Below",e[e.U_Combining_Left_Angle_Below=841]="U_Combining_Left_Angle_Below",e[e.U_Combining_Not_Tilde_Above=842]="U_Combining_Not_Tilde_Above",e[e.U_Combining_Homothetic_Above=843]="U_Combining_Homothetic_Above",e[e.U_Combining_Almost_Equal_To_Above=844]="U_Combining_Almost_Equal_To_Above",e[e.U_Combining_Left_Right_Arrow_Below=845]="U_Combining_Left_Right_Arrow_Below",e[e.U_Combining_Upwards_Arrow_Below=846]="U_Combining_Upwards_Arrow_Below",e[e.U_Combining_Grapheme_Joiner=847]="U_Combining_Grapheme_Joiner",e[e.U_Combining_Right_Arrowhead_Above=848]="U_Combining_Right_Arrowhead_Above",e[e.U_Combining_Left_Half_Ring_Above=849]="U_Combining_Left_Half_Ring_Above",e[e.U_Combining_Fermata=850]="U_Combining_Fermata",e[e.U_Combining_X_Below=851]="U_Combining_X_Below",e[e.U_Combining_Left_Arrowhead_Below=852]="U_Combining_Left_Arrowhead_Below",e[e.U_Combining_Right_Arrowhead_Below=853]="U_Combining_Right_Arrowhead_Below",e[e.U_Combining_Right_Arrowhead_And_Up_Arrowhead_Below=854]="U_Combining_Right_Arrowhead_And_Up_Arrowhead_Below",e[e.U_Combining_Right_Half_Ring_Above=855]="U_Combining_Right_Half_Ring_Above",e[e.U_Combining_Dot_Above_Right=856]="U_Combining_Dot_Above_Right",e[e.U_Combining_Asterisk_Below=857]="U_Combining_Asterisk_Below",e[e.U_Combining_Double_Ring_Below=858]="U_Combining_Double_Ring_Below",e[e.U_Combining_Zigzag_Above=859]="U_Combining_Zigzag_Above",e[e.U_Combining_Double_Breve_Below=860]="U_Combining_Double_Breve_Below",e[e.U_Combining_Double_Breve=861]="U_Combining_Double_Breve",e[e.U_Combining_Double_Macron=862]="U_Combining_Double_Macron",e[e.U_Combining_Double_Macron_Below=863]="U_Combining_Double_Macron_Below",e[e.U_Combining_Double_Tilde=864]="U_Combining_Double_Tilde",e[e.U_Combining_Double_Inverted_Breve=865]="U_Combining_Double_Inverted_Breve",e[e.U_Combining_Double_Rightwards_Arrow_Below=866]="U_Combining_Double_Rightwards_Arrow_Below",e[e.U_Combining_Latin_Small_Letter_A=867]="U_Combining_Latin_Small_Letter_A",e[e.U_Combining_Latin_Small_Letter_E=868]="U_Combining_Latin_Small_Letter_E",e[e.U_Combining_Latin_Small_Letter_I=869]="U_Combining_Latin_Small_Letter_I",e[e.U_Combining_Latin_Small_Letter_O=870]="U_Combining_Latin_Small_Letter_O",e[e.U_Combining_Latin_Small_Letter_U=871]="U_Combining_Latin_Small_Letter_U",e[e.U_Combining_Latin_Small_Letter_C=872]="U_Combining_Latin_Small_Letter_C",e[e.U_Combining_Latin_Small_Letter_D=873]="U_Combining_Latin_Small_Letter_D",e[e.U_Combining_Latin_Small_Letter_H=874]="U_Combining_Latin_Small_Letter_H",e[e.U_Combining_Latin_Small_Letter_M=875]="U_Combining_Latin_Small_Letter_M",e[e.U_Combining_Latin_Small_Letter_R=876]="U_Combining_Latin_Small_Letter_R",e[e.U_Combining_Latin_Small_Letter_T=877]="U_Combining_Latin_Small_Letter_T",e[e.U_Combining_Latin_Small_Letter_V=878]="U_Combining_Latin_Small_Letter_V",e[e.U_Combining_Latin_Small_Letter_X=879]="U_Combining_Latin_Small_Letter_X",e[e.LINE_SEPARATOR=8232]="LINE_SEPARATOR",e[e.PARAGRAPH_SEPARATOR=8233]="PARAGRAPH_SEPARATOR",e[e.NEXT_LINE=133]="NEXT_LINE",e[e.U_CIRCUMFLEX=94]="U_CIRCUMFLEX",e[e.U_GRAVE_ACCENT=96]="U_GRAVE_ACCENT",e[e.U_DIAERESIS=168]="U_DIAERESIS",e[e.U_MACRON=175]="U_MACRON",e[e.U_ACUTE_ACCENT=180]="U_ACUTE_ACCENT",e[e.U_CEDILLA=184]="U_CEDILLA",e[e.U_MODIFIER_LETTER_LEFT_ARROWHEAD=706]="U_MODIFIER_LETTER_LEFT_ARROWHEAD",e[e.U_MODIFIER_LETTER_RIGHT_ARROWHEAD=707]="U_MODIFIER_LETTER_RIGHT_ARROWHEAD",e[e.U_MODIFIER_LETTER_UP_ARROWHEAD=708]="U_MODIFIER_LETTER_UP_ARROWHEAD",e[e.U_MODIFIER_LETTER_DOWN_ARROWHEAD=709]="U_MODIFIER_LETTER_DOWN_ARROWHEAD",e[e.U_MODIFIER_LETTER_CENTRED_RIGHT_HALF_RING=722]="U_MODIFIER_LETTER_CENTRED_RIGHT_HALF_RING",e[e.U_MODIFIER_LETTER_CENTRED_LEFT_HALF_RING=723]="U_MODIFIER_LETTER_CENTRED_LEFT_HALF_RING",e[e.U_MODIFIER_LETTER_UP_TACK=724]="U_MODIFIER_LETTER_UP_TACK",e[e.U_MODIFIER_LETTER_DOWN_TACK=725]="U_MODIFIER_LETTER_DOWN_TACK",e[e.U_MODIFIER_LETTER_PLUS_SIGN=726]="U_MODIFIER_LETTER_PLUS_SIGN",e[e.U_MODIFIER_LETTER_MINUS_SIGN=727]="U_MODIFIER_LETTER_MINUS_SIGN",e[e.U_BREVE=728]="U_BREVE",e[e.U_DOT_ABOVE=729]="U_DOT_ABOVE",e[e.U_RING_ABOVE=730]="U_RING_ABOVE",e[e.U_OGONEK=731]="U_OGONEK",e[e.U_SMALL_TILDE=732]="U_SMALL_TILDE",e[e.U_DOUBLE_ACUTE_ACCENT=733]="U_DOUBLE_ACUTE_ACCENT",e[e.U_MODIFIER_LETTER_RHOTIC_HOOK=734]="U_MODIFIER_LETTER_RHOTIC_HOOK",e[e.U_MODIFIER_LETTER_CROSS_ACCENT=735]="U_MODIFIER_LETTER_CROSS_ACCENT",e[e.U_MODIFIER_LETTER_EXTRA_HIGH_TONE_BAR=741]="U_MODIFIER_LETTER_EXTRA_HIGH_TONE_BAR",e[e.U_MODIFIER_LETTER_HIGH_TONE_BAR=742]="U_MODIFIER_LETTER_HIGH_TONE_BAR",e[e.U_MODIFIER_LETTER_MID_TONE_BAR=743]="U_MODIFIER_LETTER_MID_TONE_BAR",e[e.U_MODIFIER_LETTER_LOW_TONE_BAR=744]="U_MODIFIER_LETTER_LOW_TONE_BAR",e[e.U_MODIFIER_LETTER_EXTRA_LOW_TONE_BAR=745]="U_MODIFIER_LETTER_EXTRA_LOW_TONE_BAR",e[e.U_MODIFIER_LETTER_YIN_DEPARTING_TONE_MARK=746]="U_MODIFIER_LETTER_YIN_DEPARTING_TONE_MARK",e[e.U_MODIFIER_LETTER_YANG_DEPARTING_TONE_MARK=747]="U_MODIFIER_LETTER_YANG_DEPARTING_TONE_MARK",e[e.U_MODIFIER_LETTER_UNASPIRATED=749]="U_MODIFIER_LETTER_UNASPIRATED",e[e.U_MODIFIER_LETTER_LOW_DOWN_ARROWHEAD=751]="U_MODIFIER_LETTER_LOW_DOWN_ARROWHEAD",e[e.U_MODIFIER_LETTER_LOW_UP_ARROWHEAD=752]="U_MODIFIER_LETTER_LOW_UP_ARROWHEAD",e[e.U_MODIFIER_LETTER_LOW_LEFT_ARROWHEAD=753]="U_MODIFIER_LETTER_LOW_LEFT_ARROWHEAD",e[e.U_MODIFIER_LETTER_LOW_RIGHT_ARROWHEAD=754]="U_MODIFIER_LETTER_LOW_RIGHT_ARROWHEAD",e[e.U_MODIFIER_LETTER_LOW_RING=755]="U_MODIFIER_LETTER_LOW_RING",e[e.U_MODIFIER_LETTER_MIDDLE_GRAVE_ACCENT=756]="U_MODIFIER_LETTER_MIDDLE_GRAVE_ACCENT",e[e.U_MODIFIER_LETTER_MIDDLE_DOUBLE_GRAVE_ACCENT=757]="U_MODIFIER_LETTER_MIDDLE_DOUBLE_GRAVE_ACCENT",e[e.U_MODIFIER_LETTER_MIDDLE_DOUBLE_ACUTE_ACCENT=758]="U_MODIFIER_LETTER_MIDDLE_DOUBLE_ACUTE_ACCENT",e[e.U_MODIFIER_LETTER_LOW_TILDE=759]="U_MODIFIER_LETTER_LOW_TILDE",e[e.U_MODIFIER_LETTER_RAISED_COLON=760]="U_MODIFIER_LETTER_RAISED_COLON",e[e.U_MODIFIER_LETTER_BEGIN_HIGH_TONE=761]="U_MODIFIER_LETTER_BEGIN_HIGH_TONE",e[e.U_MODIFIER_LETTER_END_HIGH_TONE=762]="U_MODIFIER_LETTER_END_HIGH_TONE",e[e.U_MODIFIER_LETTER_BEGIN_LOW_TONE=763]="U_MODIFIER_LETTER_BEGIN_LOW_TONE",e[e.U_MODIFIER_LETTER_END_LOW_TONE=764]="U_MODIFIER_LETTER_END_LOW_TONE",e[e.U_MODIFIER_LETTER_SHELF=765]="U_MODIFIER_LETTER_SHELF",e[e.U_MODIFIER_LETTER_OPEN_SHELF=766]="U_MODIFIER_LETTER_OPEN_SHELF",e[e.U_MODIFIER_LETTER_LOW_LEFT_ARROW=767]="U_MODIFIER_LETTER_LOW_LEFT_ARROW",e[e.U_GREEK_LOWER_NUMERAL_SIGN=885]="U_GREEK_LOWER_NUMERAL_SIGN",e[e.U_GREEK_TONOS=900]="U_GREEK_TONOS",e[e.U_GREEK_DIALYTIKA_TONOS=901]="U_GREEK_DIALYTIKA_TONOS",e[e.U_GREEK_KORONIS=8125]="U_GREEK_KORONIS",e[e.U_GREEK_PSILI=8127]="U_GREEK_PSILI",e[e.U_GREEK_PERISPOMENI=8128]="U_GREEK_PERISPOMENI",e[e.U_GREEK_DIALYTIKA_AND_PERISPOMENI=8129]="U_GREEK_DIALYTIKA_AND_PERISPOMENI",e[e.U_GREEK_PSILI_AND_VARIA=8141]="U_GREEK_PSILI_AND_VARIA",e[e.U_GREEK_PSILI_AND_OXIA=8142]="U_GREEK_PSILI_AND_OXIA",e[e.U_GREEK_PSILI_AND_PERISPOMENI=8143]="U_GREEK_PSILI_AND_PERISPOMENI",e[e.U_GREEK_DASIA_AND_VARIA=8157]="U_GREEK_DASIA_AND_VARIA",e[e.U_GREEK_DASIA_AND_OXIA=8158]="U_GREEK_DASIA_AND_OXIA",e[e.U_GREEK_DASIA_AND_PERISPOMENI=8159]="U_GREEK_DASIA_AND_PERISPOMENI",e[e.U_GREEK_DIALYTIKA_AND_VARIA=8173]="U_GREEK_DIALYTIKA_AND_VARIA",e[e.U_GREEK_DIALYTIKA_AND_OXIA=8174]="U_GREEK_DIALYTIKA_AND_OXIA",e[e.U_GREEK_VARIA=8175]="U_GREEK_VARIA",e[e.U_GREEK_OXIA=8189]="U_GREEK_OXIA",e[e.U_GREEK_DASIA=8190]="U_GREEK_DASIA",e[e.U_IDEOGRAPHIC_FULL_STOP=12290]="U_IDEOGRAPHIC_FULL_STOP",e[e.U_LEFT_CORNER_BRACKET=12300]="U_LEFT_CORNER_BRACKET",e[e.U_RIGHT_CORNER_BRACKET=12301]="U_RIGHT_CORNER_BRACKET",e[e.U_LEFT_BLACK_LENTICULAR_BRACKET=12304]="U_LEFT_BLACK_LENTICULAR_BRACKET",e[e.U_RIGHT_BLACK_LENTICULAR_BRACKET=12305]="U_RIGHT_BLACK_LENTICULAR_BRACKET",e[e.U_OVERLINE=8254]="U_OVERLINE",e[e.UTF8_BOM=65279]="UTF8_BOM",e[e.U_FULLWIDTH_SEMICOLON=65307]="U_FULLWIDTH_SEMICOLON",e[e.U_FULLWIDTH_COMMA=65292]="U_FULLWIDTH_COMMA"}(i||(t.CharCode=i={}))},9087:(e,t)=>{var i;Object.defineProperty(t,"__esModule",{value:!0}),t.SetWithKey=void 0,t.groupBy=function(e,t){const i=Object.create(null);for(const s of e){const e=t(s);let r=i[e];r||(r=i[e]=[]),r.push(s)}return i},t.diffSets=function(e,t){const i=[],s=[];for(const s of e)t.has(s)||i.push(s);for(const i of t)e.has(i)||s.push(i);return{removed:i,added:s}},t.diffMaps=function(e,t){const i=[],s=[];for(const[s,r]of e)t.has(s)||i.push(r);for(const[i,r]of t)e.has(i)||s.push(r);return{removed:i,added:s}},t.intersection=function(e,t){const i=new Set;for(const s of t)e.has(s)&&i.add(s);return i};class s{static{i=Symbol.toStringTag}constructor(e,t){this.toKey=t,this._map=new Map,this[i]="SetWithKey";for(const t of e)this.add(t)}get size(){return this._map.size}add(e){const t=this.toKey(e);return this._map.set(t,e),this}delete(e){return this._map.delete(this.toKey(e))}has(e){return this._map.has(this.toKey(e))}*entries(){for(const e of this._map.values())yield[e,e]}keys(){return this.values()}*values(){for(const e of this._map.values())yield e}clear(){this._map.clear()}forEach(e,t){this._map.forEach((i=>e.call(t,i,i,this)))}[Symbol.iterator](){return this.values()}}t.SetWithKey=s},4838:(e,t)=>{function i(e){return(t,i,s)=>{let r=null,n=null;if("function"==typeof s.value?(r="value",n=s.value):"function"==typeof s.get&&(r="get",n=s.get),!n)throw new Error("not supported");s[r]=e(n,i)}}Object.defineProperty(t,"__esModule",{value:!0}),t.memoize=function(e,t,i){let s=null,r=null;if("function"==typeof i.value?(s="value",r=i.value,0!==r.length&&console.warn("Memoize should only be used in functions with zero parameters")):"function"==typeof i.get&&(s="get",r=i.get),!r)throw new Error("not supported");const n=`$memoize$${t}`;i[s]=function(...e){return this.hasOwnProperty(n)||Object.defineProperty(this,n,{configurable:!1,enumerable:!1,writable:!1,value:r.apply(this,e)}),this[n]}},t.debounce=function(e,t,s){return i(((i,r)=>{const n=`$debounce$${r}`,o=`$debounce$result$${r}`;return function(...r){this[o]||(this[o]=s?s():void 0),clearTimeout(this[n]),t&&(this[o]=t(this[o],...r),r=[this[o]]),this[n]=setTimeout((()=>{i.apply(this,r),this[o]=s?s():void 0}),e)}}))},t.throttle=function(e,t,s){return i(((i,r)=>{const n=`$throttle$timer$${r}`,o=`$throttle$result$${r}`,a=`$throttle$lastRun$${r}`,l=`$throttle$pending$${r}`;return function(...r){if(this[o]||(this[o]=s?s():void 0),null!==this[a]&&void 0!==this[a]||(this[a]=-Number.MAX_VALUE),t&&(this[o]=t(this[o],...r)),this[l])return;const h=this[a]+e;h<=Date.now()?(this[a]=Date.now(),i.apply(this,[this[o]]),this[o]=s?s():void 0):(this[l]=!0,this[n]=setTimeout((()=>{this[l]=!1,this[a]=Date.now(),i.apply(this,[this[o]]),this[o]=s?s():void 0}),h-Date.now()))}}))}},9807:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.BugIndicatingError=t.ErrorNoTelemetry=t.ExpectedError=t.NotSupportedError=t.NotImplementedError=t.ReadonlyError=t.CancellationError=t.errorHandler=t.ErrorHandler=void 0,t.setUnexpectedErrorHandler=function(e){t.errorHandler.setUnexpectedErrorHandler(e)},t.isSigPipeError=function(e){if(!e||"object"!=typeof e)return!1;const t=e;return"EPIPE"===t.code&&"WRITE"===t.syscall?.toUpperCase()},t.onUnexpectedError=function(e){r(e)||t.errorHandler.onUnexpectedError(e)},t.onUnexpectedExternalError=function(e){r(e)||t.errorHandler.onUnexpectedExternalError(e)},t.transformErrorForSerialization=function(e){if(e instanceof Error){const{name:t,message:i}=e;return{$isError:!0,name:t,message:i,stack:e.stacktrace||e.stack,noTelemetry:c.isErrorNoTelemetry(e)}}return e},t.transformErrorFromSerialization=function(e){let t;return e.noTelemetry?t=new c:(t=new Error,t.name=e.name),t.message=e.message,t.stack=e.stack,t},t.isCancellationError=r,t.canceled=function(){const e=new Error(s);return e.name=e.message,e},t.illegalArgument=function(e){return e?new Error(`Illegal argument: ${e}`):new Error("Illegal argument")},t.illegalState=function(e){return e?new Error(`Illegal state: ${e}`):new Error("Illegal state")},t.getErrorMessage=function(e){return e?e.message?e.message:e.stack?e.stack.split("\n")[0]:String(e):"Error"};class i{constructor(){this.listeners=[],this.unexpectedErrorHandler=function(e){setTimeout((()=>{if(e.stack){if(c.isErrorNoTelemetry(e))throw new c(e.message+"\n\n"+e.stack);throw new Error(e.message+"\n\n"+e.stack)}throw e}),0)}}addListener(e){return this.listeners.push(e),()=>{this._removeListener(e)}}emit(e){this.listeners.forEach((t=>{t(e)}))}_removeListener(e){this.listeners.splice(this.listeners.indexOf(e),1)}setUnexpectedErrorHandler(e){this.unexpectedErrorHandler=e}getUnexpectedErrorHandler(){return this.unexpectedErrorHandler}onUnexpectedError(e){this.unexpectedErrorHandler(e),this.emit(e)}onUnexpectedExternalError(e){this.unexpectedErrorHandler(e)}}t.ErrorHandler=i,t.errorHandler=new i;const s="Canceled";function r(e){return e instanceof n||e instanceof Error&&e.name===s&&e.message===s}class n extends Error{constructor(){super(s),this.name=this.message}}t.CancellationError=n;class o extends TypeError{constructor(e){super(e?`${e} is read-only and cannot be changed`:"Cannot change read-only property")}}t.ReadonlyError=o;class a extends Error{constructor(e){super("NotImplemented"),e&&(this.message=e)}}t.NotImplementedError=a;class l extends Error{constructor(e){super("NotSupported"),e&&(this.message=e)}}t.NotSupportedError=l;class h extends Error{constructor(){super(...arguments),this.isExpected=!0}}t.ExpectedError=h;class c extends Error{constructor(e){super(e),this.name="CodeExpectedError"}static fromError(e){if(e instanceof c)return e;const t=new c;return t.message=e.message,t.stack=e.stack,t}static isErrorNoTelemetry(e){return"CodeExpectedError"===e.name}}t.ErrorNoTelemetry=c;class d extends Error{constructor(e){super(e||"An unexpected bug occurred."),Object.setPrototypeOf(this,d.prototype)}}t.BugIndicatingError=d},802:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ValueWithChangeEvent=t.Relay=t.EventBufferer=t.DynamicListEventMultiplexer=t.EventMultiplexer=t.MicrotaskEmitter=t.DebounceEmitter=t.PauseableEmitter=t.AsyncEmitter=t.createEventDeliveryQueue=t.Emitter=t.ListenerRefusalError=t.ListenerLeakError=t.EventProfiling=t.Event=void 0,t.setGlobalLeakWarningThreshold=function(e){const t=c;return c=e,{dispose(){c=t}}};const s=i(9807),r=i(8841),n=i(7150),o=i(6317),a=i(9725);var l;!function(e){function t(e){return(t,i=null,s)=>{let r,n=!1;return r=e((e=>{if(!n)return r?r.dispose():n=!0,t.call(i,e)}),null,s),n&&r.dispose(),r}}function i(e,t,i){return r(((i,s=null,r)=>e((e=>i.call(s,t(e))),null,r)),i)}function s(e,t,i){return r(((i,s=null,r)=>e((e=>t(e)&&i.call(s,e)),null,r)),i)}function r(e,t){let i;const s=new m({onWillAddFirstListener(){i=e(s.fire,s)},onDidRemoveLastListener(){i?.dispose()}});return t?.add(s),s.event}function o(e,t,i=100,s=!1,r=!1,n,o){let a,l,h,c,d=0;const u=new m({leakWarningThreshold:n,onWillAddFirstListener(){a=e((e=>{d++,l=t(l,e),s&&!h&&(u.fire(l),l=void 0),c=()=>{const e=l;l=void 0,h=void 0,(!s||d>1)&&u.fire(e),d=0},"number"==typeof i?(clearTimeout(h),h=setTimeout(c,i)):void 0===h&&(h=0,queueMicrotask(c))}))},onWillRemoveListener(){r&&d>0&&c?.()},onDidRemoveLastListener(){c=void 0,a.dispose()}});return o?.add(u),u.event}e.None=()=>n.Disposable.None,e.defer=function(e,t){return o(e,(()=>{}),0,void 0,!0,void 0,t)},e.once=t,e.map=i,e.forEach=function(e,t,i){return r(((i,s=null,r)=>e((e=>{t(e),i.call(s,e)}),null,r)),i)},e.filter=s,e.signal=function(e){return e},e.any=function(...e){return(t,i=null,s)=>{return r=(0,n.combinedDisposable)(...e.map((e=>e((e=>t.call(i,e)))))),(o=s)instanceof Array?o.push(r):o&&o.add(r),r;var r,o}},e.reduce=function(e,t,s,r){let n=s;return i(e,(e=>(n=t(n,e),n)),r)},e.debounce=o,e.accumulate=function(t,i=0,s){return e.debounce(t,((e,t)=>e?(e.push(t),e):[t]),i,void 0,!0,void 0,s)},e.latch=function(e,t=(e,t)=>e===t,i){let r,n=!0;return s(e,(e=>{const i=n||!t(e,r);return n=!1,r=e,i}),i)},e.split=function(t,i,s){return[e.filter(t,i,s),e.filter(t,(e=>!i(e)),s)]},e.buffer=function(e,t=!1,i=[],s){let r=i.slice(),n=e((e=>{r?r.push(e):a.fire(e)}));s&&s.add(n);const o=()=>{r?.forEach((e=>a.fire(e))),r=null},a=new m({onWillAddFirstListener(){n||(n=e((e=>a.fire(e))),s&&s.add(n))},onDidAddFirstListener(){r&&(t?setTimeout(o):o())},onDidRemoveLastListener(){n&&n.dispose(),n=null}});return s&&s.add(a),a.event},e.chain=function(e,t){return(i,s,r)=>{const n=t(new l);return e((function(e){const t=n.evaluate(e);t!==a&&i.call(s,t)}),void 0,r)}};const a=Symbol("HaltChainable");class l{constructor(){this.steps=[]}map(e){return this.steps.push(e),this}forEach(e){return this.steps.push((t=>(e(t),t))),this}filter(e){return this.steps.push((t=>e(t)?t:a)),this}reduce(e,t){let i=t;return this.steps.push((t=>(i=e(i,t),i))),this}latch(e=(e,t)=>e===t){let t,i=!0;return this.steps.push((s=>{const r=i||!e(s,t);return i=!1,t=s,r?s:a})),this}evaluate(e){for(const t of this.steps)if((e=t(e))===a)break;return e}}e.fromNodeEventEmitter=function(e,t,i=e=>e){const s=(...e)=>r.fire(i(...e)),r=new m({onWillAddFirstListener:()=>e.on(t,s),onDidRemoveLastListener:()=>e.removeListener(t,s)});return r.event},e.fromDOMEventEmitter=function(e,t,i=e=>e){const s=(...e)=>r.fire(i(...e)),r=new m({onWillAddFirstListener:()=>e.addEventListener(t,s),onDidRemoveLastListener:()=>e.removeEventListener(t,s)});return r.event},e.toPromise=function(e){return new Promise((i=>t(e)(i)))},e.fromPromise=function(e){const t=new m;return e.then((e=>{t.fire(e)}),(()=>{t.fire(void 0)})).finally((()=>{t.dispose()})),t.event},e.forward=function(e,t){return e((e=>t.fire(e)))},e.runAndSubscribe=function(e,t,i){return t(i),e((e=>t(e)))};class h{constructor(e,t){this._observable=e,this._counter=0,this._hasChanged=!1;const i={onWillAddFirstListener:()=>{e.addObserver(this)},onDidRemoveLastListener:()=>{e.removeObserver(this)}};this.emitter=new m(i),t&&t.add(this.emitter)}beginUpdate(e){this._counter++}handlePossibleChange(e){}handleChange(e,t){this._hasChanged=!0}endUpdate(e){this._counter--,0===this._counter&&(this._observable.reportChanges(),this._hasChanged&&(this._hasChanged=!1,this.emitter.fire(this._observable.get())))}}e.fromObservable=function(e,t){return new h(e,t).emitter.event},e.fromObservableLight=function(e){return(t,i,s)=>{let r=0,o=!1;const a={beginUpdate(){r++},endUpdate(){r--,0===r&&(e.reportChanges(),o&&(o=!1,t.call(i)))},handlePossibleChange(){},handleChange(){o=!0}};e.addObserver(a),e.reportChanges();const l={dispose(){e.removeObserver(a)}};return s instanceof n.DisposableStore?s.add(l):Array.isArray(s)&&s.push(l),l}}}(l||(t.Event=l={}));class h{static{this.all=new Set}static{this._idPool=0}constructor(e){this.listenerCount=0,this.invocationCount=0,this.elapsedOverall=0,this.durations=[],this.name=`${e}_${h._idPool++}`,h.all.add(this)}start(e){this._stopWatch=new a.StopWatch,this.listenerCount=e}stop(){if(this._stopWatch){const e=this._stopWatch.elapsed();this.durations.push(e),this.elapsedOverall+=e,this.invocationCount+=1,this._stopWatch=void 0}}}t.EventProfiling=h;let c=-1;class d{static{this._idPool=1}constructor(e,t,i=(d._idPool++).toString(16).padStart(3,"0")){this._errorHandler=e,this.threshold=t,this.name=i,this._warnCountdown=0}dispose(){this._stacks?.clear()}check(e,t){const i=this.threshold;if(i<=0||t<i)return;this._stacks||(this._stacks=new Map);const s=this._stacks.get(e.value)||0;if(this._stacks.set(e.value,s+1),this._warnCountdown-=1,this._warnCountdown<=0){this._warnCountdown=.5*i;const[e,s]=this.getMostFrequentStack(),r=`[${this.name}] potential listener LEAK detected, having ${t} listeners already. MOST frequent listener (${s}):`;console.warn(r),console.warn(e);const n=new _(r,e);this._errorHandler(n)}return()=>{const t=this._stacks.get(e.value)||0;this._stacks.set(e.value,t-1)}}getMostFrequentStack(){if(!this._stacks)return;let e,t=0;for(const[i,s]of this._stacks)(!e||t<s)&&(e=[i,s],t=s);return e}}class u{static create(){const e=new Error;return new u(e.stack??"")}constructor(e){this.value=e}print(){console.warn(this.value.split("\n").slice(2).join("\n"))}}class _ extends Error{constructor(e,t){super(e),this.name="ListenerLeakError",this.stack=t}}t.ListenerLeakError=_;class f extends Error{constructor(e,t){super(e),this.name="ListenerRefusalError",this.stack=t}}t.ListenerRefusalError=f;let p=0;class g{constructor(e){this.value=e,this.id=p++}}class m{constructor(e){this._size=0,this._options=e,this._leakageMon=c>0||this._options?.leakWarningThreshold?new d(e?.onListenerError??s.onUnexpectedError,this._options?.leakWarningThreshold??c):void 0,this._perfMon=this._options?._profName?new h(this._options._profName):void 0,this._deliveryQueue=this._options?.deliveryQueue}dispose(){this._disposed||(this._disposed=!0,this._deliveryQueue?.current===this&&this._deliveryQueue.reset(),this._listeners&&(this._listeners=void 0,this._size=0),this._options?.onDidRemoveLastListener?.(),this._leakageMon?.dispose())}get event(){return this._event??=(e,t,i)=>{if(this._leakageMon&&this._size>this._leakageMon.threshold**2){const e=`[${this._leakageMon.name}] REFUSES to accept new listeners because it exceeded its threshold by far (${this._size} vs ${this._leakageMon.threshold})`;console.warn(e);const t=this._leakageMon.getMostFrequentStack()??["UNKNOWN stack",-1],i=new f(`${e}. HINT: Stack shows most frequent listener (${t[1]}-times)`,t[0]);return(this._options?.onListenerError||s.onUnexpectedError)(i),n.Disposable.None}if(this._disposed)return n.Disposable.None;t&&(e=e.bind(t));const r=new g(e);let o;this._leakageMon&&this._size>=Math.ceil(.2*this._leakageMon.threshold)&&(r.stack=u.create(),o=this._leakageMon.check(r.stack,this._size+1)),this._listeners?this._listeners instanceof g?(this._deliveryQueue??=new v,this._listeners=[this._listeners,r]):this._listeners.push(r):(this._options?.onWillAddFirstListener?.(this),this._listeners=r,this._options?.onDidAddFirstListener?.(this)),this._size++;const a=(0,n.toDisposable)((()=>{o?.(),this._removeListener(r)}));return i instanceof n.DisposableStore?i.add(a):Array.isArray(i)&&i.push(a),a},this._event}_removeListener(e){if(this._options?.onWillRemoveListener?.(this),!this._listeners)return;if(1===this._size)return this._listeners=void 0,this._options?.onDidRemoveLastListener?.(this),void(this._size=0);const t=this._listeners,i=t.indexOf(e);if(-1===i)throw console.log("disposed?",this._disposed),console.log("size?",this._size),console.log("arr?",JSON.stringify(this._listeners)),new Error("Attempted to dispose unknown listener");this._size--,t[i]=void 0;const s=this._deliveryQueue.current===this;if(2*this._size<=t.length){let e=0;for(let i=0;i<t.length;i++)t[i]?t[e++]=t[i]:s&&(this._deliveryQueue.end--,e<this._deliveryQueue.i&&this._deliveryQueue.i--);t.length=e}}_deliver(e,t){if(!e)return;const i=this._options?.onListenerError||s.onUnexpectedError;if(i)try{e.value(t)}catch(e){i(e)}else e.value(t)}_deliverQueue(e){const t=e.current._listeners;for(;e.i<e.end;)this._deliver(t[e.i++],e.value);e.reset()}fire(e){if(this._deliveryQueue?.current&&(this._deliverQueue(this._deliveryQueue),this._perfMon?.stop()),this._perfMon?.start(this._size),this._listeners)if(this._listeners instanceof g)this._deliver(this._listeners,e);else{const t=this._deliveryQueue;t.enqueue(this,e,this._listeners.length),this._deliverQueue(t)}this._perfMon?.stop()}hasListeners(){return this._size>0}}t.Emitter=m,t.createEventDeliveryQueue=()=>new v;class v{constructor(){this.i=-1,this.end=0}enqueue(e,t,i){this.i=0,this.end=i,this.current=e,this.value=t}reset(){this.i=this.end,this.current=void 0,this.value=void 0}}t.AsyncEmitter=class extends m{async fireAsync(e,t,i){if(this._listeners)for(this._asyncDeliveryQueue||(this._asyncDeliveryQueue=new o.LinkedList),((e,t)=>{if(e instanceof g)t(e);else for(let i=0;i<e.length;i++){const s=e[i];s&&t(s)}})(this._listeners,(t=>this._asyncDeliveryQueue.push([t.value,e])));this._asyncDeliveryQueue.size>0&&!t.isCancellationRequested;){const[e,r]=this._asyncDeliveryQueue.shift(),n=[],o={...r,token:t,waitUntil:t=>{if(Object.isFrozen(n))throw new Error("waitUntil can NOT be called asynchronous");i&&(t=i(t,e)),n.push(t)}};try{e(o)}catch(e){(0,s.onUnexpectedError)(e);continue}Object.freeze(n),await Promise.allSettled(n).then((e=>{for(const t of e)"rejected"===t.status&&(0,s.onUnexpectedError)(t.reason)}))}}};class S extends m{get isPaused(){return 0!==this._isPaused}constructor(e){super(e),this._isPaused=0,this._eventQueue=new o.LinkedList,this._mergeFn=e?.merge}pause(){this._isPaused++}resume(){if(0!==this._isPaused&&0==--this._isPaused)if(this._mergeFn){if(this._eventQueue.size>0){const e=Array.from(this._eventQueue);this._eventQueue.clear(),super.fire(this._mergeFn(e))}}else for(;!this._isPaused&&0!==this._eventQueue.size;)super.fire(this._eventQueue.shift())}fire(e){this._size&&(0!==this._isPaused?this._eventQueue.push(e):super.fire(e))}}t.PauseableEmitter=S,t.DebounceEmitter=class extends S{constructor(e){super(e),this._delay=e.delay??100}fire(e){this._handle||(this.pause(),this._handle=setTimeout((()=>{this._handle=void 0,this.resume()}),this._delay)),super.fire(e)}},t.MicrotaskEmitter=class extends m{constructor(e){super(e),this._queuedEvents=[],this._mergeFn=e?.merge}fire(e){this.hasListeners()&&(this._queuedEvents.push(e),1===this._queuedEvents.length&&queueMicrotask((()=>{this._mergeFn?super.fire(this._mergeFn(this._queuedEvents)):this._queuedEvents.forEach((e=>super.fire(e))),this._queuedEvents=[]})))}};class b{constructor(){this.hasListeners=!1,this.events=[],this.emitter=new m({onWillAddFirstListener:()=>this.onFirstListenerAdd(),onDidRemoveLastListener:()=>this.onLastListenerRemove()})}get event(){return this.emitter.event}add(e){const t={event:e,listener:null};return this.events.push(t),this.hasListeners&&this.hook(t),(0,n.toDisposable)((0,r.createSingleCallFunction)((()=>{this.hasListeners&&this.unhook(t);const e=this.events.indexOf(t);this.events.splice(e,1)})))}onFirstListenerAdd(){this.hasListeners=!0,this.events.forEach((e=>this.hook(e)))}onLastListenerRemove(){this.hasListeners=!1,this.events.forEach((e=>this.unhook(e)))}hook(e){e.listener=e.event((e=>this.emitter.fire(e)))}unhook(e){e.listener?.dispose(),e.listener=null}dispose(){this.emitter.dispose();for(const e of this.events)e.listener?.dispose();this.events=[]}}t.EventMultiplexer=b,t.DynamicListEventMultiplexer=class{constructor(e,t,i,s){this._store=new n.DisposableStore;const r=this._store.add(new b),o=this._store.add(new n.DisposableMap);function a(e){o.set(e,r.add(s(e)))}for(const t of e)a(t);this._store.add(t((e=>{a(e)}))),this._store.add(i((e=>{o.deleteAndDispose(e)}))),this.event=r.event}dispose(){this._store.dispose()}},t.EventBufferer=class{constructor(){this.data=[]}wrapEvent(e,t,i){return(s,r,n)=>e((e=>{const n=this.data[this.data.length-1];if(!t)return void(n?n.buffers.push((()=>s.call(r,e))):s.call(r,e));const o=n;o?(o.items??=[],o.items.push(e),0===o.buffers.length&&n.buffers.push((()=>{o.reducedResult??=i?o.items.reduce(t,i):o.items.reduce(t),s.call(r,o.reducedResult)}))):s.call(r,t(i,e))}),void 0,n)}bufferEvents(e){const t={buffers:new Array};this.data.push(t);const i=e();return this.data.pop(),t.buffers.forEach((e=>e())),i}},t.Relay=class{constructor(){this.listening=!1,this.inputEvent=l.None,this.inputEventListener=n.Disposable.None,this.emitter=new m({onDidAddFirstListener:()=>{this.listening=!0,this.inputEventListener=this.inputEvent(this.emitter.fire,this.emitter)},onDidRemoveLastListener:()=>{this.listening=!1,this.inputEventListener.dispose()}}),this.event=this.emitter.event}set input(e){this.inputEvent=e,this.listening&&(this.inputEventListener.dispose(),this.inputEventListener=e(this.emitter.fire,this.emitter))}dispose(){this.inputEventListener.dispose(),this.emitter.dispose()}},t.ValueWithChangeEvent=class{static const(e){return new C(e)}constructor(e){this._value=e,this._onDidChange=new m,this.onDidChange=this._onDidChange.event}get value(){return this._value}set value(e){e!==this._value&&(this._value=e,this._onDidChange.fire(void 0))}};class C{constructor(e){this.value=e,this.onDidChange=l.None}}},8841:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.createSingleCallFunction=function(e,t){const i=this;let s,r=!1;return function(){if(r)return s;if(r=!0,t)try{s=e.apply(i,arguments)}finally{t()}else s=e.apply(i,arguments);return s}}},6304:function(e,t,i){var s=this&&this.__createBinding||(Object.create?function(e,t,i,s){void 0===s&&(s=i);var r=Object.getOwnPropertyDescriptor(t,i);r&&!("get"in r?!t.__esModule:r.writable||r.configurable)||(r={enumerable:!0,get:function(){return t[i]}}),Object.defineProperty(e,s,r)}:function(e,t,i,s){void 0===s&&(s=i),e[s]=t[i]}),r=this&&this.__setModuleDefault||(Object.create?function(e,t){Object.defineProperty(e,"default",{enumerable:!0,value:t})}:function(e,t){e.default=t}),n=this&&this.__importStar||function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var i in e)"default"!==i&&Object.prototype.hasOwnProperty.call(e,i)&&s(t,e,i);return r(t,e),t};Object.defineProperty(t,"__esModule",{value:!0}),t.StringSHA1=t.Hasher=void 0,t.hash=function(e){return a(e,0)},t.doHash=a,t.numberHash=l,t.stringHash=h,t.toHexString=_;const o=n(i(1316));function a(e,t){switch(typeof e){case"object":return null===e?l(349,t):Array.isArray(e)?(i=e,s=l(104579,s=t),i.reduce(((e,t)=>a(t,e)),s)):function(e,t){return t=l(181387,t),Object.keys(e).sort().reduce(((t,i)=>(t=h(i,t),a(e[i],t))),t)}(e,t);case"string":return h(e,t);case"boolean":return function(e,t){return l(e?433:863,t)}(e,t);case"number":return l(e,t);case"undefined":return l(937,t);default:return l(617,t)}var i,s}function l(e,t){return(t<<5)-t+e|0}function h(e,t){t=l(149417,t);for(let i=0,s=e.length;i<s;i++)t=l(e.charCodeAt(i),t);return t}var c;function d(e,t,i=32){const s=i-t;return(e<<t|(~((1<<s)-1)&e)>>>s)>>>0}function u(e,t=0,i=e.byteLength,s=0){for(let r=0;r<i;r++)e[t+r]=s}function _(e,t=32){return e instanceof ArrayBuffer?Array.from(new Uint8Array(e)).map((e=>e.toString(16).padStart(2,"0"))).join(""):function(e,t,i="0"){for(;e.length<t;)e=i+e;return e}((e>>>0).toString(16),t/4)}t.Hasher=class{constructor(){this._value=0}get value(){return this._value}hash(e){return this._value=a(e,this._value),this._value}},function(e){e[e.BLOCK_SIZE=64]="BLOCK_SIZE",e[e.UNICODE_REPLACEMENT=65533]="UNICODE_REPLACEMENT"}(c||(c={}));class f{static{this._bigBlock32=new DataView(new ArrayBuffer(320))}constructor(){this._h0=1732584193,this._h1=4023233417,this._h2=2562383102,this._h3=271733878,this._h4=3285377520,this._buff=new Uint8Array(c.BLOCK_SIZE+3),this._buffDV=new DataView(this._buff.buffer),this._buffLen=0,this._totalLen=0,this._leftoverHighSurrogate=0,this._finished=!1}update(e){const t=e.length;if(0===t)return;const i=this._buff;let s,r,n=this._buffLen,a=this._leftoverHighSurrogate;for(0!==a?(s=a,r=-1,a=0):(s=e.charCodeAt(0),r=0);;){let l=s;if(o.isHighSurrogate(s)){if(!(r+1<t)){a=s;break}{const t=e.charCodeAt(r+1);o.isLowSurrogate(t)?(r++,l=o.computeCodePoint(s,t)):l=c.UNICODE_REPLACEMENT}}else o.isLowSurrogate(s)&&(l=c.UNICODE_REPLACEMENT);if(n=this._push(i,n,l),r++,!(r<t))break;s=e.charCodeAt(r)}this._buffLen=n,this._leftoverHighSurrogate=a}_push(e,t,i){return i<128?e[t++]=i:i<2048?(e[t++]=192|(1984&i)>>>6,e[t++]=128|(63&i)>>>0):i<65536?(e[t++]=224|(61440&i)>>>12,e[t++]=128|(4032&i)>>>6,e[t++]=128|(63&i)>>>0):(e[t++]=240|(1835008&i)>>>18,e[t++]=128|(258048&i)>>>12,e[t++]=128|(4032&i)>>>6,e[t++]=128|(63&i)>>>0),t>=c.BLOCK_SIZE&&(this._step(),t-=c.BLOCK_SIZE,this._totalLen+=c.BLOCK_SIZE,e[0]=e[c.BLOCK_SIZE+0],e[1]=e[c.BLOCK_SIZE+1],e[2]=e[c.BLOCK_SIZE+2]),t}digest(){return this._finished||(this._finished=!0,this._leftoverHighSurrogate&&(this._leftoverHighSurrogate=0,this._buffLen=this._push(this._buff,this._buffLen,c.UNICODE_REPLACEMENT)),this._totalLen+=this._buffLen,this._wrapUp()),_(this._h0)+_(this._h1)+_(this._h2)+_(this._h3)+_(this._h4)}_wrapUp(){this._buff[this._buffLen++]=128,u(this._buff,this._buffLen),this._buffLen>56&&(this._step(),u(this._buff));const e=8*this._totalLen;this._buffDV.setUint32(56,Math.floor(e/4294967296),!1),this._buffDV.setUint32(60,e%4294967296,!1),this._step()}_step(){const e=f._bigBlock32,t=this._buffDV;for(let i=0;i<64;i+=4)e.setUint32(i,t.getUint32(i,!1),!1);for(let t=64;t<320;t+=4)e.setUint32(t,d(e.getUint32(t-12,!1)^e.getUint32(t-32,!1)^e.getUint32(t-56,!1)^e.getUint32(t-64,!1),1),!1);let i,s,r,n=this._h0,o=this._h1,a=this._h2,l=this._h3,h=this._h4;for(let t=0;t<80;t++)t<20?(i=o&a|~o&l,s=1518500249):t<40?(i=o^a^l,s=1859775393):t<60?(i=o&a|o&l|a&l,s=2400959708):(i=o^a^l,s=3395469782),r=d(n,5)+i+h+s+e.getUint32(4*t,!1)&4294967295,h=l,l=a,a=d(o,30),o=n,n=r;this._h0=this._h0+n&4294967295,this._h1=this._h1+o&4294967295,this._h2=this._h2+a&4294967295,this._h3=this._h3+l&4294967295,this._h4=this._h4+h&4294967295}}t.StringSHA1=f},4218:(e,t)=>{var i;Object.defineProperty(t,"__esModule",{value:!0}),t.Iterable=void 0,function(e){function t(e){return e&&"object"==typeof e&&"function"==typeof e[Symbol.iterator]}e.is=t;const i=Object.freeze([]);function*s(e){yield e}e.empty=function(){return i},e.single=s,e.wrap=function(e){return t(e)?e:s(e)},e.from=function(e){return e||i},e.reverse=function*(e){for(let t=e.length-1;t>=0;t--)yield e[t]},e.isEmpty=function(e){return!e||!0===e[Symbol.iterator]().next().done},e.first=function(e){return e[Symbol.iterator]().next().value},e.some=function(e,t){let i=0;for(const s of e)if(t(s,i++))return!0;return!1},e.find=function(e,t){for(const i of e)if(t(i))return i},e.filter=function*(e,t){for(const i of e)t(i)&&(yield i)},e.map=function*(e,t){let i=0;for(const s of e)yield t(s,i++)},e.flatMap=function*(e,t){let i=0;for(const s of e)yield*t(s,i++)},e.concat=function*(...e){for(const t of e)yield*t},e.reduce=function(e,t,i){let s=i;for(const i of e)s=t(s,i);return s},e.slice=function*(e,t,i=e.length){for(t<0&&(t+=e.length),i<0?i+=e.length:i>e.length&&(i=e.length);t<i;t++)yield e[t]},e.consume=function(t,i=Number.POSITIVE_INFINITY){const s=[];if(0===i)return[s,t];const r=t[Symbol.iterator]();for(let t=0;t<i;t++){const t=r.next();if(t.done)return[s,e.empty()];s.push(t.value)}return[s,{[Symbol.iterator]:()=>r}]},e.asyncToArray=async function(e){const t=[];for await(const i of e)t.push(i);return Promise.resolve(t)}}(i||(t.Iterable=i={}))},7883:(e,t)=>{var i,s;Object.defineProperty(t,"__esModule",{value:!0}),t.KeyMod=t.KeyCodeUtils=t.ScanCodeUtils=t.NATIVE_WINDOWS_KEY_CODE_TO_KEY_CODE=t.EVENT_KEY_CODE_MAP=t.ScanCode=t.KeyCode=void 0,t.KeyChord=function(e,t){return(e|(65535&t)<<16>>>0)>>>0},function(e){e[e.DependsOnKbLayout=-1]="DependsOnKbLayout",e[e.Unknown=0]="Unknown",e[e.Backspace=1]="Backspace",e[e.Tab=2]="Tab",e[e.Enter=3]="Enter",e[e.Shift=4]="Shift",e[e.Ctrl=5]="Ctrl",e[e.Alt=6]="Alt",e[e.PauseBreak=7]="PauseBreak",e[e.CapsLock=8]="CapsLock",e[e.Escape=9]="Escape",e[e.Space=10]="Space",e[e.PageUp=11]="PageUp",e[e.PageDown=12]="PageDown",e[e.End=13]="End",e[e.Home=14]="Home",e[e.LeftArrow=15]="LeftArrow",e[e.UpArrow=16]="UpArrow",e[e.RightArrow=17]="RightArrow",e[e.DownArrow=18]="DownArrow",e[e.Insert=19]="Insert",e[e.Delete=20]="Delete",e[e.Digit0=21]="Digit0",e[e.Digit1=22]="Digit1",e[e.Digit2=23]="Digit2",e[e.Digit3=24]="Digit3",e[e.Digit4=25]="Digit4",e[e.Digit5=26]="Digit5",e[e.Digit6=27]="Digit6",e[e.Digit7=28]="Digit7",e[e.Digit8=29]="Digit8",e[e.Digit9=30]="Digit9",e[e.KeyA=31]="KeyA",e[e.KeyB=32]="KeyB",e[e.KeyC=33]="KeyC",e[e.KeyD=34]="KeyD",e[e.KeyE=35]="KeyE",e[e.KeyF=36]="KeyF",e[e.KeyG=37]="KeyG",e[e.KeyH=38]="KeyH",e[e.KeyI=39]="KeyI",e[e.KeyJ=40]="KeyJ",e[e.KeyK=41]="KeyK",e[e.KeyL=42]="KeyL",e[e.KeyM=43]="KeyM",e[e.KeyN=44]="KeyN",e[e.KeyO=45]="KeyO",e[e.KeyP=46]="KeyP",e[e.KeyQ=47]="KeyQ",e[e.KeyR=48]="KeyR",e[e.KeyS=49]="KeyS",e[e.KeyT=50]="KeyT",e[e.KeyU=51]="KeyU",e[e.KeyV=52]="KeyV",e[e.KeyW=53]="KeyW",e[e.KeyX=54]="KeyX",e[e.KeyY=55]="KeyY",e[e.KeyZ=56]="KeyZ",e[e.Meta=57]="Meta",e[e.ContextMenu=58]="ContextMenu",e[e.F1=59]="F1",e[e.F2=60]="F2",e[e.F3=61]="F3",e[e.F4=62]="F4",e[e.F5=63]="F5",e[e.F6=64]="F6",e[e.F7=65]="F7",e[e.F8=66]="F8",e[e.F9=67]="F9",e[e.F10=68]="F10",e[e.F11=69]="F11",e[e.F12=70]="F12",e[e.F13=71]="F13",e[e.F14=72]="F14",e[e.F15=73]="F15",e[e.F16=74]="F16",e[e.F17=75]="F17",e[e.F18=76]="F18",e[e.F19=77]="F19",e[e.F20=78]="F20",e[e.F21=79]="F21",e[e.F22=80]="F22",e[e.F23=81]="F23",e[e.F24=82]="F24",e[e.NumLock=83]="NumLock",e[e.ScrollLock=84]="ScrollLock",e[e.Semicolon=85]="Semicolon",e[e.Equal=86]="Equal",e[e.Comma=87]="Comma",e[e.Minus=88]="Minus",e[e.Period=89]="Period",e[e.Slash=90]="Slash",e[e.Backquote=91]="Backquote",e[e.BracketLeft=92]="BracketLeft",e[e.Backslash=93]="Backslash",e[e.BracketRight=94]="BracketRight",e[e.Quote=95]="Quote",e[e.OEM_8=96]="OEM_8",e[e.IntlBackslash=97]="IntlBackslash",e[e.Numpad0=98]="Numpad0",e[e.Numpad1=99]="Numpad1",e[e.Numpad2=100]="Numpad2",e[e.Numpad3=101]="Numpad3",e[e.Numpad4=102]="Numpad4",e[e.Numpad5=103]="Numpad5",e[e.Numpad6=104]="Numpad6",e[e.Numpad7=105]="Numpad7",e[e.Numpad8=106]="Numpad8",e[e.Numpad9=107]="Numpad9",e[e.NumpadMultiply=108]="NumpadMultiply",e[e.NumpadAdd=109]="NumpadAdd",e[e.NUMPAD_SEPARATOR=110]="NUMPAD_SEPARATOR",e[e.NumpadSubtract=111]="NumpadSubtract",e[e.NumpadDecimal=112]="NumpadDecimal",e[e.NumpadDivide=113]="NumpadDivide",e[e.KEY_IN_COMPOSITION=114]="KEY_IN_COMPOSITION",e[e.ABNT_C1=115]="ABNT_C1",e[e.ABNT_C2=116]="ABNT_C2",e[e.AudioVolumeMute=117]="AudioVolumeMute",e[e.AudioVolumeUp=118]="AudioVolumeUp",e[e.AudioVolumeDown=119]="AudioVolumeDown",e[e.BrowserSearch=120]="BrowserSearch",e[e.BrowserHome=121]="BrowserHome",e[e.BrowserBack=122]="BrowserBack",e[e.BrowserForward=123]="BrowserForward",e[e.MediaTrackNext=124]="MediaTrackNext",e[e.MediaTrackPrevious=125]="MediaTrackPrevious",e[e.MediaStop=126]="MediaStop",e[e.MediaPlayPause=127]="MediaPlayPause",e[e.LaunchMediaPlayer=128]="LaunchMediaPlayer",e[e.LaunchMail=129]="LaunchMail",e[e.LaunchApp2=130]="LaunchApp2",e[e.Clear=131]="Clear",e[e.MAX_VALUE=132]="MAX_VALUE"}(i||(t.KeyCode=i={})),function(e){e[e.DependsOnKbLayout=-1]="DependsOnKbLayout",e[e.None=0]="None",e[e.Hyper=1]="Hyper",e[e.Super=2]="Super",e[e.Fn=3]="Fn",e[e.FnLock=4]="FnLock",e[e.Suspend=5]="Suspend",e[e.Resume=6]="Resume",e[e.Turbo=7]="Turbo",e[e.Sleep=8]="Sleep",e[e.WakeUp=9]="WakeUp",e[e.KeyA=10]="KeyA",e[e.KeyB=11]="KeyB",e[e.KeyC=12]="KeyC",e[e.KeyD=13]="KeyD",e[e.KeyE=14]="KeyE",e[e.KeyF=15]="KeyF",e[e.KeyG=16]="KeyG",e[e.KeyH=17]="KeyH",e[e.KeyI=18]="KeyI",e[e.KeyJ=19]="KeyJ",e[e.KeyK=20]="KeyK",e[e.KeyL=21]="KeyL",e[e.KeyM=22]="KeyM",e[e.KeyN=23]="KeyN",e[e.KeyO=24]="KeyO",e[e.KeyP=25]="KeyP",e[e.KeyQ=26]="KeyQ",e[e.KeyR=27]="KeyR",e[e.KeyS=28]="KeyS",e[e.KeyT=29]="KeyT",e[e.KeyU=30]="KeyU",e[e.KeyV=31]="KeyV",e[e.KeyW=32]="KeyW",e[e.KeyX=33]="KeyX",e[e.KeyY=34]="KeyY",e[e.KeyZ=35]="KeyZ",e[e.Digit1=36]="Digit1",e[e.Digit2=37]="Digit2",e[e.Digit3=38]="Digit3",e[e.Digit4=39]="Digit4",e[e.Digit5=40]="Digit5",e[e.Digit6=41]="Digit6",e[e.Digit7=42]="Digit7",e[e.Digit8=43]="Digit8",e[e.Digit9=44]="Digit9",e[e.Digit0=45]="Digit0",e[e.Enter=46]="Enter",e[e.Escape=47]="Escape",e[e.Backspace=48]="Backspace",e[e.Tab=49]="Tab",e[e.Space=50]="Space",e[e.Minus=51]="Minus",e[e.Equal=52]="Equal",e[e.BracketLeft=53]="BracketLeft",e[e.BracketRight=54]="BracketRight",e[e.Backslash=55]="Backslash",e[e.IntlHash=56]="IntlHash",e[e.Semicolon=57]="Semicolon",e[e.Quote=58]="Quote",e[e.Backquote=59]="Backquote",e[e.Comma=60]="Comma",e[e.Period=61]="Period",e[e.Slash=62]="Slash",e[e.CapsLock=63]="CapsLock",e[e.F1=64]="F1",e[e.F2=65]="F2",e[e.F3=66]="F3",e[e.F4=67]="F4",e[e.F5=68]="F5",e[e.F6=69]="F6",e[e.F7=70]="F7",e[e.F8=71]="F8",e[e.F9=72]="F9",e[e.F10=73]="F10",e[e.F11=74]="F11",e[e.F12=75]="F12",e[e.PrintScreen=76]="PrintScreen",e[e.ScrollLock=77]="ScrollLock",e[e.Pause=78]="Pause",e[e.Insert=79]="Insert",e[e.Home=80]="Home",e[e.PageUp=81]="PageUp",e[e.Delete=82]="Delete",e[e.End=83]="End",e[e.PageDown=84]="PageDown",e[e.ArrowRight=85]="ArrowRight",e[e.ArrowLeft=86]="ArrowLeft",e[e.ArrowDown=87]="ArrowDown",e[e.ArrowUp=88]="ArrowUp",e[e.NumLock=89]="NumLock",e[e.NumpadDivide=90]="NumpadDivide",e[e.NumpadMultiply=91]="NumpadMultiply",e[e.NumpadSubtract=92]="NumpadSubtract",e[e.NumpadAdd=93]="NumpadAdd",e[e.NumpadEnter=94]="NumpadEnter",e[e.Numpad1=95]="Numpad1",e[e.Numpad2=96]="Numpad2",e[e.Numpad3=97]="Numpad3",e[e.Numpad4=98]="Numpad4",e[e.Numpad5=99]="Numpad5",e[e.Numpad6=100]="Numpad6",e[e.Numpad7=101]="Numpad7",e[e.Numpad8=102]="Numpad8",e[e.Numpad9=103]="Numpad9",e[e.Numpad0=104]="Numpad0",e[e.NumpadDecimal=105]="NumpadDecimal",e[e.IntlBackslash=106]="IntlBackslash",e[e.ContextMenu=107]="ContextMenu",e[e.Power=108]="Power",e[e.NumpadEqual=109]="NumpadEqual",e[e.F13=110]="F13",e[e.F14=111]="F14",e[e.F15=112]="F15",e[e.F16=113]="F16",e[e.F17=114]="F17",e[e.F18=115]="F18",e[e.F19=116]="F19",e[e.F20=117]="F20",e[e.F21=118]="F21",e[e.F22=119]="F22",e[e.F23=120]="F23",e[e.F24=121]="F24",e[e.Open=122]="Open",e[e.Help=123]="Help",e[e.Select=124]="Select",e[e.Again=125]="Again",e[e.Undo=126]="Undo",e[e.Cut=127]="Cut",e[e.Copy=128]="Copy",e[e.Paste=129]="Paste",e[e.Find=130]="Find",e[e.AudioVolumeMute=131]="AudioVolumeMute",e[e.AudioVolumeUp=132]="AudioVolumeUp",e[e.AudioVolumeDown=133]="AudioVolumeDown",e[e.NumpadComma=134]="NumpadComma",e[e.IntlRo=135]="IntlRo",e[e.KanaMode=136]="KanaMode",e[e.IntlYen=137]="IntlYen",e[e.Convert=138]="Convert",e[e.NonConvert=139]="NonConvert",e[e.Lang1=140]="Lang1",e[e.Lang2=141]="Lang2",e[e.Lang3=142]="Lang3",e[e.Lang4=143]="Lang4",e[e.Lang5=144]="Lang5",e[e.Abort=145]="Abort",e[e.Props=146]="Props",e[e.NumpadParenLeft=147]="NumpadParenLeft",e[e.NumpadParenRight=148]="NumpadParenRight",e[e.NumpadBackspace=149]="NumpadBackspace",e[e.NumpadMemoryStore=150]="NumpadMemoryStore",e[e.NumpadMemoryRecall=151]="NumpadMemoryRecall",e[e.NumpadMemoryClear=152]="NumpadMemoryClear",e[e.NumpadMemoryAdd=153]="NumpadMemoryAdd",e[e.NumpadMemorySubtract=154]="NumpadMemorySubtract",e[e.NumpadClear=155]="NumpadClear",e[e.NumpadClearEntry=156]="NumpadClearEntry",e[e.ControlLeft=157]="ControlLeft",e[e.ShiftLeft=158]="ShiftLeft",e[e.AltLeft=159]="AltLeft",e[e.MetaLeft=160]="MetaLeft",e[e.ControlRight=161]="ControlRight",e[e.ShiftRight=162]="ShiftRight",e[e.AltRight=163]="AltRight",e[e.MetaRight=164]="MetaRight",e[e.BrightnessUp=165]="BrightnessUp",e[e.BrightnessDown=166]="BrightnessDown",e[e.MediaPlay=167]="MediaPlay",e[e.MediaRecord=168]="MediaRecord",e[e.MediaFastForward=169]="MediaFastForward",e[e.MediaRewind=170]="MediaRewind",e[e.MediaTrackNext=171]="MediaTrackNext",e[e.MediaTrackPrevious=172]="MediaTrackPrevious",e[e.MediaStop=173]="MediaStop",e[e.Eject=174]="Eject",e[e.MediaPlayPause=175]="MediaPlayPause",e[e.MediaSelect=176]="MediaSelect",e[e.LaunchMail=177]="LaunchMail",e[e.LaunchApp2=178]="LaunchApp2",e[e.LaunchApp1=179]="LaunchApp1",e[e.SelectTask=180]="SelectTask",e[e.LaunchScreenSaver=181]="LaunchScreenSaver",e[e.BrowserSearch=182]="BrowserSearch",e[e.BrowserHome=183]="BrowserHome",e[e.BrowserBack=184]="BrowserBack",e[e.BrowserForward=185]="BrowserForward",e[e.BrowserStop=186]="BrowserStop",e[e.BrowserRefresh=187]="BrowserRefresh",e[e.BrowserFavorites=188]="BrowserFavorites",e[e.ZoomToggle=189]="ZoomToggle",e[e.MailReply=190]="MailReply",e[e.MailForward=191]="MailForward",e[e.MailSend=192]="MailSend",e[e.MAX_VALUE=193]="MAX_VALUE"}(s||(t.ScanCode=s={}));class r{constructor(){this._keyCodeToStr=[],this._strToKeyCode=Object.create(null)}define(e,t){this._keyCodeToStr[e]=t,this._strToKeyCode[t.toLowerCase()]=e}keyCodeToStr(e){return this._keyCodeToStr[e]}strToKeyCode(e){return this._strToKeyCode[e.toLowerCase()]||i.Unknown}}const n=new r,o=new r,a=new r;t.EVENT_KEY_CODE_MAP=new Array(230),t.NATIVE_WINDOWS_KEY_CODE_TO_KEY_CODE={};const l=[],h=Object.create(null),c=Object.create(null);var d,u;t.ScanCodeUtils={lowerCaseToEnum:e=>c[e]||s.None,toEnum:e=>h[e]||s.None,toString:e=>l[e]||"None"},function(e){e.toString=function(e){return n.keyCodeToStr(e)},e.fromString=function(e){return n.strToKeyCode(e)},e.toUserSettingsUS=function(e){return o.keyCodeToStr(e)},e.toUserSettingsGeneral=function(e){return a.keyCodeToStr(e)},e.fromUserSettings=function(e){return o.strToKeyCode(e)||a.strToKeyCode(e)},e.toElectronAccelerator=function(e){if(e>=i.Numpad0&&e<=i.NumpadDivide)return null;switch(e){case i.UpArrow:return"Up";case i.DownArrow:return"Down";case i.LeftArrow:return"Left";case i.RightArrow:return"Right"}return n.keyCodeToStr(e)}}(d||(t.KeyCodeUtils=d={})),function(e){e[e.CtrlCmd=2048]="CtrlCmd",e[e.Shift=1024]="Shift",e[e.Alt=512]="Alt",e[e.WinCtrl=256]="WinCtrl"}(u||(t.KeyMod=u={}))},2811:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.ResolvedKeybinding=t.ResolvedChord=t.Keybinding=t.ScanCodeChord=t.KeyCodeChord=void 0,t.decodeKeybinding=function(e,t){if("number"==typeof e){if(0===e)return null;const i=(65535&e)>>>0,s=(4294901760&e)>>>16;return new c(0!==s?[a(i,t),a(s,t)]:[a(i,t)])}{const i=[];for(let s=0;s<e.length;s++)i.push(a(e[s],t));return new c(i)}},t.createSimpleKeybinding=a;const s=i(9807),r=i(7883),n=i(8163);var o;function a(e,t){const i=!!(e&o.CtrlCmd),s=!!(e&o.WinCtrl),r=t===n.OperatingSystem.Macintosh?s:i,a=!!(e&o.Shift),h=!!(e&o.Alt),c=t===n.OperatingSystem.Macintosh?i:s,d=e&o.KeyCode;return new l(r,a,h,c,d)}!function(e){e[e.CtrlCmd=2048]="CtrlCmd",e[e.Shift=1024]="Shift",e[e.Alt=512]="Alt",e[e.WinCtrl=256]="WinCtrl",e[e.KeyCode=255]="KeyCode"}(o||(o={}));class l{constructor(e,t,i,s,r){this.ctrlKey=e,this.shiftKey=t,this.altKey=i,this.metaKey=s,this.keyCode=r}equals(e){return e instanceof l&&this.ctrlKey===e.ctrlKey&&this.shiftKey===e.shiftKey&&this.altKey===e.altKey&&this.metaKey===e.metaKey&&this.keyCode===e.keyCode}getHashCode(){return`K${this.ctrlKey?"1":"0"}${this.shiftKey?"1":"0"}${this.altKey?"1":"0"}${this.metaKey?"1":"0"}${this.keyCode}`}isModifierKey(){return this.keyCode===r.KeyCode.Unknown||this.keyCode===r.KeyCode.Ctrl||this.keyCode===r.KeyCode.Meta||this.keyCode===r.KeyCode.Alt||this.keyCode===r.KeyCode.Shift}toKeybinding(){return new c([this])}isDuplicateModifierCase(){return this.ctrlKey&&this.keyCode===r.KeyCode.Ctrl||this.shiftKey&&this.keyCode===r.KeyCode.Shift||this.altKey&&this.keyCode===r.KeyCode.Alt||this.metaKey&&this.keyCode===r.KeyCode.Meta}}t.KeyCodeChord=l;class h{constructor(e,t,i,s,r){this.ctrlKey=e,this.shiftKey=t,this.altKey=i,this.metaKey=s,this.scanCode=r}equals(e){return e instanceof h&&this.ctrlKey===e.ctrlKey&&this.shiftKey===e.shiftKey&&this.altKey===e.altKey&&this.metaKey===e.metaKey&&this.scanCode===e.scanCode}getHashCode(){return`S${this.ctrlKey?"1":"0"}${this.shiftKey?"1":"0"}${this.altKey?"1":"0"}${this.metaKey?"1":"0"}${this.scanCode}`}isDuplicateModifierCase(){return this.ctrlKey&&(this.scanCode===r.ScanCode.ControlLeft||this.scanCode===r.ScanCode.ControlRight)||this.shiftKey&&(this.scanCode===r.ScanCode.ShiftLeft||this.scanCode===r.ScanCode.ShiftRight)||this.altKey&&(this.scanCode===r.ScanCode.AltLeft||this.scanCode===r.ScanCode.AltRight)||this.metaKey&&(this.scanCode===r.ScanCode.MetaLeft||this.scanCode===r.ScanCode.MetaRight)}}t.ScanCodeChord=h;class c{constructor(e){if(0===e.length)throw(0,s.illegalArgument)("chords");this.chords=e}getHashCode(){let e="";for(let t=0,i=this.chords.length;t<i;t++)0!==t&&(e+=";"),e+=this.chords[t].getHashCode();return e}equals(e){if(null===e)return!1;if(this.chords.length!==e.chords.length)return!1;for(let t=0;t<this.chords.length;t++)if(!this.chords[t].equals(e.chords[t]))return!1;return!0}}t.Keybinding=c,t.ResolvedChord=class{constructor(e,t,i,s,r,n){this.ctrlKey=e,this.shiftKey=t,this.altKey=i,this.metaKey=s,this.keyLabel=r,this.keyAriaLabel=n}},t.ResolvedKeybinding=class{}},626:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.Lazy=void 0,t.Lazy=class{constructor(e){this.executor=e,this._didRun=!1}get hasValue(){return this._didRun}get value(){if(!this._didRun)try{this._value=this.executor()}catch(e){this._error=e}finally{this._didRun=!0}if(this._error)throw this._error;return this._value}get rawValue(){return this._value}}},7150:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.DisposableMap=t.ImmortalReference=t.AsyncReferenceCollection=t.ReferenceCollection=t.SafeDisposable=t.RefCountedDisposable=t.MandatoryMutableDisposable=t.MutableDisposable=t.Disposable=t.DisposableStore=t.DisposableTracker=void 0,t.setDisposableTracker=function(e){l=e},t.trackDisposable=c,t.markAsDisposed=d,t.markAsSingleton=function(e){return l?.markAsSingleton(e),e},t.isDisposable=_,t.dispose=f,t.disposeIfDisposable=function(e){for(const t of e)_(t)&&t.dispose();return[]},t.combinedDisposable=function(...e){const t=p((()=>f(e)));return function(e,t){if(l)for(const i of e)l.setParent(i,t)}(e,t),t},t.toDisposable=p,t.disposeOnReturn=function(e){const t=new g;try{e(t)}finally{t.dispose()}};const s=i(3058),r=i(9087),n=i(2608),o=i(8841),a=i(4218);let l=null;class h{constructor(){this.livingDisposables=new Map}static{this.idx=0}getDisposableData(e){let t=this.livingDisposables.get(e);return t||(t={parent:null,source:null,isSingleton:!1,value:e,idx:h.idx++},this.livingDisposables.set(e,t)),t}trackDisposable(e){const t=this.getDisposableData(e);t.source||(t.source=(new Error).stack)}setParent(e,t){this.getDisposableData(e).parent=t}markAsDisposed(e){this.livingDisposables.delete(e)}markAsSingleton(e){this.getDisposableData(e).isSingleton=!0}getRootParent(e,t){const i=t.get(e);if(i)return i;const s=e.parent?this.getRootParent(this.getDisposableData(e.parent),t):e;return t.set(e,s),s}getTrackedDisposables(){const e=new Map;return[...this.livingDisposables.entries()].filter((([,t])=>null!==t.source&&!this.getRootParent(t,e).isSingleton)).flatMap((([e])=>e))}computeLeakingDisposables(e=10,t){let i;if(t)i=t;else{const e=new Map,t=[...this.livingDisposables.values()].filter((t=>null!==t.source&&!this.getRootParent(t,e).isSingleton));if(0===t.length)return;const s=new Set(t.map((e=>e.value)));if(i=t.filter((e=>!(e.parent&&s.has(e.parent)))),0===i.length)throw new Error("There are cyclic diposable chains!")}if(!i)return;function o(e){const t=e.source.split("\n").map((e=>e.trim().replace("at ",""))).filter((e=>""!==e));return function(e,t){for(;e.length>0&&t.some((t=>"string"==typeof t?t===e[0]:e[0].match(t)));)e.shift()}(t,["Error",/^trackDisposable \(.*\)$/,/^DisposableTracker.trackDisposable \(.*\)$/]),t.reverse()}const a=new n.SetMap;for(const e of i){const t=o(e);for(let i=0;i<=t.length;i++)a.add(t.slice(0,i).join("\n"),e)}i.sort((0,s.compareBy)((e=>e.idx),s.numberComparator));let l="",h=0;for(const t of i.slice(0,e)){h++;const e=o(t),s=[];for(let t=0;t<e.length;t++){let n=e[t];n=`(shared with ${a.get(e.slice(0,t+1).join("\n")).size}/${i.length} leaks) at ${n}`;const l=a.get(e.slice(0,t).join("\n")),h=(0,r.groupBy)([...l].map((e=>o(e)[t])),(e=>e));delete h[e[t]];for(const[e,t]of Object.entries(h))s.unshift(` - stacktraces of ${t.length} other leaks continue with ${e}`);s.unshift(n)}l+=`\n\n\n==================== Leaking disposable ${h}/${i.length}: ${t.value.constructor.name} ====================\n${s.join("\n")}\n============================================================\n\n`}return i.length>e&&(l+=`\n\n\n... and ${i.length-e} more leaking disposables\n\n`),{leaks:i,details:l}}}function c(e){return l?.trackDisposable(e),e}function d(e){l?.markAsDisposed(e)}function u(e,t){l?.setParent(e,t)}function _(e){return"object"==typeof e&&null!==e&&"function"==typeof e.dispose&&0===e.dispose.length}function f(e){if(a.Iterable.is(e)){const t=[];for(const i of e)if(i)try{i.dispose()}catch(e){t.push(e)}if(1===t.length)throw t[0];if(t.length>1)throw new AggregateError(t,"Encountered errors while disposing of store");return Array.isArray(e)?[]:e}if(e)return e.dispose(),e}function p(e){const t=c({dispose:(0,o.createSingleCallFunction)((()=>{d(t),e()}))});return t}t.DisposableTracker=h;class g{static{this.DISABLE_DISPOSED_WARNING=!1}constructor(){this._toDispose=new Set,this._isDisposed=!1,c(this)}dispose(){this._isDisposed||(d(this),this._isDisposed=!0,this.clear())}get isDisposed(){return this._isDisposed}clear(){if(0!==this._toDispose.size)try{f(this._toDispose)}finally{this._toDispose.clear()}}add(e){if(!e)return e;if(e===this)throw new Error("Cannot register a disposable on itself!");return u(e,this),this._isDisposed?g.DISABLE_DISPOSED_WARNING||console.warn(new Error("Trying to add a disposable to a DisposableStore that has already been disposed of. The added object will be leaked!").stack):this._toDispose.add(e),e}delete(e){if(e){if(e===this)throw new Error("Cannot dispose a disposable on itself!");this._toDispose.delete(e),e.dispose()}}deleteAndLeak(e){e&&this._toDispose.has(e)&&(this._toDispose.delete(e),u(e,null))}}t.DisposableStore=g;class m{static{this.None=Object.freeze({dispose(){}})}constructor(){this._store=new g,c(this),u(this._store,this)}dispose(){d(this),this._store.dispose()}_register(e){if(e===this)throw new Error("Cannot register a disposable on itself!");return this._store.add(e)}}t.Disposable=m;class v{constructor(){this._isDisposed=!1,c(this)}get value(){return this._isDisposed?void 0:this._value}set value(e){this._isDisposed||e===this._value||(this._value?.dispose(),e&&u(e,this),this._value=e)}clear(){this.value=void 0}dispose(){this._isDisposed=!0,d(this),this._value?.dispose(),this._value=void 0}clearAndLeak(){const e=this._value;return this._value=void 0,e&&u(e,null),e}}t.MutableDisposable=v,t.MandatoryMutableDisposable=class{constructor(e){this._disposable=new v,this._isDisposed=!1,this._disposable.value=e}get value(){return this._disposable.value}set value(e){this._isDisposed||e===this._disposable.value||(this._disposable.value=e)}dispose(){this._isDisposed=!0,this._disposable.dispose()}},t.RefCountedDisposable=class{constructor(e){this._disposable=e,this._counter=1}acquire(){return this._counter++,this}release(){return 0==--this._counter&&this._disposable.dispose(),this}},t.SafeDisposable=class{constructor(){this.dispose=()=>{},this.unset=()=>{},this.isset=()=>!1,c(this)}set(e){let t=e;return this.unset=()=>t=void 0,this.isset=()=>void 0!==t,this.dispose=()=>{t&&(t(),t=void 0,d(this))},this}},t.ReferenceCollection=class{constructor(){this.references=new Map}acquire(e,...t){let i=this.references.get(e);i||(i={counter:0,object:this.createReferencedObject(e,...t)},this.references.set(e,i));const{object:s}=i,r=(0,o.createSingleCallFunction)((()=>{0==--i.counter&&(this.destroyReferencedObject(e,i.object),this.references.delete(e))}));return i.counter++,{object:s,dispose:r}}},t.AsyncReferenceCollection=class{constructor(e){this.referenceCollection=e}async acquire(e,...t){const i=this.referenceCollection.acquire(e,...t);try{return{object:await i.object,dispose:()=>i.dispose()}}catch(e){throw i.dispose(),e}}},t.ImmortalReference=class{constructor(e){this.object=e}dispose(){}};class S{constructor(){this._store=new Map,this._isDisposed=!1,c(this)}dispose(){d(this),this._isDisposed=!0,this.clearAndDisposeAll()}clearAndDisposeAll(){if(this._store.size)try{f(this._store.values())}finally{this._store.clear()}}has(e){return this._store.has(e)}get size(){return this._store.size}get(e){return this._store.get(e)}set(e,t,i=!1){this._isDisposed&&console.warn(new Error("Trying to add a disposable to a DisposableMap that has already been disposed of. The added object will be leaked!").stack),i||this._store.get(e)?.dispose(),this._store.set(e,t)}deleteAndDispose(e){this._store.get(e)?.dispose(),this._store.delete(e)}deleteAndLeak(e){const t=this._store.get(e);return this._store.delete(e),t}keys(){return this._store.keys()}values(){return this._store.values()}[Symbol.iterator](){return this._store[Symbol.iterator]()}}t.DisposableMap=S},6317:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.LinkedList=void 0;class i{static{this.Undefined=new i(void 0)}constructor(e){this.element=e,this.next=i.Undefined,this.prev=i.Undefined}}class s{constructor(){this._first=i.Undefined,this._last=i.Undefined,this._size=0}get size(){return this._size}isEmpty(){return this._first===i.Undefined}clear(){let e=this._first;for(;e!==i.Undefined;){const t=e.next;e.prev=i.Undefined,e.next=i.Undefined,e=t}this._first=i.Undefined,this._last=i.Undefined,this._size=0}unshift(e){return this._insert(e,!1)}push(e){return this._insert(e,!0)}_insert(e,t){const s=new i(e);if(this._first===i.Undefined)this._first=s,this._last=s;else if(t){const e=this._last;this._last=s,s.prev=e,e.next=s}else{const e=this._first;this._first=s,s.next=e,e.prev=s}this._size+=1;let r=!1;return()=>{r||(r=!0,this._remove(s))}}shift(){if(this._first!==i.Undefined){const e=this._first.element;return this._remove(this._first),e}}pop(){if(this._last!==i.Undefined){const e=this._last.element;return this._remove(this._last),e}}_remove(e){if(e.prev!==i.Undefined&&e.next!==i.Undefined){const t=e.prev;t.next=e.next,e.next.prev=t}else e.prev===i.Undefined&&e.next===i.Undefined?(this._first=i.Undefined,this._last=i.Undefined):e.next===i.Undefined?(this._last=this._last.prev,this._last.next=i.Undefined):e.prev===i.Undefined&&(this._first=this._first.next,this._first.prev=i.Undefined);this._size-=1}*[Symbol.iterator](){let e=this._first;for(;e!==i.Undefined;)yield e.element,e=e.next}}t.LinkedList=s},2608:(e,t)=>{var i;Object.defineProperty(t,"__esModule",{value:!0}),t.SetMap=t.BidirectionalMap=t.CounterSet=t.Touch=void 0,t.getOrSet=function(e,t,i){let s=e.get(t);return void 0===s&&(s=i,e.set(t,s)),s},t.mapToString=function(e){const t=[];return e.forEach(((e,i)=>{t.push(`${i} => ${e}`)})),`Map(${e.size}) {${t.join(", ")}}`},t.setToString=function(e){const t=[];return e.forEach((e=>{t.push(e)})),`Set(${e.size}) {${t.join(", ")}}`},t.mapsStrictEqualIgnoreOrder=function(e,t){if(e===t)return!0;if(e.size!==t.size)return!1;for(const[i,s]of e)if(!t.has(i)||t.get(i)!==s)return!1;for(const[i]of t)if(!e.has(i))return!1;return!0},function(e){e[e.None=0]="None",e[e.AsOld=1]="AsOld",e[e.AsNew=2]="AsNew"}(i||(t.Touch=i={})),t.CounterSet=class{constructor(){this.map=new Map}add(e){return this.map.set(e,(this.map.get(e)||0)+1),this}delete(e){let t=this.map.get(e)||0;return 0!==t&&(t--,0===t?this.map.delete(e):this.map.set(e,t),!0)}has(e){return this.map.has(e)}},t.BidirectionalMap=class{constructor(e){if(this._m1=new Map,this._m2=new Map,e)for(const[t,i]of e)this.set(t,i)}clear(){this._m1.clear(),this._m2.clear()}set(e,t){this._m1.set(e,t),this._m2.set(t,e)}get(e){return this._m1.get(e)}getKey(e){return this._m2.get(e)}delete(e){const t=this._m1.get(e);return void 0!==t&&(this._m1.delete(e),this._m2.delete(t),!0)}forEach(e,t){this._m1.forEach(((i,s)=>{e.call(t,i,s,this)}))}keys(){return this._m1.keys()}values(){return this._m1.values()}},t.SetMap=class{constructor(){this.map=new Map}add(e,t){let i=this.map.get(e);i||(i=new Set,this.map.set(e,i)),i.add(t)}delete(e,t){const i=this.map.get(e);i&&(i.delete(t),0===i.size&&this.map.delete(e))}forEach(e,t){const i=this.map.get(e);i&&i.forEach(t)}get(e){return this.map.get(e)||new Set}}},7704:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.SlidingWindowAverage=t.MovingAverage=t.Counter=void 0,t.clamp=function(e,t,i){return Math.min(Math.max(e,t),i)},t.rot=function(e,t){return(t+e%t)%t},t.isPointWithinTriangle=function(e,t,i,s,r,n,o,a){const l=o-i,h=a-s,c=r-i,d=n-s,u=e-i,_=t-s,f=l*l+h*h,p=l*c+h*d,g=l*u+h*_,m=c*c+d*d,v=c*u+d*_,S=1/(f*m-p*p),b=(m*g-p*v)*S,C=(f*v-p*g)*S;return b>=0&&C>=0&&b+C<1},t.Counter=class{constructor(){this._next=0}getNext(){return this._next++}},t.MovingAverage=class{constructor(){this._n=1,this._val=0}update(e){return this._val=this._val+(e-this._val)/this._n,this._n+=1,this._val}get value(){return this._val}},t.SlidingWindowAverage=class{constructor(e){this._n=0,this._val=0,this._values=[],this._index=0,this._sum=0,this._values=new Array(e),this._values.fill(0,0,e)}update(e){const t=this._values[this._index];return this._values[this._index]=e,this._index=(this._index+1)%this._values.length,this._sum-=t,this._sum+=e,this._n<this._values.length&&(this._n+=1),this._val=this._sum/this._n,this._val}get value(){return this._val}}},8163:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.isAndroid=t.isEdge=t.isSafari=t.isFirefox=t.isChrome=t.OS=t.OperatingSystem=t.setTimeout0=t.setTimeout0IsFaster=t.translationsConfigFile=t.platformLocale=t.locale=t.Language=t.language=t.userAgent=t.platform=t.isCI=t.isMobile=t.isIOS=t.webWorkerOrigin=t.isWebWorker=t.isWeb=t.isElectron=t.isNative=t.isLinuxSnap=t.isLinux=t.isMacintosh=t.isWindows=t.Platform=t.LANGUAGE_DEFAULT=void 0,t.PlatformToString=function(e){switch(e){case C.Web:return"Web";case C.Mac:return"Mac";case C.Linux:return"Linux";case C.Windows:return"Windows"}},t.isLittleEndian=function(){if(!L){L=!0;const e=new Uint8Array(2);e[0]=1,e[1]=2;const t=new Uint16Array(e.buffer);D=513===t[0]}return D},t.isBigSurOrNewer=function(e){return parseFloat(e)>=20},t.LANGUAGE_DEFAULT="en";let i,s,r,n=!1,o=!1,a=!1,l=!1,h=!1,c=!1,d=!1,u=!1,_=!1,f=!1,p=t.LANGUAGE_DEFAULT,g=t.LANGUAGE_DEFAULT;const m=globalThis;let v;void 0!==m.vscode&&void 0!==m.vscode.process?v=m.vscode.process:"undefined"!=typeof process&&"string"==typeof process?.versions?.node&&(v=process);const S="string"==typeof v?.versions?.electron,b=S&&"renderer"===v?.type;if("object"==typeof v){n="win32"===v.platform,o="darwin"===v.platform,a="linux"===v.platform,l=a&&!!v.env.SNAP&&!!v.env.SNAP_REVISION,d=S,_=!!v.env.CI||!!v.env.BUILD_ARTIFACTSTAGINGDIRECTORY,i=t.LANGUAGE_DEFAULT,p=t.LANGUAGE_DEFAULT;const e=v.env.VSCODE_NLS_CONFIG;if(e)try{const r=JSON.parse(e);i=r.userLocale,g=r.osLocale,p=r.resolvedLanguage||t.LANGUAGE_DEFAULT,s=r.languagePack?.translationsConfigFile}catch(e){}h=!0}else"object"!=typeof navigator||b?console.error("Unable to resolve platform."):(r=navigator.userAgent,n=r.indexOf("Windows")>=0,o=r.indexOf("Macintosh")>=0,u=(r.indexOf("Macintosh")>=0||r.indexOf("iPad")>=0||r.indexOf("iPhone")>=0)&&!!navigator.maxTouchPoints&&navigator.maxTouchPoints>0,a=r.indexOf("Linux")>=0,f=r?.indexOf("Mobi")>=0,c=!0,p=globalThis._VSCODE_NLS_LANGUAGE||t.LANGUAGE_DEFAULT,i=navigator.language.toLowerCase(),g=i);var C;!function(e){e[e.Web=0]="Web",e[e.Mac=1]="Mac",e[e.Linux=2]="Linux",e[e.Windows=3]="Windows"}(C||(t.Platform=C={}));let y=C.Web;var w,E;o?y=C.Mac:n?y=C.Windows:a&&(y=C.Linux),t.isWindows=n,t.isMacintosh=o,t.isLinux=a,t.isLinuxSnap=l,t.isNative=h,t.isElectron=d,t.isWeb=c,t.isWebWorker=c&&"function"==typeof m.importScripts,t.webWorkerOrigin=t.isWebWorker?m.origin:void 0,t.isIOS=u,t.isMobile=f,t.isCI=_,t.platform=y,t.userAgent=r,t.language=p,function(e){e.value=function(){return t.language},e.isDefaultVariant=function(){return 2===t.language.length?"en"===t.language:t.language.length>=3&&"e"===t.language[0]&&"n"===t.language[1]&&"-"===t.language[2]},e.isDefault=function(){return"en"===t.language}}(w||(t.Language=w={})),t.locale=i,t.platformLocale=g,t.translationsConfigFile=s,t.setTimeout0IsFaster="function"==typeof m.postMessage&&!m.importScripts,t.setTimeout0=(()=>{if(t.setTimeout0IsFaster){const e=[];m.addEventListener("message",(t=>{if(t.data&&t.data.vscodeScheduleAsyncWork)for(let i=0,s=e.length;i<s;i++){const s=e[i];if(s.id===t.data.vscodeScheduleAsyncWork)return e.splice(i,1),void s.callback()}}));let t=0;return i=>{const s=++t;e.push({id:s,callback:i}),m.postMessage({vscodeScheduleAsyncWork:s},"*")}}return e=>setTimeout(e)})(),function(e){e[e.Windows=1]="Windows",e[e.Macintosh=2]="Macintosh",e[e.Linux=3]="Linux"}(E||(t.OperatingSystem=E={})),t.OS=o||u?E.Macintosh:n?E.Windows:E.Linux;let D=!0,L=!1;t.isChrome=!!(t.userAgent&&t.userAgent.indexOf("Chrome")>=0),t.isFirefox=!!(t.userAgent&&t.userAgent.indexOf("Firefox")>=0),t.isSafari=!!(!t.isChrome&&t.userAgent&&t.userAgent.indexOf("Safari")>=0),t.isEdge=!!(t.userAgent&&t.userAgent.indexOf("Edg/")>=0),t.isAndroid=!!(t.userAgent&&t.userAgent.indexOf("Android")>=0)},9881:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.SmoothScrollingOperation=t.SmoothScrollingUpdate=t.Scrollable=t.ScrollState=t.ScrollbarVisibility=void 0;const s=i(802),r=i(7150);var n;!function(e){e[e.Auto=1]="Auto",e[e.Hidden=2]="Hidden",e[e.Visible=3]="Visible"}(n||(t.ScrollbarVisibility=n={}));class o{constructor(e,t,i,s,r,n,o){this._forceIntegerValues=e,this._scrollStateBrand=void 0,this._forceIntegerValues&&(t|=0,i|=0,s|=0,r|=0,n|=0,o|=0),this.rawScrollLeft=s,this.rawScrollTop=o,t<0&&(t=0),s+t>i&&(s=i-t),s<0&&(s=0),r<0&&(r=0),o+r>n&&(o=n-r),o<0&&(o=0),this.width=t,this.scrollWidth=i,this.scrollLeft=s,this.height=r,this.scrollHeight=n,this.scrollTop=o}equals(e){return this.rawScrollLeft===e.rawScrollLeft&&this.rawScrollTop===e.rawScrollTop&&this.width===e.width&&this.scrollWidth===e.scrollWidth&&this.scrollLeft===e.scrollLeft&&this.height===e.height&&this.scrollHeight===e.scrollHeight&&this.scrollTop===e.scrollTop}withScrollDimensions(e,t){return new o(this._forceIntegerValues,void 0!==e.width?e.width:this.width,void 0!==e.scrollWidth?e.scrollWidth:this.scrollWidth,t?this.rawScrollLeft:this.scrollLeft,void 0!==e.height?e.height:this.height,void 0!==e.scrollHeight?e.scrollHeight:this.scrollHeight,t?this.rawScrollTop:this.scrollTop)}withScrollPosition(e){return new o(this._forceIntegerValues,this.width,this.scrollWidth,void 0!==e.scrollLeft?e.scrollLeft:this.rawScrollLeft,this.height,this.scrollHeight,void 0!==e.scrollTop?e.scrollTop:this.rawScrollTop)}createScrollEvent(e,t){const i=this.width!==e.width,s=this.scrollWidth!==e.scrollWidth,r=this.scrollLeft!==e.scrollLeft,n=this.height!==e.height,o=this.scrollHeight!==e.scrollHeight,a=this.scrollTop!==e.scrollTop;return{inSmoothScrolling:t,oldWidth:e.width,oldScrollWidth:e.scrollWidth,oldScrollLeft:e.scrollLeft,width:this.width,scrollWidth:this.scrollWidth,scrollLeft:this.scrollLeft,oldHeight:e.height,oldScrollHeight:e.scrollHeight,oldScrollTop:e.scrollTop,height:this.height,scrollHeight:this.scrollHeight,scrollTop:this.scrollTop,widthChanged:i,scrollWidthChanged:s,scrollLeftChanged:r,heightChanged:n,scrollHeightChanged:o,scrollTopChanged:a}}}t.ScrollState=o;class a extends r.Disposable{constructor(e){super(),this._scrollableBrand=void 0,this._onScroll=this._register(new s.Emitter),this.onScroll=this._onScroll.event,this._smoothScrollDuration=e.smoothScrollDuration,this._scheduleAtNextAnimationFrame=e.scheduleAtNextAnimationFrame,this._state=new o(e.forceIntegerValues,0,0,0,0,0,0),this._smoothScrolling=null}dispose(){this._smoothScrolling&&(this._smoothScrolling.dispose(),this._smoothScrolling=null),super.dispose()}setSmoothScrollDuration(e){this._smoothScrollDuration=e}validateScrollPosition(e){return this._state.withScrollPosition(e)}getScrollDimensions(){return this._state}setScrollDimensions(e,t){const i=this._state.withScrollDimensions(e,t);this._setState(i,Boolean(this._smoothScrolling)),this._smoothScrolling?.acceptScrollDimensions(this._state)}getFutureScrollPosition(){return this._smoothScrolling?this._smoothScrolling.to:this._state}getCurrentScrollPosition(){return this._state}setScrollPositionNow(e){const t=this._state.withScrollPosition(e);this._smoothScrolling&&(this._smoothScrolling.dispose(),this._smoothScrolling=null),this._setState(t,!1)}setScrollPositionSmooth(e,t){if(0===this._smoothScrollDuration)return this.setScrollPositionNow(e);if(this._smoothScrolling){e={scrollLeft:void 0===e.scrollLeft?this._smoothScrolling.to.scrollLeft:e.scrollLeft,scrollTop:void 0===e.scrollTop?this._smoothScrolling.to.scrollTop:e.scrollTop};const i=this._state.withScrollPosition(e);if(this._smoothScrolling.to.scrollLeft===i.scrollLeft&&this._smoothScrolling.to.scrollTop===i.scrollTop)return;let s;s=t?new c(this._smoothScrolling.from,i,this._smoothScrolling.startTime,this._smoothScrolling.duration):this._smoothScrolling.combine(this._state,i,this._smoothScrollDuration),this._smoothScrolling.dispose(),this._smoothScrolling=s}else{const t=this._state.withScrollPosition(e);this._smoothScrolling=c.start(this._state,t,this._smoothScrollDuration)}this._smoothScrolling.animationFrameDisposable=this._scheduleAtNextAnimationFrame((()=>{this._smoothScrolling&&(this._smoothScrolling.animationFrameDisposable=null,this._performSmoothScrolling())}))}hasPendingScrollAnimation(){return Boolean(this._smoothScrolling)}_performSmoothScrolling(){if(!this._smoothScrolling)return;const e=this._smoothScrolling.tick(),t=this._state.withScrollPosition(e);return this._setState(t,!0),this._smoothScrolling?e.isDone?(this._smoothScrolling.dispose(),void(this._smoothScrolling=null)):void(this._smoothScrolling.animationFrameDisposable=this._scheduleAtNextAnimationFrame((()=>{this._smoothScrolling&&(this._smoothScrolling.animationFrameDisposable=null,this._performSmoothScrolling())}))):void 0}_setState(e,t){const i=this._state;i.equals(e)||(this._state=e,this._onScroll.fire(this._state.createScrollEvent(i,t)))}}t.Scrollable=a;class l{constructor(e,t,i){this.scrollLeft=e,this.scrollTop=t,this.isDone=i}}function h(e,t){const i=t-e;return function(t){return e+i*(1-(s=1-t,Math.pow(s,3)));var s}}t.SmoothScrollingUpdate=l;class c{constructor(e,t,i,s){this.from=e,this.to=t,this.duration=s,this.startTime=i,this.animationFrameDisposable=null,this._initAnimations()}_initAnimations(){this.scrollLeft=this._initAnimation(this.from.scrollLeft,this.to.scrollLeft,this.to.width),this.scrollTop=this._initAnimation(this.from.scrollTop,this.to.scrollTop,this.to.height)}_initAnimation(e,t,i){if(Math.abs(e-t)>2.5*i){let o,a;return e<t?(o=e+.75*i,a=t-.75*i):(o=e-.75*i,a=t+.75*i),s=h(e,o),r=h(a,t),n=.33,function(e){return e<n?s(e/n):r((e-n)/(1-n))}}var s,r,n;return h(e,t)}dispose(){null!==this.animationFrameDisposable&&(this.animationFrameDisposable.dispose(),this.animationFrameDisposable=null)}acceptScrollDimensions(e){this.to=e.withScrollPosition(this.to),this._initAnimations()}tick(){return this._tick(Date.now())}_tick(e){const t=(e-this.startTime)/this.duration;if(t<1){const e=this.scrollLeft(t),i=this.scrollTop(t);return new l(e,i,!1)}return new l(this.to.scrollLeft,this.to.scrollTop,!0)}combine(e,t,i){return c.start(e,t,i)}static start(e,t,i){i+=10;const s=Date.now()-10;return new c(e,t,s,i)}}t.SmoothScrollingOperation=c},9725:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.StopWatch=void 0;const i=globalThis.performance&&"function"==typeof globalThis.performance.now;class s{static create(e){return new s(e)}constructor(e){this._now=i&&!1===e?Date.now:globalThis.performance.now.bind(globalThis.performance),this._startTime=this._now(),this._stopTime=-1}stop(){this._stopTime=this._now()}reset(){this._startTime=this._now(),this._stopTime=-1}elapsed(){return-1!==this._stopTime?this._stopTime-this._startTime:this._now()-this._startTime}}t.StopWatch=s},1316:(e,t,i)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.noBreakWhitespace=t.CodePointIterator=void 0,t.isFalsyOrWhitespace=function(e){return!e||"string"!=typeof e||0===e.trim().length},t.format=function(e,...t){return 0===t.length?e:e.replace(n,(function(e,i){const s=parseInt(i,10);return isNaN(s)||s<0||s>=t.length?e:t[s]}))},t.format2=function(e,t){return 0===Object.keys(t).length?e:e.replace(o,((e,i)=>t[i]??e))},t.htmlAttributeEncodeValue=function(e){return e.replace(/[<>"'&]/g,(e=>{switch(e){case"<":return"&lt;";case">":return"&gt;";case'"':return"&quot;";case"'":return"&apos;";case"&":return"&amp;"}return e}))},t.escape=function(e){return e.replace(/[<>&]/g,(function(e){switch(e){case"<":return"&lt;";case">":return"&gt;";case"&":return"&amp;";default:return e}}))},t.escapeRegExpCharacters=a,t.count=function(e,t){let i=0,s=e.indexOf(t);for(;-1!==s;)i++,s=e.indexOf(t,s+t.length);return i},t.truncate=function(e,t,i="…"){return e.length<=t?e:`${e.substr(0,t)}${i}`},t.truncateMiddle=function(e,t,i="…"){if(e.length<=t)return e;const s=Math.ceil(t/2)-i.length/2,r=Math.floor(t/2)-i.length/2;return`${e.substr(0,s)}${i}${e.substr(e.length-r)}`},t.trim=function(e,t=" "){return h(l(e,t),t)},t.ltrim=l,t.rtrim=h,t.convertSimple2RegExpPattern=function(e){return e.replace(/[\-\\\{\}\+\?\|\^\$\.\,\[\]\(\)\#\s]/g,"\\$&").replace(/[\*]/g,".*")},t.stripWildcards=function(e){return e.replace(/\*/g,"")},t.createRegExp=function(e,t,i={}){if(!e)throw new Error("Cannot create regex from empty string");t||(e=a(e)),i.wholeWord&&(/\B/.test(e.charAt(0))||(e="\\b"+e),/\B/.test(e.charAt(e.length-1))||(e+="\\b"));let s="";return i.global&&(s+="g"),i.matchCase||(s+="i"),i.multiline&&(s+="m"),i.unicode&&(s+="u"),new RegExp(e,s)},t.regExpLeadsToEndlessLoop=function(e){return"^"!==e.source&&"^$"!==e.source&&"$"!==e.source&&"^\\s*$"!==e.source&&!(!e.exec("")||0!==e.lastIndex)},t.splitLines=function(e){return e.split(/\r\n|\r|\n/)},t.splitLinesIncludeSeparators=function(e){const t=[],i=e.split(/(\r\n|\r|\n)/);for(let e=0;e<Math.ceil(i.length/2);e++)t.push(i[2*e]+(i[2*e+1]??""));return t},t.firstNonWhitespaceIndex=function(e){for(let t=0,i=e.length;t<i;t++){const i=e.charCodeAt(t);if(i!==s.CharCode.Space&&i!==s.CharCode.Tab)return t}return-1},t.getLeadingWhitespace=function(e,t=0,i=e.length){for(let r=t;r<i;r++){const i=e.charCodeAt(r);if(i!==s.CharCode.Space&&i!==s.CharCode.Tab)return e.substring(t,r)}return e.substring(t,i)},t.lastNonWhitespaceIndex=function(e,t=e.length-1){for(let i=t;i>=0;i--){const t=e.charCodeAt(i);if(t!==s.CharCode.Space&&t!==s.CharCode.Tab)return i}return-1},t.replaceAsync=function(e,t,i){const s=[];let r=0;for(const n of e.matchAll(t)){if(s.push(e.slice(r,n.index)),void 0===n.index)throw new Error("match.index should be defined");r=n.index+n[0].length,s.push(i(n[0],...n.slice(1),n.index,e,n.groups))}return s.push(e.slice(r)),Promise.all(s).then((e=>e.join("")))},t.compare=function(e,t){return e<t?-1:e>t?1:0},t.compareSubstring=c,t.compareIgnoreCase=function(e,t){return d(e,t,0,e.length,0,t.length)},t.compareSubstringIgnoreCase=d,t.isAsciiDigit=function(e){return e>=s.CharCode.Digit0&&e<=s.CharCode.Digit9},t.isLowerAsciiLetter=u,t.isUpperAsciiLetter=function(e){return e>=s.CharCode.A&&e<=s.CharCode.Z},t.equalsIgnoreCase=function(e,t){return e.length===t.length&&0===d(e,t)},t.startsWithIgnoreCase=function(e,t){const i=t.length;return!(t.length>e.length)&&0===d(e,t,0,i)},t.commonPrefixLength=function(e,t){const i=Math.min(e.length,t.length);let s;for(s=0;s<i;s++)if(e.charCodeAt(s)!==t.charCodeAt(s))return s;return i},t.commonSuffixLength=function(e,t){const i=Math.min(e.length,t.length);let s;const r=e.length-1,n=t.length-1;for(s=0;s<i;s++)if(e.charCodeAt(r-s)!==t.charCodeAt(n-s))return s;return i},t.isHighSurrogate=_,t.isLowSurrogate=f,t.computeCodePoint=p,t.getNextCodePoint=g;const s=i(4869),r=i(8960),n=/{(\d+)}/g,o=/{([^}]+)}/g;function a(e){return e.replace(/[\\\{\}\*\+\?\|\^\$\.\[\]\(\)]/g,"\\$&")}function l(e,t){if(!e||!t)return e;const i=t.length;if(0===i||0===e.length)return e;let s=0;for(;e.indexOf(t,s)===s;)s+=i;return e.substring(s)}function h(e,t){if(!e||!t)return e;const i=t.length,s=e.length;if(0===i||0===s)return e;let r=s,n=-1;for(;n=e.lastIndexOf(t,r-1),-1!==n&&n+i===r;){if(0===n)return"";r=n}return e.substring(0,r)}function c(e,t,i=0,s=e.length,r=0,n=t.length){for(;i<s&&r<n;i++,r++){const s=e.charCodeAt(i),n=t.charCodeAt(r);if(s<n)return-1;if(s>n)return 1}const o=s-i,a=n-r;return o<a?-1:o>a?1:0}function d(e,t,i=0,s=e.length,r=0,n=t.length){for(;i<s&&r<n;i++,r++){let o=e.charCodeAt(i),a=t.charCodeAt(r);if(o===a)continue;if(o>=128||a>=128)return c(e.toLowerCase(),t.toLowerCase(),i,s,r,n);u(o)&&(o-=32),u(a)&&(a-=32);const l=o-a;if(0!==l)return l}const o=s-i,a=n-r;return o<a?-1:o>a?1:0}function u(e){return e>=s.CharCode.a&&e<=s.CharCode.z}function _(e){return 55296<=e&&e<=56319}function f(e){return 56320<=e&&e<=57343}function p(e,t){return t-56320+(e-55296<<10)+65536}function g(e,t,i){const s=e.charCodeAt(i);if(_(s)&&i+1<t){const t=e.charCodeAt(i+1);if(f(t))return p(s,t)}return s}t.CodePointIterator=class{get offset(){return this._offset}constructor(e,t=0){this._str=e,this._len=e.length,this._offset=t}setOffset(e){this._offset=e}prevCodePoint(){const e=function(e,t){const i=e.charCodeAt(t-1);if(f(i)&&t>1){const s=e.charCodeAt(t-2);if(_(s))return p(s,i)}return i}(this._str,this._offset);return this._offset-=e>=r.Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN?2:1,e}nextCodePoint(){const e=g(this._str,this._len,this._offset);return this._offset+=e>=r.Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN?2:1,e}eol(){return this._offset>=this._len}},t.noBreakWhitespace=" "},5015:(e,t)=>{Object.defineProperty(t,"__esModule",{value:!0}),t.MicrotaskDelay=void 0,t.MicrotaskDelay=Symbol("MicrotaskDelay")},8960:(e,t)=>{var i;Object.defineProperty(t,"__esModule",{value:!0}),t.Constants=void 0,t.toUint8=function(e){return e<0?0:e>i.MAX_UINT_8?i.MAX_UINT_8:0|e},t.toUint32=function(e){return e<0?0:e>i.MAX_UINT_32?i.MAX_UINT_32:0|e},function(e){e[e.MAX_SAFE_SMALL_INTEGER=1073741824]="MAX_SAFE_SMALL_INTEGER",e[e.MIN_SAFE_SMALL_INTEGER=-1073741824]="MIN_SAFE_SMALL_INTEGER",e[e.MAX_UINT_8=255]="MAX_UINT_8",e[e.MAX_UINT_16=65535]="MAX_UINT_16",e[e.MAX_UINT_32=4294967295]="MAX_UINT_32",e[e.UNICODE_SUPPLEMENTARY_PLANE_BEGIN=65536]="UNICODE_SUPPLEMENTARY_PLANE_BEGIN"}(i||(t.Constants=i={}))}},t={};function i(s){var r=t[s];if(void 0!==r)return r.exports;var n=t[s]={exports:{}};return e[s].call(n.exports,n,n.exports,i),n.exports}var s={};return(()=>{var e=s;Object.defineProperty(e,"__esModule",{value:!0}),e.Terminal=void 0;const t=i(7721),r=i(1718),n=i(7150),o=i(3027),a=i(5101),l=i(6097),h=i(4335),c=["cols","rows"];let d=0;class u extends n.Disposable{constructor(e){super(),this._core=this._register(new r.CoreBrowserTerminal(e)),this._addonManager=this._register(new o.AddonManager),this._publicOptions={...this._core.options};const t=e=>this._core.options[e],i=(e,t)=>{this._checkReadonlyOptions(e),this._core.options[e]=t};for(const e in this._core.options){const s={get:t.bind(this,e),set:i.bind(this,e)};Object.defineProperty(this._publicOptions,e,s)}}_checkReadonlyOptions(e){if(c.includes(e))throw new Error(`Option "${e}" can only be set in the constructor`)}_checkProposedApi(){if(!this._core.optionsService.rawOptions.allowProposedApi)throw new Error("You must set the allowProposedApi option to true to use proposed API")}get onBell(){return this._core.onBell}get onBinary(){return this._core.onBinary}get onCursorMove(){return this._core.onCursorMove}get onData(){return this._core.onData}get onKey(){return this._core.onKey}get onLineFeed(){return this._core.onLineFeed}get onRender(){return this._core.onRender}get onResize(){return this._core.onResize}get onScroll(){return this._core.onScroll}get onSelectionChange(){return this._core.onSelectionChange}get onTitleChange(){return this._core.onTitleChange}get onWriteParsed(){return this._core.onWriteParsed}get element(){return this._core.element}get parser(){return this._parser||(this._parser=new l.ParserApi(this._core)),this._parser}get unicode(){return this._checkProposedApi(),new h.UnicodeApi(this._core)}get textarea(){return this._core.textarea}get rows(){return this._core.rows}get cols(){return this._core.cols}get buffer(){return this._buffer||(this._buffer=this._register(new a.BufferNamespaceApi(this._core))),this._buffer}get markers(){return this._checkProposedApi(),this._core.markers}get modes(){const e=this._core.coreService.decPrivateModes;let t="none";switch(this._core.coreMouseService.activeProtocol){case"X10":t="x10";break;case"VT200":t="vt200";break;case"DRAG":t="drag";break;case"ANY":t="any"}return{applicationCursorKeysMode:e.applicationCursorKeys,applicationKeypadMode:e.applicationKeypad,bracketedPasteMode:e.bracketedPasteMode,insertMode:this._core.coreService.modes.insertMode,mouseTrackingMode:t,originMode:e.origin,reverseWraparoundMode:e.reverseWraparound,sendFocusMode:e.sendFocus,synchronizedOutputMode:e.synchronizedOutput,wraparoundMode:e.wraparound}}get options(){return this._publicOptions}set options(e){for(const t in e)this._publicOptions[t]=e[t]}blur(){this._core.blur()}focus(){this._core.focus()}input(e,t=!0){this._core.input(e,t)}resize(e,t){this._verifyIntegers(e,t),this._core.resize(e,t)}open(e){this._core.open(e)}attachCustomKeyEventHandler(e){this._core.attachCustomKeyEventHandler(e)}attachCustomWheelEventHandler(e){this._core.attachCustomWheelEventHandler(e)}registerLinkProvider(e){return this._core.registerLinkProvider(e)}registerCharacterJoiner(e){return this._checkProposedApi(),this._core.registerCharacterJoiner(e)}deregisterCharacterJoiner(e){this._checkProposedApi(),this._core.deregisterCharacterJoiner(e)}registerMarker(e=0){return this._verifyIntegers(e),this._core.registerMarker(e)}registerDecoration(e){return this._checkProposedApi(),this._verifyPositiveIntegers(e.x??0,e.width??0,e.height??0),this._core.registerDecoration(e)}hasSelection(){return this._core.hasSelection()}select(e,t,i){this._verifyIntegers(e,t,i),this._core.select(e,t,i)}getSelection(){return this._core.getSelection()}getSelectionPosition(){return this._core.getSelectionPosition()}clearSelection(){this._core.clearSelection()}selectAll(){this._core.selectAll()}selectLines(e,t){this._verifyIntegers(e,t),this._core.selectLines(e,t)}dispose(){super.dispose()}scrollLines(e){this._verifyIntegers(e),this._core.scrollLines(e)}scrollPages(e){this._verifyIntegers(e),this._core.scrollPages(e)}scrollToTop(){this._core.scrollToTop()}scrollToBottom(){this._core.scrollToBottom()}scrollToLine(e){this._verifyIntegers(e),this._core.scrollToLine(e)}clear(){this._core.clear()}write(e,t){this._core.write(e,t)}writeln(e,t){this._core.write(e),this._core.write("\r\n",t)}paste(e){this._core.paste(e)}refresh(e,t){this._verifyIntegers(e,t),this._core.refresh(e,t)}reset(){this._core.reset()}clearTextureAtlas(){this._core.clearTextureAtlas()}loadAddon(e){this._addonManager.loadAddon(this,e)}static get strings(){return{get promptLabel(){return t.promptLabel.get()},set promptLabel(e){t.promptLabel.set(e)},get tooMuchOutput(){return t.tooMuchOutput.get()},set tooMuchOutput(e){t.tooMuchOutput.set(e)}}}_verifyIntegers(...e){for(d of e)if(d===1/0||isNaN(d)||d%1!=0)throw new Error("This API only accepts integers")}_verifyPositiveIntegers(...e){for(d of e)if(d&&(d===1/0||isNaN(d)||d%1!=0||d<0))throw new Error("This API only accepts positive integers")}}e.Terminal=u})(),s})()));
2+//# sourceMappingURL=xterm.js.map
addedpackage-lock.json+391 −0
1+{
2+ "name": "browser-terminal-extension",
3+ "version": "1.0.0",
4+ "lockfileVersion": 3,
5+ "requires": true,
6+ "packages": {
7+ "": {
8+ "name": "browser-terminal-extension",
9+ "version": "1.0.0",
10+ "license": "ISC",
11+ "devDependencies": {
12+ "typescript": "^7.0.2"
13+ }
14+ },
15+ "node_modules/@typescript/typescript-aix-ppc64": {
16+ "version": "7.0.2",
17+ "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz",
18+ "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==",
19+ "cpu": [
20+ "ppc64"
21+ ],
22+ "dev": true,
23+ "license": "Apache-2.0",
24+ "optional": true,
25+ "os": [
26+ "aix"
27+ ],
28+ "engines": {
29+ "node": ">=16.20.0"
30+ }
31+ },
32+ "node_modules/@typescript/typescript-darwin-arm64": {
33+ "version": "7.0.2",
34+ "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz",
35+ "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==",
36+ "cpu": [
37+ "arm64"
38+ ],
39+ "dev": true,
40+ "license": "Apache-2.0",
41+ "optional": true,
42+ "os": [
43+ "darwin"
44+ ],
45+ "engines": {
46+ "node": ">=16.20.0"
47+ }
48+ },
49+ "node_modules/@typescript/typescript-darwin-x64": {
50+ "version": "7.0.2",
51+ "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz",
52+ "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==",
53+ "cpu": [
54+ "x64"
55+ ],
56+ "dev": true,
57+ "license": "Apache-2.0",
58+ "optional": true,
59+ "os": [
60+ "darwin"
61+ ],
62+ "engines": {
63+ "node": ">=16.20.0"
64+ }
65+ },
66+ "node_modules/@typescript/typescript-freebsd-arm64": {
67+ "version": "7.0.2",
68+ "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz",
69+ "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==",
70+ "cpu": [
71+ "arm64"
72+ ],
73+ "dev": true,
74+ "license": "Apache-2.0",
75+ "optional": true,
76+ "os": [
77+ "freebsd"
78+ ],
79+ "engines": {
80+ "node": ">=16.20.0"
81+ }
82+ },
83+ "node_modules/@typescript/typescript-freebsd-x64": {
84+ "version": "7.0.2",
85+ "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz",
86+ "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==",
87+ "cpu": [
88+ "x64"
89+ ],
90+ "dev": true,
91+ "license": "Apache-2.0",
92+ "optional": true,
93+ "os": [
94+ "freebsd"
95+ ],
96+ "engines": {
97+ "node": ">=16.20.0"
98+ }
99+ },
100+ "node_modules/@typescript/typescript-linux-arm": {
101+ "version": "7.0.2",
102+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz",
103+ "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==",
104+ "cpu": [
105+ "arm"
106+ ],
107+ "dev": true,
108+ "license": "Apache-2.0",
109+ "optional": true,
110+ "os": [
111+ "linux"
112+ ],
113+ "engines": {
114+ "node": ">=16.20.0"
115+ }
116+ },
117+ "node_modules/@typescript/typescript-linux-arm64": {
118+ "version": "7.0.2",
119+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz",
120+ "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==",
121+ "cpu": [
122+ "arm64"
123+ ],
124+ "dev": true,
125+ "license": "Apache-2.0",
126+ "optional": true,
127+ "os": [
128+ "linux"
129+ ],
130+ "engines": {
131+ "node": ">=16.20.0"
132+ }
133+ },
134+ "node_modules/@typescript/typescript-linux-loong64": {
135+ "version": "7.0.2",
136+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz",
137+ "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==",
138+ "cpu": [
139+ "loong64"
140+ ],
141+ "dev": true,
142+ "license": "Apache-2.0",
143+ "optional": true,
144+ "os": [
145+ "linux"
146+ ],
147+ "engines": {
148+ "node": ">=16.20.0"
149+ }
150+ },
151+ "node_modules/@typescript/typescript-linux-mips64el": {
152+ "version": "7.0.2",
153+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz",
154+ "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==",
155+ "cpu": [
156+ "mips64el"
157+ ],
158+ "dev": true,
159+ "license": "Apache-2.0",
160+ "optional": true,
161+ "os": [
162+ "linux"
163+ ],
164+ "engines": {
165+ "node": ">=16.20.0"
166+ }
167+ },
168+ "node_modules/@typescript/typescript-linux-ppc64": {
169+ "version": "7.0.2",
170+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz",
171+ "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==",
172+ "cpu": [
173+ "ppc64"
174+ ],
175+ "dev": true,
176+ "license": "Apache-2.0",
177+ "optional": true,
178+ "os": [
179+ "linux"
180+ ],
181+ "engines": {
182+ "node": ">=16.20.0"
183+ }
184+ },
185+ "node_modules/@typescript/typescript-linux-riscv64": {
186+ "version": "7.0.2",
187+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz",
188+ "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==",
189+ "cpu": [
190+ "riscv64"
191+ ],
192+ "dev": true,
193+ "license": "Apache-2.0",
194+ "optional": true,
195+ "os": [
196+ "linux"
197+ ],
198+ "engines": {
199+ "node": ">=16.20.0"
200+ }
201+ },
202+ "node_modules/@typescript/typescript-linux-s390x": {
203+ "version": "7.0.2",
204+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz",
205+ "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==",
206+ "cpu": [
207+ "s390x"
208+ ],
209+ "dev": true,
210+ "license": "Apache-2.0",
211+ "optional": true,
212+ "os": [
213+ "linux"
214+ ],
215+ "engines": {
216+ "node": ">=16.20.0"
217+ }
218+ },
219+ "node_modules/@typescript/typescript-linux-x64": {
220+ "version": "7.0.2",
221+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz",
222+ "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==",
223+ "cpu": [
224+ "x64"
225+ ],
226+ "dev": true,
227+ "license": "Apache-2.0",
228+ "optional": true,
229+ "os": [
230+ "linux"
231+ ],
232+ "engines": {
233+ "node": ">=16.20.0"
234+ }
235+ },
236+ "node_modules/@typescript/typescript-netbsd-arm64": {
237+ "version": "7.0.2",
238+ "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz",
239+ "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==",
240+ "cpu": [
241+ "arm64"
242+ ],
243+ "dev": true,
244+ "license": "Apache-2.0",
245+ "optional": true,
246+ "os": [
247+ "netbsd"
248+ ],
249+ "engines": {
250+ "node": ">=16.20.0"
251+ }
252+ },
253+ "node_modules/@typescript/typescript-netbsd-x64": {
254+ "version": "7.0.2",
255+ "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz",
256+ "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==",
257+ "cpu": [
258+ "x64"
259+ ],
260+ "dev": true,
261+ "license": "Apache-2.0",
262+ "optional": true,
263+ "os": [
264+ "netbsd"
265+ ],
266+ "engines": {
267+ "node": ">=16.20.0"
268+ }
269+ },
270+ "node_modules/@typescript/typescript-openbsd-arm64": {
271+ "version": "7.0.2",
272+ "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz",
273+ "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==",
274+ "cpu": [
275+ "arm64"
276+ ],
277+ "dev": true,
278+ "license": "Apache-2.0",
279+ "optional": true,
280+ "os": [
281+ "openbsd"
282+ ],
283+ "engines": {
284+ "node": ">=16.20.0"
285+ }
286+ },
287+ "node_modules/@typescript/typescript-openbsd-x64": {
288+ "version": "7.0.2",
289+ "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz",
290+ "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==",
291+ "cpu": [
292+ "x64"
293+ ],
294+ "dev": true,
295+ "license": "Apache-2.0",
296+ "optional": true,
297+ "os": [
298+ "openbsd"
299+ ],
300+ "engines": {
301+ "node": ">=16.20.0"
302+ }
303+ },
304+ "node_modules/@typescript/typescript-sunos-x64": {
305+ "version": "7.0.2",
306+ "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz",
307+ "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==",
308+ "cpu": [
309+ "x64"
310+ ],
311+ "dev": true,
312+ "license": "Apache-2.0",
313+ "optional": true,
314+ "os": [
315+ "sunos"
316+ ],
317+ "engines": {
318+ "node": ">=16.20.0"
319+ }
320+ },
321+ "node_modules/@typescript/typescript-win32-arm64": {
322+ "version": "7.0.2",
323+ "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz",
324+ "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==",
325+ "cpu": [
326+ "arm64"
327+ ],
328+ "dev": true,
329+ "license": "Apache-2.0",
330+ "optional": true,
331+ "os": [
332+ "win32"
333+ ],
334+ "engines": {
335+ "node": ">=16.20.0"
336+ }
337+ },
338+ "node_modules/@typescript/typescript-win32-x64": {
339+ "version": "7.0.2",
340+ "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz",
341+ "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==",
342+ "cpu": [
343+ "x64"
344+ ],
345+ "dev": true,
346+ "license": "Apache-2.0",
347+ "optional": true,
348+ "os": [
349+ "win32"
350+ ],
351+ "engines": {
352+ "node": ">=16.20.0"
353+ }
354+ },
355+ "node_modules/typescript": {
356+ "version": "7.0.2",
357+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz",
358+ "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==",
359+ "dev": true,
360+ "license": "Apache-2.0",
361+ "bin": {
362+ "tsc": "bin/tsc"
363+ },
364+ "engines": {
365+ "node": ">=16.20.0"
366+ },
367+ "optionalDependencies": {
368+ "@typescript/typescript-aix-ppc64": "7.0.2",
369+ "@typescript/typescript-darwin-arm64": "7.0.2",
370+ "@typescript/typescript-darwin-x64": "7.0.2",
371+ "@typescript/typescript-freebsd-arm64": "7.0.2",
372+ "@typescript/typescript-freebsd-x64": "7.0.2",
373+ "@typescript/typescript-linux-arm": "7.0.2",
374+ "@typescript/typescript-linux-arm64": "7.0.2",
375+ "@typescript/typescript-linux-loong64": "7.0.2",
376+ "@typescript/typescript-linux-mips64el": "7.0.2",
377+ "@typescript/typescript-linux-ppc64": "7.0.2",
378+ "@typescript/typescript-linux-riscv64": "7.0.2",
379+ "@typescript/typescript-linux-s390x": "7.0.2",
380+ "@typescript/typescript-linux-x64": "7.0.2",
381+ "@typescript/typescript-netbsd-arm64": "7.0.2",
382+ "@typescript/typescript-netbsd-x64": "7.0.2",
383+ "@typescript/typescript-openbsd-arm64": "7.0.2",
384+ "@typescript/typescript-openbsd-x64": "7.0.2",
385+ "@typescript/typescript-sunos-x64": "7.0.2",
386+ "@typescript/typescript-win32-arm64": "7.0.2",
387+ "@typescript/typescript-win32-x64": "7.0.2"
388+ }
389+ }
390+ }
391+}
addedpackage.json+13 −0
1+{
2+ "name": "browser-terminal-extension",
3+ "description": "A tmux sidebar for Chrome and Firefox.",
4+ "private": true,
5+ "//": "The extension ships as plain files that the browser loads directly — npm is here for the type checker and nothing else. Nothing in node_modules reaches dist/.",
6+ "scripts": {
7+ "test": "node --test extension/lib/*.test.js",
8+ "check": "tsc -p extension/jsconfig.json && tsc -p extension/jsconfig.sw.json"
9+ },
10+ "devDependencies": {
11+ "typescript": "^7.0.2"
12+ }
13+}
addedrun.sh+16 −0
1+#!/usr/bin/env bash
2+# Build and run the termbridge daemon.
3+# ./run.sh serve on the default port
4+# ./run.sh --port 8080 pass flags through to `termbridge serve`
5+# ./run.sh token run any other subcommand instead of serve
6+set -euo pipefail
7+cd "$(dirname "$0")/daemon"
8+
9+cargo build --release
10+
11+bin=target/release/termbridge
12+
13+case "${1-}" in
14+ serve|token|pair|unpair|list|cert) exec "$bin" "$@" ;;
15+ *) exec "$bin" serve "$@" ;;
16+esac