@zenbu-labs/pixel 0.0.0 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/dist/bin.d.ts +2 -0
  2. package/dist/bin.js +161 -0
  3. package/dist/bootstrap.d.ts +1 -0
  4. package/dist/bootstrap.js +75 -0
  5. package/dist/context-menu.d.ts +20 -0
  6. package/dist/context-menu.js +57 -0
  7. package/dist/debug.d.ts +1 -0
  8. package/dist/debug.js +9 -0
  9. package/dist/devtools-layout.d.ts +21 -0
  10. package/dist/devtools-layout.js +45 -0
  11. package/dist/devtools.d.ts +33 -0
  12. package/dist/devtools.js +125 -0
  13. package/dist/electron/index.d.ts +3 -0
  14. package/dist/electron/index.js +4 -0
  15. package/dist/host/guest.d.ts +5 -0
  16. package/dist/host/guest.js +178 -0
  17. package/dist/host/server.d.ts +91 -0
  18. package/dist/host/server.js +296 -0
  19. package/dist/host/shell.d.ts +35 -0
  20. package/dist/host/shell.js +113 -0
  21. package/dist/host/strip.d.ts +6 -0
  22. package/dist/host/strip.js +63 -0
  23. package/dist/icons.d.ts +30 -0
  24. package/dist/icons.js +58 -0
  25. package/dist/index.d.ts +17 -0
  26. package/dist/index.js +39 -0
  27. package/dist/instances.d.ts +30 -0
  28. package/dist/instances.js +177 -0
  29. package/dist/layout.d.ts +5 -0
  30. package/dist/layout.js +34 -0
  31. package/dist/popup.d.ts +21 -0
  32. package/dist/popup.js +47 -0
  33. package/dist/preload/index.d.ts +15 -0
  34. package/dist/preload/index.js +3 -0
  35. package/dist/react/colors.d.ts +3 -0
  36. package/dist/react/colors.js +25 -0
  37. package/dist/react/components.d.ts +21 -0
  38. package/dist/react/components.js +45 -0
  39. package/dist/react/devtools/app.d.ts +3 -0
  40. package/dist/react/devtools/app.js +89 -0
  41. package/dist/react/devtools/console-capture.d.ts +1 -0
  42. package/dist/react/devtools/console-capture.js +41 -0
  43. package/dist/react/devtools/controller.d.ts +45 -0
  44. package/dist/react/devtools/controller.js +252 -0
  45. package/dist/react/devtools/elements.d.ts +4 -0
  46. package/dist/react/devtools/elements.js +152 -0
  47. package/dist/react/devtools/export-profile.d.ts +2 -0
  48. package/dist/react/devtools/export-profile.js +225 -0
  49. package/dist/react/devtools/fiber-hook.d.ts +1 -0
  50. package/dist/react/devtools/fiber-hook.js +89 -0
  51. package/dist/react/devtools/log-panel.d.ts +4 -0
  52. package/dist/react/devtools/log-panel.js +113 -0
  53. package/dist/react/devtools/profiler.d.ts +11 -0
  54. package/dist/react/devtools/profiler.js +374 -0
  55. package/dist/react/devtools/store.d.ts +27 -0
  56. package/dist/react/devtools/store.js +61 -0
  57. package/dist/react/devtools/stores.d.ts +92 -0
  58. package/dist/react/devtools/stores.js +41 -0
  59. package/dist/react/devtools/theme.d.ts +35 -0
  60. package/dist/react/devtools/theme.js +77 -0
  61. package/dist/react/devtools/ui.d.ts +24 -0
  62. package/dist/react/devtools/ui.js +59 -0
  63. package/dist/react/index.d.ts +110 -0
  64. package/dist/react/index.js +538 -0
  65. package/dist/react/markdown.d.ts +25 -0
  66. package/dist/react/markdown.js +270 -0
  67. package/dist/react/native.d.ts +116 -0
  68. package/dist/react/native.js +56 -0
  69. package/dist/react/reconciler-config.d.ts +241 -0
  70. package/dist/react/reconciler-config.js +412 -0
  71. package/dist/react/styles.d.ts +88 -0
  72. package/dist/react/styles.js +85 -0
  73. package/dist/react/surface.d.ts +55 -0
  74. package/dist/react/surface.js +74 -0
  75. package/dist/registry.d.ts +53 -0
  76. package/dist/registry.js +193 -0
  77. package/dist/root.d.ts +19 -0
  78. package/dist/root.js +342 -0
  79. package/dist/sandbox.d.ts +4 -0
  80. package/dist/sandbox.js +84 -0
  81. package/dist/scale.d.ts +7 -0
  82. package/dist/scale.js +31 -0
  83. package/dist/ssh/index.d.ts +58 -0
  84. package/dist/ssh/index.js +445 -0
  85. package/dist/terminal/applescript.d.ts +2 -0
  86. package/dist/terminal/applescript.js +20 -0
  87. package/dist/terminal/detect.d.ts +10 -0
  88. package/dist/terminal/detect.js +34 -0
  89. package/dist/terminal/graphics.d.ts +9 -0
  90. package/dist/terminal/graphics.js +142 -0
  91. package/dist/terminal/index.d.ts +12 -0
  92. package/dist/terminal/index.js +23 -0
  93. package/dist/terminal/run.d.ts +2 -0
  94. package/dist/terminal/run.js +15 -0
  95. package/dist/terminal/shared.d.ts +20 -0
  96. package/dist/terminal/shared.js +88 -0
  97. package/dist/terminal/terminal.d.ts +66 -0
  98. package/dist/terminal/terminal.js +6 -0
  99. package/dist/terminal/terminals/cmux.d.ts +2 -0
  100. package/dist/terminal/terminals/cmux.js +98 -0
  101. package/dist/terminal/terminals/ghostty-script.d.ts +1 -0
  102. package/dist/terminal/terminals/ghostty-script.js +179 -0
  103. package/dist/terminal/terminals/ghostty.d.ts +2 -0
  104. package/dist/terminal/terminals/ghostty.js +336 -0
  105. package/dist/terminal/terminals/herdr.d.ts +2 -0
  106. package/dist/terminal/terminals/herdr.js +141 -0
  107. package/dist/terminal/terminals/index.d.ts +5 -0
  108. package/dist/terminal/terminals/index.js +26 -0
  109. package/dist/terminal/terminals/kitty.d.ts +2 -0
  110. package/dist/terminal/terminals/kitty.js +209 -0
  111. package/dist/terminal/terminals/supacode.d.ts +2 -0
  112. package/dist/terminal/terminals/supacode.js +79 -0
  113. package/dist/terminal/terminals/tmux.d.ts +2 -0
  114. package/dist/terminal/terminals/tmux.js +132 -0
  115. package/dist/terminal/terminals/tty7.d.ts +2 -0
  116. package/dist/terminal/terminals/tty7.js +58 -0
  117. package/dist/terminal/terminals/vscode.d.ts +2 -0
  118. package/dist/terminal/terminals/vscode.js +13 -0
  119. package/dist/terminal/terminals/wezterm.d.ts +2 -0
  120. package/dist/terminal/terminals/wezterm.js +59 -0
  121. package/dist/theme.d.ts +22 -0
  122. package/dist/theme.js +40 -0
  123. package/dist/web/browser-session.d.ts +13 -0
  124. package/dist/web/browser-session.js +186 -0
  125. package/dist/web/cursor.d.ts +1 -0
  126. package/dist/web/cursor.js +24 -0
  127. package/dist/web/devtools.d.ts +39 -0
  128. package/dist/web/devtools.js +300 -0
  129. package/dist/web/favicon.d.ts +7 -0
  130. package/dist/web/favicon.js +73 -0
  131. package/dist/web/frame-rate.d.ts +1 -0
  132. package/dist/web/frame-rate.js +12 -0
  133. package/dist/web/host.d.ts +123 -0
  134. package/dist/web/host.js +573 -0
  135. package/dist/web/input.d.ts +49 -0
  136. package/dist/web/input.js +468 -0
  137. package/dist/web/offscreen.d.ts +2 -0
  138. package/dist/web/offscreen.js +23 -0
  139. package/dist/web/paint.d.ts +11 -0
  140. package/dist/web/paint.js +128 -0
  141. package/dist/web/popup.d.ts +40 -0
  142. package/dist/web/popup.js +144 -0
  143. package/dist/web/session.d.ts +20 -0
  144. package/dist/web/session.js +110 -0
  145. package/dist/web/types.d.ts +38 -0
  146. package/dist/web/types.js +39 -0
  147. package/dist/web/zoom.d.ts +5 -0
  148. package/dist/web/zoom.js +36 -0
  149. package/dist/webview.d.ts +55 -0
  150. package/dist/webview.js +481 -0
  151. package/package.json +69 -3
  152. package/scripts/apparmor.sh +71 -0
  153. package/scripts/postinstall.mjs +150 -0
@@ -0,0 +1,123 @@
1
+ import type { EngineKeyEvent, PastedImage, PointerEvent, Surface, WheelEvent } from "../react";
2
+ import type { DownloadProgress } from "./browser-session";
3
+ import { DevtoolsWindow } from "./devtools";
4
+ import type { DevtoolsAction } from "./devtools";
5
+ import { PopupWindow } from "./popup";
6
+ import type { DevtoolsDock, SurfaceLayout, WebViewState } from "./types";
7
+ import type { ZoomDirection } from "./zoom";
8
+ export declare const QUIT_URL = "pixel://quit";
9
+ export type OpenWindowDecision = "popup" | "deny" | "navigate";
10
+ export type OpenWindowPolicy = OpenWindowDecision | ((details: Electron.HandlerDetails) => OpenWindowDecision);
11
+ export declare function defaultOpenWindow(details: Electron.HandlerDetails): OpenWindowDecision;
12
+ export declare function decideOpenWindow(policy: OpenWindowPolicy | undefined, details: Electron.HandlerDetails): OpenWindowDecision;
13
+ type ReservedWindowOptions = "width" | "height" | "useContentSize" | "show" | "paintWhenInitiallyHidden" | "webPreferences";
14
+ type ReservedWebPreferences = "offscreen" | "disableDialogs" | "backgroundThrottling";
15
+ export type BrowserWindowOptions = Omit<Electron.BrowserWindowConstructorOptions, ReservedWindowOptions> & {
16
+ webPreferences?: Omit<Electron.WebPreferences, ReservedWebPreferences>;
17
+ };
18
+ export interface HostOptions {
19
+ url: string;
20
+ background: string;
21
+ clipboardRead: boolean;
22
+ proxy: string | null;
23
+ browserWindowOptions: BrowserWindowOptions;
24
+ }
25
+ export declare class PageHost {
26
+ readonly surface: Surface;
27
+ private readonly popupSurface;
28
+ private readonly window;
29
+ private readonly browserWindowOptions;
30
+ private readonly onState;
31
+ private renderScale;
32
+ private layout;
33
+ private state;
34
+ private stopped;
35
+ private contentFocused;
36
+ private readonly input;
37
+ private readonly clipboardRead;
38
+ private background;
39
+ private pendingPopupSize;
40
+ private readonly favicons;
41
+ private faviconSeq;
42
+ private findText;
43
+ private cdpAttached;
44
+ private cdpEventHandlers;
45
+ private readonly onDisplayChange;
46
+ private visible;
47
+ private framePinned;
48
+ private wholeSurfaceNext;
49
+ private readonly bitmaps;
50
+ private lastFrameSize;
51
+ onFrameSubmitted: (() => void) | null;
52
+ cursorShape: string;
53
+ onCursorChange: ((shape: string) => void) | null;
54
+ onOpenWindow: OpenWindowPolicy | null;
55
+ onQuit: (() => void) | null;
56
+ onDownload: ((progress: DownloadProgress) => void) | null;
57
+ private readonly popups;
58
+ onPopupChange: (() => void) | null;
59
+ get popup(): PopupWindow | null;
60
+ devtools: DevtoolsWindow | null;
61
+ devtoolsFocused: boolean;
62
+ onDevtoolsChange: (() => void) | null;
63
+ onDevtoolsAction: ((action: DevtoolsAction) => void) | null;
64
+ onContextMenu: ((params: Electron.ContextMenuParams) => void) | null;
65
+ onClosed: (() => void) | null;
66
+ constructor(surface: Surface, popupSurface: Surface, layout: SurfaceLayout, options: HostOptions, onState: (state: WebViewState) => void);
67
+ get webContents(): Electron.WebContents;
68
+ resize(layout: SurfaceLayout, options?: {
69
+ keepFrame?: boolean;
70
+ }): void;
71
+ navigate(url: string): void;
72
+ back(): void;
73
+ forward(): void;
74
+ reload(): void;
75
+ zoom(direction: ZoomDirection): number;
76
+ scaleZoom(ratio: number): number;
77
+ attachCdp(): Promise<void>;
78
+ setBackground(background: string): Promise<void>;
79
+ private emulateColorScheme;
80
+ cdp(method: string, params?: Record<string, unknown>): Promise<Record<string, unknown>>;
81
+ onCdpEvent(method: string, handler: ((params: unknown) => void) | null): void;
82
+ private applyFrameRate;
83
+ pinFrameRate(pinned: boolean): void;
84
+ frameSize(): {
85
+ width: number;
86
+ height: number;
87
+ } | null;
88
+ find(text: string): void;
89
+ findNext(forward: boolean): void;
90
+ stopFind(): void;
91
+ focusContent(): Promise<void> | undefined;
92
+ openDevtools(surface: Surface, layout: SurfaceLayout, dock: DevtoolsDock): void;
93
+ closeDevtools(): void;
94
+ focusDevtools(): void;
95
+ blurDevtools(): void;
96
+ inspect(x: number, y: number): void;
97
+ selectionText(): Promise<string>;
98
+ blurContent(): void;
99
+ private setFocusEmulation;
100
+ pointer(event: PointerEvent): void;
101
+ wheel(event: WheelEvent): void;
102
+ key(event: EngineKeyEvent): void;
103
+ sendToPage(channel: string, payload: unknown): void;
104
+ hasContents(id: number): boolean;
105
+ paste(text: string): void;
106
+ pasteImage(image: PastedImage): void;
107
+ setActive(active: boolean): void;
108
+ stop(): void;
109
+ private teardown;
110
+ private readonly onWindowClosed;
111
+ setVisible(visible: boolean): void;
112
+ private contentSize;
113
+ invalidate(): void;
114
+ private loadFavicon;
115
+ private updateNavigation;
116
+ private updateState;
117
+ private quitLink;
118
+ private handleWindowOpen;
119
+ private adoptPopup;
120
+ private tabPopupSize;
121
+ private popupSize;
122
+ }
123
+ export {};
@@ -0,0 +1,573 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PageHost = exports.QUIT_URL = void 0;
4
+ exports.defaultOpenWindow = defaultOpenWindow;
5
+ exports.decideOpenWindow = decideOpenWindow;
6
+ const electron_1 = require("electron");
7
+ const browser_session_1 = require("./browser-session");
8
+ const cursor_1 = require("./cursor");
9
+ const devtools_1 = require("./devtools");
10
+ const favicon_1 = require("./favicon");
11
+ const frame_rate_1 = require("./frame-rate");
12
+ const input_1 = require("./input");
13
+ const offscreen_1 = require("./offscreen");
14
+ const paint_1 = require("./paint");
15
+ const popup_1 = require("./popup");
16
+ const session_1 = require("./session");
17
+ const types_1 = require("./types");
18
+ const zoom_1 = require("./zoom");
19
+ exports.QUIT_URL = "pixel://quit";
20
+ function defaultOpenWindow(details) {
21
+ return details.disposition === "new-window" ? "popup" : "navigate";
22
+ }
23
+ function decideOpenWindow(policy, details) {
24
+ if (typeof policy === "function")
25
+ return policy(details);
26
+ return policy ?? defaultOpenWindow(details);
27
+ }
28
+ function windowOptions(passthrough, size, offscreen) {
29
+ const { webPreferences, ...window } = passthrough;
30
+ return {
31
+ frame: false,
32
+ skipTaskbar: true,
33
+ fullscreenable: false,
34
+ resizable: false,
35
+ acceptFirstMouse: true,
36
+ ...window,
37
+ width: size.width,
38
+ height: size.height,
39
+ useContentSize: true,
40
+ show: false,
41
+ paintWhenInitiallyHidden: true,
42
+ webPreferences: {
43
+ sandbox: true,
44
+ nodeIntegration: false,
45
+ // with sandbox true this is safe, we enable so a users preload script runs inside iframes/webviews
46
+ nodeIntegrationInSubFrames: true,
47
+ contextIsolation: true,
48
+ ...webPreferences,
49
+ offscreen,
50
+ disableDialogs: true,
51
+ backgroundThrottling: false,
52
+ },
53
+ };
54
+ }
55
+ class PageHost {
56
+ surface;
57
+ popupSurface;
58
+ window;
59
+ browserWindowOptions;
60
+ onState;
61
+ renderScale;
62
+ layout;
63
+ state;
64
+ stopped = false;
65
+ contentFocused = false;
66
+ input;
67
+ clipboardRead;
68
+ background;
69
+ pendingPopupSize = null;
70
+ favicons = new favicon_1.FaviconCache();
71
+ faviconSeq = 0;
72
+ findText = "";
73
+ cdpAttached = false;
74
+ cdpEventHandlers = new Map();
75
+ onDisplayChange = () => {
76
+ if (this.stopped)
77
+ return;
78
+ this.applyFrameRate();
79
+ };
80
+ visible = true;
81
+ framePinned = false;
82
+ wholeSurfaceNext = true;
83
+ bitmaps;
84
+ lastFrameSize = null;
85
+ onFrameSubmitted = null;
86
+ cursorShape = "default";
87
+ onCursorChange = null;
88
+ onOpenWindow = null;
89
+ onQuit = null;
90
+ onDownload = null;
91
+ popups = [];
92
+ onPopupChange = null;
93
+ get popup() {
94
+ return this.popups[this.popups.length - 1] ?? null;
95
+ }
96
+ devtools = null;
97
+ devtoolsFocused = false;
98
+ onDevtoolsChange = null;
99
+ onDevtoolsAction = null;
100
+ onContextMenu = null;
101
+ onClosed = null;
102
+ constructor(surface, popupSurface, layout, options, onState) {
103
+ this.clipboardRead = options.clipboardRead;
104
+ this.browserWindowOptions = options.browserWindowOptions;
105
+ this.surface = surface;
106
+ this.bitmaps = new paint_1.BitmapPresenter(surface);
107
+ this.popupSurface = popupSurface;
108
+ this.background = options.background;
109
+ this.layout = layout;
110
+ this.onState = onState;
111
+ this.renderScale = renderScaleFor(layout);
112
+ this.state = (0, types_1.initialWebViewState)(options.url);
113
+ const size = this.contentSize(layout);
114
+ this.window = new electron_1.BrowserWindow(windowOptions(this.browserWindowOptions, size, (0, offscreen_1.offscreenPreferences)(this.renderScale)));
115
+ (0, session_1.prepareSession)(this.window.webContents.session);
116
+ if (options.proxy)
117
+ void (0, browser_session_1.routeThroughProxy)(this.window.webContents.session, options.proxy);
118
+ if (this.clipboardRead)
119
+ (0, browser_session_1.allowClipboardRead)(this.window.webContents);
120
+ (0, browser_session_1.onDownloadFor)(this.window.webContents, (progress) => this.onDownload?.(progress));
121
+ this.input = new input_1.PageInput({
122
+ contents: () => this.window.webContents,
123
+ scale: () => this.layout.scale,
124
+ focus: () => this.focusContent(),
125
+ cdp: async (method, params) => {
126
+ await this.attachCdp();
127
+ return this.cdp(method, params);
128
+ },
129
+ });
130
+ this.window.webContents.setFrameRate((0, frame_rate_1.frameRate)());
131
+ this.window.on("closed", this.onWindowClosed);
132
+ this.window.webContents.on("will-navigate", (event, url) => {
133
+ if (this.quitLink(url))
134
+ event.preventDefault();
135
+ });
136
+ electron_1.screen.on("display-added", this.onDisplayChange);
137
+ electron_1.screen.on("display-removed", this.onDisplayChange);
138
+ electron_1.screen.on("display-metrics-changed", this.onDisplayChange);
139
+ this.window.webContents.on("paint", (event, dirtyRect, image) => {
140
+ const shmFrame = event.softwareFrame;
141
+ const size = event.texture
142
+ ? {
143
+ width: event.texture.textureInfo.codedSize.width,
144
+ height: event.texture.textureInfo.codedSize.height,
145
+ }
146
+ : shmFrame
147
+ ? {
148
+ width: shmFrame.frameInfo.contentRect.width,
149
+ height: shmFrame.frameInfo.contentRect.height,
150
+ }
151
+ : image.getSize();
152
+ const presented = event.texture || shmFrame
153
+ ? (0, paint_1.presentPaint)(this.surface, event.texture, shmFrame, image, dirtyRect, this.wholeSurfaceNext)
154
+ : this.bitmaps.push(image, dirtyRect, this.wholeSurfaceNext);
155
+ if (!presented)
156
+ return;
157
+ this.wholeSurfaceNext = false;
158
+ this.lastFrameSize = size;
159
+ this.onFrameSubmitted?.();
160
+ });
161
+ this.window.webContents.on("did-start-navigation", (_event, _url, isInPlace, isMainFrame) => {
162
+ if (isMainFrame && !isInPlace)
163
+ this.updateState({ loading: true });
164
+ });
165
+ this.window.webContents.on("did-stop-loading", () => this.updateNavigation(false));
166
+ this.window.webContents.on("did-navigate", (_event, url) => {
167
+ if (urlHost(url) !== urlHost(this.state.url))
168
+ this.updateState({ favicon: null });
169
+ this.updateNavigation(this.state.loading, url);
170
+ });
171
+ this.window.webContents.on("did-navigate-in-page", (_event, url, mainFrame) => {
172
+ if (mainFrame)
173
+ this.updateNavigation(this.state.loading, url);
174
+ });
175
+ this.window.webContents.on("page-favicon-updated", (_event, favicons) => {
176
+ void this.loadFavicon(favicons);
177
+ });
178
+ this.window.webContents.on("page-title-updated", (_event, title) => {
179
+ this.updateState({ title });
180
+ });
181
+ this.window.webContents.on("cursor-changed", (_event, type) => {
182
+ const shape = (0, cursor_1.cursorShapeFor)(type);
183
+ if (shape === this.cursorShape)
184
+ return;
185
+ this.cursorShape = shape;
186
+ this.onCursorChange?.(shape);
187
+ });
188
+ this.window.webContents.on("context-menu", (_event, params) => {
189
+ this.onContextMenu?.(params);
190
+ });
191
+ this.window.webContents.on("found-in-page", (_event, result) => {
192
+ this.updateState({
193
+ findMatches: { active: result.activeMatchOrdinal, total: result.matches },
194
+ });
195
+ });
196
+ this.window.webContents.setWindowOpenHandler((details) => this.handleWindowOpen(details, this.window.webContents));
197
+ this.window.webContents.on("did-create-window", (child) => this.adoptPopup(child));
198
+ void this.window.loadURL(options.url);
199
+ this.onState(this.state);
200
+ }
201
+ get webContents() {
202
+ return this.window.webContents;
203
+ }
204
+ resize(layout, options) {
205
+ if (this.stopped)
206
+ return;
207
+ if (this.layout.x === layout.x &&
208
+ this.layout.y === layout.y &&
209
+ this.layout.width === layout.width &&
210
+ this.layout.height === layout.height &&
211
+ this.layout.scale === layout.scale) {
212
+ return;
213
+ }
214
+ const sizeChanged = this.layout.width !== layout.width ||
215
+ this.layout.height !== layout.height ||
216
+ this.layout.scale !== layout.scale;
217
+ this.layout = layout;
218
+ this.renderScale = renderScaleFor(layout);
219
+ if (!sizeChanged)
220
+ return;
221
+ if (!options?.keepFrame)
222
+ this.surface.clear();
223
+ const size = this.contentSize(layout);
224
+ this.window.setContentSize(size.width, size.height, false);
225
+ }
226
+ navigate(url) {
227
+ void this.window.webContents.loadURL(url);
228
+ }
229
+ back() {
230
+ if (this.window.webContents.navigationHistory.canGoBack()) {
231
+ this.window.webContents.navigationHistory.goBack();
232
+ }
233
+ }
234
+ forward() {
235
+ if (this.window.webContents.navigationHistory.canGoForward()) {
236
+ this.window.webContents.navigationHistory.goForward();
237
+ }
238
+ }
239
+ reload() {
240
+ if (this.state.loading)
241
+ this.window.webContents.stop();
242
+ else
243
+ this.window.webContents.reload();
244
+ }
245
+ zoom(direction) {
246
+ const factor = (0, zoom_1.stepZoom)(this.window.webContents, direction);
247
+ this.updateState({ zoom: factor });
248
+ return factor;
249
+ }
250
+ scaleZoom(ratio) {
251
+ const factor = (0, zoom_1.scaleZoom)(this.window.webContents, ratio);
252
+ this.updateState({ zoom: factor });
253
+ return factor;
254
+ }
255
+ async attachCdp() {
256
+ if (this.cdpAttached)
257
+ return;
258
+ this.window.webContents.debugger.attach("1.3");
259
+ this.cdpAttached = true;
260
+ this.window.webContents.debugger.on("message", (_event, method, params) => {
261
+ this.cdpEventHandlers.get(method)?.(params);
262
+ });
263
+ await this.cdp("Runtime.enable");
264
+ await this.cdp("Page.enable");
265
+ await this.emulateColorScheme();
266
+ }
267
+ async setBackground(background) {
268
+ this.background = background;
269
+ await this.emulateColorScheme();
270
+ }
271
+ async emulateColorScheme() {
272
+ if (!this.cdpAttached)
273
+ return;
274
+ const n = parseInt(this.background.slice(1), 16);
275
+ const [r, g, b] = [(n >> 16) & 255, (n >> 8) & 255, n & 255];
276
+ const dark = 0.2126 * r + 0.7152 * g + 0.0722 * b < 128;
277
+ await this.cdp("Emulation.setEmulatedMedia", {
278
+ features: [{ name: "prefers-color-scheme", value: dark ? "dark" : "light" }],
279
+ });
280
+ }
281
+ cdp(method, params) {
282
+ return this.window.webContents.debugger.sendCommand(method, params);
283
+ }
284
+ onCdpEvent(method, handler) {
285
+ if (handler)
286
+ this.cdpEventHandlers.set(method, handler);
287
+ else
288
+ this.cdpEventHandlers.delete(method);
289
+ }
290
+ applyFrameRate() {
291
+ this.window.webContents.setFrameRate(this.visible || this.framePinned ? (0, frame_rate_1.frameRate)() : 4);
292
+ }
293
+ pinFrameRate(pinned) {
294
+ if (this.framePinned === pinned || this.stopped)
295
+ return;
296
+ this.framePinned = pinned;
297
+ this.applyFrameRate();
298
+ }
299
+ frameSize() {
300
+ return this.lastFrameSize;
301
+ }
302
+ find(text) {
303
+ this.findText = text;
304
+ if (!text) {
305
+ this.stopFind();
306
+ return;
307
+ }
308
+ this.window.webContents.findInPage(text);
309
+ }
310
+ findNext(forward) {
311
+ if (!this.findText)
312
+ return;
313
+ this.window.webContents.findInPage(this.findText, { forward, findNext: true });
314
+ }
315
+ stopFind() {
316
+ this.findText = "";
317
+ this.window.webContents.stopFindInPage("clearSelection");
318
+ this.updateState({ findMatches: null });
319
+ }
320
+ focusContent() {
321
+ if (this.stopped)
322
+ return;
323
+ this.blurDevtools();
324
+ if (this.contentFocused)
325
+ return;
326
+ this.window.focus();
327
+ this.window.webContents.focus();
328
+ this.contentFocused = true;
329
+ return this.setFocusEmulation(true).catch(() => { });
330
+ }
331
+ openDevtools(surface, layout, dock) {
332
+ if (this.devtools || this.stopped)
333
+ return;
334
+ const devtools = new devtools_1.DevtoolsWindow(this.window.webContents, surface, layout, dock, this.background, this.renderScale, (action) => this.onDevtoolsAction?.(action), () => {
335
+ if (this.devtools !== devtools)
336
+ return;
337
+ this.devtools = null;
338
+ this.devtoolsFocused = false;
339
+ this.onDevtoolsChange?.();
340
+ });
341
+ devtools.onCursorChange = () => this.onCursorChange?.(devtools.cursorShape);
342
+ devtools.setVisible(this.visible);
343
+ this.devtools = devtools;
344
+ this.onDevtoolsChange?.();
345
+ }
346
+ closeDevtools() {
347
+ this.devtools?.close();
348
+ }
349
+ focusDevtools() {
350
+ if (this.devtoolsFocused || !this.devtools)
351
+ return;
352
+ this.blurContent();
353
+ this.devtoolsFocused = true;
354
+ this.devtools.focus();
355
+ }
356
+ blurDevtools() {
357
+ if (!this.devtoolsFocused)
358
+ return;
359
+ this.devtoolsFocused = false;
360
+ this.devtools?.blur();
361
+ }
362
+ inspect(x, y) {
363
+ this.window.webContents.inspectElement(Math.round(x), Math.round(y));
364
+ }
365
+ selectionText() {
366
+ return this.input.selectionText();
367
+ }
368
+ blurContent() {
369
+ if (!this.contentFocused)
370
+ return;
371
+ this.input.releaseKeys();
372
+ this.window.blurWebView();
373
+ this.contentFocused = false;
374
+ void this.setFocusEmulation(false).catch(() => { });
375
+ }
376
+ async setFocusEmulation(enabled) {
377
+ await this.attachCdp();
378
+ await this.cdp("Emulation.setFocusEmulationEnabled", { enabled });
379
+ }
380
+ pointer(event) {
381
+ if (this.stopped)
382
+ return;
383
+ this.input.pointer(event);
384
+ }
385
+ wheel(event) {
386
+ if (this.stopped)
387
+ return;
388
+ this.input.wheel(event);
389
+ }
390
+ key(event) {
391
+ if (this.stopped)
392
+ return;
393
+ this.input.key(event);
394
+ }
395
+ sendToPage(channel, payload) {
396
+ try {
397
+ this.window.webContents.send(channel, payload);
398
+ }
399
+ catch { }
400
+ }
401
+ hasContents(id) {
402
+ return this.window.webContents.id === id;
403
+ }
404
+ paste(text) {
405
+ this.input.paste(text);
406
+ }
407
+ pasteImage(image) {
408
+ this.input.pasteImage(image);
409
+ }
410
+ setActive(active) {
411
+ if (!active) {
412
+ this.blurContent();
413
+ this.input.releaseModifiers();
414
+ }
415
+ }
416
+ stop() {
417
+ if (this.stopped)
418
+ return;
419
+ this.stopped = true;
420
+ this.teardown();
421
+ this.window.destroy();
422
+ }
423
+ teardown() {
424
+ for (const popup of [...this.popups])
425
+ popup.close();
426
+ this.devtools?.close();
427
+ electron_1.screen.off("display-added", this.onDisplayChange);
428
+ electron_1.screen.off("display-removed", this.onDisplayChange);
429
+ electron_1.screen.off("display-metrics-changed", this.onDisplayChange);
430
+ this.surface.close();
431
+ }
432
+ onWindowClosed = () => {
433
+ if (this.stopped)
434
+ return;
435
+ this.stopped = true;
436
+ this.teardown();
437
+ this.onClosed?.();
438
+ };
439
+ setVisible(visible) {
440
+ if (this.stopped)
441
+ return;
442
+ if (this.visible === visible)
443
+ return;
444
+ this.visible = visible;
445
+ this.popup?.setVisible(visible);
446
+ this.devtools?.setVisible(visible);
447
+ this.applyFrameRate();
448
+ if (visible)
449
+ this.window.webContents.invalidate();
450
+ }
451
+ contentSize(layout) {
452
+ return (0, types_1.cssSize)(layout.width, layout.height, layout.scale);
453
+ }
454
+ invalidate() {
455
+ if (this.stopped)
456
+ return;
457
+ this.wholeSurfaceNext = true;
458
+ this.window.webContents.invalidate();
459
+ }
460
+ async loadFavicon(urls) {
461
+ const seq = ++this.faviconSeq;
462
+ const file = await this.favicons.resolve(urls, this.window.webContents.session).catch(() => null);
463
+ if (file && seq === this.faviconSeq && !this.stopped)
464
+ this.updateState({ favicon: file });
465
+ }
466
+ updateNavigation(loading, url = this.window.webContents.getURL()) {
467
+ this.updateState({
468
+ url,
469
+ loading,
470
+ canGoBack: this.window.webContents.navigationHistory.canGoBack(),
471
+ canGoForward: this.window.webContents.navigationHistory.canGoForward(),
472
+ zoom: this.window.webContents.getZoomFactor(),
473
+ });
474
+ }
475
+ updateState(update) {
476
+ this.state = { ...this.state, ...update };
477
+ this.onState(this.state);
478
+ }
479
+ quitLink(url) {
480
+ if (!url.startsWith(exports.QUIT_URL))
481
+ return false;
482
+ setImmediate(() => {
483
+ if (!this.stopped)
484
+ this.onQuit?.();
485
+ });
486
+ return true;
487
+ }
488
+ handleWindowOpen(details, opener) {
489
+ const { url, disposition, features } = details;
490
+ if (this.quitLink(url))
491
+ return { action: "deny" };
492
+ const decision = decideOpenWindow(this.onOpenWindow ?? undefined, details);
493
+ if (decision === "deny")
494
+ return { action: "deny" };
495
+ if (decision === "navigate") {
496
+ void opener.loadURL(url);
497
+ return { action: "deny" };
498
+ }
499
+ const wantsTab = disposition === "foreground-tab" || disposition === "background-tab";
500
+ const size = wantsTab ? this.tabPopupSize() : this.popupSize(features);
501
+ this.pendingPopupSize = size;
502
+ return {
503
+ action: "allow",
504
+ overrideBrowserWindowOptions: windowOptions(this.browserWindowOptions, size, {
505
+ useSharedTexture: false,
506
+ deviceScaleFactor: this.renderScale,
507
+ }),
508
+ };
509
+ }
510
+ adoptPopup(child) {
511
+ if (this.clipboardRead)
512
+ (0, browser_session_1.allowClipboardRead)(child.webContents);
513
+ const size = this.pendingPopupSize ?? { width: 480, height: 360 };
514
+ this.pendingPopupSize = null;
515
+ child.webContents.on("did-create-window", (grandchild) => this.adoptPopup(grandchild));
516
+ const popup = new popup_1.PopupWindow(child, this.popupSurface, size, this.renderScale, () => this.layout.scale, () => this.onPopupChange?.(), () => {
517
+ const at = this.popups.indexOf(popup);
518
+ if (at < 0)
519
+ return;
520
+ const wasTop = at === this.popups.length - 1;
521
+ this.popups.splice(at, 1);
522
+ if (wasTop && this.visible)
523
+ this.popup?.setVisible(true);
524
+ this.onPopupChange?.();
525
+ }, (details) => this.handleWindowOpen(details, child.webContents));
526
+ popup.onCursorChange = () => {
527
+ if (this.popup === popup)
528
+ this.onCursorChange?.(popup.cursorShape);
529
+ };
530
+ this.popup?.setVisible(false);
531
+ this.popups.push(popup);
532
+ this.onPopupChange?.();
533
+ }
534
+ tabPopupSize() {
535
+ const content = this.contentSize(this.layout);
536
+ return {
537
+ width: Math.max(280, Math.round(content.width * 0.9)),
538
+ height: Math.max(280, Math.round(content.height * 0.9)),
539
+ };
540
+ }
541
+ popupSize(features) {
542
+ const requested = (name) => {
543
+ const match = features.match(new RegExp(`${name}=(\\d+)`));
544
+ return match ? Number(match[1]) : 0;
545
+ };
546
+ const content = this.contentSize(this.layout);
547
+ const clamp = (value, fallback, max) => Math.max(280, Math.min(value || fallback, max));
548
+ return {
549
+ width: clamp(requested("width"), Math.round(content.width * 0.62), Math.round(content.width * 0.85)),
550
+ height: clamp(requested("height"), Math.round(content.height * 0.68), Math.round(content.height * 0.8)),
551
+ };
552
+ }
553
+ }
554
+ exports.PageHost = PageHost;
555
+ function renderScaleFor(layout) {
556
+ const explicit = Number(process.env.PIXEL_RENDER_SCALE);
557
+ if (Number.isFinite(explicit) && explicit > 0) {
558
+ return Math.max(0.5, Math.min(layout.scale, explicit));
559
+ }
560
+ const maxPixels = Number(process.env.PIXEL_MAX_PIXELS ?? 0);
561
+ if (!Number.isFinite(maxPixels) || maxPixels <= 0)
562
+ return layout.scale;
563
+ const cssPixels = layout.width * layout.height / (layout.scale * layout.scale);
564
+ return Math.max(0.5, Math.min(layout.scale, Math.sqrt(maxPixels / cssPixels)));
565
+ }
566
+ function urlHost(url) {
567
+ try {
568
+ return new URL(url).host;
569
+ }
570
+ catch {
571
+ return "";
572
+ }
573
+ }