zidane-ui 9.1.13 → 9.1.16
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/dist/adapters/index.d.ts +2 -2
- package/dist/adapters/index.js +2 -2
- package/dist/adapters/live.d.ts +29 -3
- package/dist/adapters/live.js +9 -1
- package/dist/adapters/server-composer-host.js +17 -2
- package/dist/adapters/server-extensions-host.d.ts +0 -1
- package/dist/adapters/server-extensions-host.js +4 -15
- package/dist/adapters/server-git-host.js +7 -16
- package/dist/adapters/server-host.js +181 -43
- package/dist/adapters/server-machines-host.d.ts +2 -0
- package/dist/adapters/server-machines-host.js +22 -23
- package/dist/adapters/server-onboarding-host.d.ts +1 -1
- package/dist/adapters/server-onboarding-host.js +3 -4
- package/dist/adapters/server-session-host.js +22 -3
- package/dist/adapters/server-settings-accounts.js +2 -2
- package/dist/adapters/server-settings-host.js +2 -2
- package/dist/adapters/server-settings-performance.js +7 -9
- package/dist/adapters/server-settings-sections.js +13 -14
- package/dist/adapters/server-sidebar-host.js +2 -2
- package/dist/adapters/sse-host.js +68 -3
- package/dist/adapters/sse-protocol.d.ts +12 -2
- package/dist/adapters/sse-protocol.js +4 -2
- package/dist/app/AppHostProvider.js +4 -3
- package/dist/app/ModalProvider.d.ts +13 -3
- package/dist/app/ModalProvider.js +103 -24
- package/dist/app/NoticeStack.d.ts +33 -0
- package/dist/app/NoticeStack.js +90 -0
- package/dist/app/index.d.ts +3 -2
- package/dist/app/index.js +3 -2
- package/dist/chat/SessionFilterMenu.d.ts +1 -1
- package/dist/chat/SessionFilterMenu.js +2 -1
- package/dist/chat/composer/Composer.js +10 -8
- package/dist/chat/composer/ModelChainList.js +17 -8
- package/dist/chat/composer/ModelChainTooltip.js +5 -2
- package/dist/chat/composer/use-draft-target.d.ts +20 -1
- package/dist/chat/composer/use-draft-target.js +121 -5
- package/dist/chat/composer/use-new-session-defaults.d.ts +5 -1
- package/dist/chat/composer/use-new-session-defaults.js +17 -4
- package/dist/chat/headless/chip-labels.d.ts +1 -0
- package/dist/chat/headless/chip-labels.js +1 -0
- package/dist/chat/headless/composer/labels-editor.d.ts +0 -2
- package/dist/chat/headless/composer/labels-editor.js +0 -2
- package/dist/chat/headless/draft-target.d.ts +21 -1
- package/dist/chat/headless/fixture-host.js +17 -2
- package/dist/chat/headless/host.d.ts +3 -3
- package/dist/chat/headless/index.d.ts +9 -9
- package/dist/chat/headless/index.js +2 -2
- package/dist/chat/headless/labels.d.ts +13 -6
- package/dist/chat/headless/labels.js +10 -9
- package/dist/chat/headless/session-list.d.ts +20 -8
- package/dist/chat/headless/session-list.js +1 -0
- package/dist/chat/headless/sidebar/directory.d.ts +56 -14
- package/dist/chat/headless/sidebar/directory.js +149 -70
- package/dist/chat/headless/sidebar/index.d.ts +3 -3
- package/dist/chat/headless/sidebar/index.js +2 -2
- package/dist/chat/headless/sidebar/labels.d.ts +15 -8
- package/dist/chat/headless/sidebar/types.d.ts +3 -1
- package/dist/chat/headless/sidebar/view.d.ts +13 -5
- package/dist/chat/headless/sidebar/view.js +31 -11
- package/dist/chat/headless/streams/actions.d.ts +5 -3
- package/dist/chat/headless/streams/actions.js +3 -2
- package/dist/chat/headless/streams/index.d.ts +3 -3
- package/dist/chat/headless/streams/merge.d.ts +6 -10
- package/dist/chat/headless/streams/merge.js +7 -21
- package/dist/chat/headless/streams/session-streams.d.ts +29 -4
- package/dist/chat/headless/streams/session-streams.js +86 -0
- package/dist/chat/headless/streams/store.d.ts +162 -16
- package/dist/chat/headless/streams/store.js +334 -38
- package/dist/chat/headless/streams/types.d.ts +10 -7
- package/dist/chat/headless/transcript-labels.d.ts +1 -0
- package/dist/chat/interactions/PlanCard.js +10 -3
- package/dist/chat/interactions/QuestionForm.js +7 -3
- package/dist/chat/interactions/card-ui.d.ts +5 -3
- package/dist/chat/interactions/card-ui.js +9 -7
- package/dist/chat/rich-composer/RichComposer.js +1 -7
- package/dist/chat/rich-composer/composer-extensions.d.ts +0 -2
- package/dist/chat/rich-composer/composer-extensions.js +2 -2
- package/dist/chat/rich-composer/use-composer-editor.d.ts +0 -2
- package/dist/chat/rich-composer/use-composer-editor.js +1 -2
- package/dist/chat/sidebar/SessionSidebar.js +5 -5
- package/dist/chat/sidebar/SidebarFilterMenu.js +3 -1
- package/dist/chat/sidebar/SidebarFrame.d.ts +4 -0
- package/dist/chat/sidebar/SidebarFrame.js +27 -9
- package/dist/chat/sidebar/SidebarGroupHeader.js +1 -1
- package/dist/chat/sidebar/SidebarHostProvider.d.ts +8 -5
- package/dist/chat/sidebar/SidebarHostProvider.js +13 -9
- package/dist/chat/sidebar/SidebarToggleButton.js +7 -3
- package/dist/chat/sidebar/index.d.ts +2 -2
- package/dist/chat/sidebar/session-menu.js +4 -4
- package/dist/chat/sidebar/useSaveViewAsSpace.js +2 -2
- package/dist/chat/testing/contract.js +2 -2
- package/dist/chat/transcript/AuthFailureCard.js +11 -8
- package/dist/chat/transcript/BadgeMenu.js +13 -2
- package/dist/chat/transcript/MermaidDiagram.js +7 -3
- package/dist/chat/transcript/fold.d.ts +4 -0
- package/dist/chat/transcript/fold.js +53 -22
- package/dist/chat/transcript/script-input.d.ts +1 -1
- package/dist/chat/transcript/shell-block.js +7 -3
- package/dist/chat/transcript/useStickToBottom.js +12 -0
- package/dist/components/BrowserSignIn.js +23 -21
- package/dist/components/Form.d.ts +6 -4
- package/dist/components/Form.js +16 -8
- package/dist/components/LoadError.d.ts +4 -2
- package/dist/components/LoadError.js +13 -9
- package/dist/components/Notice.d.ts +29 -0
- package/dist/components/Notice.js +63 -0
- package/dist/components/PanelEmpty.d.ts +1 -1
- package/dist/components/PanelEmpty.js +4 -2
- package/dist/components/Restricted.d.ts +1 -1
- package/dist/components/palette/PaletteList.js +8 -1
- package/dist/components/palette/PalettePanel.js +14 -6
- package/dist/core.css +24 -5
- package/dist/dist/{index-Bq87OcGy.d.ts → index-BZM8tu4L.d.ts} +28 -3
- package/dist/dist/index.d.ts +2 -2
- package/dist/dist/{timeout-B5pO5I5a.d.ts → timeout-Cyx3VGPz.d.ts} +1 -1
- package/dist/extensions/ExtensionSettingsPage.js +1 -4
- package/dist/extensions/ExtensionSettingsSection.js +4 -3
- package/dist/extensions/ExtensionSettingsSidebar.js +2 -6
- package/dist/extensions/SettingsWireForm.d.ts +1 -7
- package/dist/extensions/SettingsWireForm.js +7 -5
- package/dist/extensions/headless/fixture-host.d.ts +1 -5
- package/dist/extensions/headless/fixture-host.js +1 -4
- package/dist/extensions/headless/host.d.ts +1 -11
- package/dist/extensions/headless/index.d.ts +3 -3
- package/dist/extensions/headless/index.js +2 -2
- package/dist/extensions/headless/select.d.ts +1 -11
- package/dist/extensions/headless/select.js +1 -18
- package/dist/files/EditorColumn.js +12 -1
- package/dist/files/TreeColumn.js +13 -20
- package/dist/files/useOpenFiles.js +2 -0
- package/dist/git/BranchMenu.js +5 -3
- package/dist/git/ImportRepoDialog.js +18 -9
- package/dist/git/MoveSessionDialog.js +15 -7
- package/dist/git/RemoteFolderPicker.js +34 -16
- package/dist/git/SyncChip.js +17 -17
- package/dist/git/WherePicker.d.ts +16 -1
- package/dist/git/WherePicker.js +80 -36
- package/dist/git/headless/environment.d.ts +33 -3
- package/dist/git/headless/environment.js +33 -5
- package/dist/git/headless/index.d.ts +2 -2
- package/dist/git/headless/index.js +2 -2
- package/dist/git/headless/labels.d.ts +15 -2
- package/dist/git/headless/labels.js +17 -4
- package/dist/git/useEnvironmentChoices.d.ts +3 -1
- package/dist/git/useEnvironmentChoices.js +6 -3
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/labels.d.ts +5 -0
- package/dist/labels.js +5 -0
- package/dist/machines/AddMachineDialog.d.ts +34 -0
- package/dist/machines/AddMachineDialog.js +464 -0
- package/dist/machines/Banners.d.ts +33 -4
- package/dist/machines/Banners.js +64 -3
- package/dist/machines/DaemonMenu.js +27 -4
- package/dist/machines/HostCapabilities.d.ts +1 -3
- package/dist/machines/HostCapabilities.js +24 -45
- package/dist/machines/HostSyncPanel.js +123 -129
- package/dist/machines/HostsSection.d.ts +4 -1
- package/dist/machines/HostsSection.js +30 -7
- package/dist/machines/MachinesExplainer.d.ts +17 -0
- package/dist/machines/MachinesExplainer.js +71 -0
- package/dist/machines/MachinesProvider.d.ts +14 -2
- package/dist/machines/MachinesProvider.js +35 -1
- package/dist/machines/ServerAddedCard.d.ts +31 -0
- package/dist/machines/ServerAddedCard.js +72 -0
- package/dist/machines/ServerCard.js +91 -215
- package/dist/machines/ServerMachinesPanel.d.ts +2 -2
- package/dist/machines/ServerMachinesPanel.js +184 -323
- package/dist/machines/ServerUpdatePanel.js +51 -40
- package/dist/machines/ShareThisComputerForm.d.ts +19 -0
- package/dist/machines/ShareThisComputerForm.js +133 -0
- package/dist/machines/TokensPanel.d.ts +5 -3
- package/dist/machines/TokensPanel.js +98 -78
- package/dist/machines/headless/fixture-host.d.ts +19 -3
- package/dist/machines/headless/fixture-host.js +42 -5
- package/dist/machines/headless/fixtures.js +4 -6
- package/dist/machines/headless/host.d.ts +40 -19
- package/dist/machines/headless/index.d.ts +3 -3
- package/dist/machines/headless/index.js +2 -2
- package/dist/machines/headless/labels.d.ts +63 -20
- package/dist/machines/headless/labels.js +88 -25
- package/dist/machines/headless/messages.js +8 -0
- package/dist/machines/headless/stores.d.ts +9 -2
- package/dist/machines/headless/stores.js +28 -1
- package/dist/machines/index.d.ts +7 -3
- package/dist/machines/index.js +8 -4
- package/dist/machines/testing/contract.d.ts +15 -3
- package/dist/machines/testing/contract.js +68 -14
- package/dist/motion/grow.d.ts +8 -1
- package/dist/motion/grow.js +10 -4
- package/dist/onboarding/demos/index.js +1 -1
- package/dist/onboarding/headless/labels.d.ts +13 -0
- package/dist/onboarding/headless/labels.js +8 -0
- package/dist/onboarding/index.js +3 -3
- package/dist/onboarding/sources/SourceBrowser.js +11 -7
- package/dist/onboarding/steps/ConnectorsStep.js +1 -1
- package/dist/onboarding/steps/ImportStep.js +4 -1
- package/dist/onboarding/steps/ReadyStep.js +9 -2
- package/dist/onboarding/steps/SkillsStep.js +1 -1
- package/dist/onboarding/steps/connect/KeyForm.js +6 -2
- package/dist/onboarding/steps/connect/ProviderPanel.js +6 -2
- package/dist/onboarding/steps/connect/ProviderReady.js +25 -20
- package/dist/onboarding/steps/tasks.js +17 -14
- package/dist/server/sse-handler.js +141 -19
- package/dist/session/ArtifactsPanel.js +39 -17
- package/dist/session/PlanTracker.js +12 -7
- package/dist/session/ProjectHome.js +13 -11
- package/dist/session/RemindersPanel.js +34 -16
- package/dist/session/RunningWorkDialog.js +6 -2
- package/dist/session/SecretsPanel.js +29 -10
- package/dist/session/SessionDetailsDialog.d.ts +1 -1
- package/dist/session/SessionDetailsDialog.js +18 -10
- package/dist/session/SideChat.js +10 -7
- package/dist/session/TurnDetailsDialog.js +19 -5
- package/dist/session/UsagePanel.js +10 -4
- package/dist/session/headless/host.d.ts +2 -0
- package/dist/session/headless/labels.d.ts +19 -1
- package/dist/session/headless/labels.js +15 -1
- package/dist/session/headless/queue.js +2 -1
- package/dist/session/headless/reminder-labels.d.ts +4 -0
- package/dist/session/headless/reminder-labels.js +4 -0
- package/dist/settings/SettingsRestricted.d.ts +1 -1
- package/dist/settings/SettingsRestricted.js +4 -4
- package/dist/settings/SettingsShell.d.ts +7 -0
- package/dist/settings/SettingsShell.js +6 -1
- package/dist/settings/headless/fixture-performance.d.ts +1 -1
- package/dist/settings/headless/host.d.ts +2 -2
- package/dist/settings/headless/labels.d.ts +3 -3
- package/dist/settings/headless/labels.js +3 -3
- package/dist/settings/headless/performance.d.ts +3 -4
- package/dist/settings/headless/search.d.ts +1 -1
- package/dist/settings/index.d.ts +2 -1
- package/dist/settings/index.js +2 -1
- package/dist/settings/sections/editor/EditorSection.js +7 -4
- package/dist/settings/sections/editor/labels.d.ts +1 -0
- package/dist/settings/sections/editor/labels.js +1 -0
- package/dist/settings/sections/environments/EnvironmentsSection.js +19 -12
- package/dist/settings/sections/environments/labels.d.ts +1 -0
- package/dist/settings/sections/environments/labels.js +2 -1
- package/dist/settings/sections/extensions/ExtensionConfigRows.d.ts +1 -3
- package/dist/settings/sections/extensions/ExtensionConfigRows.js +20 -19
- package/dist/settings/sections/extensions/ExtensionOptionsSection.js +2 -1
- package/dist/settings/sections/extensions/options-labels.d.ts +2 -1
- package/dist/settings/sections/extensions/options-labels.js +2 -1
- package/dist/settings/sections/index.d.ts +2 -1
- package/dist/settings/sections/index.js +2 -1
- package/dist/settings/sections/logs/LogsSection.d.ts +2 -2
- package/dist/settings/sections/logs/LogsSection.js +2 -2
- package/dist/settings/sections/mcp/McpSection.d.ts +1 -1
- package/dist/settings/sections/mcp/McpSection.js +12 -6
- package/dist/settings/sections/mcp/McpServerRow.js +20 -13
- package/dist/settings/sections/mcp/labels.d.ts +8 -2
- package/dist/settings/sections/mcp/labels.js +8 -2
- package/dist/settings/sections/models/ModelRows.js +1 -1
- package/dist/settings/sections/models/NewChatPlaceRow.d.ts +26 -0
- package/dist/settings/sections/models/NewChatPlaceRow.js +78 -0
- package/dist/settings/sections/models/SpacesDefaultsSection.js +15 -3
- package/dist/settings/sections/models/labels.d.ts +7 -0
- package/dist/settings/sections/models/labels.js +7 -0
- package/dist/settings/sections/models/model-test.js +7 -4
- package/dist/settings/sections/performance/PerformanceSection.js +16 -6
- package/dist/settings/sections/performance/labels.d.ts +1 -1
- package/dist/settings/sections/performance/labels.js +1 -1
- package/dist/settings/sections/providers/ProvidersSection.d.ts +1 -1
- package/dist/settings/sections/providers/ProvidersSection.js +14 -7
- package/dist/settings/sections/providers/ServerProvidersSection.d.ts +1 -1
- package/dist/settings/sections/providers/ServerProvidersSection.js +11 -5
- package/dist/settings/sections/providers/labels.d.ts +2 -2
- package/dist/settings/sections/providers/labels.js +2 -2
- package/dist/settings/sections/skills/SkillList.js +1 -1
- package/dist/settings/sections/skills/SkillsSection.js +1 -1
- package/dist/settings/testing/performance.contract.js +8 -12
- package/dist/styles.css +32 -3
- package/dist/terminal/HostTerminalSurface.js +15 -5
- package/dist/terminal/TerminalPanel.js +30 -17
- package/dist/terminal/TerminalSurface.js +11 -4
- package/dist/terminal/headless/labels.d.ts +5 -0
- package/dist/terminal/headless/labels.js +5 -0
- package/dist/terminal/headless/store.d.ts +1 -1
- package/dist/terminal/headless/store.js +6 -3
- package/dist/workspace/LayoutBudget.d.ts +5 -1
- package/dist/workspace/LayoutBudget.js +14 -1
- package/dist/workspace/PanelContentView.d.ts +1 -1
- package/dist/workspace/PanelContentView.js +17 -6
- package/dist/workspace/WorkspaceProvider.js +3 -2
- package/dist/workspace/headless/labels.d.ts +1 -0
- package/dist/workspace/headless/labels.js +1 -0
- package/package.json +3 -3
- package/src/styles/motion.css +8 -0
- package/src/styles/primitives.css +7 -0
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
import { useLabels } from "../labels.js";
|
|
2
|
+
import { IconButton } from "../components/IconButton.js";
|
|
3
|
+
import { BTN_PRIMARY, BTN_SECONDARY } from "../components/button.js";
|
|
4
|
+
import { CopyButton } from "../components/CopyButton.js";
|
|
5
|
+
import { Notice } from "../components/Notice.js";
|
|
6
|
+
import { Segmented } from "../components/Segmented.js";
|
|
7
|
+
import { FIELD_LABEL_CLASS, INPUT_CLASS, MetaChip, OpLine } from "../components/Form.js";
|
|
8
|
+
import { SegmentSpinner } from "../components/SegmentSpinner.js";
|
|
9
|
+
import { useMinuteTick } from "../hooks/useMinuteTick.js";
|
|
10
|
+
import { constant, useReadable } from "../chat/headless/store.js";
|
|
11
|
+
import { machinesError } from "./headless/host.js";
|
|
12
|
+
import { useMachinesBridge, useMachinesHost, useMachinesLabels, usePermitted } from "./MachinesProvider.js";
|
|
13
|
+
import { useOptionalModal } from "../app/ModalProvider.js";
|
|
14
|
+
import { ShareThisComputerForm } from "./ShareThisComputerForm.js";
|
|
15
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
16
|
+
import { useEffect, useMemo, useState } from "react";
|
|
17
|
+
import { isValidMachineName, machineInstallCommand, machineSetupCommand } from "zidane/chat/pure";
|
|
18
|
+
//#region src/machines/AddMachineDialog.tsx
|
|
19
|
+
/**
|
|
20
|
+
* Add a machine to a server, in one dialog that follows the machine until
|
|
21
|
+
* it joins:
|
|
22
|
+
*
|
|
23
|
+
* - **Another computer** (the host's `machines.invite`): an optional name,
|
|
24
|
+
* then the command to run there, with the time the code has left. The
|
|
25
|
+
* dialog then waits on the server's machine list and turns into "<name>
|
|
26
|
+
* joined <server>" with Start a chat on it (the host's `newChat`). An
|
|
27
|
+
* ended code greys out and offers a new one.
|
|
28
|
+
* - **This computer** (the host's `links.shareStart`): share this computer
|
|
29
|
+
* with the server, in folders you pick.
|
|
30
|
+
*
|
|
31
|
+
* `useAddMachine` opens it above whatever is open (the composer's where
|
|
32
|
+
* menu, Settings).
|
|
33
|
+
*/
|
|
34
|
+
const TITLE_ID = "add-machine-title";
|
|
35
|
+
const NO_LIST = constant({
|
|
36
|
+
status: "idle",
|
|
37
|
+
machines: [],
|
|
38
|
+
error: null
|
|
39
|
+
});
|
|
40
|
+
const NO_HOSTS = constant({
|
|
41
|
+
enabled: true,
|
|
42
|
+
hosts: []
|
|
43
|
+
});
|
|
44
|
+
/** An address that other computers usually cannot reach: loopback, a private network, a `.local` name. */
|
|
45
|
+
function isLocalAddress(url) {
|
|
46
|
+
let host;
|
|
47
|
+
try {
|
|
48
|
+
host = new URL(url).hostname.replace(/^\[|\]$/g, "");
|
|
49
|
+
} catch {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return host === "localhost" || host === "::1" || host.endsWith(".local") || /^127\./.test(host) || /^10\./.test(host) || /^192\.168\./.test(host) || /^172\.(1[6-9]|2\d|3[01])\./.test(host);
|
|
53
|
+
}
|
|
54
|
+
/** Minutes a code has left, rounded up; 0 once it ended. */
|
|
55
|
+
function minutesLeft(expiresAt) {
|
|
56
|
+
return Math.max(0, Math.ceil((expiresAt - Date.now()) / 6e4));
|
|
57
|
+
}
|
|
58
|
+
/** The machine that joined: one the server lists that it did not have when the code was made. */
|
|
59
|
+
function joinedMachine(join, machines) {
|
|
60
|
+
if (join.step !== "code" || !join.known) return void 0;
|
|
61
|
+
const known = join.known;
|
|
62
|
+
return machines.find((machine) => !known.has(machine.id));
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Renders the Add a machine dialog's panel (for a modal layer: {@link useAddMachine} opens it).
|
|
66
|
+
*
|
|
67
|
+
* @param props - The server, the first tab and what closes it.
|
|
68
|
+
*/
|
|
69
|
+
function AddMachineDialog({ hostId, tab: initialTab, onClose, className, style }) {
|
|
70
|
+
const labels = useMachinesLabels();
|
|
71
|
+
const kitLabels = useLabels();
|
|
72
|
+
const { machines, links, hosts, newChat } = useMachinesHost();
|
|
73
|
+
const invite = usePermitted("machines.invite") ? machines?.invite : void 0;
|
|
74
|
+
const canShare = Boolean(links?.shareStart);
|
|
75
|
+
const host = useReadable(hosts ?? NO_HOSTS).hosts.find((h) => h.id === hostId) ?? {
|
|
76
|
+
id: hostId,
|
|
77
|
+
kind: "server",
|
|
78
|
+
name: hostId,
|
|
79
|
+
status: "connected"
|
|
80
|
+
};
|
|
81
|
+
const list = machines ? machines.list(hostId) : null;
|
|
82
|
+
const listState = useReadable(list ? list.state : NO_LIST);
|
|
83
|
+
const [tab, setTab] = useState(initialTab ?? (invite ? "another" : "this"));
|
|
84
|
+
const [forThisComputer, setForThisComputer] = useState(false);
|
|
85
|
+
const [name, setName] = useState("");
|
|
86
|
+
const [join, setJoin] = useState({ step: "name" });
|
|
87
|
+
const [busy, setBusy] = useState(false);
|
|
88
|
+
const [error, setError] = useState(null);
|
|
89
|
+
const trimmed = name.trim();
|
|
90
|
+
const nameInvalid = trimmed !== "" && !isValidMachineName(trimmed);
|
|
91
|
+
if (join.step === "code" && !join.known && listState.status === "ready") setJoin({
|
|
92
|
+
...join,
|
|
93
|
+
known: new Set(listState.machines.map((m) => m.id))
|
|
94
|
+
});
|
|
95
|
+
const joined = joinedMachine(join, listState.machines);
|
|
96
|
+
const [heard, setHeard] = useState(null);
|
|
97
|
+
const pairingId = join.step === "code" ? join.invite.code.replace(/[^0-9A-Z]/gi, "").slice(0, 4).toUpperCase() : null;
|
|
98
|
+
const onJoin = machines?.onJoin;
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
if (!pairingId || !onJoin) return void 0;
|
|
101
|
+
return onJoin(hostId, (event) => {
|
|
102
|
+
if (event.pairingId.toUpperCase() === pairingId) setHeard(event);
|
|
103
|
+
});
|
|
104
|
+
}, [
|
|
105
|
+
pairingId,
|
|
106
|
+
onJoin,
|
|
107
|
+
hostId
|
|
108
|
+
]);
|
|
109
|
+
const [address, setAddress] = useState(null);
|
|
110
|
+
const givenAddress = join.step === "code" ? join.invite.join.slice(0, join.invite.join.lastIndexOf("#")) : "";
|
|
111
|
+
const joinAddress = address ?? givenAddress;
|
|
112
|
+
const local = isLocalAddress(givenAddress);
|
|
113
|
+
useMinuteTick();
|
|
114
|
+
const expiresAt = join.step === "code" ? join.invite.expiresAt : null;
|
|
115
|
+
const [endedAt, setEndedAt] = useState(null);
|
|
116
|
+
const expired = expiresAt !== null && endedAt === expiresAt;
|
|
117
|
+
useEffect(() => {
|
|
118
|
+
if (expiresAt === null) return void 0;
|
|
119
|
+
const timer = setTimeout(() => setEndedAt(expiresAt), Math.max(0, expiresAt - Date.now()));
|
|
120
|
+
return () => clearTimeout(timer);
|
|
121
|
+
}, [expiresAt]);
|
|
122
|
+
const create = async () => {
|
|
123
|
+
if (!invite || nameInvalid) return;
|
|
124
|
+
setBusy(true);
|
|
125
|
+
setError(null);
|
|
126
|
+
try {
|
|
127
|
+
const made = await invite(hostId, trimmed ? { name: trimmed } : {});
|
|
128
|
+
const known = listState.status === "ready" ? new Set(listState.machines.map((m) => m.id)) : null;
|
|
129
|
+
setJoin({
|
|
130
|
+
step: "code",
|
|
131
|
+
invite: made,
|
|
132
|
+
known
|
|
133
|
+
});
|
|
134
|
+
setHeard(null);
|
|
135
|
+
list?.refresh();
|
|
136
|
+
} catch (err) {
|
|
137
|
+
setError(labels.machinesError(machinesError("invite", err)));
|
|
138
|
+
} finally {
|
|
139
|
+
setBusy(false);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
const startChat = (machine) => {
|
|
143
|
+
newChat?.(hostId, machine.name);
|
|
144
|
+
onClose();
|
|
145
|
+
};
|
|
146
|
+
const joinString = join.step === "code" ? `${joinAddress}#${join.invite.code}` : "";
|
|
147
|
+
const command = joinString ? machineInstallCommand(joinString) : "";
|
|
148
|
+
const setupCommand = joinString ? machineSetupCommand(joinString) : "";
|
|
149
|
+
const refused = heard?.outcome === "refused" ? heard : null;
|
|
150
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
151
|
+
className: `zg-modal-panel zg-float w-[520px] max-w-[92vw] flex flex-col overflow-hidden${className ? ` ${className}` : ""}`,
|
|
152
|
+
style,
|
|
153
|
+
onMouseDown: (event) => event.stopPropagation(),
|
|
154
|
+
"data-testid": "add-machine-dialog",
|
|
155
|
+
children: [
|
|
156
|
+
/* @__PURE__ */ jsxs("header", {
|
|
157
|
+
className: "flex items-start gap-2 px-5 py-3.5 border-b border-[var(--zg-hairline)]",
|
|
158
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
159
|
+
className: "flex-1 min-w-0",
|
|
160
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
161
|
+
id: TITLE_ID,
|
|
162
|
+
className: "m-0 text-sm font-semibold text-[var(--zg-text)]",
|
|
163
|
+
children: labels.machinesInviteTitle(host.name)
|
|
164
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
165
|
+
className: "m-0 mt-1 text-xs text-[var(--zg-text-muted)]",
|
|
166
|
+
children: labels.machinesExplain
|
|
167
|
+
})]
|
|
168
|
+
}), /* @__PURE__ */ jsx(IconButton, {
|
|
169
|
+
icon: "i-ph-x",
|
|
170
|
+
label: kitLabels.close,
|
|
171
|
+
size: "lg",
|
|
172
|
+
onClick: onClose,
|
|
173
|
+
className: "-mr-2"
|
|
174
|
+
})]
|
|
175
|
+
}),
|
|
176
|
+
/* @__PURE__ */ jsxs("div", {
|
|
177
|
+
className: "px-5 py-4 space-y-4 overflow-y-auto",
|
|
178
|
+
children: [
|
|
179
|
+
invite && canShare && !joined && /* @__PURE__ */ jsx(Segmented, {
|
|
180
|
+
value: tab,
|
|
181
|
+
onChange: setTab,
|
|
182
|
+
ariaLabel: labels.machinesAddWhich,
|
|
183
|
+
options: [{
|
|
184
|
+
value: "another",
|
|
185
|
+
label: labels.machinesAddAnother,
|
|
186
|
+
icon: "i-ph-desktop-tower"
|
|
187
|
+
}, {
|
|
188
|
+
value: "this",
|
|
189
|
+
label: labels.machinesAddThis,
|
|
190
|
+
icon: "i-ph-laptop"
|
|
191
|
+
}]
|
|
192
|
+
}),
|
|
193
|
+
tab === "this" && canShare ? /* @__PURE__ */ jsxs("div", {
|
|
194
|
+
className: "space-y-3",
|
|
195
|
+
children: [
|
|
196
|
+
/* @__PURE__ */ jsx("p", {
|
|
197
|
+
className: "m-0 text-xs text-[var(--zg-text-muted)]",
|
|
198
|
+
children: labels.machinesAddThisHint(host.name)
|
|
199
|
+
}),
|
|
200
|
+
/* @__PURE__ */ jsx(ShareThisComputerForm, {
|
|
201
|
+
host,
|
|
202
|
+
onShared: onClose,
|
|
203
|
+
onCancel: onClose
|
|
204
|
+
}),
|
|
205
|
+
invite && /* @__PURE__ */ jsxs("div", {
|
|
206
|
+
className: "zg-well rounded-zg-md p-3 flex items-center gap-3",
|
|
207
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
208
|
+
className: "flex-1 min-w-0",
|
|
209
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
210
|
+
className: "text-xs font-medium text-[var(--zg-text)]",
|
|
211
|
+
children: labels.machinesAddKeepOn
|
|
212
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
213
|
+
className: "text-xs text-[var(--zg-text-muted)]",
|
|
214
|
+
children: labels.machinesAddKeepOnHint
|
|
215
|
+
})]
|
|
216
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
217
|
+
type: "button",
|
|
218
|
+
className: BTN_SECONDARY,
|
|
219
|
+
onClick: () => {
|
|
220
|
+
setForThisComputer(true);
|
|
221
|
+
setTab("another");
|
|
222
|
+
},
|
|
223
|
+
children: labels.machinesAddKeepOnOpen
|
|
224
|
+
})]
|
|
225
|
+
})
|
|
226
|
+
]
|
|
227
|
+
}) : joined ? /* @__PURE__ */ jsxs("div", {
|
|
228
|
+
className: "flex items-start gap-3",
|
|
229
|
+
role: "status",
|
|
230
|
+
"data-testid": "add-machine-joined",
|
|
231
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
232
|
+
"aria-hidden": true,
|
|
233
|
+
className: "shrink-0 inline-flex items-center justify-center size-9 rounded-full bg-zg-success/15 text-zg-success text-base",
|
|
234
|
+
children: /* @__PURE__ */ jsx("span", { className: "i-ph-check-bold" })
|
|
235
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
236
|
+
className: "min-w-0 space-y-1",
|
|
237
|
+
children: [
|
|
238
|
+
/* @__PURE__ */ jsx("div", {
|
|
239
|
+
className: "text-sm font-medium text-[var(--zg-text)]",
|
|
240
|
+
children: labels.machinesAddJoined(joined.name, host.name)
|
|
241
|
+
}),
|
|
242
|
+
/* @__PURE__ */ jsx("div", {
|
|
243
|
+
className: "text-xs text-[var(--zg-text-muted)]",
|
|
244
|
+
children: labels.machinesAddJoinedHint
|
|
245
|
+
}),
|
|
246
|
+
(joined.capabilities?.gpu?.length ?? 0) > 0 && /* @__PURE__ */ jsx("div", {
|
|
247
|
+
className: "flex flex-wrap gap-1 pt-0.5",
|
|
248
|
+
children: joined.capabilities.gpu.map((gpu) => /* @__PURE__ */ jsx(MetaChip, {
|
|
249
|
+
tone: "info",
|
|
250
|
+
children: gpu
|
|
251
|
+
}, gpu))
|
|
252
|
+
})
|
|
253
|
+
]
|
|
254
|
+
})]
|
|
255
|
+
}) : join.step === "code" ? /* @__PURE__ */ jsxs("div", {
|
|
256
|
+
className: "space-y-3",
|
|
257
|
+
children: [
|
|
258
|
+
/* @__PURE__ */ jsxs("div", {
|
|
259
|
+
className: "flex items-center gap-2",
|
|
260
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
261
|
+
className: `${FIELD_LABEL_CLASS} flex-1`,
|
|
262
|
+
children: labels.machinesInviteCommand
|
|
263
|
+
}), !expired && /* @__PURE__ */ jsx(MetaChip, {
|
|
264
|
+
tone: "muted",
|
|
265
|
+
children: labels.machinesAddTimeLeft(minutesLeft(join.invite.expiresAt))
|
|
266
|
+
})]
|
|
267
|
+
}),
|
|
268
|
+
/* @__PURE__ */ jsxs("div", {
|
|
269
|
+
className: `zg-well rounded-zg-md flex items-start gap-2 p-2.5 min-w-0${expired ? " opacity-50" : ""}`,
|
|
270
|
+
"aria-disabled": expired,
|
|
271
|
+
children: [/* @__PURE__ */ jsx("code", {
|
|
272
|
+
className: "flex-1 min-w-0 break-all font-mono text-xs text-[var(--zg-text)] select-all",
|
|
273
|
+
children: command
|
|
274
|
+
}), !expired && /* @__PURE__ */ jsx(CopyButton, {
|
|
275
|
+
text: command,
|
|
276
|
+
label: labels.machinesInviteCopy
|
|
277
|
+
})]
|
|
278
|
+
}),
|
|
279
|
+
/* @__PURE__ */ jsx("p", {
|
|
280
|
+
className: "m-0 text-xs text-[var(--zg-text-muted)]",
|
|
281
|
+
children: forThisComputer ? labels.machinesAddThisTerminalHint : labels.machinesInviteHint
|
|
282
|
+
}),
|
|
283
|
+
!expired && /* @__PURE__ */ jsxs("div", {
|
|
284
|
+
className: "flex items-center gap-2 min-w-0 text-xs text-[var(--zg-text-muted)]",
|
|
285
|
+
children: [
|
|
286
|
+
/* @__PURE__ */ jsx("span", {
|
|
287
|
+
className: "shrink-0",
|
|
288
|
+
children: labels.machinesAddInstalled
|
|
289
|
+
}),
|
|
290
|
+
/* @__PURE__ */ jsx("code", {
|
|
291
|
+
className: "flex-1 min-w-0 truncate font-mono",
|
|
292
|
+
title: setupCommand,
|
|
293
|
+
children: setupCommand
|
|
294
|
+
}),
|
|
295
|
+
/* @__PURE__ */ jsx(CopyButton, {
|
|
296
|
+
text: setupCommand,
|
|
297
|
+
label: labels.machinesAddInstalledCopy
|
|
298
|
+
})
|
|
299
|
+
]
|
|
300
|
+
}),
|
|
301
|
+
local && /* @__PURE__ */ jsxs("div", {
|
|
302
|
+
className: "space-y-1.5",
|
|
303
|
+
children: [
|
|
304
|
+
/* @__PURE__ */ jsx(Notice, {
|
|
305
|
+
tone: "warning",
|
|
306
|
+
compact: true,
|
|
307
|
+
children: labels.machinesAddLocalAddress
|
|
308
|
+
}),
|
|
309
|
+
/* @__PURE__ */ jsx("label", {
|
|
310
|
+
className: "block text-xs text-[var(--zg-text-muted)] mb-1",
|
|
311
|
+
htmlFor: `add-machine-address-${hostId}`,
|
|
312
|
+
children: labels.machinesAddAddress
|
|
313
|
+
}),
|
|
314
|
+
/* @__PURE__ */ jsx("input", {
|
|
315
|
+
id: `add-machine-address-${hostId}`,
|
|
316
|
+
className: "zg-well w-full h-8 px-2.5 text-zg-ui placeholder:text-[var(--zg-text-subtle)] disabled:opacity-zg-disabled",
|
|
317
|
+
value: joinAddress,
|
|
318
|
+
spellCheck: false,
|
|
319
|
+
onChange: (event) => setAddress(event.target.value.trim())
|
|
320
|
+
})
|
|
321
|
+
]
|
|
322
|
+
}),
|
|
323
|
+
refused ? /* @__PURE__ */ jsx(Notice, {
|
|
324
|
+
tone: "error",
|
|
325
|
+
...refused.detail ? { detail: refused.detail } : {},
|
|
326
|
+
action: /* @__PURE__ */ jsx("button", {
|
|
327
|
+
type: "button",
|
|
328
|
+
className: BTN_SECONDARY,
|
|
329
|
+
disabled: busy,
|
|
330
|
+
onClick: () => void create(),
|
|
331
|
+
children: labels.machinesAddNewCode
|
|
332
|
+
}),
|
|
333
|
+
children: labels.machinesAddRefused(refused.reason ?? "other", refused.name)
|
|
334
|
+
}) : expired ? /* @__PURE__ */ jsx(Notice, {
|
|
335
|
+
tone: "warning",
|
|
336
|
+
compact: true,
|
|
337
|
+
action: /* @__PURE__ */ jsx("button", {
|
|
338
|
+
type: "button",
|
|
339
|
+
className: BTN_SECONDARY,
|
|
340
|
+
disabled: busy,
|
|
341
|
+
onClick: () => void create(),
|
|
342
|
+
children: labels.machinesAddNewCode
|
|
343
|
+
}),
|
|
344
|
+
children: labels.machinesAddExpired
|
|
345
|
+
}) : /* @__PURE__ */ jsxs("div", {
|
|
346
|
+
className: "flex items-center gap-2 text-xs text-[var(--zg-text-muted)]",
|
|
347
|
+
"data-testid": "add-machine-waiting",
|
|
348
|
+
children: [/* @__PURE__ */ jsx(SegmentSpinner, {
|
|
349
|
+
size: "xs",
|
|
350
|
+
tone: "muted",
|
|
351
|
+
label: heard?.outcome === "paired" ? labels.machinesAddPaired(heard.name) : labels.machinesAddWaiting
|
|
352
|
+
}), /* @__PURE__ */ jsx("span", { children: heard?.outcome === "paired" ? labels.machinesAddPaired(heard.name) : labels.machinesAddWaiting })]
|
|
353
|
+
})
|
|
354
|
+
]
|
|
355
|
+
}) : /* @__PURE__ */ jsxs("form", {
|
|
356
|
+
id: "add-machine-form",
|
|
357
|
+
className: "space-y-2",
|
|
358
|
+
"aria-label": labels.machinesInviteForm,
|
|
359
|
+
onSubmit: (event) => {
|
|
360
|
+
event.preventDefault();
|
|
361
|
+
create();
|
|
362
|
+
},
|
|
363
|
+
children: [
|
|
364
|
+
/* @__PURE__ */ jsx("label", {
|
|
365
|
+
className: FIELD_LABEL_CLASS,
|
|
366
|
+
htmlFor: `add-machine-name-${hostId}`,
|
|
367
|
+
children: labels.machinesInviteName
|
|
368
|
+
}),
|
|
369
|
+
/* @__PURE__ */ jsx("input", {
|
|
370
|
+
id: `add-machine-name-${hostId}`,
|
|
371
|
+
className: INPUT_CLASS,
|
|
372
|
+
placeholder: labels.machinesInviteNamePlaceholder,
|
|
373
|
+
value: name,
|
|
374
|
+
maxLength: 64,
|
|
375
|
+
"aria-invalid": nameInvalid,
|
|
376
|
+
...nameInvalid ? { "aria-describedby": `add-machine-rule-${hostId}` } : {},
|
|
377
|
+
onChange: (event) => setName(event.target.value)
|
|
378
|
+
}),
|
|
379
|
+
nameInvalid && /* @__PURE__ */ jsx("div", {
|
|
380
|
+
id: `add-machine-rule-${hostId}`,
|
|
381
|
+
children: /* @__PURE__ */ jsx(Notice, {
|
|
382
|
+
tone: "error",
|
|
383
|
+
compact: true,
|
|
384
|
+
children: labels.machinesInviteNameRule
|
|
385
|
+
})
|
|
386
|
+
})
|
|
387
|
+
]
|
|
388
|
+
}),
|
|
389
|
+
busy && /* @__PURE__ */ jsx(OpLine, {
|
|
390
|
+
kind: "busy",
|
|
391
|
+
children: labels.machinesInviteCreating
|
|
392
|
+
}),
|
|
393
|
+
error && !busy && /* @__PURE__ */ jsx(OpLine, {
|
|
394
|
+
kind: "error",
|
|
395
|
+
children: error
|
|
396
|
+
})
|
|
397
|
+
]
|
|
398
|
+
}),
|
|
399
|
+
!(tab === "this" && canShare) && /* @__PURE__ */ jsx("footer", {
|
|
400
|
+
className: "flex items-center justify-end gap-2 px-5 py-3 bg-[var(--zg-tray)]",
|
|
401
|
+
children: joined ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("button", {
|
|
402
|
+
type: "button",
|
|
403
|
+
className: BTN_SECONDARY,
|
|
404
|
+
onClick: onClose,
|
|
405
|
+
children: labels.machinesInviteDone
|
|
406
|
+
}), newChat && /* @__PURE__ */ jsx("button", {
|
|
407
|
+
type: "button",
|
|
408
|
+
className: BTN_PRIMARY,
|
|
409
|
+
onClick: () => startChat(joined),
|
|
410
|
+
children: labels.machinesAddStartChat
|
|
411
|
+
})] }) : join.step === "code" ? /* @__PURE__ */ jsx("button", {
|
|
412
|
+
type: "button",
|
|
413
|
+
className: BTN_SECONDARY,
|
|
414
|
+
onClick: onClose,
|
|
415
|
+
children: labels.machinesInviteDone
|
|
416
|
+
}) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("button", {
|
|
417
|
+
type: "button",
|
|
418
|
+
className: BTN_SECONDARY,
|
|
419
|
+
onClick: onClose,
|
|
420
|
+
children: labels.machinesCancel
|
|
421
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
422
|
+
type: "submit",
|
|
423
|
+
form: "add-machine-form",
|
|
424
|
+
className: BTN_PRIMARY,
|
|
425
|
+
disabled: busy || nameInvalid || !invite,
|
|
426
|
+
children: labels.machinesInviteCreate
|
|
427
|
+
})] })
|
|
428
|
+
})
|
|
429
|
+
]
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Opens Add a machine above whatever is open (the app's modal layer). `undefined` when there is nothing to add
|
|
434
|
+
* with (the host makes no join codes and does not share this computer) or no modal layer: hide the control.
|
|
435
|
+
*/
|
|
436
|
+
function useAddMachine() {
|
|
437
|
+
const { machines, links } = useMachinesHost();
|
|
438
|
+
const canInvite = usePermitted("machines.invite");
|
|
439
|
+
const modal = useOptionalModal();
|
|
440
|
+
const bridge = useMachinesBridge();
|
|
441
|
+
const available = Boolean(modal) && Boolean(machines?.invite && canInvite || links?.shareStart);
|
|
442
|
+
const openLayer = modal?.openLayer;
|
|
443
|
+
const closeLayer = modal?.closeLayer;
|
|
444
|
+
return useMemo(() => {
|
|
445
|
+
if (!available || !openLayer || !closeLayer) return void 0;
|
|
446
|
+
return (hostId, options) => {
|
|
447
|
+
openLayer(bridge(/* @__PURE__ */ jsx(AddMachineDialog, {
|
|
448
|
+
hostId,
|
|
449
|
+
...options?.tab ? { tab: options.tab } : {},
|
|
450
|
+
onClose: closeLayer
|
|
451
|
+
})), {
|
|
452
|
+
onDismiss: () => {},
|
|
453
|
+
labelledBy: TITLE_ID
|
|
454
|
+
});
|
|
455
|
+
};
|
|
456
|
+
}, [
|
|
457
|
+
available,
|
|
458
|
+
openLayer,
|
|
459
|
+
closeLayer,
|
|
460
|
+
bridge
|
|
461
|
+
]);
|
|
462
|
+
}
|
|
463
|
+
//#endregion
|
|
464
|
+
export { AddMachineDialog, useAddMachine };
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { HostSummary } from "./headless/host.js";
|
|
2
|
+
import { NoticeTone } from "../components/Notice.js";
|
|
3
|
+
import { DraftPlaceIssue } from "../chat/headless/draft-target.js";
|
|
2
4
|
import { CSSProperties } from "react";
|
|
3
5
|
//#region src/machines/Banners.d.ts
|
|
4
6
|
/** Props for {@link ServerOfflineBanner}. */
|
|
@@ -21,8 +23,12 @@ interface ServerOfflineBannerProps {
|
|
|
21
23
|
declare function ServerOfflineBanner({ host, name, className, style }: ServerOfflineBannerProps): import("react").JSX.Element;
|
|
22
24
|
/** Props for {@link ComposerNotice}. */
|
|
23
25
|
interface ComposerNoticeProps {
|
|
24
|
-
/** What happened. */
|
|
26
|
+
/** What happened, worded for people. */
|
|
25
27
|
text: string;
|
|
28
|
+
/** A warning or an error: a {@link Notice} of that tone. Default: a muted line. */
|
|
29
|
+
tone?: NoticeTone;
|
|
30
|
+
/** The raw words behind it (an exception's message): folded under Details. Needs `tone`. */
|
|
31
|
+
detail?: string;
|
|
26
32
|
/** Called when the user dismisses it. */
|
|
27
33
|
onDismiss: () => void;
|
|
28
34
|
/** The root's `data-testid`. Default `composer-notice`. */
|
|
@@ -35,9 +41,32 @@ interface ComposerNoticeProps {
|
|
|
35
41
|
/**
|
|
36
42
|
* A dismissable notice above the composer: why a new chat could not be created (its server went offline, …).
|
|
37
43
|
*
|
|
38
|
-
* @param props - The text, the dismiss callback, a test id, `className` and `style`.
|
|
44
|
+
* @param props - The text, its tone and detail, the dismiss callback, a test id, `className` and `style`.
|
|
39
45
|
*/
|
|
40
|
-
declare function ComposerNotice({ text, onDismiss, testId, className, style }: ComposerNoticeProps): import("react").JSX.Element;
|
|
46
|
+
declare function ComposerNotice({ text, tone, detail, onDismiss, testId, className, style }: ComposerNoticeProps): import("react").JSX.Element;
|
|
47
|
+
/** Props for {@link DraftPlaceNotice}. */
|
|
48
|
+
interface DraftPlaceNoticeProps {
|
|
49
|
+
/** Why the draft cannot use the default place as it is. */
|
|
50
|
+
issue?: DraftPlaceIssue | null;
|
|
51
|
+
/** The draft's server, when the app cannot reach it now. A chat there waits for it. */
|
|
52
|
+
offlineServer?: string;
|
|
53
|
+
/** Moves the draft to this computer (an offline server). Absent: no button. */
|
|
54
|
+
onUseThisComputer?: () => void;
|
|
55
|
+
/** Opens Add a server (an unsaved server). Absent: no button. */
|
|
56
|
+
onAddServer?: (hostName: string, hostUrl?: string) => void;
|
|
57
|
+
/** Hides the issue. */
|
|
58
|
+
onDismiss: () => void;
|
|
59
|
+
/** Extra classes on the root. */
|
|
60
|
+
className?: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The new chat's place, when it cannot be what the default says: the default names a server this device has not
|
|
64
|
+
* saved (Add it), a machine that left the server, a folder that is gone, or its server is offline (use this
|
|
65
|
+
* computer instead). Nothing when all is well.
|
|
66
|
+
*
|
|
67
|
+
* @param props - The issue, the offline server, the actions and `className`.
|
|
68
|
+
*/
|
|
69
|
+
declare function DraftPlaceNotice({ issue, offlineServer, onUseThisComputer, onAddServer, onDismiss, className }: DraftPlaceNoticeProps): import("react").JSX.Element | null;
|
|
41
70
|
/** Props for {@link HostRemovedBanner}. */
|
|
42
71
|
interface HostRemovedBannerProps {
|
|
43
72
|
/** What was removed (`"<server> was removed"`). */
|
|
@@ -72,4 +101,4 @@ interface MachineWaitingBannerProps {
|
|
|
72
101
|
*/
|
|
73
102
|
declare function MachineWaitingBanner({ machine, className, style }: MachineWaitingBannerProps): import("react").JSX.Element | null;
|
|
74
103
|
//#endregion
|
|
75
|
-
export { ComposerNotice, ComposerNoticeProps, HostRemovedBanner, HostRemovedBannerProps, MachineWaitingBanner, MachineWaitingBannerProps, ServerOfflineBanner, ServerOfflineBannerProps };
|
|
104
|
+
export { ComposerNotice, ComposerNoticeProps, DraftPlaceNotice, DraftPlaceNoticeProps, HostRemovedBanner, HostRemovedBannerProps, MachineWaitingBanner, MachineWaitingBannerProps, ServerOfflineBanner, ServerOfflineBannerProps };
|
package/dist/machines/Banners.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Notice } from "../components/Notice.js";
|
|
1
2
|
import { Tray } from "../components/Surface.js";
|
|
2
3
|
import { useMachinesHost, useMachinesLabels, useOnlineMachines } from "./MachinesProvider.js";
|
|
3
4
|
import { hostStatusText } from "./headless/messages.js";
|
|
@@ -34,10 +35,26 @@ function ServerOfflineBanner({ host, name, className = "", style }) {
|
|
|
34
35
|
/**
|
|
35
36
|
* A dismissable notice above the composer: why a new chat could not be created (its server went offline, …).
|
|
36
37
|
*
|
|
37
|
-
* @param props - The text, the dismiss callback, a test id, `className` and `style`.
|
|
38
|
+
* @param props - The text, its tone and detail, the dismiss callback, a test id, `className` and `style`.
|
|
38
39
|
*/
|
|
39
|
-
function ComposerNotice({ text, onDismiss, testId, className = "", style }) {
|
|
40
|
+
function ComposerNotice({ text, tone, detail, onDismiss, testId, className = "", style }) {
|
|
40
41
|
const labels = useMachinesLabels();
|
|
42
|
+
if (tone) return /* @__PURE__ */ jsx("div", {
|
|
43
|
+
className: className || void 0,
|
|
44
|
+
style,
|
|
45
|
+
"data-testid": testId ?? "composer-notice",
|
|
46
|
+
children: /* @__PURE__ */ jsx(Notice, {
|
|
47
|
+
tone,
|
|
48
|
+
...detail ? { detail } : {},
|
|
49
|
+
action: /* @__PURE__ */ jsx("button", {
|
|
50
|
+
type: "button",
|
|
51
|
+
onClick: onDismiss,
|
|
52
|
+
className: NOTICE_BUTTON_CLASS,
|
|
53
|
+
children: labels.machinesDismiss
|
|
54
|
+
}),
|
|
55
|
+
children: text
|
|
56
|
+
})
|
|
57
|
+
});
|
|
41
58
|
return /* @__PURE__ */ jsxs(Tray, {
|
|
42
59
|
radius: "xl",
|
|
43
60
|
className: `flex items-center justify-between gap-3 pl-4 pr-2 py-2 ${className}`.trimEnd(),
|
|
@@ -56,6 +73,50 @@ function ComposerNotice({ text, onDismiss, testId, className = "", style }) {
|
|
|
56
73
|
});
|
|
57
74
|
}
|
|
58
75
|
/**
|
|
76
|
+
* The new chat's place, when it cannot be what the default says: the default names a server this device has not
|
|
77
|
+
* saved (Add it), a machine that left the server, a folder that is gone, or its server is offline (use this
|
|
78
|
+
* computer instead). Nothing when all is well.
|
|
79
|
+
*
|
|
80
|
+
* @param props - The issue, the offline server, the actions and `className`.
|
|
81
|
+
*/
|
|
82
|
+
function DraftPlaceNotice({ issue, offlineServer, onUseThisComputer, onAddServer, onDismiss, className = "" }) {
|
|
83
|
+
const labels = useMachinesLabels();
|
|
84
|
+
const text = offlineServer ? labels.machinesDraftServerOffline(offlineServer) : issue?.kind === "unsaved" ? labels.machinesPlaceUnsaved(issue.hostName) : issue?.kind === "machine-gone" ? labels.machinesPlaceMachineGone(issue.machine, issue.hostName) : issue?.kind === "folder-gone" ? labels.machinesPlaceFolderGone(issue.folder) : null;
|
|
85
|
+
if (!text) return null;
|
|
86
|
+
const action = offlineServer ? onUseThisComputer && /* @__PURE__ */ jsx("button", {
|
|
87
|
+
type: "button",
|
|
88
|
+
onClick: onUseThisComputer,
|
|
89
|
+
className: NOTICE_BUTTON_CLASS,
|
|
90
|
+
children: labels.machinesUseThisComputer
|
|
91
|
+
}) : issue?.kind === "unsaved" && onAddServer && /* @__PURE__ */ jsx("button", {
|
|
92
|
+
type: "button",
|
|
93
|
+
onClick: () => onAddServer(issue.hostName, issue.hostUrl),
|
|
94
|
+
className: NOTICE_BUTTON_CLASS,
|
|
95
|
+
children: labels.machinesPlaceAddServer(issue.hostName)
|
|
96
|
+
});
|
|
97
|
+
return /* @__PURE__ */ jsxs(Tray, {
|
|
98
|
+
radius: "xl",
|
|
99
|
+
className: `flex items-center justify-between gap-3 pl-4 pr-2 py-2 ${className}`.trimEnd(),
|
|
100
|
+
"data-testid": "draft-place-notice",
|
|
101
|
+
role: "status",
|
|
102
|
+
children: [/* @__PURE__ */ jsxs("span", {
|
|
103
|
+
className: "flex items-center gap-2 text-zg-ui text-[var(--zg-text-muted)]",
|
|
104
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
105
|
+
"aria-hidden": true,
|
|
106
|
+
className: offlineServer ? "i-ph-cloud-slash shrink-0" : "i-ph-signpost shrink-0"
|
|
107
|
+
}), text]
|
|
108
|
+
}), /* @__PURE__ */ jsxs("span", {
|
|
109
|
+
className: "flex items-center gap-1 shrink-0",
|
|
110
|
+
children: [action, !offlineServer && /* @__PURE__ */ jsx("button", {
|
|
111
|
+
type: "button",
|
|
112
|
+
onClick: onDismiss,
|
|
113
|
+
className: NOTICE_BUTTON_CLASS,
|
|
114
|
+
children: labels.machinesDismiss
|
|
115
|
+
})]
|
|
116
|
+
})]
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
59
120
|
* The chat that was open closed because its server was removed.
|
|
60
121
|
*
|
|
61
122
|
* @param props - The notice, the dismiss callback, `className` and `style`.
|
|
@@ -96,4 +157,4 @@ function MachineWaitingBanner({ machine, className = "", style }) {
|
|
|
96
157
|
});
|
|
97
158
|
}
|
|
98
159
|
//#endregion
|
|
99
|
-
export { ComposerNotice, HostRemovedBanner, MachineWaitingBanner, ServerOfflineBanner };
|
|
160
|
+
export { ComposerNotice, DraftPlaceNotice, HostRemovedBanner, MachineWaitingBanner, ServerOfflineBanner };
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { Notice } from "../components/Notice.js";
|
|
1
2
|
import { MetaChip } from "../components/Form.js";
|
|
3
|
+
import { errorMessage } from "../components/report-error.js";
|
|
2
4
|
import { FloatingSurface } from "../components/floating/FloatingSurface.js";
|
|
3
5
|
import { useReadable } from "../chat/headless/store.js";
|
|
4
6
|
import { useMachinesConfirm, useMachinesHost, useMachinesLabels } from "./MachinesProvider.js";
|
|
@@ -76,6 +78,8 @@ function DaemonMenuBody({ daemon, running: runningCount = 0, attention = 0, sess
|
|
|
76
78
|
const [open, setOpen] = useState(false);
|
|
77
79
|
const [status, setStatus] = useState(null);
|
|
78
80
|
const [notice, setNotice] = useState(null);
|
|
81
|
+
/** What the last action's failure said: worded by a label, the raw words under Details. */
|
|
82
|
+
const [failure, setFailure] = useState(null);
|
|
79
83
|
const [busy, setBusy] = useState(false);
|
|
80
84
|
const wrapperRef = useRef(null);
|
|
81
85
|
const menuRef = useRef(null);
|
|
@@ -112,17 +116,26 @@ function DaemonMenuBody({ daemon, running: runningCount = 0, attention = 0, sess
|
|
|
112
116
|
refresh
|
|
113
117
|
]);
|
|
114
118
|
useEffect(() => {
|
|
115
|
-
if (!notice || busy) return void 0;
|
|
116
|
-
const timer = setTimeout(() =>
|
|
119
|
+
if (!notice && failure === null || busy) return void 0;
|
|
120
|
+
const timer = setTimeout(() => {
|
|
121
|
+
setNotice(null);
|
|
122
|
+
setFailure(null);
|
|
123
|
+
}, 4e3);
|
|
117
124
|
return () => clearTimeout(timer);
|
|
118
|
-
}, [
|
|
125
|
+
}, [
|
|
126
|
+
notice,
|
|
127
|
+
failure,
|
|
128
|
+
busy
|
|
129
|
+
]);
|
|
119
130
|
const run = useCallback(async (pending, action) => {
|
|
120
131
|
setBusy(true);
|
|
132
|
+
setFailure(null);
|
|
121
133
|
setNotice(pending);
|
|
122
134
|
try {
|
|
123
135
|
setNotice(await action());
|
|
124
136
|
} catch (err) {
|
|
125
|
-
setNotice(
|
|
137
|
+
setNotice(null);
|
|
138
|
+
setFailure(errorMessage(err));
|
|
126
139
|
} finally {
|
|
127
140
|
setBusy(false);
|
|
128
141
|
refresh();
|
|
@@ -161,6 +174,7 @@ function DaemonMenuBody({ daemon, running: runningCount = 0, attention = 0, sess
|
|
|
161
174
|
title: label,
|
|
162
175
|
onClick: () => {
|
|
163
176
|
setNotice(null);
|
|
177
|
+
setFailure(null);
|
|
164
178
|
setOpen((v) => !v);
|
|
165
179
|
}
|
|
166
180
|
}), /* @__PURE__ */ jsx(FloatingSurface, {
|
|
@@ -307,6 +321,15 @@ function DaemonMenuBody({ daemon, running: runningCount = 0, attention = 0, sess
|
|
|
307
321
|
role: "status",
|
|
308
322
|
className: "px-3 pt-1.5 pb-1 text-zg-meta leading-4 text-[var(--zg-text-muted)]",
|
|
309
323
|
children: notice
|
|
324
|
+
}),
|
|
325
|
+
failure !== null && /* @__PURE__ */ jsx("div", {
|
|
326
|
+
className: "px-3 pt-1.5 pb-1",
|
|
327
|
+
children: /* @__PURE__ */ jsx(Notice, {
|
|
328
|
+
tone: "error",
|
|
329
|
+
compact: true,
|
|
330
|
+
detail: failure,
|
|
331
|
+
children: labels.machinesDaemonActionFailed
|
|
332
|
+
})
|
|
310
333
|
})
|
|
311
334
|
]
|
|
312
335
|
})
|