overmux 0.0.3 → 0.0.5
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 +44 -0
- package/README.md +2 -2
- package/dist/auth/.vite/manifest.json +2 -2
- package/dist/auth/assets/{index-Bx8JhwVi.css → index-BM9gETvx.css} +1 -1
- package/dist/auth/assets/{index-CHIE7qYj.js → index-DabAfcs1.js} +1 -1
- package/dist/auth/index.html +2 -2
- package/dist/bin.js +219 -126
- package/dist/bin.js.map +1 -1
- package/dist/docs/000-index.md +2 -4
- package/dist/docs/100-introduction/200-how-overmux-works.md +126 -2
- package/{docs/100-introduction/300-why-overmux.md → dist/docs/100-introduction/300-why-i-built-overmux.md} +1 -1
- package/dist/docs/200-getting-started/100-install-and-run-overmux.md +2 -2
- package/dist/docs/200-getting-started/400-secure-with-https/200-tailscale-serve.md +3 -3
- package/dist/docs/200-getting-started/400-secure-with-https/300-cloudflare-tunnel.md +2 -2
- package/dist/docs/200-getting-started/400-secure-with-https/400-self-hosted-reverse-proxy.md +2 -2
- package/dist/docs/400-reference/100-project-structure.md +83 -0
- package/dist/docs/400-reference/200-configuration.md +257 -0
- package/dist/docs/400-reference/300-storage-locations.md +46 -0
- package/dist/docs/400-reference/400-authentication-and-security.md +37 -0
- package/dist/docs/400-reference/500-server/000-index.md +13 -0
- package/dist/docs/400-reference/500-server/100-resources.md +191 -0
- package/dist/docs/400-reference/500-server/200-operations.md +111 -0
- package/dist/docs/400-reference/500-server/300-streams.md +140 -0
- package/dist/docs/400-reference/500-server/400-notifications.md +32 -0
- package/dist/docs/400-reference/500-server/500-api.md +118 -0
- package/dist/docs/400-reference/600-client/000-index.md +9 -0
- package/dist/docs/400-reference/600-client/100-api.md +370 -0
- package/dist/docs/400-reference/600-client/200-theming.md +94 -0
- package/dist/docs/400-reference/600-client/300-tech-stack-recommendations.md +12 -0
- package/{docs/400-reference/400-cli → dist/docs/400-reference/700-cli}/050-init.md +2 -4
- package/dist/docs/400-reference/{400-cli → 700-cli}/100-serve.md +9 -4
- package/{docs/400-reference/400-cli → dist/docs/400-reference/700-cli}/200-auth.md +1 -4
- package/{docs/400-reference/400-cli → dist/docs/400-reference/700-cli}/300-call.md +1 -4
- package/dist/docs/400-reference/700-cli/350-instance.md +21 -0
- package/dist/docs/400-reference/700-cli/600-docs.md +128 -0
- package/{docs/400-reference/400-cli → dist/docs/400-reference/700-cli}/700-desktop.md +1 -3
- package/dist/docs/500-hosted-pages.md +0 -1
- package/dist/exports/client.d.ts +19 -15
- package/dist/exports/client.d.ts.map +1 -1
- package/dist/exports/client.js +240 -58
- package/dist/exports/client.js.map +1 -1
- package/dist/exports/{index-D3_Dpy_T.d.ts → index-Cz3xkCa4.d.ts} +62 -36
- package/dist/exports/index-Cz3xkCa4.d.ts.map +1 -0
- package/dist/exports/index.d.ts +2 -2
- package/dist/exports/index.js +19 -5
- package/dist/exports/index.js.map +1 -1
- package/dist/exports/{notifications-kDp16bU_.js → notifications-BFAD3QQl.js} +34 -4
- package/dist/exports/notifications-BFAD3QQl.js.map +1 -0
- package/dist/exports/server.d.ts +1 -44
- package/dist/exports/server.d.ts.map +1 -1
- package/dist/exports/server.js +8 -1018
- package/dist/exports/server.js.map +1 -1
- package/dist/internal/server/coordinator/server-child.js +153 -85
- package/dist/internal/server/coordinator/server-child.js.map +1 -1
- package/docs/000-index.md +2 -4
- package/docs/100-introduction/200-how-overmux-works.md +126 -2
- package/{dist/docs/100-introduction/300-why-overmux.md → docs/100-introduction/300-why-i-built-overmux.md} +1 -1
- package/docs/200-getting-started/100-install-and-run-overmux.md +2 -2
- package/docs/200-getting-started/400-secure-with-https/200-tailscale-serve.md +3 -3
- package/docs/200-getting-started/400-secure-with-https/300-cloudflare-tunnel.md +2 -2
- package/docs/200-getting-started/400-secure-with-https/400-self-hosted-reverse-proxy.md +2 -2
- package/docs/400-reference/100-project-structure.md +83 -0
- package/docs/400-reference/200-configuration.md +257 -0
- package/docs/400-reference/300-storage-locations.md +46 -0
- package/docs/400-reference/400-authentication-and-security.md +37 -0
- package/docs/400-reference/500-server/000-index.md +13 -0
- package/docs/400-reference/500-server/100-resources.md +191 -0
- package/docs/400-reference/500-server/200-operations.md +111 -0
- package/docs/400-reference/500-server/300-streams.md +140 -0
- package/docs/400-reference/500-server/400-notifications.md +32 -0
- package/docs/400-reference/500-server/500-api.md +118 -0
- package/docs/400-reference/600-client/000-index.md +9 -0
- package/docs/400-reference/600-client/100-api.md +370 -0
- package/docs/400-reference/600-client/200-theming.md +94 -0
- package/docs/400-reference/600-client/300-tech-stack-recommendations.md +12 -0
- package/{dist/docs/400-reference/400-cli → docs/400-reference/700-cli}/050-init.md +2 -4
- package/docs/400-reference/{400-cli → 700-cli}/100-serve.md +9 -4
- package/{dist/docs/400-reference/400-cli → docs/400-reference/700-cli}/200-auth.md +1 -4
- package/{dist/docs/400-reference/400-cli → docs/400-reference/700-cli}/300-call.md +1 -4
- package/docs/400-reference/700-cli/350-instance.md +21 -0
- package/docs/400-reference/700-cli/600-docs.md +128 -0
- package/{dist/docs/400-reference/400-cli → docs/400-reference/700-cli}/700-desktop.md +1 -3
- package/docs/500-hosted-pages.md +0 -1
- package/package.json +3 -2
- package/src/internal/cli/app.ts +5 -5
- package/src/internal/cli/commands/docs-ai-context.ts +33 -0
- package/src/internal/cli/commands/docs.ts +19 -2
- package/src/internal/cli/commands/init-template.ts +1 -1
- package/src/internal/cli/commands/instance.ts +58 -0
- package/src/internal/cli/commands/serve.ts +3 -0
- package/src/internal/cli/login.ts +9 -9
- package/src/internal/cli/parse-port.ts +2 -1
- package/src/internal/client/auth/auth-shell.css +17 -1
- package/src/internal/client/auth/auth-shell.tsx +2 -11
- package/src/internal/client/auth/login-code-input.tsx +140 -0
- package/src/internal/client/browser-api.ts +17 -3
- package/src/internal/client/client-definition.ts +6 -8
- package/src/internal/client/host/deep-link-navigation.ts +75 -0
- package/src/internal/client/host/desktop-host.ts +3 -0
- package/src/internal/client/host/environment.ts +67 -0
- package/src/internal/client/host/instance-forwarding.ts +21 -0
- package/src/internal/client/host/overmux-host.tsx +17 -0
- package/src/internal/client/index.ts +0 -6
- package/src/internal/client/overmux-react.ts +95 -42
- package/src/internal/client/websocket.ts +32 -12
- package/src/internal/server/auth/auth-service.ts +2 -2
- package/src/internal/server/auth/instance-control.ts +89 -17
- package/src/internal/server/coordinator/ipc-protocol.ts +0 -1
- package/src/internal/server/runtime/create-runtime.ts +15 -2
- package/src/internal/server/runtime/runtime-instance.ts +47 -0
- package/src/internal/server/runtime/runtime-operations.ts +9 -3
- package/src/internal/server/runtime/runtime-resources.ts +15 -32
- package/src/internal/server/runtime/runtime-streams.ts +5 -6
- package/src/internal/server/server-logger.ts +5 -10
- package/src/internal/server/server-startup-options.ts +14 -14
- package/src/internal/server/start-application-server.ts +24 -19
- package/src/internal/server/websocket-server.ts +7 -0
- package/src/internal/shared/index.ts +2 -0
- package/src/internal/shared/protocol.ts +10 -1
- package/src/public/ai-context.ts +7 -29
- package/src/public/client.ts +1 -6
- package/src/public/config.ts +163 -31
- package/src/public/index.ts +2 -0
- package/src/public/server.ts +1 -16
- package/dist/docs/100-introduction/100-what-is-overmux.md +0 -7
- package/dist/docs/300-fundamentals/100-project-structure.md +0 -23
- package/dist/docs/300-fundamentals/200-configuration.md +0 -3
- package/dist/docs/300-fundamentals/300-theming.md +0 -3
- package/dist/docs/300-fundamentals/400-server.md +0 -3
- package/dist/docs/300-fundamentals/500-client.md +0 -3
- package/dist/docs/300-fundamentals/600-operations.md +0 -3
- package/dist/docs/300-fundamentals/700-resources.md +0 -3
- package/dist/docs/300-fundamentals/800-streams.md +0 -3
- package/dist/docs/300-fundamentals/900-authentication-and-security.md +0 -3
- package/dist/docs/400-reference/100-configuration.md +0 -3
- package/dist/docs/400-reference/200-server-api.md +0 -3
- package/dist/docs/400-reference/300-client-api.md +0 -20
- package/dist/docs/400-reference/400-cli/400-check.md +0 -20
- package/dist/docs/400-reference/400-cli/500-ai-context.md +0 -102
- package/dist/docs/400-reference/400-cli/600-docs.md +0 -23
- package/dist/exports/index-D3_Dpy_T.d.ts.map +0 -1
- package/dist/exports/notifications-kDp16bU_.js.map +0 -1
- package/docs/100-introduction/100-what-is-overmux.md +0 -7
- package/docs/300-fundamentals/100-project-structure.md +0 -23
- package/docs/300-fundamentals/200-configuration.md +0 -3
- package/docs/300-fundamentals/300-theming.md +0 -3
- package/docs/300-fundamentals/400-server.md +0 -3
- package/docs/300-fundamentals/500-client.md +0 -3
- package/docs/300-fundamentals/600-operations.md +0 -3
- package/docs/300-fundamentals/700-resources.md +0 -3
- package/docs/300-fundamentals/800-streams.md +0 -3
- package/docs/300-fundamentals/900-authentication-and-security.md +0 -3
- package/docs/400-reference/100-configuration.md +0 -3
- package/docs/400-reference/200-server-api.md +0 -3
- package/docs/400-reference/300-client-api.md +0 -20
- package/docs/400-reference/400-cli/400-check.md +0 -20
- package/docs/400-reference/400-cli/500-ai-context.md +0 -102
- package/docs/400-reference/400-cli/600-docs.md +0 -23
- package/src/internal/cli/commands/ai.ts +0 -44
package/dist/exports/client.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import "./style.css";
|
|
2
|
-
import {
|
|
2
|
+
import { a as notificationSchema, c as parseDeepLink, l as resolveOvermuxReturnTo, o as overmuxLogoutPath, r as instanceIdSchema, t as createInstanceIdentity } from "./notifications-BFAD3QQl.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { QueryClient, QueryClientProvider, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
5
|
-
import { Component, createContext, createElement, useCallback, useContext, useEffect, useMemo, useReducer, useRef, useState } from "react";
|
|
6
|
-
import { formatKeyBinding,
|
|
5
|
+
import { Component, createContext, createElement, useCallback, useContext, useEffect, useMemo, useReducer, useRef, useState, useSyncExternalStore } from "react";
|
|
6
|
+
import { formatKeyBinding, keyBindingSchema, matchesKeyBinding, platformBinding } 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
|
|
@@ -28,7 +28,7 @@ const webSocketCloseCode = {
|
|
|
28
28
|
const runtimeManifestSchema = z.object({
|
|
29
29
|
debug: z.boolean(),
|
|
30
30
|
operations: z.array(z.string().min(1)),
|
|
31
|
-
protocolVersion: z.literal(
|
|
31
|
+
protocolVersion: z.literal(10),
|
|
32
32
|
resources: z.array(z.string().min(1)),
|
|
33
33
|
streams: z.array(z.string().min(1))
|
|
34
34
|
});
|
|
@@ -134,7 +134,12 @@ const protocolErrorMessageSchema = z.object({
|
|
|
134
134
|
subscriptionId: operationIdSchema.optional(),
|
|
135
135
|
type: z.literal("error")
|
|
136
136
|
});
|
|
137
|
+
const serverInfoMessageSchema = z.object({
|
|
138
|
+
type: z.literal("server-info"),
|
|
139
|
+
instanceId: instanceIdSchema
|
|
140
|
+
}).strict();
|
|
137
141
|
const serverProtocolMessageSchema = z.discriminatedUnion("type", [
|
|
142
|
+
serverInfoMessageSchema,
|
|
138
143
|
resourceResultMessageSchema,
|
|
139
144
|
resourceInvalidatedMessageSchema,
|
|
140
145
|
streamOpenedMessageSchema,
|
|
@@ -315,16 +320,16 @@ const writeClipboardText = async (text) => {
|
|
|
315
320
|
};
|
|
316
321
|
//#endregion
|
|
317
322
|
//#region src/internal/client/client-definition.ts
|
|
318
|
-
const shortcutBindingSchema = z.union([keyBindingSchema
|
|
323
|
+
const shortcutBindingSchema = z.union([keyBindingSchema, z.array(keyBindingSchema).min(2)]);
|
|
319
324
|
const commandBindingSchema = z.union([shortcutBindingSchema, z.object({
|
|
320
325
|
binding: shortcutBindingSchema,
|
|
321
326
|
when: z.object({ media: z.string().trim().min(1) })
|
|
322
327
|
})]);
|
|
323
328
|
const chordPrefixSchema = z.object({
|
|
324
|
-
binding: keyBindingSchema
|
|
329
|
+
binding: keyBindingSchema,
|
|
325
330
|
unmatched: z.literal("replay-to-focused-input")
|
|
326
331
|
});
|
|
327
|
-
const formatShortcutBinding = (binding) => typeof binding === "string" ? formatKeyBinding
|
|
332
|
+
const formatShortcutBinding = (binding) => typeof binding === "string" ? formatKeyBinding(binding) : binding.map(formatKeyBinding).join(" ");
|
|
328
333
|
const validateBindings = (bindings) => bindings?.forEach((binding) => commandBindingSchema.parse(binding));
|
|
329
334
|
const defineCommandRegistry = () => (commands) => Object.fromEntries(Object.entries(commands).map(([id, command]) => {
|
|
330
335
|
validateBindings(command.defaultBindings);
|
|
@@ -514,60 +519,107 @@ const useOperationById = (id, signal) => {
|
|
|
514
519
|
const useStreamById = (id, input) => {
|
|
515
520
|
const { manifest, overmuxServerApi } = useRuntime();
|
|
516
521
|
const streamAvailable = manifest.streams.includes(id);
|
|
517
|
-
const [state, setState] = useState({ status: "opening" });
|
|
518
|
-
const listeners = useMemo(() => /* @__PURE__ */ new Set(), []);
|
|
519
522
|
const canonicalInput = useCanonicalInput(input);
|
|
523
|
+
const scope = useMemo(() => ({
|
|
524
|
+
canonicalInput,
|
|
525
|
+
id,
|
|
526
|
+
overmuxServerApi,
|
|
527
|
+
streamAvailable
|
|
528
|
+
}), [
|
|
529
|
+
canonicalInput,
|
|
530
|
+
id,
|
|
531
|
+
overmuxServerApi,
|
|
532
|
+
streamAvailable
|
|
533
|
+
]);
|
|
534
|
+
const [state, setState] = useState({
|
|
535
|
+
scope,
|
|
536
|
+
status: "opening"
|
|
537
|
+
});
|
|
538
|
+
const [listeners] = useState(() => /* @__PURE__ */ new Set());
|
|
520
539
|
const streamRef = useRef(void 0);
|
|
521
540
|
useEffect(() => {
|
|
522
541
|
streamRef.current?.close();
|
|
523
542
|
streamRef.current = void 0;
|
|
524
|
-
if (!streamAvailable) {
|
|
525
|
-
setState({
|
|
543
|
+
if (!scope.streamAvailable) {
|
|
544
|
+
setState({
|
|
545
|
+
scope,
|
|
546
|
+
status: "closed"
|
|
547
|
+
});
|
|
526
548
|
return;
|
|
527
549
|
}
|
|
528
|
-
setState({
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
550
|
+
setState({
|
|
551
|
+
scope,
|
|
552
|
+
status: "opening"
|
|
553
|
+
});
|
|
554
|
+
let active = true;
|
|
555
|
+
const stream = scope.overmuxServerApi.openStream({
|
|
556
|
+
id: scope.id,
|
|
557
|
+
input: scope.canonicalInput,
|
|
558
|
+
onClose: () => active && setState({
|
|
559
|
+
scope,
|
|
560
|
+
status: "closed"
|
|
561
|
+
}),
|
|
562
|
+
onError: (error) => active && setState({
|
|
563
|
+
scope,
|
|
534
564
|
error,
|
|
535
565
|
status: "closed"
|
|
536
566
|
}),
|
|
537
|
-
onOpen: () => setState({
|
|
538
|
-
|
|
567
|
+
onOpen: () => active && setState({
|
|
568
|
+
scope,
|
|
569
|
+
connectionId: Symbol(scope.id),
|
|
570
|
+
status: "open"
|
|
571
|
+
}),
|
|
572
|
+
onMessage: (message) => active && listeners.forEach((listener) => listener(message))
|
|
539
573
|
});
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
574
|
+
const connection = {
|
|
575
|
+
close: () => {
|
|
576
|
+
if (!active) return;
|
|
577
|
+
active = false;
|
|
578
|
+
if (streamRef.current === connection) streamRef.current = void 0;
|
|
579
|
+
setState({
|
|
580
|
+
scope,
|
|
581
|
+
status: "closed"
|
|
582
|
+
});
|
|
583
|
+
stream.close();
|
|
584
|
+
},
|
|
585
|
+
send: stream.send
|
|
544
586
|
};
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
}
|
|
587
|
+
streamRef.current = connection;
|
|
588
|
+
return connection.close;
|
|
589
|
+
}, [listeners, scope]);
|
|
590
|
+
const [methods] = useState(() => ({
|
|
591
|
+
close: () => streamRef.current?.close(),
|
|
592
|
+
send: (message) => streamRef.current?.send(message) ?? false,
|
|
593
|
+
subscribe: (listener) => {
|
|
594
|
+
listeners.add(listener);
|
|
595
|
+
return () => listeners.delete(listener);
|
|
596
|
+
}
|
|
597
|
+
}));
|
|
598
|
+
if (!streamAvailable) return {
|
|
599
|
+
...methods,
|
|
600
|
+
connectionId: void 0,
|
|
601
|
+
error: /* @__PURE__ */ new Error(`Stream is not available: ${id}`),
|
|
602
|
+
status: "closed"
|
|
603
|
+
};
|
|
604
|
+
if (state.scope !== scope) return {
|
|
605
|
+
...methods,
|
|
606
|
+
connectionId: void 0,
|
|
607
|
+
status: "opening"
|
|
608
|
+
};
|
|
562
609
|
return {
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
status:
|
|
567
|
-
subscribe
|
|
610
|
+
...methods,
|
|
611
|
+
connectionId: state.connectionId,
|
|
612
|
+
error: state.error,
|
|
613
|
+
status: state.status
|
|
568
614
|
};
|
|
569
615
|
};
|
|
616
|
+
const undiscoveredInstance = () => void 0;
|
|
617
|
+
const useInstance = () => {
|
|
618
|
+
const { overmuxServerApi } = useRuntime();
|
|
619
|
+
return useSyncExternalStore(overmuxServerApi.subscribeInstance, overmuxServerApi.getInstance, undiscoveredInstance);
|
|
620
|
+
};
|
|
570
621
|
const createOvermuxHooks = () => ({
|
|
622
|
+
useInstance,
|
|
571
623
|
useOperation: ({ id, signal }) => {
|
|
572
624
|
return useOperationById(id, signal);
|
|
573
625
|
},
|
|
@@ -614,6 +666,10 @@ const availableName = (names, kind, name) => {
|
|
|
614
666
|
return name;
|
|
615
667
|
};
|
|
616
668
|
const createOvermuxServerApi = ({ manifest, transport }) => ({
|
|
669
|
+
getInstance: transport.getInstance,
|
|
670
|
+
subscribeInstance: transport.subscribeInstance,
|
|
671
|
+
getInstanceId: () => transport.getInstance()?.instanceId,
|
|
672
|
+
getDeepLinkPrefix: () => transport.getInstance()?.deepLinkPrefix,
|
|
617
673
|
executeOperation: (id, input, signal) => {
|
|
618
674
|
if (!manifest.operations.includes(id)) throw new Error(`Operation is not available: ${id}`);
|
|
619
675
|
return transport.invokeOperation({
|
|
@@ -731,12 +787,79 @@ const installNotificationForwarding = (transport) => transport.subscribeNotifica
|
|
|
731
787
|
if (notification.success) window.overmuxHost?.notifications?.show(notification.data);
|
|
732
788
|
});
|
|
733
789
|
//#endregion
|
|
790
|
+
//#region src/internal/client/host/instance-forwarding.ts
|
|
791
|
+
const installInstanceForwarding = (transport) => {
|
|
792
|
+
const report = () => {
|
|
793
|
+
const identity = transport.getInstance();
|
|
794
|
+
const bridge = window.overmuxHost?.instance;
|
|
795
|
+
if (!identity || bridge?.version !== 1) return;
|
|
796
|
+
try {
|
|
797
|
+
bridge.report({ instanceId: identity.instanceId });
|
|
798
|
+
} catch (cause) {
|
|
799
|
+
console.error("Overmux host instance reporting failed", cause);
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
const unsubscribe = transport.subscribeInstance(report);
|
|
803
|
+
report();
|
|
804
|
+
return unsubscribe;
|
|
805
|
+
};
|
|
806
|
+
//#endregion
|
|
807
|
+
//#region src/internal/client/host/deep-link-navigation.ts
|
|
808
|
+
const defaultNavigate = (route) => window.location.assign(route);
|
|
809
|
+
/**
|
|
810
|
+
* Intercepts overmux:// anchor activation inside the running app, including the
|
|
811
|
+
* temporary anchors created by xterm's default link handler. Without this,
|
|
812
|
+
* browser/PWA clicks dispatch the custom protocol to the OS, which fails in
|
|
813
|
+
* Android PWAs where it is not possible to install an Overmux protocol handler.
|
|
814
|
+
*
|
|
815
|
+
* Validates the original URL and compares its instance ID with authenticated
|
|
816
|
+
* runtime discovery. For same-instance links, preventDefault stops OS dispatch
|
|
817
|
+
* and navigate receives only the local path, query, and fragment. By default,
|
|
818
|
+
* this loads that route on the current origin; a supplied router callback avoids
|
|
819
|
+
* a document reload. Instance IDs are never treated as network hostnames.
|
|
820
|
+
*
|
|
821
|
+
* Valid cross-instance links remain Desktop's responsibility. Browsers/PWAs
|
|
822
|
+
* block unknown or different instances; all hosts block invalid links. Ordinary
|
|
823
|
+
* links and clicks already cancelled by application code are left untouched.
|
|
824
|
+
* Returns cleanup for the document listeners when the runtime unmounts.
|
|
825
|
+
*/
|
|
826
|
+
const installDeepLinkNavigation = ({ getInstance, navigate = defaultNavigate }) => {
|
|
827
|
+
const onClick = (event) => {
|
|
828
|
+
if (event.defaultPrevented || event.button > 1) return;
|
|
829
|
+
const anchor = event.composedPath().find((target) => target instanceof HTMLAnchorElement);
|
|
830
|
+
if (anchor?.protocol !== "overmux:") return;
|
|
831
|
+
let link;
|
|
832
|
+
try {
|
|
833
|
+
link = parseDeepLink(anchor.getAttribute("href") ?? "");
|
|
834
|
+
} catch {
|
|
835
|
+
event.preventDefault();
|
|
836
|
+
console.warn("Cannot open an invalid Overmux deep link.");
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
if (link.instanceId !== getInstance()?.instanceId) {
|
|
840
|
+
if (window.overmuxHost?.version !== 1) {
|
|
841
|
+
event.preventDefault();
|
|
842
|
+
console.warn("Cannot open an Overmux link to an unknown or different instance in this browser.");
|
|
843
|
+
}
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
event.preventDefault();
|
|
847
|
+
navigate(link.route);
|
|
848
|
+
};
|
|
849
|
+
document.addEventListener("click", onClick);
|
|
850
|
+
document.addEventListener("auxclick", onClick);
|
|
851
|
+
return () => {
|
|
852
|
+
document.removeEventListener("click", onClick);
|
|
853
|
+
document.removeEventListener("auxclick", onClick);
|
|
854
|
+
};
|
|
855
|
+
};
|
|
856
|
+
//#endregion
|
|
734
857
|
//#region src/internal/client/shortcuts.tsx
|
|
735
858
|
const bindingSequence = (binding) => typeof binding === "string" ? [binding] : [...binding];
|
|
736
859
|
const normalizedBinding = (binding) => bindingSequence(binding).map((key) => platformBinding(key));
|
|
737
860
|
const sequenceMatches = (binding, pending, event) => {
|
|
738
861
|
const sequence = normalizedBinding(binding);
|
|
739
|
-
return sequence.length > pending.length && pending.every((key, index) => sequence[index] === key) && matchesKeyBinding
|
|
862
|
+
return sequence.length > pending.length && pending.every((key, index) => sequence[index] === key) && matchesKeyBinding(sequence[pending.length], event);
|
|
740
863
|
};
|
|
741
864
|
const isTextInput = (element) => element instanceof HTMLInputElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement || element instanceof HTMLElement && (element.isContentEditable || element.getAttribute("role") === "textbox");
|
|
742
865
|
const acceptsTextInput = (bindings) => {
|
|
@@ -892,7 +1015,7 @@ const ShortcutHost = ({ children, definition, registrations, registrationRevisio
|
|
|
892
1015
|
if (runShortcut(complete.shortcut, event, registrations)) event.stopPropagation();
|
|
893
1016
|
return;
|
|
894
1017
|
}
|
|
895
|
-
if (!pending.length) pendingTarget = definition.chordPrefixes?.some((prefix) => matchesKeyBinding
|
|
1018
|
+
if (!pending.length) pendingTarget = definition.chordPrefixes?.some((prefix) => matchesKeyBinding(prefix.binding, event)) ? selectShortcutInputTarget(inputTargets, document.activeElement) : void 0;
|
|
896
1019
|
pending = [...pending, normalizedBinding(matches[0].binding)[pending.length]];
|
|
897
1020
|
pendingEvents = [...pendingEvents, event];
|
|
898
1021
|
event.preventDefault();
|
|
@@ -941,6 +1064,12 @@ const createWebSocketTransport = ({ createSocket = () => new WebSocket(socketUrl
|
|
|
941
1064
|
let reconnectTimer;
|
|
942
1065
|
let socket;
|
|
943
1066
|
let status = "disconnected";
|
|
1067
|
+
let instance;
|
|
1068
|
+
const instanceListeners = /* @__PURE__ */ new Set();
|
|
1069
|
+
const setInstance = (next) => {
|
|
1070
|
+
instance = next;
|
|
1071
|
+
instanceListeners.forEach((listener) => listener());
|
|
1072
|
+
};
|
|
944
1073
|
const diagnosticQueue = [];
|
|
945
1074
|
const requests = /* @__PURE__ */ new Map();
|
|
946
1075
|
const subscriptions = /* @__PURE__ */ new Map();
|
|
@@ -954,7 +1083,7 @@ const createWebSocketTransport = ({ createSocket = () => new WebSocket(socketUrl
|
|
|
954
1083
|
statusListeners.forEach((listener) => listener(status));
|
|
955
1084
|
};
|
|
956
1085
|
const send = (message) => {
|
|
957
|
-
if (socket?.readyState !== WebSocket.OPEN) return false;
|
|
1086
|
+
if (socket?.readyState !== WebSocket.OPEN || !instance) return false;
|
|
958
1087
|
if (socket.bufferedAmount > maxWebSocketBufferedBytes) {
|
|
959
1088
|
socket.close(1013, "client could not keep up");
|
|
960
1089
|
return false;
|
|
@@ -1008,6 +1137,7 @@ const createWebSocketTransport = ({ createSocket = () => new WebSocket(socketUrl
|
|
|
1008
1137
|
requests.clear();
|
|
1009
1138
|
};
|
|
1010
1139
|
const reportDisconnect = () => {
|
|
1140
|
+
setInstance(void 0);
|
|
1011
1141
|
const error = disconnected();
|
|
1012
1142
|
rejectRequests(error);
|
|
1013
1143
|
subscriptions.forEach(({ onError }) => onError(error));
|
|
@@ -1023,15 +1153,8 @@ const createWebSocketTransport = ({ createSocket = () => new WebSocket(socketUrl
|
|
|
1023
1153
|
const next = createSocket();
|
|
1024
1154
|
next.binaryType = "arraybuffer";
|
|
1025
1155
|
socket = next;
|
|
1026
|
-
next.addEventListener("open", () => {
|
|
1027
|
-
if (socket !== next) return;
|
|
1028
|
-
reconnectDelay = 250;
|
|
1029
|
-
setStatus("connected");
|
|
1030
|
-
flushDiagnostics();
|
|
1031
|
-
restore();
|
|
1032
|
-
});
|
|
1033
1156
|
next.addEventListener("message", (event) => {
|
|
1034
|
-
if (socket !== next) return;
|
|
1157
|
+
if (socket !== next || disposed) return;
|
|
1035
1158
|
let decoded;
|
|
1036
1159
|
try {
|
|
1037
1160
|
decoded = decodeProtocolFrame(event.data);
|
|
@@ -1049,6 +1172,19 @@ const createWebSocketTransport = ({ createSocket = () => new WebSocket(socketUrl
|
|
|
1049
1172
|
return;
|
|
1050
1173
|
}
|
|
1051
1174
|
const message = parsed.data;
|
|
1175
|
+
if (message.type === "server-info" && !instance) {
|
|
1176
|
+
setInstance(createInstanceIdentity(message.instanceId));
|
|
1177
|
+
reconnectDelay = 250;
|
|
1178
|
+
setStatus("connected");
|
|
1179
|
+
flushDiagnostics();
|
|
1180
|
+
restore();
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
if (!instance || message.type === "server-info") {
|
|
1184
|
+
reportDisconnect();
|
|
1185
|
+
next.close(1002, "expected server discovery before traffic");
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1052
1188
|
if (message.type === "update-available" || message.type === "restarting") lifecycleListeners.forEach((listener) => listener(message));
|
|
1053
1189
|
if (message.type === "notification") notificationListeners.forEach((listener) => listener(message));
|
|
1054
1190
|
if (message.type === "resource-invalidated") subscriptions.get(message.subscriptionId)?.onInvalidate();
|
|
@@ -1102,6 +1238,11 @@ const createWebSocketTransport = ({ createSocket = () => new WebSocket(socketUrl
|
|
|
1102
1238
|
});
|
|
1103
1239
|
};
|
|
1104
1240
|
return {
|
|
1241
|
+
getInstance: () => instance,
|
|
1242
|
+
subscribeInstance: (listener) => {
|
|
1243
|
+
instanceListeners.add(listener);
|
|
1244
|
+
return () => instanceListeners.delete(listener);
|
|
1245
|
+
},
|
|
1105
1246
|
activate: () => {
|
|
1106
1247
|
active = true;
|
|
1107
1248
|
connect();
|
|
@@ -1135,7 +1276,7 @@ const createWebSocketTransport = ({ createSocket = () => new WebSocket(socketUrl
|
|
|
1135
1276
|
streamName
|
|
1136
1277
|
};
|
|
1137
1278
|
streams.set(streamId, stream);
|
|
1138
|
-
stream.status =
|
|
1279
|
+
stream.status = status === "connecting" || send({
|
|
1139
1280
|
input,
|
|
1140
1281
|
operationId: streamId,
|
|
1141
1282
|
streamId,
|
|
@@ -1561,7 +1702,43 @@ const UpdatePopover = ({ transport }) => {
|
|
|
1561
1702
|
});
|
|
1562
1703
|
};
|
|
1563
1704
|
//#endregion
|
|
1705
|
+
//#region src/internal/client/host/environment.ts
|
|
1706
|
+
const browserPlatform = (navigator) => {
|
|
1707
|
+
const { userAgent, maxTouchPoints } = navigator;
|
|
1708
|
+
const identity = `${navigator.userAgentData?.platform ?? navigator.platform} ${userAgent}`;
|
|
1709
|
+
if (/iPad|iPhone|iPod/i.test(identity) || /Mac/i.test(identity) && maxTouchPoints > 1) return "ios";
|
|
1710
|
+
if (/Android/i.test(identity)) return "android";
|
|
1711
|
+
if (/Win/i.test(identity)) return "windows";
|
|
1712
|
+
if (/Mac/i.test(identity)) return "macos";
|
|
1713
|
+
return /Linux/i.test(identity) ? "linux" : "unknown";
|
|
1714
|
+
};
|
|
1715
|
+
const desktopPlatform = (platform) => {
|
|
1716
|
+
if (platform === "darwin") return "macos";
|
|
1717
|
+
if (platform === "win32") return "windows";
|
|
1718
|
+
return platform === "linux" ? "linux" : "unknown";
|
|
1719
|
+
};
|
|
1720
|
+
const installEnvironmentSelectors = () => {
|
|
1721
|
+
const html = document.documentElement;
|
|
1722
|
+
const host = window.overmuxHost;
|
|
1723
|
+
const desktop = host?.version === 1;
|
|
1724
|
+
const navigator = window.navigator;
|
|
1725
|
+
html.dataset.omPlatform = desktop ? desktopPlatform(host.platform) : browserPlatform(navigator);
|
|
1726
|
+
if (desktop) {
|
|
1727
|
+
html.dataset.omHost = "desktop";
|
|
1728
|
+
return () => {};
|
|
1729
|
+
}
|
|
1730
|
+
const displayMode = window.matchMedia("(display-mode: standalone), (display-mode: minimal-ui), (display-mode: window-controls-overlay)");
|
|
1731
|
+
const updateHost = () => {
|
|
1732
|
+
html.dataset.omHost = displayMode.matches || navigator.standalone === true ? "pwa" : "browser";
|
|
1733
|
+
};
|
|
1734
|
+
updateHost();
|
|
1735
|
+
displayMode.addEventListener("change", updateHost);
|
|
1736
|
+
return () => displayMode.removeEventListener("change", updateHost);
|
|
1737
|
+
};
|
|
1738
|
+
//#endregion
|
|
1564
1739
|
//#region src/internal/client/host/overmux-host.tsx
|
|
1740
|
+
const disposeEnvironment = typeof window === "undefined" ? void 0 : installEnvironmentSelectors();
|
|
1741
|
+
import.meta.hot?.dispose(() => disposeEnvironment?.());
|
|
1565
1742
|
var RootErrorBoundary = class extends Component {
|
|
1566
1743
|
state = {};
|
|
1567
1744
|
static getDerivedStateFromError(error) {
|
|
@@ -1616,6 +1793,11 @@ const OvermuxRuntime = ({ definition }) => {
|
|
|
1616
1793
|
transport.dispose();
|
|
1617
1794
|
}, [transport]);
|
|
1618
1795
|
useEffect(() => installNotificationForwarding(transport), [transport]);
|
|
1796
|
+
useEffect(() => installInstanceForwarding(transport), [transport]);
|
|
1797
|
+
useEffect(() => installDeepLinkNavigation({
|
|
1798
|
+
getInstance: transport.getInstance,
|
|
1799
|
+
navigate: definition.navigate
|
|
1800
|
+
}), [transport, definition.navigate]);
|
|
1619
1801
|
const refreshCommands = useCallback(() => setRegistrationRevision((revision) => revision + 1), []);
|
|
1620
1802
|
const registerCommand = useCallback((registration) => {
|
|
1621
1803
|
registrations.add(registration);
|
|
@@ -1660,6 +1842,6 @@ const OvermuxHost = ({ definition }) => /* @__PURE__ */ jsx(OvermuxThemeScope, {
|
|
|
1660
1842
|
children: location.pathname === "/_overmux/settings" ? /* @__PURE__ */ jsx(HostedSettingsPage, {}) : location.pathname === "/_overmux/logout" ? /* @__PURE__ */ jsx(HostedLogoutPage, {}) : /* @__PURE__ */ jsx(OvermuxRuntime, { definition })
|
|
1661
1843
|
});
|
|
1662
1844
|
//#endregion
|
|
1663
|
-
export { OvermuxHost, OvermuxPortal, OvermuxThemeScope,
|
|
1845
|
+
export { OvermuxHost, OvermuxPortal, OvermuxThemeScope, createOvermuxHooks, defineCommandRegistry, defineOvermuxClient, disableBackgroundNotifications, enableBackgroundNotifications, formatShortcutBinding, readClipboardText, skipToken, useCommand, useCommands, useShortcutInputTarget, writeClipboardText };
|
|
1664
1846
|
|
|
1665
1847
|
//# sourceMappingURL=client.js.map
|