overmux 0.0.9 → 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/dist/bin.js +1 -1
- package/dist/bin.js.map +1 -1
- package/dist/docs/400-reference/200-configuration.md +35 -0
- package/dist/docs/400-reference/500-server/400-notifications.md +4 -0
- package/dist/docs/400-reference/600-client/030-native-web-view.md +166 -0
- package/dist/docs/400-reference/600-client/300-tech-stack-recommendations.md +1 -0
- package/dist/docs/400-reference/700-cli/600-docs.md +1 -1
- package/dist/exports/client.d.ts +43 -7
- package/dist/exports/client.d.ts.map +1 -1
- package/dist/exports/client.js +172 -22
- package/dist/exports/client.js.map +1 -1
- package/dist/exports/index.js +1 -1
- package/dist/exports/index.js.map +1 -1
- package/dist/internal/server/coordinator/server-child.js +25 -1
- package/dist/internal/server/coordinator/server-child.js.map +1 -1
- package/dist/push/THIRD_PARTY_NOTICES.md +30 -0
- package/dist/push/badge.png +0 -0
- package/dist/push/icon.png +0 -0
- package/dist/push/sw.js +88 -0
- package/dist/push/sw.js.map +1 -0
- package/docs/400-reference/200-configuration.md +35 -0
- package/docs/400-reference/500-server/400-notifications.md +4 -0
- package/docs/400-reference/600-client/030-native-web-view.md +166 -0
- package/docs/400-reference/600-client/300-tech-stack-recommendations.md +1 -0
- package/docs/400-reference/700-cli/600-docs.md +1 -1
- package/package.json +3 -3
- package/src/internal/client/background-notifications.ts +3 -1
- package/src/internal/client/client-definition.ts +9 -13
- package/src/internal/client/host/desktop-host.ts +28 -2
- package/src/internal/client/host/hosted-pages/browser-notification-settings.tsx +8 -3
- package/src/internal/client/native-web-view.tsx +158 -0
- package/src/internal/client/push-assets/badge.png +0 -0
- package/src/internal/client/push-assets/icon.png +0 -0
- package/src/internal/client/push-service-worker.ts +118 -0
- package/src/internal/client/register-push-worker.ts +63 -0
- package/src/internal/client/shortcuts.tsx +17 -31
- package/src/internal/server/http/routes.ts +47 -2
- package/src/internal/shared/routes.ts +2 -0
- package/src/public/client.ts +5 -0
|
@@ -255,3 +255,38 @@ export default defineOvermuxDesktopConfig({
|
|
|
255
255
|
macosTrafficLights: "hidden",
|
|
256
256
|
});
|
|
257
257
|
```
|
|
258
|
+
|
|
259
|
+
### `onBeforeInputEvent`
|
|
260
|
+
|
|
261
|
+
The optional `onBeforeInputEvent` callback exposes Electron's [`before-input-event`](https://www.electronjs.org/docs/latest/api/web-contents#event-before-input-event), receiving the actual Electron `Event`, `Input`, and receiving [`WebContents`](https://www.electronjs.org/docs/latest/api/web-contents).
|
|
262
|
+
|
|
263
|
+
You can use this callback to intercept Electron key presses.
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
```ts
|
|
267
|
+
import { defineOvermuxDesktopConfig } from "@overmux/desktop";
|
|
268
|
+
|
|
269
|
+
export default defineOvermuxDesktopConfig({
|
|
270
|
+
onBeforeInputEvent: ({ event, input, webContents }) => {
|
|
271
|
+
const key = input.key.toLowerCase();
|
|
272
|
+
if (
|
|
273
|
+
process.platform !== "linux" ||
|
|
274
|
+
input.type !== "keyDown" ||
|
|
275
|
+
!input.meta ||
|
|
276
|
+
input.control ||
|
|
277
|
+
input.alt ||
|
|
278
|
+
input.shift ||
|
|
279
|
+
(key !== "c" && key !== "v")
|
|
280
|
+
) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
event.preventDefault();
|
|
285
|
+
if (key === "c") {
|
|
286
|
+
webContents.copy();
|
|
287
|
+
} else {
|
|
288
|
+
webContents.paste();
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
});
|
|
292
|
+
```
|
|
@@ -30,3 +30,7 @@ export const serverConfig = defineOvermuxServer({
|
|
|
30
30
|
- `open.link`: optional destination to open when the notification is activated. Use an app-relative path starting with `/` or an HTTP(S) URL without credentials.
|
|
31
31
|
|
|
32
32
|
Each text field and link has a maximum length of 4,096 characters. `send()` returns `Promise<void>`.
|
|
33
|
+
|
|
34
|
+
## PWA background notifications
|
|
35
|
+
|
|
36
|
+
Open `/_overmux/settings` on your Overmux server, and enable browser notifications. This requires HTTPS, or localhost for development. See [Hosted pages](../../500-hosted-pages.md) for adding a settings link to your application.
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Native web views
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
`NativeWebView` embeds a website in Overmux Desktop using a chrome native webview.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { NativeWebView } from "overmux/client";
|
|
9
|
+
|
|
10
|
+
export const Website = () => (
|
|
11
|
+
<NativeWebView
|
|
12
|
+
url="https://example.com"
|
|
13
|
+
style={{ width: "100%", height: 600 }}
|
|
14
|
+
fallback={
|
|
15
|
+
<a href="https://example.com" target="_blank" rel="noopener noreferrer">
|
|
16
|
+
Open website in browser
|
|
17
|
+
</a>
|
|
18
|
+
}
|
|
19
|
+
/>
|
|
20
|
+
);
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Props
|
|
24
|
+
|
|
25
|
+
`NativeWebViewProps` and `NativeWebViewLoadError` are exported from `overmux/client`.
|
|
26
|
+
|
|
27
|
+
### `url`
|
|
28
|
+
|
|
29
|
+
**Required.** Type: `string`. The initial website URL.
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { NativeWebView } from "overmux/client";
|
|
33
|
+
|
|
34
|
+
<NativeWebView url="https://example.com" style={{ height: 600 }} />;
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Changing this string navigates; rerendering the same URL preserves in-page navigation. A remount starts again at `url`, retaining cookies and site storage.
|
|
38
|
+
|
|
39
|
+
A blocked `url` prop clears the native page, including when an HTTP allowance is revoked. Blocked in-page links leave the current page in place. See [`allowedHttpOrigins`](#allowedhttporigins) for permitted destinations.
|
|
40
|
+
|
|
41
|
+
New-window links open in the system browser, not another embedded surface. The system browser has its own login state. Popup-based authentication flows may therefore not complete inside the embedded view; use in-page sign-in where supported.
|
|
42
|
+
|
|
43
|
+
### `className`
|
|
44
|
+
|
|
45
|
+
**Optional.** Type: `string`. No default. Applies a CSS class to the desktop placeholder `div`, not the embedded website.
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import { NativeWebView } from "overmux/client";
|
|
49
|
+
|
|
50
|
+
<NativeWebView
|
|
51
|
+
url="https://example.com"
|
|
52
|
+
className="website-pane"
|
|
53
|
+
style={{ height: 600 }}
|
|
54
|
+
/>;
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Define the class in your application's stylesheet. See [Theming and CSS](./theming) for loading styles.
|
|
58
|
+
|
|
59
|
+
### `style`
|
|
60
|
+
|
|
61
|
+
**Optional.** Type: `React.CSSProperties`. No default. Applies inline styles to the desktop placeholder `div`.
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
import { NativeWebView } from "overmux/client";
|
|
65
|
+
|
|
66
|
+
<NativeWebView
|
|
67
|
+
url="https://example.com"
|
|
68
|
+
style={{ width: "100%", height: 600 }}
|
|
69
|
+
/>;
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Give the placeholder a **nonzero width and height**. The component has no default height. Desktop follows its rectangular position and size as the layout moves, scrolls or resizes. Hidden and zero-size placeholders hide the native surface without clearing its site data. See [Layout limitations](#layout-limitations) before using overlapping panes or CSS effects.
|
|
73
|
+
|
|
74
|
+
### `fallback`
|
|
75
|
+
|
|
76
|
+
**Optional.** Type: `React.ReactNode`. Content shown in browsers, PWAs and desktop versions without native web view support.
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
import { NativeWebView } from "overmux/client";
|
|
80
|
+
|
|
81
|
+
<NativeWebView
|
|
82
|
+
url="https://example.com"
|
|
83
|
+
style={{ height: 600 }}
|
|
84
|
+
fallback={
|
|
85
|
+
<a href="https://example.com" target="_blank" rel="noopener noreferrer">
|
|
86
|
+
Open website in browser
|
|
87
|
+
</a>
|
|
88
|
+
}
|
|
89
|
+
/>;
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
### `onLoadError`
|
|
94
|
+
|
|
95
|
+
**Optional.** Type: `(error: NativeWebViewLoadError) => void`. No default handler. Receives `{ url, code, message }` for main-page load failures, blocked destinations, invalid configuration or bridge errors.
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
import { NativeWebView, type NativeWebViewLoadError } from "overmux/client";
|
|
99
|
+
|
|
100
|
+
const reportLoadError = (error: NativeWebViewLoadError) => console.error(error);
|
|
101
|
+
|
|
102
|
+
<NativeWebView
|
|
103
|
+
url="https://example.com"
|
|
104
|
+
style={{ height: 600 }}
|
|
105
|
+
onLoadError={reportLoadError}
|
|
106
|
+
/>;
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Main-page failures exclude subresource failures and aborted navigation superseded by a newer request.
|
|
110
|
+
|
|
111
|
+
| Code | Meaning |
|
|
112
|
+
| --- | --- |
|
|
113
|
+
| Numeric string, such as `"-105"` | Chromium load error |
|
|
114
|
+
| `ERR_URL_BLOCKED` | Destination rejected by navigation policy |
|
|
115
|
+
| `ERR_DESKTOP_BRIDGE` | Bridge or configuration failure |
|
|
116
|
+
| `ERR_OPEN_EXTERNAL` | Failed to open the system browser |
|
|
117
|
+
| `ERR_RENDER_PROCESS_GONE` | Native renderer crashed; the surface is destroyed |
|
|
118
|
+
|
|
119
|
+
Remount to retry after a native renderer crash.
|
|
120
|
+
|
|
121
|
+
### `allowedHttpOrigins`
|
|
122
|
+
|
|
123
|
+
**Optional.** Type: `string[]`. Defaults to `[]`. Additional plain-HTTP origins to permit. An origin is the scheme, hostname and port, without a path.
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
import { NativeWebView } from "overmux/client";
|
|
127
|
+
|
|
128
|
+
<NativeWebView
|
|
129
|
+
url="http://devbox.local:3000"
|
|
130
|
+
style={{ height: 600 }}
|
|
131
|
+
allowedHttpOrigins={["http://devbox.local:3000"]}
|
|
132
|
+
/>;
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
HTTPS is allowed. HTTP is allowed for `localhost`, `127.0.0.1` and `[::1]`, plus exact origins in this list. Entries must be HTTP origins, optionally with a trailing slash. Wildcards, credentials, paths, queries and fragments are rejected. Matching includes the port. Other schemes are denied. Policy is enforced in Electron main for requested URLs, page navigation, redirects and new-window links.
|
|
136
|
+
|
|
137
|
+
### `passthroughBindings`
|
|
138
|
+
|
|
139
|
+
**Optional.** Type: `readonly ShortcutBinding[]`. Defaults to `[]`. Keyboard bindings to forward to the owning Overmux UI while the embedded page has focus. `ShortcutBinding` is exported from `overmux/client`.
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
import { NativeWebView } from "overmux/client";
|
|
143
|
+
|
|
144
|
+
<NativeWebView
|
|
145
|
+
url="https://example.com"
|
|
146
|
+
style={{ height: 600 }}
|
|
147
|
+
passthroughBindings={["Escape", "Control+Shift+X", ["F12", "P", "R"]]}
|
|
148
|
+
/>;
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Uses the same binding and chord syntax as [command shortcuts](./shortcuts), but does not require a matching command registration. A chord is a sequence of key presses, such as F12, then P, then R.
|
|
152
|
+
|
|
153
|
+
Overmux desktop intercepts a completed configured binding before the embedded page receives it and forwards its keyboard input, including modifiers and releases, to the owning Overmux UI. That UI then handles it through its ordinary DOM listeners - for example, a dialog's Escape handler, a local `onKeyDown`, or the normal shortcut host. The embedded page remains sandboxed and receives no Overmux bridge or command capability.
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
## Security and shared sessions
|
|
157
|
+
|
|
158
|
+
Embedded pages have no Overmux bridge, preload script or Node access. Sandbox, context isolation and web security stay enabled. Permissions and downloads are denied.
|
|
159
|
+
|
|
160
|
+
All native views across connected Overmux instances share **one persistent desktop browser session**, `persist:overmux-native-web`. Cookies and login state survive unmounting, switching instances and restarting Desktop. This session is separate from Overmux authentication and from the system browser. Unmounting destroys the surface, not site data. Clearing an Overmux instance does not clear native website logins. There is no per-view profile or partition option.
|
|
161
|
+
|
|
162
|
+
## Layout limitations
|
|
163
|
+
|
|
164
|
+
Native surfaces paint **above the DOM**, including React modals, menus and dropdowns. Use ordinary, non-overlapping rectangular panes. DOM stacking order, rounded corners, CSS transforms, ancestor clipping and arbitrary CSS effects are not faithfully reproduced. Bounds are clipped to the host window, not arbitrary DOM containers. Avoid layouts that depend on those effects; unmount the view when its native surface must not cover other content.
|
|
165
|
+
|
|
166
|
+
There is no `visible` prop, toolbar, zoom or history API, overlap coordination, or pane/session restoration. Host document reloads, crashes, instance changes and window destruction also destroy its native surfaces.
|
|
@@ -8,5 +8,6 @@ We recommend:
|
|
|
8
8
|
- [TanStack Router](https://tanstack.com/router) for routing.
|
|
9
9
|
- [shadcn/ui](https://ui.shadcn.com/) for UI components.
|
|
10
10
|
- [Zod](https://zod.dev/) for schemas and runtime validation.
|
|
11
|
+
- [@pierre/diffs](https://diffs.com/) for building custom Git diff views.
|
|
11
12
|
|
|
12
13
|
If your app already uses a different stack, follow its existing conventions.
|
|
@@ -122,7 +122,7 @@ Inspect these files so guidance matches the versions used by the application.
|
|
|
122
122
|
#### `tech-stack-recommendations`
|
|
123
123
|
|
|
124
124
|
```text
|
|
125
|
-
Prefer pnpm as the package manager, TanStack Router for routing, shadcn/ui for UI components, and Zod for schemas and runtime validation. Follow the application's established stack when it already differs.
|
|
125
|
+
Prefer pnpm as the package manager, TanStack Router for routing, shadcn/ui for UI components, and Zod for schemas and runtime validation. For custom Git diff views, prefer @pierre/diffs (https://diffs.com/). Follow the application's established stack when it already differs.
|
|
126
126
|
```
|
|
127
127
|
|
|
128
128
|
[//]: # (END GENERATED AI CONTEXT)
|
package/dist/exports/client.d.ts
CHANGED
|
@@ -2,12 +2,51 @@ import { B as InstanceIdentity, w as ContractInputArguments } from "./index-Dk11
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { UseMutationResult } from "@tanstack/react-query";
|
|
4
4
|
import { CSSProperties, ComponentType, ReactNode, RefObject } from "react";
|
|
5
|
-
import { KeyBinding, KeyBinding as KeyBinding$1 } from "@overmux/keybindings";
|
|
5
|
+
import { KeyBinding, KeyBinding as KeyBinding$1, KeySequence, ShortcutBinding, ShortcutBinding as ShortcutBinding$1, formatShortcutBinding } from "@overmux/keybindings";
|
|
6
6
|
declare const enableBackgroundNotifications: () => Promise<boolean>;
|
|
7
7
|
declare const disableBackgroundNotifications: () => Promise<void>;
|
|
8
|
+
type NativeWebViewLoadError = {
|
|
9
|
+
url: string;
|
|
10
|
+
code: string;
|
|
11
|
+
message: string;
|
|
12
|
+
};
|
|
13
|
+
type NativeWebViewProps = {
|
|
14
|
+
url: string;
|
|
15
|
+
className?: string;
|
|
16
|
+
style?: CSSProperties;
|
|
17
|
+
fallback?: ReactNode;
|
|
18
|
+
onLoadError?: (error: NativeWebViewLoadError) => void;
|
|
19
|
+
allowedHttpOrigins?: string[];
|
|
20
|
+
passthroughBindings?: readonly ShortcutBinding$1[];
|
|
21
|
+
};
|
|
22
|
+
declare const NativeWebView: ({ url, className, style, fallback, onLoadError, allowedHttpOrigins, passthroughBindings }: NativeWebViewProps) => import("react").JSX.Element;
|
|
23
|
+
type NativeWebViewBridge = {
|
|
24
|
+
version: 1;
|
|
25
|
+
command: (input: {
|
|
26
|
+
type: "create" | "destroy";
|
|
27
|
+
id: string;
|
|
28
|
+
} | {
|
|
29
|
+
type: "configure";
|
|
30
|
+
id: string;
|
|
31
|
+
url: string;
|
|
32
|
+
allowedHttpOrigins: string[];
|
|
33
|
+
passthroughBindings: readonly ShortcutBinding$1[];
|
|
34
|
+
}) => Promise<void>;
|
|
35
|
+
setBounds: (id: string, bounds: {
|
|
36
|
+
x: number;
|
|
37
|
+
y: number;
|
|
38
|
+
width: number;
|
|
39
|
+
height: number;
|
|
40
|
+
}) => void;
|
|
41
|
+
onLoadError: (callback: (input: {
|
|
42
|
+
id: string;
|
|
43
|
+
error: NativeWebViewLoadError;
|
|
44
|
+
}) => void) => () => void;
|
|
45
|
+
};
|
|
8
46
|
type DesktopHost = {
|
|
9
47
|
version: 1;
|
|
10
48
|
platform: string;
|
|
49
|
+
nativeWebView?: NativeWebViewBridge;
|
|
11
50
|
instance?: {
|
|
12
51
|
report: (identity: {
|
|
13
52
|
instanceId: string;
|
|
@@ -72,20 +111,17 @@ type OvermuxThemeScopeProps = {
|
|
|
72
111
|
};
|
|
73
112
|
declare const OvermuxPortal: ({ children, external }: OvermuxPortalProps) => import("react").ReactPortal | null;
|
|
74
113
|
declare const OvermuxThemeScope: ({ children, className, contrast, scheme, style }: OvermuxThemeScopeProps) => import("react").JSX.Element;
|
|
75
|
-
type KeySequence = readonly [KeyBinding$1, KeyBinding$1, ...KeyBinding$1[]];
|
|
76
|
-
type ShortcutBinding = KeyBinding$1 | KeySequence;
|
|
77
114
|
type ConditionalShortcutBinding = {
|
|
78
|
-
binding: ShortcutBinding;
|
|
115
|
+
binding: ShortcutBinding$1;
|
|
79
116
|
when: {
|
|
80
117
|
media: string;
|
|
81
118
|
};
|
|
82
119
|
};
|
|
83
|
-
type CommandBinding = ShortcutBinding | ConditionalShortcutBinding;
|
|
120
|
+
type CommandBinding = ShortcutBinding$1 | ConditionalShortcutBinding;
|
|
84
121
|
type ChordPrefix = {
|
|
85
122
|
binding: KeyBinding$1;
|
|
86
123
|
unmatched: "replay-to-focused-input";
|
|
87
124
|
};
|
|
88
|
-
declare const formatShortcutBinding: (binding: ShortcutBinding) => string;
|
|
89
125
|
type CommandRunResult$1 = Promise<unknown> | unknown;
|
|
90
126
|
type CommandRun<TServerConfig, TParams> = (input: {
|
|
91
127
|
overmuxServerApi: OvermuxServerApi<TServerConfig>;
|
|
@@ -254,5 +290,5 @@ type ShortcutInputTarget = {
|
|
|
254
290
|
input: RefObject<HTMLElement | null>;
|
|
255
291
|
};
|
|
256
292
|
declare const useShortcutInputTarget: (target: ShortcutInputTarget) => void;
|
|
257
|
-
export { type ChordPrefix, type CommandBinding, type CommandEntry, type CommandHandle, type ConditionalShortcutBinding, type InstanceIdentity, type KeyBinding, type KeySequence, type OvermuxClientAppearance, type OvermuxContrast, type OvermuxExternalPortalContainer, OvermuxHost, OvermuxPortal, type OvermuxPortalProps, type OvermuxScheme, type OvermuxServerApi, type OvermuxStyle, OvermuxThemeScope, type OvermuxThemeScopeProps, type OvermuxThemeToken, type ResourceResult, type ShortcutBinding, type ShortcutInputTarget, type SkipToken, type StreamResult, createOvermuxHooks, defineCommandRegistry, defineOvermuxClient, disableBackgroundNotifications, enableBackgroundNotifications, formatShortcutBinding, readClipboardText, skipToken, useCommand, useCommands, useShortcutInputTarget, writeClipboardText };
|
|
293
|
+
export { type ChordPrefix, type CommandBinding, type CommandEntry, type CommandHandle, type ConditionalShortcutBinding, type InstanceIdentity, type KeyBinding, type KeySequence, NativeWebView, type NativeWebViewLoadError, type NativeWebViewProps, type OvermuxClientAppearance, type OvermuxContrast, type OvermuxExternalPortalContainer, OvermuxHost, OvermuxPortal, type OvermuxPortalProps, type OvermuxScheme, type OvermuxServerApi, type OvermuxStyle, OvermuxThemeScope, type OvermuxThemeScopeProps, type OvermuxThemeToken, type ResourceResult, type ShortcutBinding, type ShortcutInputTarget, type SkipToken, type StreamResult, createOvermuxHooks, defineCommandRegistry, defineOvermuxClient, disableBackgroundNotifications, enableBackgroundNotifications, formatShortcutBinding, readClipboardText, skipToken, useCommand, useCommands, useShortcutInputTarget, writeClipboardText };
|
|
258
294
|
//# sourceMappingURL=client.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","names":[],"sources":["../../src/internal/client/background-notifications.ts","../../src/internal/client/host/desktop-host.ts","../../src/internal/client/clipboard.ts","../../src/internal/client/browser-api.ts","../../src/internal/client/theme-scope.tsx","../../src/internal/client/client-definition.ts","../../src/internal/client/commands.tsx","../../src/internal/client/overmux-react.ts","../../src/internal/client/host/overmux-host.tsx","../../src/internal/client/shortcuts.tsx"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"client.d.ts","names":[],"sources":["../../src/internal/client/background-notifications.ts","../../src/internal/client/native-web-view.tsx","../../src/internal/client/host/desktop-host.ts","../../src/internal/client/clipboard.ts","../../src/internal/client/browser-api.ts","../../src/internal/client/theme-scope.tsx","../../src/internal/client/client-definition.ts","../../src/internal/client/commands.tsx","../../src/internal/client/overmux-react.ts","../../src/internal/client/host/overmux-host.tsx","../../src/internal/client/shortcuts.tsx"],"mappings":"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"}
|
package/dist/exports/client.js
CHANGED
|
@@ -3,7 +3,7 @@ import { a as notificationSchema, c as parseDeepLink, l as resolveOvermuxReturnT
|
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { QueryClient, QueryClientProvider, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
5
5
|
import { Component, createContext, createElement, useCallback, useContext, useEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
|
|
6
|
-
import {
|
|
6
|
+
import { formatShortcutBinding, keyBindingSchema, keyBindingSequence, matchesKeyBinding, matchesShortcutBindingPrefix, normalizedShortcutBinding, shortcutBindingSchema } from "@overmux/keybindings";
|
|
7
7
|
import { createPortal } from "react-dom";
|
|
8
8
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
//#region src/internal/shared/background-notifications.ts
|
|
@@ -192,6 +192,42 @@ const decodeProtocolFrame = (frame) => {
|
|
|
192
192
|
return decodeBinaryValue(header, bytes.subarray(8 + headerLength));
|
|
193
193
|
};
|
|
194
194
|
//#endregion
|
|
195
|
+
//#region src/internal/client/register-push-worker.ts
|
|
196
|
+
const registerPushWorker = () => new Promise((resolve, reject) => {
|
|
197
|
+
let watched = null;
|
|
198
|
+
let settled = false;
|
|
199
|
+
const finish = (error, registration) => {
|
|
200
|
+
if (settled) return;
|
|
201
|
+
settled = true;
|
|
202
|
+
clearTimeout(timeout);
|
|
203
|
+
watched?.removeEventListener("statechange", onStateChange);
|
|
204
|
+
if (error) reject(error);
|
|
205
|
+
else resolve(registration);
|
|
206
|
+
};
|
|
207
|
+
let registered;
|
|
208
|
+
const onStateChange = () => {
|
|
209
|
+
if (watched?.state === "activated") finish(void 0, registered);
|
|
210
|
+
else if (watched?.state === "redundant") finish(/* @__PURE__ */ new Error("The background notification worker failed to activate"));
|
|
211
|
+
};
|
|
212
|
+
const timeout = setTimeout(() => {
|
|
213
|
+
finish(/* @__PURE__ */ new Error("Timed out starting the background notification worker"));
|
|
214
|
+
}, 1e4);
|
|
215
|
+
navigator.serviceWorker.register("/sw.js", {
|
|
216
|
+
scope: "/",
|
|
217
|
+
updateViaCache: "none"
|
|
218
|
+
}).then((registration) => {
|
|
219
|
+
if (settled) return;
|
|
220
|
+
registered = registration;
|
|
221
|
+
watched = registration.active ?? registration.installing ?? registration.waiting;
|
|
222
|
+
if (!watched) {
|
|
223
|
+
finish(/* @__PURE__ */ new Error("No background notification worker was installed"));
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
watched.addEventListener("statechange", onStateChange);
|
|
227
|
+
onStateChange();
|
|
228
|
+
}, (error) => finish(error instanceof Error ? error : new Error(String(error))));
|
|
229
|
+
});
|
|
230
|
+
//#endregion
|
|
195
231
|
//#region src/internal/client/background-notifications.ts
|
|
196
232
|
const enabledStorageKey = "overmux.backgroundNotificationsEnabled";
|
|
197
233
|
const isIos = () => /iPad|iPhone|iPod/u.test(navigator.userAgent) || navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1;
|
|
@@ -257,7 +293,7 @@ const removeCurrentSubscription = async () => {
|
|
|
257
293
|
if (!await subscription.unsubscribe()) throw new Error("The background subscription could not be removed");
|
|
258
294
|
};
|
|
259
295
|
const reconcileBackgroundNotifications = async () => {
|
|
260
|
-
const registration = await
|
|
296
|
+
const registration = await registerPushWorker();
|
|
261
297
|
const publicKey = await getPublicKey();
|
|
262
298
|
let subscription = await registration.pushManager.getSubscription();
|
|
263
299
|
const subscriptionKey = subscription ? encodeApplicationServerKey(subscription.options.applicationServerKey) : void 0;
|
|
@@ -320,7 +356,6 @@ const writeClipboardText = async (text) => {
|
|
|
320
356
|
};
|
|
321
357
|
//#endregion
|
|
322
358
|
//#region src/internal/client/client-definition.ts
|
|
323
|
-
const shortcutBindingSchema = z.union([keyBindingSchema, z.array(keyBindingSchema).min(2)]);
|
|
324
359
|
const commandBindingSchema = z.union([shortcutBindingSchema, z.object({
|
|
325
360
|
binding: shortcutBindingSchema,
|
|
326
361
|
when: z.object({ media: z.string().trim().min(1) })
|
|
@@ -329,7 +364,6 @@ const chordPrefixSchema = z.object({
|
|
|
329
364
|
binding: keyBindingSchema,
|
|
330
365
|
unmatched: z.literal("replay-to-focused-input")
|
|
331
366
|
});
|
|
332
|
-
const formatShortcutBinding = (binding) => typeof binding === "string" ? formatKeyBinding(binding) : binding.map(formatKeyBinding).join(" ");
|
|
333
367
|
const validateBindings = (bindings) => bindings?.forEach((binding) => commandBindingSchema.parse(binding));
|
|
334
368
|
const defineCommandRegistry = () => (commands) => Object.fromEntries(Object.entries(commands).map(([id, command]) => {
|
|
335
369
|
validateBindings(command.defaultBindings);
|
|
@@ -919,12 +953,6 @@ const installDeepLinkNavigation = ({ getInstance, navigate = defaultNavigate })
|
|
|
919
953
|
};
|
|
920
954
|
//#endregion
|
|
921
955
|
//#region src/internal/client/shortcuts.tsx
|
|
922
|
-
const bindingSequence = (binding) => typeof binding === "string" ? [binding] : [...binding];
|
|
923
|
-
const normalizedBinding = (binding) => bindingSequence(binding).map((key) => platformBinding(key));
|
|
924
|
-
const sequenceMatches = (binding, pending, event) => {
|
|
925
|
-
const sequence = normalizedBinding(binding);
|
|
926
|
-
return sequence.length > pending.length && pending.every((key, index) => sequence[index] === key) && matchesKeyBinding(sequence[pending.length], event);
|
|
927
|
-
};
|
|
928
956
|
const isTextInput = (element) => element instanceof HTMLInputElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement || element instanceof HTMLElement && (element.isContentEditable || element.getAttribute("role") === "textbox");
|
|
929
957
|
const acceptsTextInput = (bindings) => {
|
|
930
958
|
if (!(document.activeElement instanceof Element)) return false;
|
|
@@ -932,7 +960,7 @@ const acceptsTextInput = (bindings) => {
|
|
|
932
960
|
if (!terminal) return false;
|
|
933
961
|
if (terminal.closest("[data-copy-mode]")) return true;
|
|
934
962
|
return bindings.some((binding) => {
|
|
935
|
-
const sequence =
|
|
963
|
+
const sequence = keyBindingSequence(binding);
|
|
936
964
|
return sequence.length > 1 || sequence[0]?.startsWith("F") || sequence[0]?.includes("+");
|
|
937
965
|
});
|
|
938
966
|
};
|
|
@@ -1030,8 +1058,8 @@ const ShortcutHost = ({ children, definition, registrations, registrationRevisio
|
|
|
1030
1058
|
window.addEventListener("focusin", refreshFocus);
|
|
1031
1059
|
return () => window.removeEventListener("focusin", refreshFocus);
|
|
1032
1060
|
}, []);
|
|
1061
|
+
const shortcuts = flattenShortcuts(definition, (media) => window.matchMedia(media).matches);
|
|
1033
1062
|
useEffect(() => {
|
|
1034
|
-
const shortcuts = flattenShortcuts(definition, (media) => window.matchMedia(media).matches);
|
|
1035
1063
|
let pending = [];
|
|
1036
1064
|
let pendingEvents = [];
|
|
1037
1065
|
let pendingTarget;
|
|
@@ -1061,7 +1089,11 @@ const ShortcutHost = ({ children, definition, registrations, registrationRevisio
|
|
|
1061
1089
|
clearPending();
|
|
1062
1090
|
return;
|
|
1063
1091
|
}
|
|
1064
|
-
const matches = shortcuts.flatMap((shortcut) => shortcut.bindings.filter((binding) =>
|
|
1092
|
+
const matches = shortcuts.flatMap((shortcut) => shortcut.bindings.filter((binding) => matchesShortcutBindingPrefix({
|
|
1093
|
+
binding,
|
|
1094
|
+
input: event,
|
|
1095
|
+
pending
|
|
1096
|
+
}) && canRunShortcut(shortcut, event, registrations)).map((binding) => ({
|
|
1065
1097
|
binding,
|
|
1066
1098
|
shortcut
|
|
1067
1099
|
})));
|
|
@@ -1073,14 +1105,14 @@ const ShortcutHost = ({ children, definition, registrations, registrationRevisio
|
|
|
1073
1105
|
}
|
|
1074
1106
|
return;
|
|
1075
1107
|
}
|
|
1076
|
-
const complete = matches.find(({ binding }) =>
|
|
1108
|
+
const complete = matches.find(({ binding }) => normalizedShortcutBinding(binding).length === pending.length + 1);
|
|
1077
1109
|
if (complete) {
|
|
1078
1110
|
clearPending();
|
|
1079
1111
|
if (runShortcut(complete.shortcut, event, registrations)) event.stopPropagation();
|
|
1080
1112
|
return;
|
|
1081
1113
|
}
|
|
1082
1114
|
if (!pending.length) pendingTarget = definition.chordPrefixes?.some((prefix) => matchesKeyBinding(prefix.binding, event)) ? selectShortcutInputTarget(inputTargets, document.activeElement) : void 0;
|
|
1083
|
-
pending = [...pending,
|
|
1115
|
+
pending = [...pending, normalizedShortcutBinding(matches[0].binding)[pending.length]];
|
|
1084
1116
|
pendingEvents = [...pendingEvents, event];
|
|
1085
1117
|
event.preventDefault();
|
|
1086
1118
|
event.stopPropagation();
|
|
@@ -1514,13 +1546,17 @@ const statusMessage = (status) => {
|
|
|
1514
1546
|
};
|
|
1515
1547
|
const BrowserNotificationSettings = () => {
|
|
1516
1548
|
const [status, setStatus] = useState("checking");
|
|
1549
|
+
const [error, setError] = useState();
|
|
1517
1550
|
useEffect(() => {
|
|
1518
1551
|
let active = true;
|
|
1519
1552
|
const reconcile = () => {
|
|
1520
1553
|
reconciledStatus().then((next) => {
|
|
1521
1554
|
if (active) setStatus(next);
|
|
1522
|
-
}).catch(() => {
|
|
1523
|
-
if (active)
|
|
1555
|
+
}).catch((cause) => {
|
|
1556
|
+
if (active) {
|
|
1557
|
+
setError(cause instanceof Error ? cause.message : void 0);
|
|
1558
|
+
setStatus("error");
|
|
1559
|
+
}
|
|
1524
1560
|
});
|
|
1525
1561
|
};
|
|
1526
1562
|
const onVisibilityChange = () => {
|
|
@@ -1540,7 +1576,8 @@ const BrowserNotificationSettings = () => {
|
|
|
1540
1576
|
setStatus("checking");
|
|
1541
1577
|
const enabled = await enableBackgroundNotifications();
|
|
1542
1578
|
setStatus(enabled ? "enabled" : Notification.permission === "denied" ? "denied" : "disabled");
|
|
1543
|
-
} catch {
|
|
1579
|
+
} catch (cause) {
|
|
1580
|
+
setError(cause instanceof Error ? cause.message : void 0);
|
|
1544
1581
|
setStatus("error");
|
|
1545
1582
|
}
|
|
1546
1583
|
}, []);
|
|
@@ -1549,7 +1586,8 @@ const BrowserNotificationSettings = () => {
|
|
|
1549
1586
|
setStatus("checking");
|
|
1550
1587
|
await disableBackgroundNotifications();
|
|
1551
1588
|
setStatus("disabled");
|
|
1552
|
-
} catch {
|
|
1589
|
+
} catch (cause) {
|
|
1590
|
+
setError(cause instanceof Error ? cause.message : void 0);
|
|
1553
1591
|
setStatus("error");
|
|
1554
1592
|
}
|
|
1555
1593
|
}, []);
|
|
@@ -1559,10 +1597,10 @@ const BrowserNotificationSettings = () => {
|
|
|
1559
1597
|
"data-om-browser-notifications": "",
|
|
1560
1598
|
children: [
|
|
1561
1599
|
/* @__PURE__ */ jsx("h3", { children: "Browser notifications" }),
|
|
1562
|
-
/* @__PURE__ */
|
|
1600
|
+
/* @__PURE__ */ jsxs("p", {
|
|
1563
1601
|
"aria-live": "polite",
|
|
1564
1602
|
"data-om-background-notifications-status": "",
|
|
1565
|
-
children: statusMessage(status)
|
|
1603
|
+
children: [statusMessage(status), status === "error" && error ? ` ${error}` : null]
|
|
1566
1604
|
}),
|
|
1567
1605
|
/* @__PURE__ */ jsxs("div", {
|
|
1568
1606
|
"data-om-hosted-actions": "",
|
|
@@ -1906,6 +1944,118 @@ const OvermuxHost = ({ definition }) => /* @__PURE__ */ jsx(OvermuxThemeScope, {
|
|
|
1906
1944
|
children: location.pathname === "/_overmux/settings" ? /* @__PURE__ */ jsx(HostedSettingsPage, {}) : location.pathname === "/_overmux/logout" ? /* @__PURE__ */ jsx(HostedLogoutPage, {}) : /* @__PURE__ */ jsx(OvermuxRuntime, { definition })
|
|
1907
1945
|
});
|
|
1908
1946
|
//#endregion
|
|
1909
|
-
|
|
1947
|
+
//#region src/internal/client/native-web-view.tsx
|
|
1948
|
+
const command = async (view, input, url) => {
|
|
1949
|
+
try {
|
|
1950
|
+
await view.bridge.command(input);
|
|
1951
|
+
} catch (error) {
|
|
1952
|
+
view.report({
|
|
1953
|
+
url,
|
|
1954
|
+
code: "ERR_DESKTOP_BRIDGE",
|
|
1955
|
+
message: String(error)
|
|
1956
|
+
});
|
|
1957
|
+
}
|
|
1958
|
+
};
|
|
1959
|
+
const trackBounds = (element, view) => {
|
|
1960
|
+
let previous = "";
|
|
1961
|
+
let frame = 0;
|
|
1962
|
+
const measure = () => {
|
|
1963
|
+
const rect = element.getBoundingClientRect();
|
|
1964
|
+
const visible = !document.hidden && element.checkVisibility({
|
|
1965
|
+
opacityProperty: true,
|
|
1966
|
+
visibilityProperty: true
|
|
1967
|
+
});
|
|
1968
|
+
const bounds = {
|
|
1969
|
+
x: Math.round(rect.x),
|
|
1970
|
+
y: Math.round(rect.y),
|
|
1971
|
+
width: visible ? Math.max(0, Math.round(rect.width)) : 0,
|
|
1972
|
+
height: visible ? Math.max(0, Math.round(rect.height)) : 0
|
|
1973
|
+
};
|
|
1974
|
+
const key = JSON.stringify(bounds);
|
|
1975
|
+
if (key !== previous) {
|
|
1976
|
+
previous = key;
|
|
1977
|
+
view.bridge.setBounds(view.id, bounds);
|
|
1978
|
+
}
|
|
1979
|
+
};
|
|
1980
|
+
const tick = () => {
|
|
1981
|
+
measure();
|
|
1982
|
+
frame = requestAnimationFrame(tick);
|
|
1983
|
+
};
|
|
1984
|
+
tick();
|
|
1985
|
+
document.addEventListener("visibilitychange", measure);
|
|
1986
|
+
return () => {
|
|
1987
|
+
cancelAnimationFrame(frame);
|
|
1988
|
+
document.removeEventListener("visibilitychange", measure);
|
|
1989
|
+
};
|
|
1990
|
+
};
|
|
1991
|
+
const NativeWebView = ({ url, className, style, fallback, onLoadError, allowedHttpOrigins = [], passthroughBindings = [] }) => {
|
|
1992
|
+
const bridge = typeof window === "undefined" || window.overmuxHost?.version !== 1 || window.overmuxHost.nativeWebView?.version !== 1 ? void 0 : window.overmuxHost.nativeWebView;
|
|
1993
|
+
const element = useRef(null);
|
|
1994
|
+
const mounted = useRef(void 0);
|
|
1995
|
+
const latest = useRef({
|
|
1996
|
+
url,
|
|
1997
|
+
onLoadError
|
|
1998
|
+
});
|
|
1999
|
+
latest.current = {
|
|
2000
|
+
url,
|
|
2001
|
+
onLoadError
|
|
2002
|
+
};
|
|
2003
|
+
const origins = JSON.stringify(allowedHttpOrigins);
|
|
2004
|
+
const bindings = JSON.stringify(passthroughBindings);
|
|
2005
|
+
useEffect(() => {
|
|
2006
|
+
if (!bridge || !element.current) return;
|
|
2007
|
+
const view = {
|
|
2008
|
+
id: crypto.randomUUID(),
|
|
2009
|
+
active: true,
|
|
2010
|
+
bridge,
|
|
2011
|
+
report: (error) => {
|
|
2012
|
+
queueMicrotask(() => {
|
|
2013
|
+
if (view.active) latest.current.onLoadError?.(error);
|
|
2014
|
+
});
|
|
2015
|
+
}
|
|
2016
|
+
};
|
|
2017
|
+
mounted.current = view;
|
|
2018
|
+
const unsubscribe = bridge.onLoadError(({ id, error }) => {
|
|
2019
|
+
if (id === view.id) view.report(error);
|
|
2020
|
+
});
|
|
2021
|
+
command(view, {
|
|
2022
|
+
type: "create",
|
|
2023
|
+
id: view.id
|
|
2024
|
+
}, latest.current.url);
|
|
2025
|
+
const stopTracking = trackBounds(element.current, view);
|
|
2026
|
+
return () => {
|
|
2027
|
+
view.active = false;
|
|
2028
|
+
mounted.current = void 0;
|
|
2029
|
+
stopTracking();
|
|
2030
|
+
unsubscribe();
|
|
2031
|
+
command(view, {
|
|
2032
|
+
type: "destroy",
|
|
2033
|
+
id: view.id
|
|
2034
|
+
}, latest.current.url);
|
|
2035
|
+
};
|
|
2036
|
+
}, [bridge]);
|
|
2037
|
+
useEffect(() => {
|
|
2038
|
+
const view = mounted.current;
|
|
2039
|
+
if (view) command(view, {
|
|
2040
|
+
type: "configure",
|
|
2041
|
+
id: view.id,
|
|
2042
|
+
url,
|
|
2043
|
+
allowedHttpOrigins: JSON.parse(origins),
|
|
2044
|
+
passthroughBindings: JSON.parse(bindings)
|
|
2045
|
+
}, url);
|
|
2046
|
+
}, [
|
|
2047
|
+
bindings,
|
|
2048
|
+
bridge,
|
|
2049
|
+
url,
|
|
2050
|
+
origins
|
|
2051
|
+
]);
|
|
2052
|
+
return bridge ? /* @__PURE__ */ jsx("div", {
|
|
2053
|
+
ref: element,
|
|
2054
|
+
className,
|
|
2055
|
+
style
|
|
2056
|
+
}) : /* @__PURE__ */ jsx(Fragment, { children: fallback });
|
|
2057
|
+
};
|
|
2058
|
+
//#endregion
|
|
2059
|
+
export { NativeWebView, OvermuxHost, OvermuxPortal, OvermuxThemeScope, createOvermuxHooks, defineCommandRegistry, defineOvermuxClient, disableBackgroundNotifications, enableBackgroundNotifications, formatShortcutBinding, readClipboardText, skipToken, useCommand, useCommands, useShortcutInputTarget, writeClipboardText };
|
|
1910
2060
|
|
|
1911
2061
|
//# sourceMappingURL=client.js.map
|