@fungi.computer/tourtois 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +127 -2
- package/dist/AgentMcpServers.d.ts +34 -0
- package/dist/AgentMcpServers.js +124 -0
- package/dist/AppAccessConsent.d.ts +23 -0
- package/dist/AppAccessConsent.js +122 -0
- package/dist/AppCallPermissions.d.ts +9 -0
- package/dist/AppCallPermissions.js +21 -0
- package/dist/AppDesktopSettings.d.ts +11 -0
- package/dist/AppDesktopSettings.js +102 -0
- package/dist/AppPane.d.ts +37 -0
- package/dist/AppPane.js +219 -0
- package/dist/AppWorkbench.d.ts +44 -0
- package/dist/AppWorkbench.js +575 -0
- package/dist/AvatarPicker.d.ts +7 -0
- package/dist/AvatarPicker.js +78 -0
- package/dist/AvatarPreview.d.ts +10 -0
- package/dist/AvatarPreview.js +6 -0
- package/dist/DesktopMenuBar.d.ts +18 -0
- package/dist/DesktopMenuBar.js +23 -0
- package/dist/FocusedAppMenus.d.ts +2 -0
- package/dist/FocusedAppMenus.js +10 -0
- package/dist/Keys.d.ts +131 -0
- package/dist/Keys.js +201 -0
- package/dist/MushroomMark.d.ts +2 -0
- package/dist/MushroomMark.js +5 -0
- package/dist/SettingsHome.d.ts +18 -0
- package/dist/SettingsHome.js +48 -0
- package/dist/SettingsNavigation.d.ts +17 -0
- package/dist/SettingsNavigation.js +153 -0
- package/dist/SystemSettings.d.ts +24 -0
- package/dist/SystemSettings.js +177 -0
- package/dist/TeamDesktop.d.ts +36 -0
- package/dist/TeamDesktop.js +39 -0
- package/dist/WorkbenchAppBar.d.ts +5 -0
- package/dist/WorkbenchAppBar.js +12 -0
- package/dist/app-authority.d.ts +150 -0
- package/dist/app-authority.js +41 -0
- package/dist/app-directory-reconciliation.d.ts +14 -0
- package/dist/app-directory-reconciliation.js +72 -0
- package/dist/app-directory.d.ts +206 -0
- package/dist/app-directory.js +21 -0
- package/dist/app-icon.d.ts +9 -0
- package/dist/app-icon.js +27 -0
- package/dist/app-launch.d.ts +53 -0
- package/dist/app-launch.js +204 -0
- package/dist/app-menus.d.ts +6 -0
- package/dist/app-menus.js +81 -0
- package/dist/app-open-url.d.ts +8 -0
- package/dist/app-open-url.js +63 -0
- package/dist/app-surface.d.ts +62 -0
- package/dist/app-surface.js +10 -0
- package/dist/app-windows.d.ts +134 -0
- package/dist/app-windows.js +21 -0
- package/dist/chrome.css +153 -0
- package/dist/command-dialogs.d.ts +42 -0
- package/dist/command-dialogs.js +194 -0
- package/dist/desktop/Desktop.d.ts +34 -0
- package/dist/desktop/Desktop.js +275 -0
- package/dist/desktop/consent.d.ts +4 -0
- package/dist/desktop/consent.js +64 -0
- package/dist/desktop/gill-frame.d.ts +14 -0
- package/dist/desktop/gill-frame.js +41 -0
- package/dist/desktop/gill-param-picker.d.ts +15 -0
- package/dist/desktop/gill-param-picker.js +145 -0
- package/dist/desktop/launcher-gill.d.ts +20 -0
- package/dist/desktop/launcher-gill.js +49 -0
- package/dist/desktop/own-launchers.d.ts +9 -0
- package/dist/desktop/own-launchers.js +9 -0
- package/dist/desktop/row-gill.d.ts +24 -0
- package/dist/desktop/row-gill.js +136 -0
- package/dist/desktop-api.d.ts +63 -0
- package/dist/desktop-api.js +31 -0
- package/dist/desktop-client.d.ts +19 -0
- package/dist/desktop-client.js +127 -0
- package/dist/desktop-layout.d.ts +117 -0
- package/dist/desktop-layout.js +305 -0
- package/dist/desktop-visibility.d.ts +8 -0
- package/dist/desktop-visibility.js +5 -0
- package/dist/layers.d.ts +5 -0
- package/dist/layers.js +31 -0
- package/dist/main.d.ts +2 -0
- package/dist/main.js +17 -0
- package/dist/member-avatar-cache.d.ts +15 -0
- package/dist/member-avatar-cache.js +69 -0
- package/dist/member-avatar.d.ts +39 -0
- package/dist/member-avatar.js +88 -0
- package/dist/menu-bar.d.ts +5 -0
- package/dist/menu-bar.js +5 -0
- package/dist/model.d.ts +6 -0
- package/dist/model.js +13 -0
- package/dist/oyster-platform-controls.d.ts +4 -0
- package/dist/oyster-platform-controls.js +107 -0
- package/dist/platform-controls.d.ts +84 -0
- package/dist/platform-controls.js +266 -0
- package/dist/queries.d.ts +7 -0
- package/dist/queries.js +7 -0
- package/dist/session-registry.d.ts +16 -0
- package/dist/session-registry.js +69 -0
- package/dist/session-selection.d.ts +6 -0
- package/dist/session-selection.js +22 -0
- package/dist/settings-leave-guard.d.ts +5 -0
- package/dist/settings-leave-guard.js +9 -0
- package/dist/settings-navigation.css +129 -0
- package/dist/settings-routes.d.ts +118 -0
- package/dist/settings-routes.js +216 -0
- package/dist/settings-sections.d.ts +74 -0
- package/dist/settings-sections.js +17 -0
- package/dist/shell-commands.d.ts +15 -0
- package/dist/shell-commands.js +95 -0
- package/dist/shell-host.d.ts +52 -0
- package/dist/shell-host.js +288 -0
- package/dist/shell-module.d.ts +12 -0
- package/dist/shell-module.js +17 -0
- package/dist/shell-release.d.ts +3 -0
- package/dist/shell-release.js +4 -0
- package/dist/shell.css +858 -0
- package/dist/shell.d.ts +6 -0
- package/dist/shell.js +83 -0
- package/dist/spa.css +11 -0
- package/dist/status-bar.d.ts +9 -0
- package/dist/status-bar.js +51 -0
- package/dist/team-directory.d.ts +3 -0
- package/dist/team-directory.js +6 -0
- package/dist/use-window-motion.d.ts +3 -0
- package/dist/use-window-motion.js +37 -0
- package/dist/useAppOpenUrlHandoff.d.ts +11 -0
- package/dist/useAppOpenUrlHandoff.js +62 -0
- package/dist/useAppWindowManager.d.ts +351 -0
- package/dist/useAppWindowManager.js +47 -0
- package/dist/useDirectoryCommands.d.ts +12 -0
- package/dist/useDirectoryCommands.js +80 -0
- package/dist/workbench.d.ts +3 -0
- package/dist/workbench.js +2 -0
- package/package.json +222 -3
package/dist/shell.d.ts
ADDED
package/dist/shell.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { tourtoisQueryKeys } from "./queries.js";
|
|
3
|
+
import { useEffect } from "react";
|
|
4
|
+
import { createRoot } from "react-dom/client";
|
|
5
|
+
import { createQueryClient } from "@fungi.computer/agar/query";
|
|
6
|
+
import { DropdownMenuItem } from "@fungi.computer/caps/components/dropdown-menu";
|
|
7
|
+
import { tourtoisLaunchers } from "./desktop/own-launchers.js";
|
|
8
|
+
import { Sigil } from "@fungi.computer/sigils";
|
|
9
|
+
import { QueryClientProvider, useQuery, useQueryClient, } from "@tanstack/react-query";
|
|
10
|
+
import { TeamTourtois } from "./TeamDesktop.js";
|
|
11
|
+
import { AppSurfaceHostProvider } from "./app-surface.js";
|
|
12
|
+
import { accountAvatarQuery, MemberAvatarSource } from "./member-avatar.js";
|
|
13
|
+
import { createTourtoisShell } from "./shell-host.js";
|
|
14
|
+
function CanonicalStatus({ message }) {
|
|
15
|
+
return (_jsx("main", { className: "tourtois-shell tourtois-team-desktop", "data-tourtois-shell": true, children: _jsx("p", { className: "tourtois-directory-status", role: "status", children: message }) }));
|
|
16
|
+
}
|
|
17
|
+
/** Tourtois's own settings page for the Team's role defaults. */
|
|
18
|
+
const settingsLaunchers = tourtoisLaunchers.map((launcher) => ({
|
|
19
|
+
...launcher,
|
|
20
|
+
icon: _jsx(Sigil, { name: launcher.icon }),
|
|
21
|
+
}));
|
|
22
|
+
/** Read the host's Team and offered destinations before mounting the desktop. */
|
|
23
|
+
function MountedTourtois({ shell, }) {
|
|
24
|
+
const directory = useQuery({
|
|
25
|
+
queryKey: tourtoisQueryKeys.workbench(),
|
|
26
|
+
staleTime: Infinity,
|
|
27
|
+
gcTime: 0,
|
|
28
|
+
queryFn: async ({ signal }) => {
|
|
29
|
+
const workbench = createTourtoisShell(shell.workbench);
|
|
30
|
+
const [team, destinations, sections] = await Promise.all([
|
|
31
|
+
workbench.team(),
|
|
32
|
+
workbench.platformDestinations(),
|
|
33
|
+
workbench.settingsSections(),
|
|
34
|
+
]);
|
|
35
|
+
signal.throwIfAborted();
|
|
36
|
+
return {
|
|
37
|
+
team,
|
|
38
|
+
destinations,
|
|
39
|
+
sections,
|
|
40
|
+
workbench,
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
const connection = directory.data;
|
|
45
|
+
const client = useQueryClient();
|
|
46
|
+
const platform = connection?.workbench.platform;
|
|
47
|
+
// Avatar edits happen in host-rendered Settings; re-read the saved avatar.
|
|
48
|
+
useEffect(() => platform?.subscribeChanges(() => {
|
|
49
|
+
void client.invalidateQueries({ queryKey: accountAvatarQuery });
|
|
50
|
+
}), [client, platform]);
|
|
51
|
+
if (directory.isError)
|
|
52
|
+
return _jsx(CanonicalStatus, { message: "This Team dashboard is unavailable." });
|
|
53
|
+
if (!connection)
|
|
54
|
+
return _jsx(CanonicalStatus, { message: "Connecting\u2026" });
|
|
55
|
+
return (_jsx(AppSurfaceHostProvider, { value: connection.workbench.host, children: _jsx(MemberAvatarSource, { read: () => connection.workbench.memberAvatar(), children: _jsx(TeamTourtois, { teamId: connection.team.teamId, teamName: connection.team.name, computersReader: (signal) => connection.workbench
|
|
56
|
+
.computers({ limit: 100, offset: 0 })
|
|
57
|
+
.then((page) => {
|
|
58
|
+
if (signal.aborted)
|
|
59
|
+
throw new DOMException("", "AbortError");
|
|
60
|
+
return page.computers;
|
|
61
|
+
}), directoryReader: () => connection.workbench.directory(), takeAppOpenRequest: () => connection.workbench.takeAppOpenRequest(), platform: connection.workbench.platform, desktopStorage: shell.storage, desktopMethods: shell.methods, gills: connection.workbench, settingsLaunchers: settingsLaunchers.filter((launcher) => connection.sections === null ||
|
|
62
|
+
connection.sections.includes(launcher.location.category)), systemMenuItems: connection.destinations.map(({ id, label }) => (_jsx(DropdownMenuItem, { onSelect: () => {
|
|
63
|
+
void connection.workbench
|
|
64
|
+
.openPlatformDestination(id)
|
|
65
|
+
.catch((cause) => {
|
|
66
|
+
console.error("Tourtois host operation failed", cause);
|
|
67
|
+
return undefined;
|
|
68
|
+
});
|
|
69
|
+
}, children: label }, id))) }, connection.team.teamId) }) }));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Tourtois's shell module contract: render Tourtois as the host's trusted
|
|
73
|
+
* shell in this top-level document.
|
|
74
|
+
*/
|
|
75
|
+
export const mountShell = (root, shell) => {
|
|
76
|
+
const queryClient = createQueryClient();
|
|
77
|
+
const reactRoot = createRoot(root);
|
|
78
|
+
reactRoot.render(_jsx(QueryClientProvider, { client: queryClient, children: _jsx(MountedTourtois, { shell: shell }) }));
|
|
79
|
+
return Promise.resolve(() => {
|
|
80
|
+
reactRoot.unmount();
|
|
81
|
+
queryClient.clear();
|
|
82
|
+
});
|
|
83
|
+
};
|
package/dist/spa.css
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { AppStatusAction, AppStatusItem as AppStatusItemData } from "@fungi.computer/agar";
|
|
2
|
+
/** One App-declared status item, drawn by the shell as a flat status chip. */
|
|
3
|
+
export declare function WindowStatusItem({ item, container, onAction, anchorFor, }: {
|
|
4
|
+
item: AppStatusItemData;
|
|
5
|
+
container: HTMLElement | null;
|
|
6
|
+
onAction: (action: AppStatusAction) => void;
|
|
7
|
+
/** Where a chip sits along the App's surface, for UI the App opens. */
|
|
8
|
+
anchorFor: (key: HTMLElement) => AppStatusAction["anchor"];
|
|
9
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef } from "react";
|
|
3
|
+
import { AppStatusItem } from "@fungi.computer/caps/blocks/app-status-bar";
|
|
4
|
+
import { Button, } from "@fungi.computer/caps/components/button";
|
|
5
|
+
import { CountBadge } from "@fungi.computer/caps/components/count-badge";
|
|
6
|
+
import { Status } from "@fungi.computer/caps/components/status";
|
|
7
|
+
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@fungi.computer/caps/components/dropdown-menu";
|
|
8
|
+
import { Activity, Check, Folder, Plug, ShieldCheck } from "lucide-react";
|
|
9
|
+
const statusIcons = {
|
|
10
|
+
folder: _jsx(Folder, { "aria-hidden": "true" }),
|
|
11
|
+
ports: _jsx(Plug, { "aria-hidden": "true" }),
|
|
12
|
+
processes: _jsx(Activity, { "aria-hidden": "true" }),
|
|
13
|
+
access: _jsx(ShieldCheck, { "aria-hidden": "true" }),
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* The one control every App status action draws with: a flat chip beside
|
|
17
|
+
* the Computer picker, never a raised key. Caps' status-bar chip replaces
|
|
18
|
+
* this composition when it ships.
|
|
19
|
+
*/
|
|
20
|
+
const StatusBarButton = forwardRef(function StatusBarButton({ label, icon, count, pressed, expanded, ...props }, ref) {
|
|
21
|
+
return (_jsxs(Button, { ...props, ref: ref, variant: "ghost", size: "xs", className: "caps-status-bar-picker tourtois-status-chip", ...(pressed === undefined ? {} : { "aria-pressed": pressed }), ...(expanded === undefined ? {} : { "aria-expanded": expanded }), children: [icon, _jsx("span", { className: "caps-status-bar-picker-label tourtois-status-chip-label", children: label }), count] }));
|
|
22
|
+
});
|
|
23
|
+
/** One App-declared status item, drawn by the shell as a flat status chip. */
|
|
24
|
+
export function WindowStatusItem({ item, container, onAction, anchorFor, }) {
|
|
25
|
+
const count = (item.kind === "button" || item.kind === "menu") &&
|
|
26
|
+
item.count !== undefined &&
|
|
27
|
+
item.count > 0 ? (_jsx(CountBadge, { count: item.count })) : null;
|
|
28
|
+
switch (item.kind) {
|
|
29
|
+
case "text":
|
|
30
|
+
return item.tone === undefined ? (_jsx(AppStatusItem, { role: "status", children: item.text })) : (_jsx(Status, { size: "compact", tone: item.tone, label: item.text }));
|
|
31
|
+
case "button":
|
|
32
|
+
return (_jsx(StatusBarButton, { label: item.text, icon: item.icon === undefined ? undefined : statusIcons[item.icon], count: count, title: item.title ?? item.text, disabled: item.disabled, "aria-haspopup": item.opens, ...(item.expanded === undefined ? {} : { expanded: item.expanded }), onClick: (event) => {
|
|
33
|
+
const anchor = anchorFor(event.currentTarget);
|
|
34
|
+
onAction(anchor === undefined
|
|
35
|
+
? { itemId: item.id }
|
|
36
|
+
: { itemId: item.id, anchor });
|
|
37
|
+
} }));
|
|
38
|
+
case "toggle":
|
|
39
|
+
return (_jsx(StatusBarButton, { label: item.text, pressed: item.pressed, onClick: () => {
|
|
40
|
+
onAction({ itemId: item.id });
|
|
41
|
+
} }));
|
|
42
|
+
case "menu":
|
|
43
|
+
// An App's menu is a plain chip: only the host's picker wears the
|
|
44
|
+
// up-down mark, so an App can't pass a menu off as the Computer.
|
|
45
|
+
return (_jsxs(DropdownMenu, { modal: false, children: [_jsx(DropdownMenuTrigger, { render: _jsx(StatusBarButton, { label: item.text, count: count, title: item.title }) }), _jsx(DropdownMenuContent, { container: container, align: "start", children: item.options.map((option) => (_jsxs(DropdownMenuItem, { disabled: option.disabled, ...(option.checked === undefined
|
|
46
|
+
? {}
|
|
47
|
+
: { role: "menuitemradio", "aria-checked": option.checked }), onSelect: () => {
|
|
48
|
+
onAction({ itemId: item.id, optionId: option.id });
|
|
49
|
+
}, children: [_jsx("span", { className: "flex-1", children: option.text }), option.checked === true ? (_jsx(Check, { "aria-hidden": "true", size: 16 })) : null] }, option.id))) })] }));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { MaosaicoItemBinding } from "@fungi.computer/maosaico";
|
|
2
|
+
/** Animate layout changes, but track pointer gestures and reduced motion directly. */
|
|
3
|
+
export declare function useWindowMotion(binding: MaosaicoItemBinding, interacting: boolean): import("react").RefObject<HTMLElement>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { animate } from "animejs";
|
|
2
|
+
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
|
3
|
+
/** Animate layout changes, but track pointer gestures and reduced motion directly. */
|
|
4
|
+
export function useWindowMotion(binding, interacting) {
|
|
5
|
+
const element = useRef(null);
|
|
6
|
+
const placed = useRef(false);
|
|
7
|
+
const [reduced, setReduced] = useState(true);
|
|
8
|
+
useEffect(() => {
|
|
9
|
+
const media = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
10
|
+
const update = () => {
|
|
11
|
+
setReduced(media.matches);
|
|
12
|
+
};
|
|
13
|
+
update();
|
|
14
|
+
media.addEventListener("change", update);
|
|
15
|
+
return () => {
|
|
16
|
+
media.removeEventListener("change", update);
|
|
17
|
+
};
|
|
18
|
+
}, []);
|
|
19
|
+
const { x, y, width, height } = binding.frame;
|
|
20
|
+
useLayoutEffect(() => {
|
|
21
|
+
if (!element.current || !binding.visible)
|
|
22
|
+
return;
|
|
23
|
+
const motion = animate(element.current, {
|
|
24
|
+
x,
|
|
25
|
+
y,
|
|
26
|
+
width,
|
|
27
|
+
height,
|
|
28
|
+
duration: !placed.current || interacting || reduced ? 0 : 200,
|
|
29
|
+
ease: "outQuad",
|
|
30
|
+
});
|
|
31
|
+
placed.current = true;
|
|
32
|
+
return () => {
|
|
33
|
+
motion.cancel();
|
|
34
|
+
};
|
|
35
|
+
}, [x, y, width, height, binding.visible, interacting, reduced]);
|
|
36
|
+
return element;
|
|
37
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AppOpenInput } from "@fungi.computer/agar/apps";
|
|
2
|
+
import type { TourtoisAppSurfaceHost } from "./app-surface.js";
|
|
3
|
+
/** Consume the host or browser address once, after the App directory is ready. */
|
|
4
|
+
export declare function useAppOpenUrlHandoff({ teamId, host, ready, takeAppOpenRequest, onOpen, setError, }: {
|
|
5
|
+
teamId: string;
|
|
6
|
+
host: TourtoisAppSurfaceHost | null;
|
|
7
|
+
ready: boolean;
|
|
8
|
+
takeAppOpenRequest: (() => Promise<AppOpenInput | null>) | undefined;
|
|
9
|
+
onOpen: (input: AppOpenInput) => Promise<void>;
|
|
10
|
+
setError: (error: Error) => void;
|
|
11
|
+
}): void;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
import { appOpenInputFromUrl, cleanInvalidAppOpenUrlSearch, withoutAppOpenUrlSearch, } from "./app-open-url.js";
|
|
3
|
+
/** Consume the host or browser address once, after the App directory is ready. */
|
|
4
|
+
export function useAppOpenUrlHandoff({ teamId, host, ready, takeAppOpenRequest, onOpen, setError, }) {
|
|
5
|
+
const handledAppOpenUrl = useRef(null);
|
|
6
|
+
const handlers = useRef({ onOpen, setError });
|
|
7
|
+
handlers.current = { onOpen, setError };
|
|
8
|
+
const takeAppOpenRequestRef = useRef(takeAppOpenRequest);
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
const take = takeAppOpenRequestRef.current;
|
|
11
|
+
if (!take || !host || !ready)
|
|
12
|
+
return;
|
|
13
|
+
// An isolated dashboard's own address never carries the handoff; its host
|
|
14
|
+
// hands over the one from the visitor's address, exactly once.
|
|
15
|
+
takeAppOpenRequestRef.current = undefined;
|
|
16
|
+
void (async () => {
|
|
17
|
+
try {
|
|
18
|
+
const input = await take();
|
|
19
|
+
if (input)
|
|
20
|
+
await handlers.current.onOpen(input);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
const { setError } = handlers.current;
|
|
24
|
+
setError(error instanceof Error
|
|
25
|
+
? error
|
|
26
|
+
: new Error("This App could not open.", { cause: error }));
|
|
27
|
+
}
|
|
28
|
+
})();
|
|
29
|
+
}, [host, ready]);
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (takeAppOpenRequest)
|
|
32
|
+
return;
|
|
33
|
+
const current = window.location.search;
|
|
34
|
+
const cleaned = cleanInvalidAppOpenUrlSearch(current);
|
|
35
|
+
if (cleaned !== current) {
|
|
36
|
+
const destination = new URL(window.location.href);
|
|
37
|
+
destination.search = cleaned;
|
|
38
|
+
window.history.replaceState(window.history.state, "", destination);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (!host || !ready || !current)
|
|
42
|
+
return;
|
|
43
|
+
const input = appOpenInputFromUrl(current);
|
|
44
|
+
if (!input || handledAppOpenUrl.current === current)
|
|
45
|
+
return;
|
|
46
|
+
handledAppOpenUrl.current = current;
|
|
47
|
+
const destination = new URL(window.location.href);
|
|
48
|
+
destination.search = withoutAppOpenUrlSearch(current);
|
|
49
|
+
window.history.replaceState(window.history.state, "", destination);
|
|
50
|
+
void (async () => {
|
|
51
|
+
try {
|
|
52
|
+
await handlers.current.onOpen(input);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
const { setError } = handlers.current;
|
|
56
|
+
setError(error instanceof Error
|
|
57
|
+
? error
|
|
58
|
+
: new Error("This App could not open.", { cause: error }));
|
|
59
|
+
}
|
|
60
|
+
})();
|
|
61
|
+
}, [host, ready, teamId]);
|
|
62
|
+
}
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
import type { TourtoisAppMountEvents, TourtoisAppSurfaceHost } from "./app-surface.js";
|
|
2
|
+
import { type AppLaunchDescriptor } from "./app-windows.js";
|
|
3
|
+
export type WindowEvents = Required<Pick<TourtoisAppMountEvents, "onNavigation" | "onAppOpen" | "onAppRole">> & {
|
|
4
|
+
onSelectionChanged: (launch: AppLaunchDescriptor) => Promise<boolean>;
|
|
5
|
+
/** The App's own document took focus; raise its window. */
|
|
6
|
+
onAppFocus: (launch: AppLaunchDescriptor) => void;
|
|
7
|
+
};
|
|
8
|
+
/** One mounted host connection owns its windows, including StrictMode teardown. */
|
|
9
|
+
export declare function useAppWindowManager(host: TourtoisAppSurfaceHost | null, teamId: string, events: WindowEvents): Readonly<{
|
|
10
|
+
open: (launch: Readonly<{
|
|
11
|
+
windowId: string;
|
|
12
|
+
surfaceId: string;
|
|
13
|
+
installationId: string | null;
|
|
14
|
+
appId: string;
|
|
15
|
+
releaseId: string;
|
|
16
|
+
title: string;
|
|
17
|
+
href: string;
|
|
18
|
+
openRequest: import("@fungi.computer/agar").AppOpenRequest | null;
|
|
19
|
+
roles: readonly string[];
|
|
20
|
+
commands: readonly Readonly<{
|
|
21
|
+
id: string;
|
|
22
|
+
title: string;
|
|
23
|
+
category: string;
|
|
24
|
+
description?: string | undefined;
|
|
25
|
+
sigil?: string | undefined;
|
|
26
|
+
projections?: Readonly<{
|
|
27
|
+
bindings?: (Readonly<{
|
|
28
|
+
order?: number | undefined;
|
|
29
|
+
}> & Readonly<{
|
|
30
|
+
values: readonly string[];
|
|
31
|
+
}>) | undefined;
|
|
32
|
+
agent?: (Readonly<{
|
|
33
|
+
order?: number | undefined;
|
|
34
|
+
}> & Readonly<{
|
|
35
|
+
keybind?: string | undefined;
|
|
36
|
+
}>) | undefined;
|
|
37
|
+
help?: Readonly<{
|
|
38
|
+
order?: number | undefined;
|
|
39
|
+
}> | undefined;
|
|
40
|
+
menu?: Readonly<{
|
|
41
|
+
order?: number | undefined;
|
|
42
|
+
}> | undefined;
|
|
43
|
+
palette?: Readonly<{
|
|
44
|
+
order?: number | undefined;
|
|
45
|
+
}> | undefined;
|
|
46
|
+
slash?: (Readonly<{
|
|
47
|
+
order?: number | undefined;
|
|
48
|
+
}> & Readonly<{
|
|
49
|
+
aliases: readonly string[];
|
|
50
|
+
}>) | undefined;
|
|
51
|
+
}> | undefined;
|
|
52
|
+
}>[];
|
|
53
|
+
computers?: readonly Readonly<{
|
|
54
|
+
computerId: string;
|
|
55
|
+
name: string;
|
|
56
|
+
}>[];
|
|
57
|
+
}>) => Promise<void>;
|
|
58
|
+
attach: (windowId: string) => void;
|
|
59
|
+
minimize: (windowId: string) => Promise<void>;
|
|
60
|
+
restore: (windowId: string) => Promise<void>;
|
|
61
|
+
hideAll: () => Promise<void>;
|
|
62
|
+
restoreHidden: () => Promise<void>;
|
|
63
|
+
close: (windowId: string) => Promise<void>;
|
|
64
|
+
reconfigure: (windowId: string, launch: Readonly<{
|
|
65
|
+
windowId: string;
|
|
66
|
+
surfaceId: string;
|
|
67
|
+
installationId: string | null;
|
|
68
|
+
appId: string;
|
|
69
|
+
releaseId: string;
|
|
70
|
+
title: string;
|
|
71
|
+
href: string;
|
|
72
|
+
openRequest: import("@fungi.computer/agar").AppOpenRequest | null;
|
|
73
|
+
roles: readonly string[];
|
|
74
|
+
commands: readonly Readonly<{
|
|
75
|
+
id: string;
|
|
76
|
+
title: string;
|
|
77
|
+
category: string;
|
|
78
|
+
description?: string | undefined;
|
|
79
|
+
sigil?: string | undefined;
|
|
80
|
+
projections?: Readonly<{
|
|
81
|
+
bindings?: (Readonly<{
|
|
82
|
+
order?: number | undefined;
|
|
83
|
+
}> & Readonly<{
|
|
84
|
+
values: readonly string[];
|
|
85
|
+
}>) | undefined;
|
|
86
|
+
agent?: (Readonly<{
|
|
87
|
+
order?: number | undefined;
|
|
88
|
+
}> & Readonly<{
|
|
89
|
+
keybind?: string | undefined;
|
|
90
|
+
}>) | undefined;
|
|
91
|
+
help?: Readonly<{
|
|
92
|
+
order?: number | undefined;
|
|
93
|
+
}> | undefined;
|
|
94
|
+
menu?: Readonly<{
|
|
95
|
+
order?: number | undefined;
|
|
96
|
+
}> | undefined;
|
|
97
|
+
palette?: Readonly<{
|
|
98
|
+
order?: number | undefined;
|
|
99
|
+
}> | undefined;
|
|
100
|
+
slash?: (Readonly<{
|
|
101
|
+
order?: number | undefined;
|
|
102
|
+
}> & Readonly<{
|
|
103
|
+
aliases: readonly string[];
|
|
104
|
+
}>) | undefined;
|
|
105
|
+
}> | undefined;
|
|
106
|
+
}>[];
|
|
107
|
+
computers?: readonly Readonly<{
|
|
108
|
+
computerId: string;
|
|
109
|
+
name: string;
|
|
110
|
+
}>[];
|
|
111
|
+
}>) => Promise<void>;
|
|
112
|
+
update: (windowId: string, derive: (current: Readonly<{
|
|
113
|
+
windowId: string;
|
|
114
|
+
surfaceId: string;
|
|
115
|
+
installationId: string | null;
|
|
116
|
+
appId: string;
|
|
117
|
+
releaseId: string;
|
|
118
|
+
title: string;
|
|
119
|
+
href: string;
|
|
120
|
+
openRequest: import("@fungi.computer/agar").AppOpenRequest | null;
|
|
121
|
+
roles: readonly string[];
|
|
122
|
+
commands: readonly Readonly<{
|
|
123
|
+
id: string;
|
|
124
|
+
title: string;
|
|
125
|
+
category: string;
|
|
126
|
+
description?: string | undefined;
|
|
127
|
+
sigil?: string | undefined;
|
|
128
|
+
projections?: Readonly<{
|
|
129
|
+
bindings?: (Readonly<{
|
|
130
|
+
order?: number | undefined;
|
|
131
|
+
}> & Readonly<{
|
|
132
|
+
values: readonly string[];
|
|
133
|
+
}>) | undefined;
|
|
134
|
+
agent?: (Readonly<{
|
|
135
|
+
order?: number | undefined;
|
|
136
|
+
}> & Readonly<{
|
|
137
|
+
keybind?: string | undefined;
|
|
138
|
+
}>) | undefined;
|
|
139
|
+
help?: Readonly<{
|
|
140
|
+
order?: number | undefined;
|
|
141
|
+
}> | undefined;
|
|
142
|
+
menu?: Readonly<{
|
|
143
|
+
order?: number | undefined;
|
|
144
|
+
}> | undefined;
|
|
145
|
+
palette?: Readonly<{
|
|
146
|
+
order?: number | undefined;
|
|
147
|
+
}> | undefined;
|
|
148
|
+
slash?: (Readonly<{
|
|
149
|
+
order?: number | undefined;
|
|
150
|
+
}> & Readonly<{
|
|
151
|
+
aliases: readonly string[];
|
|
152
|
+
}>) | undefined;
|
|
153
|
+
}> | undefined;
|
|
154
|
+
}>[];
|
|
155
|
+
computers?: readonly Readonly<{
|
|
156
|
+
computerId: string;
|
|
157
|
+
name: string;
|
|
158
|
+
}>[];
|
|
159
|
+
}>) => Readonly<{
|
|
160
|
+
windowId: string;
|
|
161
|
+
surfaceId: string;
|
|
162
|
+
installationId: string | null;
|
|
163
|
+
appId: string;
|
|
164
|
+
releaseId: string;
|
|
165
|
+
title: string;
|
|
166
|
+
href: string;
|
|
167
|
+
openRequest: import("@fungi.computer/agar").AppOpenRequest | null;
|
|
168
|
+
roles: readonly string[];
|
|
169
|
+
commands: readonly Readonly<{
|
|
170
|
+
id: string;
|
|
171
|
+
title: string;
|
|
172
|
+
category: string;
|
|
173
|
+
description?: string | undefined;
|
|
174
|
+
sigil?: string | undefined;
|
|
175
|
+
projections?: Readonly<{
|
|
176
|
+
bindings?: (Readonly<{
|
|
177
|
+
order?: number | undefined;
|
|
178
|
+
}> & Readonly<{
|
|
179
|
+
values: readonly string[];
|
|
180
|
+
}>) | undefined;
|
|
181
|
+
agent?: (Readonly<{
|
|
182
|
+
order?: number | undefined;
|
|
183
|
+
}> & Readonly<{
|
|
184
|
+
keybind?: string | undefined;
|
|
185
|
+
}>) | undefined;
|
|
186
|
+
help?: Readonly<{
|
|
187
|
+
order?: number | undefined;
|
|
188
|
+
}> | undefined;
|
|
189
|
+
menu?: Readonly<{
|
|
190
|
+
order?: number | undefined;
|
|
191
|
+
}> | undefined;
|
|
192
|
+
palette?: Readonly<{
|
|
193
|
+
order?: number | undefined;
|
|
194
|
+
}> | undefined;
|
|
195
|
+
slash?: (Readonly<{
|
|
196
|
+
order?: number | undefined;
|
|
197
|
+
}> & Readonly<{
|
|
198
|
+
aliases: readonly string[];
|
|
199
|
+
}>) | undefined;
|
|
200
|
+
}> | undefined;
|
|
201
|
+
}>[];
|
|
202
|
+
computers?: readonly Readonly<{
|
|
203
|
+
computerId: string;
|
|
204
|
+
name: string;
|
|
205
|
+
}>[];
|
|
206
|
+
}>) => Promise<void>;
|
|
207
|
+
replace: (windowId: string, launch: Readonly<{
|
|
208
|
+
windowId: string;
|
|
209
|
+
surfaceId: string;
|
|
210
|
+
installationId: string | null;
|
|
211
|
+
appId: string;
|
|
212
|
+
releaseId: string;
|
|
213
|
+
title: string;
|
|
214
|
+
href: string;
|
|
215
|
+
openRequest: import("@fungi.computer/agar").AppOpenRequest | null;
|
|
216
|
+
roles: readonly string[];
|
|
217
|
+
commands: readonly Readonly<{
|
|
218
|
+
id: string;
|
|
219
|
+
title: string;
|
|
220
|
+
category: string;
|
|
221
|
+
description?: string | undefined;
|
|
222
|
+
sigil?: string | undefined;
|
|
223
|
+
projections?: Readonly<{
|
|
224
|
+
bindings?: (Readonly<{
|
|
225
|
+
order?: number | undefined;
|
|
226
|
+
}> & Readonly<{
|
|
227
|
+
values: readonly string[];
|
|
228
|
+
}>) | undefined;
|
|
229
|
+
agent?: (Readonly<{
|
|
230
|
+
order?: number | undefined;
|
|
231
|
+
}> & Readonly<{
|
|
232
|
+
keybind?: string | undefined;
|
|
233
|
+
}>) | undefined;
|
|
234
|
+
help?: Readonly<{
|
|
235
|
+
order?: number | undefined;
|
|
236
|
+
}> | undefined;
|
|
237
|
+
menu?: Readonly<{
|
|
238
|
+
order?: number | undefined;
|
|
239
|
+
}> | undefined;
|
|
240
|
+
palette?: Readonly<{
|
|
241
|
+
order?: number | undefined;
|
|
242
|
+
}> | undefined;
|
|
243
|
+
slash?: (Readonly<{
|
|
244
|
+
order?: number | undefined;
|
|
245
|
+
}> & Readonly<{
|
|
246
|
+
aliases: readonly string[];
|
|
247
|
+
}>) | undefined;
|
|
248
|
+
}> | undefined;
|
|
249
|
+
}>[];
|
|
250
|
+
computers?: readonly Readonly<{
|
|
251
|
+
computerId: string;
|
|
252
|
+
name: string;
|
|
253
|
+
}>[];
|
|
254
|
+
}>) => Promise<void>;
|
|
255
|
+
back: (windowId: string) => Promise<boolean>;
|
|
256
|
+
activateNavigation: (windowId: string, activation: import("@fungi.computer/agar").AppNavigationActivation) => Promise<boolean>;
|
|
257
|
+
activateStatus: (windowId: string, action: import("@fungi.computer/agar").AppStatusAction) => Promise<boolean>;
|
|
258
|
+
registerComputerChoice: (windowId: string, handler: () => void) => () => void;
|
|
259
|
+
dispose: () => Promise<void>;
|
|
260
|
+
read: () => Readonly<{
|
|
261
|
+
windows: readonly Readonly<{
|
|
262
|
+
windowId: string;
|
|
263
|
+
launch: Readonly<{
|
|
264
|
+
windowId: string;
|
|
265
|
+
surfaceId: string;
|
|
266
|
+
installationId: string | null;
|
|
267
|
+
appId: string;
|
|
268
|
+
releaseId: string;
|
|
269
|
+
title: string;
|
|
270
|
+
href: string;
|
|
271
|
+
openRequest: import("@fungi.computer/agar").AppOpenRequest | null;
|
|
272
|
+
roles: readonly string[];
|
|
273
|
+
commands: readonly Readonly<{
|
|
274
|
+
id: string;
|
|
275
|
+
title: string;
|
|
276
|
+
category: string;
|
|
277
|
+
description?: string | undefined;
|
|
278
|
+
sigil?: string | undefined;
|
|
279
|
+
projections?: Readonly<{
|
|
280
|
+
bindings?: (Readonly<{
|
|
281
|
+
order?: number | undefined;
|
|
282
|
+
}> & Readonly<{
|
|
283
|
+
values: readonly string[];
|
|
284
|
+
}>) | undefined;
|
|
285
|
+
agent?: (Readonly<{
|
|
286
|
+
order?: number | undefined;
|
|
287
|
+
}> & Readonly<{
|
|
288
|
+
keybind?: string | undefined;
|
|
289
|
+
}>) | undefined;
|
|
290
|
+
help?: Readonly<{
|
|
291
|
+
order?: number | undefined;
|
|
292
|
+
}> | undefined;
|
|
293
|
+
menu?: Readonly<{
|
|
294
|
+
order?: number | undefined;
|
|
295
|
+
}> | undefined;
|
|
296
|
+
palette?: Readonly<{
|
|
297
|
+
order?: number | undefined;
|
|
298
|
+
}> | undefined;
|
|
299
|
+
slash?: (Readonly<{
|
|
300
|
+
order?: number | undefined;
|
|
301
|
+
}> & Readonly<{
|
|
302
|
+
aliases: readonly string[];
|
|
303
|
+
}>) | undefined;
|
|
304
|
+
}> | undefined;
|
|
305
|
+
}>[];
|
|
306
|
+
computers?: readonly Readonly<{
|
|
307
|
+
computerId: string;
|
|
308
|
+
name: string;
|
|
309
|
+
}>[];
|
|
310
|
+
}>;
|
|
311
|
+
container: HTMLElement;
|
|
312
|
+
status: import("@fungi.computer/oyster/shell/window-manager").WindowStatus;
|
|
313
|
+
generation: number;
|
|
314
|
+
error?: unknown;
|
|
315
|
+
cleanupError?: unknown;
|
|
316
|
+
navigation: Readonly<{
|
|
317
|
+
canGoBack: boolean;
|
|
318
|
+
title?: string;
|
|
319
|
+
crumbs?: readonly Readonly<{
|
|
320
|
+
id: string;
|
|
321
|
+
label: string;
|
|
322
|
+
siblings?: readonly Readonly<{
|
|
323
|
+
id: string;
|
|
324
|
+
label: string;
|
|
325
|
+
}>[];
|
|
326
|
+
}>[];
|
|
327
|
+
primary?: readonly Readonly<{
|
|
328
|
+
id: string;
|
|
329
|
+
label: string;
|
|
330
|
+
variant?: "primary" | "outline" | "ghost";
|
|
331
|
+
tone?: "destructive";
|
|
332
|
+
disabled?: boolean;
|
|
333
|
+
}>[];
|
|
334
|
+
menu?: readonly Readonly<{
|
|
335
|
+
id: string;
|
|
336
|
+
label: string;
|
|
337
|
+
variant?: "primary" | "outline" | "ghost";
|
|
338
|
+
tone?: "destructive";
|
|
339
|
+
disabled?: boolean;
|
|
340
|
+
}>[];
|
|
341
|
+
status?: readonly Readonly<{
|
|
342
|
+
id: string;
|
|
343
|
+
label: string;
|
|
344
|
+
tone?: "neutral" | "info" | "warning" | "success" | "error";
|
|
345
|
+
}>[];
|
|
346
|
+
}> | null;
|
|
347
|
+
statusItems: readonly import("@fungi.computer/agar").AppStatusItem[];
|
|
348
|
+
}>[];
|
|
349
|
+
}>;
|
|
350
|
+
subscribe: (listener: () => void) => () => void;
|
|
351
|
+
}>;
|