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.
Files changed (290) hide show
  1. package/dist/adapters/index.d.ts +2 -2
  2. package/dist/adapters/index.js +2 -2
  3. package/dist/adapters/live.d.ts +29 -3
  4. package/dist/adapters/live.js +9 -1
  5. package/dist/adapters/server-composer-host.js +17 -2
  6. package/dist/adapters/server-extensions-host.d.ts +0 -1
  7. package/dist/adapters/server-extensions-host.js +4 -15
  8. package/dist/adapters/server-git-host.js +7 -16
  9. package/dist/adapters/server-host.js +181 -43
  10. package/dist/adapters/server-machines-host.d.ts +2 -0
  11. package/dist/adapters/server-machines-host.js +22 -23
  12. package/dist/adapters/server-onboarding-host.d.ts +1 -1
  13. package/dist/adapters/server-onboarding-host.js +3 -4
  14. package/dist/adapters/server-session-host.js +22 -3
  15. package/dist/adapters/server-settings-accounts.js +2 -2
  16. package/dist/adapters/server-settings-host.js +2 -2
  17. package/dist/adapters/server-settings-performance.js +7 -9
  18. package/dist/adapters/server-settings-sections.js +13 -14
  19. package/dist/adapters/server-sidebar-host.js +2 -2
  20. package/dist/adapters/sse-host.js +68 -3
  21. package/dist/adapters/sse-protocol.d.ts +12 -2
  22. package/dist/adapters/sse-protocol.js +4 -2
  23. package/dist/app/AppHostProvider.js +4 -3
  24. package/dist/app/ModalProvider.d.ts +13 -3
  25. package/dist/app/ModalProvider.js +103 -24
  26. package/dist/app/NoticeStack.d.ts +33 -0
  27. package/dist/app/NoticeStack.js +90 -0
  28. package/dist/app/index.d.ts +3 -2
  29. package/dist/app/index.js +3 -2
  30. package/dist/chat/SessionFilterMenu.d.ts +1 -1
  31. package/dist/chat/SessionFilterMenu.js +2 -1
  32. package/dist/chat/composer/Composer.js +10 -8
  33. package/dist/chat/composer/ModelChainList.js +17 -8
  34. package/dist/chat/composer/ModelChainTooltip.js +5 -2
  35. package/dist/chat/composer/use-draft-target.d.ts +20 -1
  36. package/dist/chat/composer/use-draft-target.js +121 -5
  37. package/dist/chat/composer/use-new-session-defaults.d.ts +5 -1
  38. package/dist/chat/composer/use-new-session-defaults.js +17 -4
  39. package/dist/chat/headless/chip-labels.d.ts +1 -0
  40. package/dist/chat/headless/chip-labels.js +1 -0
  41. package/dist/chat/headless/composer/labels-editor.d.ts +0 -2
  42. package/dist/chat/headless/composer/labels-editor.js +0 -2
  43. package/dist/chat/headless/draft-target.d.ts +21 -1
  44. package/dist/chat/headless/fixture-host.js +17 -2
  45. package/dist/chat/headless/host.d.ts +3 -3
  46. package/dist/chat/headless/index.d.ts +9 -9
  47. package/dist/chat/headless/index.js +2 -2
  48. package/dist/chat/headless/labels.d.ts +13 -6
  49. package/dist/chat/headless/labels.js +10 -9
  50. package/dist/chat/headless/session-list.d.ts +20 -8
  51. package/dist/chat/headless/session-list.js +1 -0
  52. package/dist/chat/headless/sidebar/directory.d.ts +56 -14
  53. package/dist/chat/headless/sidebar/directory.js +149 -70
  54. package/dist/chat/headless/sidebar/index.d.ts +3 -3
  55. package/dist/chat/headless/sidebar/index.js +2 -2
  56. package/dist/chat/headless/sidebar/labels.d.ts +15 -8
  57. package/dist/chat/headless/sidebar/types.d.ts +3 -1
  58. package/dist/chat/headless/sidebar/view.d.ts +13 -5
  59. package/dist/chat/headless/sidebar/view.js +31 -11
  60. package/dist/chat/headless/streams/actions.d.ts +5 -3
  61. package/dist/chat/headless/streams/actions.js +3 -2
  62. package/dist/chat/headless/streams/index.d.ts +3 -3
  63. package/dist/chat/headless/streams/merge.d.ts +6 -10
  64. package/dist/chat/headless/streams/merge.js +7 -21
  65. package/dist/chat/headless/streams/session-streams.d.ts +29 -4
  66. package/dist/chat/headless/streams/session-streams.js +86 -0
  67. package/dist/chat/headless/streams/store.d.ts +162 -16
  68. package/dist/chat/headless/streams/store.js +334 -38
  69. package/dist/chat/headless/streams/types.d.ts +10 -7
  70. package/dist/chat/headless/transcript-labels.d.ts +1 -0
  71. package/dist/chat/interactions/PlanCard.js +10 -3
  72. package/dist/chat/interactions/QuestionForm.js +7 -3
  73. package/dist/chat/interactions/card-ui.d.ts +5 -3
  74. package/dist/chat/interactions/card-ui.js +9 -7
  75. package/dist/chat/rich-composer/RichComposer.js +1 -7
  76. package/dist/chat/rich-composer/composer-extensions.d.ts +0 -2
  77. package/dist/chat/rich-composer/composer-extensions.js +2 -2
  78. package/dist/chat/rich-composer/use-composer-editor.d.ts +0 -2
  79. package/dist/chat/rich-composer/use-composer-editor.js +1 -2
  80. package/dist/chat/sidebar/SessionSidebar.js +5 -5
  81. package/dist/chat/sidebar/SidebarFilterMenu.js +3 -1
  82. package/dist/chat/sidebar/SidebarFrame.d.ts +4 -0
  83. package/dist/chat/sidebar/SidebarFrame.js +27 -9
  84. package/dist/chat/sidebar/SidebarGroupHeader.js +1 -1
  85. package/dist/chat/sidebar/SidebarHostProvider.d.ts +8 -5
  86. package/dist/chat/sidebar/SidebarHostProvider.js +13 -9
  87. package/dist/chat/sidebar/SidebarToggleButton.js +7 -3
  88. package/dist/chat/sidebar/index.d.ts +2 -2
  89. package/dist/chat/sidebar/session-menu.js +4 -4
  90. package/dist/chat/sidebar/useSaveViewAsSpace.js +2 -2
  91. package/dist/chat/testing/contract.js +2 -2
  92. package/dist/chat/transcript/AuthFailureCard.js +11 -8
  93. package/dist/chat/transcript/BadgeMenu.js +13 -2
  94. package/dist/chat/transcript/MermaidDiagram.js +7 -3
  95. package/dist/chat/transcript/fold.d.ts +4 -0
  96. package/dist/chat/transcript/fold.js +53 -22
  97. package/dist/chat/transcript/script-input.d.ts +1 -1
  98. package/dist/chat/transcript/shell-block.js +7 -3
  99. package/dist/chat/transcript/useStickToBottom.js +12 -0
  100. package/dist/components/BrowserSignIn.js +23 -21
  101. package/dist/components/Form.d.ts +6 -4
  102. package/dist/components/Form.js +16 -8
  103. package/dist/components/LoadError.d.ts +4 -2
  104. package/dist/components/LoadError.js +13 -9
  105. package/dist/components/Notice.d.ts +29 -0
  106. package/dist/components/Notice.js +63 -0
  107. package/dist/components/PanelEmpty.d.ts +1 -1
  108. package/dist/components/PanelEmpty.js +4 -2
  109. package/dist/components/Restricted.d.ts +1 -1
  110. package/dist/components/palette/PaletteList.js +8 -1
  111. package/dist/components/palette/PalettePanel.js +14 -6
  112. package/dist/core.css +24 -5
  113. package/dist/dist/{index-Bq87OcGy.d.ts → index-BZM8tu4L.d.ts} +28 -3
  114. package/dist/dist/index.d.ts +2 -2
  115. package/dist/dist/{timeout-B5pO5I5a.d.ts → timeout-Cyx3VGPz.d.ts} +1 -1
  116. package/dist/extensions/ExtensionSettingsPage.js +1 -4
  117. package/dist/extensions/ExtensionSettingsSection.js +4 -3
  118. package/dist/extensions/ExtensionSettingsSidebar.js +2 -6
  119. package/dist/extensions/SettingsWireForm.d.ts +1 -7
  120. package/dist/extensions/SettingsWireForm.js +7 -5
  121. package/dist/extensions/headless/fixture-host.d.ts +1 -5
  122. package/dist/extensions/headless/fixture-host.js +1 -4
  123. package/dist/extensions/headless/host.d.ts +1 -11
  124. package/dist/extensions/headless/index.d.ts +3 -3
  125. package/dist/extensions/headless/index.js +2 -2
  126. package/dist/extensions/headless/select.d.ts +1 -11
  127. package/dist/extensions/headless/select.js +1 -18
  128. package/dist/files/EditorColumn.js +12 -1
  129. package/dist/files/TreeColumn.js +13 -20
  130. package/dist/files/useOpenFiles.js +2 -0
  131. package/dist/git/BranchMenu.js +5 -3
  132. package/dist/git/ImportRepoDialog.js +18 -9
  133. package/dist/git/MoveSessionDialog.js +15 -7
  134. package/dist/git/RemoteFolderPicker.js +34 -16
  135. package/dist/git/SyncChip.js +17 -17
  136. package/dist/git/WherePicker.d.ts +16 -1
  137. package/dist/git/WherePicker.js +80 -36
  138. package/dist/git/headless/environment.d.ts +33 -3
  139. package/dist/git/headless/environment.js +33 -5
  140. package/dist/git/headless/index.d.ts +2 -2
  141. package/dist/git/headless/index.js +2 -2
  142. package/dist/git/headless/labels.d.ts +15 -2
  143. package/dist/git/headless/labels.js +17 -4
  144. package/dist/git/useEnvironmentChoices.d.ts +3 -1
  145. package/dist/git/useEnvironmentChoices.js +6 -3
  146. package/dist/index.d.ts +2 -1
  147. package/dist/index.js +2 -1
  148. package/dist/labels.d.ts +5 -0
  149. package/dist/labels.js +5 -0
  150. package/dist/machines/AddMachineDialog.d.ts +34 -0
  151. package/dist/machines/AddMachineDialog.js +464 -0
  152. package/dist/machines/Banners.d.ts +33 -4
  153. package/dist/machines/Banners.js +64 -3
  154. package/dist/machines/DaemonMenu.js +27 -4
  155. package/dist/machines/HostCapabilities.d.ts +1 -3
  156. package/dist/machines/HostCapabilities.js +24 -45
  157. package/dist/machines/HostSyncPanel.js +123 -129
  158. package/dist/machines/HostsSection.d.ts +4 -1
  159. package/dist/machines/HostsSection.js +30 -7
  160. package/dist/machines/MachinesExplainer.d.ts +17 -0
  161. package/dist/machines/MachinesExplainer.js +71 -0
  162. package/dist/machines/MachinesProvider.d.ts +14 -2
  163. package/dist/machines/MachinesProvider.js +35 -1
  164. package/dist/machines/ServerAddedCard.d.ts +31 -0
  165. package/dist/machines/ServerAddedCard.js +72 -0
  166. package/dist/machines/ServerCard.js +91 -215
  167. package/dist/machines/ServerMachinesPanel.d.ts +2 -2
  168. package/dist/machines/ServerMachinesPanel.js +184 -323
  169. package/dist/machines/ServerUpdatePanel.js +51 -40
  170. package/dist/machines/ShareThisComputerForm.d.ts +19 -0
  171. package/dist/machines/ShareThisComputerForm.js +133 -0
  172. package/dist/machines/TokensPanel.d.ts +5 -3
  173. package/dist/machines/TokensPanel.js +98 -78
  174. package/dist/machines/headless/fixture-host.d.ts +19 -3
  175. package/dist/machines/headless/fixture-host.js +42 -5
  176. package/dist/machines/headless/fixtures.js +4 -6
  177. package/dist/machines/headless/host.d.ts +40 -19
  178. package/dist/machines/headless/index.d.ts +3 -3
  179. package/dist/machines/headless/index.js +2 -2
  180. package/dist/machines/headless/labels.d.ts +63 -20
  181. package/dist/machines/headless/labels.js +88 -25
  182. package/dist/machines/headless/messages.js +8 -0
  183. package/dist/machines/headless/stores.d.ts +9 -2
  184. package/dist/machines/headless/stores.js +28 -1
  185. package/dist/machines/index.d.ts +7 -3
  186. package/dist/machines/index.js +8 -4
  187. package/dist/machines/testing/contract.d.ts +15 -3
  188. package/dist/machines/testing/contract.js +68 -14
  189. package/dist/motion/grow.d.ts +8 -1
  190. package/dist/motion/grow.js +10 -4
  191. package/dist/onboarding/demos/index.js +1 -1
  192. package/dist/onboarding/headless/labels.d.ts +13 -0
  193. package/dist/onboarding/headless/labels.js +8 -0
  194. package/dist/onboarding/index.js +3 -3
  195. package/dist/onboarding/sources/SourceBrowser.js +11 -7
  196. package/dist/onboarding/steps/ConnectorsStep.js +1 -1
  197. package/dist/onboarding/steps/ImportStep.js +4 -1
  198. package/dist/onboarding/steps/ReadyStep.js +9 -2
  199. package/dist/onboarding/steps/SkillsStep.js +1 -1
  200. package/dist/onboarding/steps/connect/KeyForm.js +6 -2
  201. package/dist/onboarding/steps/connect/ProviderPanel.js +6 -2
  202. package/dist/onboarding/steps/connect/ProviderReady.js +25 -20
  203. package/dist/onboarding/steps/tasks.js +17 -14
  204. package/dist/server/sse-handler.js +141 -19
  205. package/dist/session/ArtifactsPanel.js +39 -17
  206. package/dist/session/PlanTracker.js +12 -7
  207. package/dist/session/ProjectHome.js +13 -11
  208. package/dist/session/RemindersPanel.js +34 -16
  209. package/dist/session/RunningWorkDialog.js +6 -2
  210. package/dist/session/SecretsPanel.js +29 -10
  211. package/dist/session/SessionDetailsDialog.d.ts +1 -1
  212. package/dist/session/SessionDetailsDialog.js +18 -10
  213. package/dist/session/SideChat.js +10 -7
  214. package/dist/session/TurnDetailsDialog.js +19 -5
  215. package/dist/session/UsagePanel.js +10 -4
  216. package/dist/session/headless/host.d.ts +2 -0
  217. package/dist/session/headless/labels.d.ts +19 -1
  218. package/dist/session/headless/labels.js +15 -1
  219. package/dist/session/headless/queue.js +2 -1
  220. package/dist/session/headless/reminder-labels.d.ts +4 -0
  221. package/dist/session/headless/reminder-labels.js +4 -0
  222. package/dist/settings/SettingsRestricted.d.ts +1 -1
  223. package/dist/settings/SettingsRestricted.js +4 -4
  224. package/dist/settings/SettingsShell.d.ts +7 -0
  225. package/dist/settings/SettingsShell.js +6 -1
  226. package/dist/settings/headless/fixture-performance.d.ts +1 -1
  227. package/dist/settings/headless/host.d.ts +2 -2
  228. package/dist/settings/headless/labels.d.ts +3 -3
  229. package/dist/settings/headless/labels.js +3 -3
  230. package/dist/settings/headless/performance.d.ts +3 -4
  231. package/dist/settings/headless/search.d.ts +1 -1
  232. package/dist/settings/index.d.ts +2 -1
  233. package/dist/settings/index.js +2 -1
  234. package/dist/settings/sections/editor/EditorSection.js +7 -4
  235. package/dist/settings/sections/editor/labels.d.ts +1 -0
  236. package/dist/settings/sections/editor/labels.js +1 -0
  237. package/dist/settings/sections/environments/EnvironmentsSection.js +19 -12
  238. package/dist/settings/sections/environments/labels.d.ts +1 -0
  239. package/dist/settings/sections/environments/labels.js +2 -1
  240. package/dist/settings/sections/extensions/ExtensionConfigRows.d.ts +1 -3
  241. package/dist/settings/sections/extensions/ExtensionConfigRows.js +20 -19
  242. package/dist/settings/sections/extensions/ExtensionOptionsSection.js +2 -1
  243. package/dist/settings/sections/extensions/options-labels.d.ts +2 -1
  244. package/dist/settings/sections/extensions/options-labels.js +2 -1
  245. package/dist/settings/sections/index.d.ts +2 -1
  246. package/dist/settings/sections/index.js +2 -1
  247. package/dist/settings/sections/logs/LogsSection.d.ts +2 -2
  248. package/dist/settings/sections/logs/LogsSection.js +2 -2
  249. package/dist/settings/sections/mcp/McpSection.d.ts +1 -1
  250. package/dist/settings/sections/mcp/McpSection.js +12 -6
  251. package/dist/settings/sections/mcp/McpServerRow.js +20 -13
  252. package/dist/settings/sections/mcp/labels.d.ts +8 -2
  253. package/dist/settings/sections/mcp/labels.js +8 -2
  254. package/dist/settings/sections/models/ModelRows.js +1 -1
  255. package/dist/settings/sections/models/NewChatPlaceRow.d.ts +26 -0
  256. package/dist/settings/sections/models/NewChatPlaceRow.js +78 -0
  257. package/dist/settings/sections/models/SpacesDefaultsSection.js +15 -3
  258. package/dist/settings/sections/models/labels.d.ts +7 -0
  259. package/dist/settings/sections/models/labels.js +7 -0
  260. package/dist/settings/sections/models/model-test.js +7 -4
  261. package/dist/settings/sections/performance/PerformanceSection.js +16 -6
  262. package/dist/settings/sections/performance/labels.d.ts +1 -1
  263. package/dist/settings/sections/performance/labels.js +1 -1
  264. package/dist/settings/sections/providers/ProvidersSection.d.ts +1 -1
  265. package/dist/settings/sections/providers/ProvidersSection.js +14 -7
  266. package/dist/settings/sections/providers/ServerProvidersSection.d.ts +1 -1
  267. package/dist/settings/sections/providers/ServerProvidersSection.js +11 -5
  268. package/dist/settings/sections/providers/labels.d.ts +2 -2
  269. package/dist/settings/sections/providers/labels.js +2 -2
  270. package/dist/settings/sections/skills/SkillList.js +1 -1
  271. package/dist/settings/sections/skills/SkillsSection.js +1 -1
  272. package/dist/settings/testing/performance.contract.js +8 -12
  273. package/dist/styles.css +32 -3
  274. package/dist/terminal/HostTerminalSurface.js +15 -5
  275. package/dist/terminal/TerminalPanel.js +30 -17
  276. package/dist/terminal/TerminalSurface.js +11 -4
  277. package/dist/terminal/headless/labels.d.ts +5 -0
  278. package/dist/terminal/headless/labels.js +5 -0
  279. package/dist/terminal/headless/store.d.ts +1 -1
  280. package/dist/terminal/headless/store.js +6 -3
  281. package/dist/workspace/LayoutBudget.d.ts +5 -1
  282. package/dist/workspace/LayoutBudget.js +14 -1
  283. package/dist/workspace/PanelContentView.d.ts +1 -1
  284. package/dist/workspace/PanelContentView.js +17 -6
  285. package/dist/workspace/WorkspaceProvider.js +3 -2
  286. package/dist/workspace/headless/labels.d.ts +1 -0
  287. package/dist/workspace/headless/labels.js +1 -0
  288. package/package.json +3 -3
  289. package/src/styles/motion.css +8 -0
  290. package/src/styles/primitives.css +7 -0
@@ -1,40 +1,37 @@
1
1
  import { IconButton } from "../components/IconButton.js";
2
2
  import { BTN_PRIMARY, BTN_SECONDARY } from "../components/button.js";
3
- import { CopyButton } from "../components/CopyButton.js";
4
3
  import { FIELD_LABEL_CLASS, MetaChip, OpLine } from "../components/Form.js";
5
- import { Restricted } from "../components/Restricted.js";
4
+ import { Popover } from "../components/floating/Popover.js";
6
5
  import { constant, useReadable } from "../chat/headless/store.js";
7
6
  import { machinesError } from "./headless/host.js";
8
7
  import { useMachinesConfirm, useMachinesHost, useMachinesLabels, usePermitted } from "./MachinesProvider.js";
8
+ import { AddMachineDialog, useAddMachine } from "./AddMachineDialog.js";
9
9
  import { linkStateText } from "./headless/messages.js";
10
+ import { MachinesExplainer } from "./MachinesExplainer.js";
10
11
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
11
12
  import { useState } from "react";
12
- import { clockOrDateText, clockText, isValidMachineName, lastSeenText, machineSetupCommand, machineVersionDiffers } from "zidane/chat/pure";
13
+ import { clockOrDateText, machineVersionDiffers } from "zidane/chat/pure";
13
14
  //#region src/machines/ServerMachinesPanel.tsx
14
15
  /**
15
16
  * A server's machines (docs/MACHINES.md § Client UI):
16
17
  *
17
- * - **Share this computer** (with the host's `links.shareStart`): a name
18
- * (default: this computer's) and the folders the server may use, picked
19
- * with the host's folder dialog; a machine token pasted when the server
20
- * can't mint one.
18
+ * - **Share this computer** (with the host's `links`): its state, Stop
19
+ * sharing, and Share… which opens Add a machine on its This computer tab.
21
20
  * - **Use <server> as a machine** (`local@<server>`), offered when the server
22
21
  * lends itself.
23
- * - **Its machines** (with the host's `machines`): name, online or offline
24
- * since when, version (flagged when it differs from the main server's),
25
- * GPUs, folders, last seen; forget asks first.
26
- * - **Add a machine** (with the host's `machines.invite`, an admin token): an
27
- * optional name (empty: the machine gives its own), then a one-time join
28
- * code shown as the command to run on the machine.
22
+ * - **Where work runs**: the server itself first (its own folders), then its
23
+ * machines: online or offline since when, GPUs, folders, the version when
24
+ * it differs from the server's. Each row has New chat (the host's
25
+ * `newChat`); a machine has Forget, which asks first.
26
+ * - **Add a machine** (`useAddMachine`): the dialog that makes the join
27
+ * command and waits until the machine joins. With no machines yet, the
28
+ * list shows what a server and its machines are; after that, How it works
29
+ * shows the same picture.
29
30
  */
30
31
  /** "14:05" today, else a date, against `now` or the clock. */
31
32
  function sinceText(at, now) {
32
33
  return clockOrDateText(at, now ?? Date.now());
33
34
  }
34
- /** "seen 5 min ago", against `now` or the clock. */
35
- function seenText(lastSeen, now) {
36
- return lastSeenText(lastSeen, now ?? Date.now());
37
- }
38
35
  const NO_LINKS = constant({
39
36
  shares: [],
40
37
  uses: []
@@ -71,150 +68,149 @@ function RowIcon({ icon }) {
71
68
  children: /* @__PURE__ */ jsx("span", { className: icon })
72
69
  });
73
70
  }
74
- /** One machine: its state, version, GPUs, folders and last seen. */
75
- function MachineRow({ machine, serverVersion, now, onForget }) {
71
+ /** A place work runs (the server, or a machine): its glyph, its name and facts, its actions. */
72
+ function PlaceRow({ icon, testId, title, facts, actions }) {
73
+ return /* @__PURE__ */ jsxs("div", {
74
+ className: "flex items-start gap-2.5 text-xs py-1",
75
+ "data-testid": testId,
76
+ children: [
77
+ /* @__PURE__ */ jsx(RowIcon, { icon }),
78
+ /* @__PURE__ */ jsxs("div", {
79
+ className: "flex-1 min-w-0 space-y-1",
80
+ children: [/* @__PURE__ */ jsx("div", {
81
+ className: "flex items-center gap-1.5 min-w-0 flex-wrap",
82
+ children: title
83
+ }), facts]
84
+ }),
85
+ actions && /* @__PURE__ */ jsx("div", {
86
+ className: "shrink-0 flex items-center gap-1",
87
+ children: actions
88
+ })
89
+ ]
90
+ });
91
+ }
92
+ /** One machine: its state, GPUs, folders, its version when it differs from the server's. */
93
+ function MachineRow({ machine, serverVersion, now, onNewChat, onForget }) {
76
94
  const labels = useMachinesLabels();
77
95
  const online = machine.status === "online";
78
96
  const skew = machineVersionDiffers(machine.version, serverVersion);
79
97
  const gpus = machine.capabilities?.gpu ?? [];
80
- return /* @__PURE__ */ jsxs("div", {
81
- className: "flex items-start gap-2.5 text-xs",
82
- "data-testid": `machine-${machine.name}`,
83
- children: [
98
+ const [first, ...more] = machine.roots.map((root) => root.path);
99
+ return /* @__PURE__ */ jsx(PlaceRow, {
100
+ icon: "i-ph-desktop-tower",
101
+ testId: `machine-${machine.name}`,
102
+ title: /* @__PURE__ */ jsxs(Fragment, { children: [
84
103
  /* @__PURE__ */ jsx("span", {
85
- "aria-hidden": true,
86
- className: "i-ph-desktop-tower text-zg-ui text-[var(--zg-text-muted)] shrink-0 mt-0.5"
104
+ className: "font-medium text-[var(--zg-text)]",
105
+ children: machine.name
87
106
  }),
88
- /* @__PURE__ */ jsxs("div", {
89
- className: "flex-1 min-w-0 space-y-0.5",
107
+ /* @__PURE__ */ jsx(MetaChip, {
108
+ tone: online ? "success" : "warn",
109
+ children: online || machine.lastSeen === void 0 ? labels.machinesStatus(machine.status) : labels.machinesOfflineSince(sinceText(machine.lastSeen, now))
110
+ }),
111
+ gpus.map((gpu) => /* @__PURE__ */ jsxs(MetaChip, {
112
+ tone: "info",
90
113
  children: [
91
- /* @__PURE__ */ jsxs("div", {
92
- className: "flex items-center gap-1.5 min-w-0 flex-wrap",
93
- children: [
94
- /* @__PURE__ */ jsx("span", {
95
- className: "font-medium text-[var(--zg-text)]",
96
- children: machine.name
97
- }),
98
- /* @__PURE__ */ jsx(MetaChip, {
99
- tone: online ? "success" : "warn",
100
- children: online || machine.lastSeen === void 0 ? labels.machinesStatus(machine.status) : labels.machinesOfflineSince(sinceText(machine.lastSeen, now))
101
- }),
102
- machine.version && /* @__PURE__ */ jsxs(MetaChip, {
103
- tone: skew ? "warn" : "muted",
104
- children: [skew && /* @__PURE__ */ jsx("span", {
105
- "aria-hidden": true,
106
- className: "i-ph-warning"
107
- }), labels.machinesVersion(machine.version)]
108
- }),
109
- /* @__PURE__ */ jsx("span", {
110
- className: "text-[var(--zg-text-muted)] whitespace-nowrap",
111
- children: seenText(machine.lastSeen, now)
112
- })
113
- ]
114
- }),
115
- skew && serverVersion && /* @__PURE__ */ jsx("div", {
116
- className: "text-[var(--zg-text-muted)]",
117
- children: labels.machinesVersionDiffers(machine.version, serverVersion)
114
+ /* @__PURE__ */ jsx("span", {
115
+ "aria-hidden": true,
116
+ className: "i-ph-graphics-card"
118
117
  }),
119
- /* @__PURE__ */ jsxs("div", {
120
- className: "truncate text-[var(--zg-text-muted)]",
121
- children: [/* @__PURE__ */ jsxs("span", {
122
- className: "sr-only",
123
- children: [labels.machinesFolders, " "]
124
- }), machine.roots.map((root) => root.path).join(", ")]
118
+ /* @__PURE__ */ jsxs("span", {
119
+ className: "sr-only",
120
+ children: [labels.machinesGpus, " "]
125
121
  }),
126
- gpus.length > 0 && /* @__PURE__ */ jsxs("div", {
127
- className: "flex items-center gap-1 min-w-0 flex-wrap",
128
- children: [/* @__PURE__ */ jsxs("span", {
129
- className: "sr-only",
130
- children: [labels.machinesGpus, " "]
131
- }), gpus.map((gpu) => /* @__PURE__ */ jsx(MetaChip, {
132
- tone: "info",
133
- children: gpu
134
- }, gpu))]
135
- })
122
+ gpu
136
123
  ]
137
- }),
138
- onForget && /* @__PURE__ */ jsx(IconButton, {
139
- icon: "i-ph-trash",
140
- label: labels.machinesForgetLabel(machine.name),
141
- title: labels.machinesForget,
142
- size: "sm",
143
- onClick: onForget
124
+ }, gpu)),
125
+ skew && machine.version && /* @__PURE__ */ jsxs(MetaChip, {
126
+ tone: "warn",
127
+ children: [/* @__PURE__ */ jsx("span", {
128
+ "aria-hidden": true,
129
+ className: "i-ph-warning"
130
+ }), labels.machinesVersion(machine.version)]
144
131
  })
145
- ]
132
+ ] }),
133
+ facts: /* @__PURE__ */ jsxs(Fragment, { children: [first !== void 0 && /* @__PURE__ */ jsxs("div", {
134
+ className: "flex items-center gap-1.5 min-w-0 text-[var(--zg-text-muted)]",
135
+ children: [
136
+ /* @__PURE__ */ jsxs("span", {
137
+ className: "sr-only",
138
+ children: [labels.machinesFolders, " "]
139
+ }),
140
+ /* @__PURE__ */ jsx("span", {
141
+ className: "truncate font-mono",
142
+ title: machine.roots.map((root) => root.path).join("\n"),
143
+ children: first
144
+ }),
145
+ more.length > 0 && /* @__PURE__ */ jsx(MetaChip, {
146
+ tone: "muted",
147
+ children: labels.machinesFoldersMore(more.length)
148
+ })
149
+ ]
150
+ }), skew && serverVersion && /* @__PURE__ */ jsx("div", {
151
+ className: "text-[var(--zg-text-muted)]",
152
+ children: labels.machinesVersionDiffers(machine.version, serverVersion)
153
+ })] }),
154
+ actions: /* @__PURE__ */ jsxs(Fragment, { children: [onNewChat && /* @__PURE__ */ jsx("button", {
155
+ type: "button",
156
+ className: BTN_SECONDARY,
157
+ "aria-label": labels.machinesNewChatLabel(machine.name),
158
+ onClick: onNewChat,
159
+ children: labels.machinesNewChat
160
+ }), onForget && /* @__PURE__ */ jsx(IconButton, {
161
+ icon: "i-ph-trash",
162
+ label: labels.machinesForgetLabel(machine.name),
163
+ title: labels.machinesForget,
164
+ size: "sm",
165
+ onClick: onForget
166
+ })] })
146
167
  });
147
168
  }
148
- /** A join code made for a new machine: the command to run there, a copy button, when it stops working. */
149
- function InviteCard({ host, invite, onDone }) {
169
+ /** How it works: the explainer picture, behind a link. */
170
+ function HowItWorks() {
150
171
  const labels = useMachinesLabels();
151
- const command = machineSetupCommand(invite.join);
152
- return /* @__PURE__ */ jsxs("div", {
153
- className: "zg-well rounded-zg-md p-3 space-y-2",
154
- role: "status",
155
- "aria-label": labels.machinesInviteTitle(host.name),
156
- children: [
157
- /* @__PURE__ */ jsx("div", {
158
- className: FIELD_LABEL_CLASS,
159
- children: labels.machinesInviteCommand
160
- }),
161
- /* @__PURE__ */ jsxs("div", {
162
- className: "flex items-center gap-2 min-w-0",
163
- children: [/* @__PURE__ */ jsx("code", {
164
- className: "flex-1 min-w-0 break-all font-mono text-xs text-[var(--zg-text)]",
165
- children: command
166
- }), /* @__PURE__ */ jsx(CopyButton, {
167
- text: command,
168
- label: labels.machinesInviteCopy
169
- })]
170
- }),
171
- /* @__PURE__ */ jsx("p", {
172
- className: "m-0 text-xs text-[var(--zg-text-muted)]",
173
- children: labels.machinesInviteExpires(clockText(invite.expiresAt))
174
- }),
175
- /* @__PURE__ */ jsx("p", {
176
- className: "m-0 text-xs text-[var(--zg-text-muted)]",
177
- children: labels.machinesInviteHint
178
- }),
179
- /* @__PURE__ */ jsx("div", {
180
- className: "flex justify-end",
181
- children: /* @__PURE__ */ jsx("button", {
182
- type: "button",
183
- className: BTN_SECONDARY,
184
- onClick: onDone,
185
- children: labels.machinesInviteDone
186
- })
187
- })
188
- ]
172
+ return /* @__PURE__ */ jsx(Popover, {
173
+ ariaLabel: labels.machinesHowItWorks,
174
+ side: "bottom",
175
+ align: "end",
176
+ width: 360,
177
+ trigger: (triggerProps) => /* @__PURE__ */ jsxs("button", {
178
+ ...triggerProps,
179
+ type: "button",
180
+ className: "inline-flex items-center gap-1 border-0 bg-transparent p-0 text-xs text-[var(--zg-text-muted)] hover:text-[var(--zg-text)] cursor-pointer",
181
+ children: [/* @__PURE__ */ jsx("span", {
182
+ "aria-hidden": true,
183
+ className: "i-ph-question"
184
+ }), labels.machinesHowItWorks]
185
+ }),
186
+ children: () => /* @__PURE__ */ jsx(MachinesExplainer, { className: "p-3" })
189
187
  });
190
188
  }
191
189
  /**
192
- * Renders a server's machine links and machines.
190
+ * Renders a server's machine links, where work runs on it, and Add a machine.
193
191
  *
194
- * @param props - The server, `confirm`, `className` and `style`.
192
+ * @param props - The server, `confirm`, `now`, `className` and `style`.
195
193
  */
196
194
  function ServerMachinesPanel({ host, confirm, now, className = "", style }) {
197
195
  const labels = useMachinesLabels();
198
196
  const machinesHost = useMachinesHost();
199
197
  const providerConfirm = useMachinesConfirm();
200
198
  const ask = confirm ?? providerConfirm;
201
- const { links, machines, pickFolder } = machinesHost;
199
+ const { links, machines, newChat } = machinesHost;
202
200
  const canList = usePermitted("machines.list");
203
201
  const canForget = usePermitted("machines.forget");
204
202
  const canInvite = usePermitted("machines.invite");
205
- const inviteNeedsAdmin = host.scope !== void 0 && host.scope !== "admin";
203
+ const openLayer = useAddMachine();
204
+ const [inlineAdd, setInlineAdd] = useState(null);
205
+ const canAdd = Boolean(machines?.invite && canInvite || links?.shareStart);
206
+ const openAdd = openLayer ?? (canAdd ? (_hostId, options) => setInlineAdd(options?.tab ?? "another") : void 0);
206
207
  const list = machines && canList ? machines.list(host.id) : null;
207
208
  const linkState = useReadable(links ? links.state : NO_LINKS);
208
209
  const listState = useReadable(list ? list.state : NO_LIST);
209
210
  const share = linkState.shares.find((entry) => entry.hostId === host.id);
210
211
  const use = linkState.uses.find((entry) => entry.hostId === host.id);
211
- const [form, setForm] = useState(null);
212
212
  const [busy, setBusy] = useState(null);
213
213
  const [line, setLine] = useState(null);
214
- const [invite, setInvite] = useState(null);
215
- const [inviteForm, setInviteForm] = useState(null);
216
- const inviteName = inviteForm?.name.trim() ?? "";
217
- const inviteNameInvalid = inviteName !== "" && !isValidMachineName(inviteName);
218
214
  const reload = async () => {
219
215
  await Promise.all([links?.refresh(), list?.refresh()]);
220
216
  };
@@ -230,36 +226,16 @@ function ServerMachinesPanel({ host, confirm, now, className = "", style }) {
230
226
  kind: "error",
231
227
  text: result.message
232
228
  });
233
- return result;
234
229
  } catch (err) {
235
230
  setLine({
236
231
  kind: "error",
237
232
  text: labels.machinesError(machinesError("read", err))
238
233
  });
239
- return null;
240
234
  } finally {
241
235
  setBusy(null);
242
236
  reload();
243
237
  }
244
238
  };
245
- const addFolder = async () => {
246
- const path = await pickFolder?.();
247
- if (path && form) setForm({
248
- ...form,
249
- roots: form.roots.includes(path) ? form.roots : [...form.roots, path]
250
- });
251
- };
252
- const startShare = async () => {
253
- const start = links?.shareStart;
254
- if (!form || !start) return;
255
- const name = form.name.trim();
256
- const result = await act(labels.machinesSharing(host.name), () => start(host.id, name, form.roots, form.token.trim() || void 0), labels.machinesShared(form.roots.join(", "), host.name));
257
- if (result?.ok) setForm(null);
258
- else if (result && !result.ok && result.needsToken) setForm({
259
- ...form,
260
- needsToken: true
261
- });
262
- };
263
239
  const forget = async (machine) => {
264
240
  const drop = machines?.forget;
265
241
  if (!drop) return;
@@ -275,34 +251,16 @@ function ServerMachinesPanel({ host, confirm, now, className = "", style }) {
275
251
  }));
276
252
  reload();
277
253
  };
278
- const makeInvite = machines?.invite;
279
- const addMachine = async () => {
280
- if (!makeInvite || inviteNameInvalid) return;
281
- setBusy(labels.machinesInviteCreating);
282
- setLine(null);
283
- setInvite(null);
284
- try {
285
- setInvite(await makeInvite(host.id, inviteName ? { name: inviteName } : {}));
286
- setInviteForm(null);
287
- } catch (err) {
288
- const error = machinesError("invite", err);
289
- setLine({
290
- kind: "error",
291
- text: error.forbidden ? labels.machinesInviteNeedsAdmin : labels.machinesError(error)
292
- });
293
- } finally {
294
- setBusy(null);
295
- }
296
- };
297
- const shareStart = links?.shareStart;
298
254
  const shareStop = links?.shareStop;
299
255
  const startUsing = links?.useServer;
256
+ const canShare = Boolean(links?.shareStart);
257
+ const empty = listState.status === "ready" && listState.machines.length === 0;
300
258
  return /* @__PURE__ */ jsxs("div", {
301
259
  className: `px-3 pb-3 space-y-3 ${className}`.trimEnd(),
302
260
  style,
303
261
  "data-testid": `machines-${host.id}`,
304
262
  children: [
305
- links && (shareStart || share) && /* @__PURE__ */ jsx(ActionRow, {
263
+ links && (canShare || share) && /* @__PURE__ */ jsx(ActionRow, {
306
264
  icon: "i-ph-laptop",
307
265
  label: labels.machinesShareTitle(host.name),
308
266
  status: share ? /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -325,99 +283,14 @@ function ServerMachinesPanel({ host, confirm, now, className = "", style }) {
325
283
  disabled: busy !== null,
326
284
  onClick: () => void act(labels.machinesStopping, () => shareStop(host.id), ""),
327
285
  children: labels.machinesShareStop
328
- }) : form ? void 0 : /* @__PURE__ */ jsx("button", {
286
+ }) : openAdd && canShare && /* @__PURE__ */ jsx("button", {
329
287
  type: "button",
330
288
  className: BTN_SECONDARY,
331
289
  disabled: busy !== null,
332
- onClick: () => setForm({
333
- name: "",
334
- roots: [],
335
- token: "",
336
- needsToken: false
337
- }),
290
+ onClick: () => openAdd(host.id, { tab: "this" }),
338
291
  children: labels.machinesShareOpen
339
292
  })
340
293
  }),
341
- form && shareStart && /* @__PURE__ */ jsxs("form", {
342
- className: "space-y-2",
343
- "aria-label": labels.machinesShareForm,
344
- onSubmit: (event) => {
345
- event.preventDefault();
346
- startShare();
347
- },
348
- children: [
349
- /* @__PURE__ */ jsx("label", {
350
- className: "block text-xs text-[var(--zg-text-muted)] mb-1",
351
- htmlFor: `machine-name-${host.id}`,
352
- children: labels.machinesShareName(host.name)
353
- }),
354
- /* @__PURE__ */ jsx("input", {
355
- id: `machine-name-${host.id}`,
356
- className: "zg-well w-full h-8 px-2.5 text-zg-ui placeholder:text-[var(--zg-text-subtle)] disabled:opacity-zg-disabled",
357
- placeholder: labels.machinesShareNamePlaceholder,
358
- value: form.name,
359
- maxLength: 64,
360
- onChange: (event) => setForm({
361
- ...form,
362
- name: event.target.value
363
- })
364
- }),
365
- /* @__PURE__ */ jsx("div", {
366
- className: "block text-xs text-[var(--zg-text-muted)] mb-1",
367
- children: labels.machinesShareFolders
368
- }),
369
- /* @__PURE__ */ jsx("ul", {
370
- className: "m-0 p-0 list-none space-y-1",
371
- children: form.roots.map((root) => /* @__PURE__ */ jsxs("li", {
372
- className: "flex items-center gap-2 text-xs",
373
- children: [/* @__PURE__ */ jsx("span", {
374
- className: "flex-1 truncate",
375
- children: root
376
- }), /* @__PURE__ */ jsx("button", {
377
- type: "button",
378
- className: BTN_SECONDARY,
379
- onClick: () => setForm({
380
- ...form,
381
- roots: form.roots.filter((r) => r !== root)
382
- }),
383
- children: labels.machinesRemove
384
- })]
385
- }, root))
386
- }),
387
- pickFolder && /* @__PURE__ */ jsx("button", {
388
- type: "button",
389
- className: BTN_SECONDARY,
390
- onClick: () => void addFolder(),
391
- children: labels.machinesShareAddFolder
392
- }),
393
- form.needsToken && /* @__PURE__ */ jsx("input", {
394
- "aria-label": labels.machinesShareToken,
395
- type: "password",
396
- autoComplete: "off",
397
- className: "zg-well w-full h-8 px-2.5 text-zg-ui placeholder:text-[var(--zg-text-subtle)] disabled:opacity-zg-disabled",
398
- placeholder: labels.machinesShareTokenPlaceholder,
399
- value: form.token,
400
- onChange: (event) => setForm({
401
- ...form,
402
- token: event.target.value
403
- })
404
- }),
405
- /* @__PURE__ */ jsxs("div", {
406
- className: "flex justify-end gap-2",
407
- children: [/* @__PURE__ */ jsx("button", {
408
- type: "button",
409
- className: BTN_SECONDARY,
410
- onClick: () => setForm(null),
411
- children: labels.machinesCancel
412
- }), /* @__PURE__ */ jsx("button", {
413
- type: "submit",
414
- className: BTN_PRIMARY,
415
- disabled: busy !== null || form.roots.length === 0,
416
- children: labels.machinesShareStart
417
- })]
418
- })
419
- ]
420
- }),
421
294
  links && (startUsing || use) && /* @__PURE__ */ jsx(ActionRow, {
422
295
  icon: "i-ph-hard-drives",
423
296
  label: labels.machinesUseTitle(host.name),
@@ -442,92 +315,80 @@ function ServerMachinesPanel({ host, confirm, now, className = "", style }) {
442
315
  children: labels.machinesUse
443
316
  }))
444
317
  }),
318
+ inlineAdd && /* @__PURE__ */ jsx(AddMachineDialog, {
319
+ hostId: host.id,
320
+ tab: inlineAdd,
321
+ onClose: () => setInlineAdd(null),
322
+ className: "!w-full !max-w-none"
323
+ }),
445
324
  list ? /* @__PURE__ */ jsxs("div", {
446
- className: "space-y-1.5",
325
+ className: "space-y-1",
447
326
  children: [
448
327
  /* @__PURE__ */ jsxs("div", {
449
- className: "flex items-center gap-2",
328
+ className: "flex items-center gap-3",
450
329
  children: [
451
330
  /* @__PURE__ */ jsx("span", {
452
331
  className: `${FIELD_LABEL_CLASS} flex-1`,
453
332
  children: labels.machinesListTitle(host.name)
454
333
  }),
455
- listState.machines.length === 0 && !listState.error && /* @__PURE__ */ jsx("span", {
456
- className: "text-xs text-[var(--zg-text-subtle)]",
457
- children: labels.machinesListEmpty
458
- }),
459
- makeInvite && (inviteNeedsAdmin || canInvite) && /* @__PURE__ */ jsx(Restricted, {
460
- restricted: inviteNeedsAdmin,
461
- reason: labels.machinesAdminOnly,
462
- inline: true,
463
- children: /* @__PURE__ */ jsx("button", {
464
- type: "button",
465
- className: BTN_SECONDARY,
466
- disabled: busy !== null || inviteForm !== null,
467
- onClick: () => {
468
- setInvite(null);
469
- setInviteForm({ name: "" });
470
- },
471
- children: labels.machinesInviteOpen
472
- })
473
- })
474
- ]
475
- }),
476
- inviteForm && makeInvite && /* @__PURE__ */ jsxs("form", {
477
- className: "space-y-2",
478
- "aria-label": labels.machinesInviteForm,
479
- onSubmit: (event) => {
480
- event.preventDefault();
481
- addMachine();
482
- },
483
- children: [
484
- /* @__PURE__ */ jsx("label", {
485
- className: "block text-xs text-[var(--zg-text-muted)] mb-1",
486
- htmlFor: `machine-invite-name-${host.id}`,
487
- children: labels.machinesInviteName
488
- }),
489
- /* @__PURE__ */ jsx("input", {
490
- id: `machine-invite-name-${host.id}`,
491
- className: "zg-well w-full h-8 px-2.5 text-zg-ui placeholder:text-[var(--zg-text-subtle)] disabled:opacity-zg-disabled",
492
- placeholder: labels.machinesInviteNamePlaceholder,
493
- value: inviteForm.name,
494
- maxLength: 64,
495
- "aria-invalid": inviteNameInvalid,
496
- ...inviteNameInvalid ? { "aria-describedby": `machine-invite-rule-${host.id}` } : {},
497
- onChange: (event) => setInviteForm({ name: event.target.value })
498
- }),
499
- inviteNameInvalid && /* @__PURE__ */ jsx("p", {
500
- id: `machine-invite-rule-${host.id}`,
501
- className: "m-0 text-xs text-[var(--zg-danger)]",
502
- children: labels.machinesInviteNameRule
503
- }),
504
- /* @__PURE__ */ jsxs("div", {
505
- className: "flex justify-end gap-2",
506
- children: [/* @__PURE__ */ jsx("button", {
507
- type: "button",
508
- className: BTN_SECONDARY,
509
- onClick: () => setInviteForm(null),
510
- children: labels.machinesCancel
511
- }), /* @__PURE__ */ jsx("button", {
512
- type: "submit",
513
- className: BTN_PRIMARY,
514
- disabled: busy !== null || inviteNameInvalid,
515
- children: labels.machinesInviteCreate
516
- })]
334
+ !empty && /* @__PURE__ */ jsx(HowItWorks, {}),
335
+ openAdd && !empty && /* @__PURE__ */ jsx("button", {
336
+ type: "button",
337
+ className: BTN_SECONDARY,
338
+ onClick: () => openAdd(host.id),
339
+ children: labels.machinesInviteOpen
517
340
  })
518
341
  ]
519
342
  }),
520
- invite && /* @__PURE__ */ jsx(InviteCard, {
521
- host,
522
- invite,
523
- onDone: () => setInvite(null)
343
+ /* @__PURE__ */ jsx(PlaceRow, {
344
+ icon: "i-ph-hard-drives",
345
+ testId: `machine-server-${host.id}`,
346
+ title: /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
347
+ className: "font-medium text-[var(--zg-text)]",
348
+ children: host.name
349
+ }), /* @__PURE__ */ jsx(MetaChip, {
350
+ tone: "muted",
351
+ children: labels.machinesServerBadge
352
+ })] }),
353
+ facts: /* @__PURE__ */ jsx("div", {
354
+ className: "text-[var(--zg-text-muted)]",
355
+ children: labels.machinesServerRowHint
356
+ }),
357
+ actions: newChat && /* @__PURE__ */ jsx("button", {
358
+ type: "button",
359
+ className: BTN_SECONDARY,
360
+ "aria-label": labels.machinesNewChatLabel(host.name),
361
+ onClick: () => newChat(host.id),
362
+ children: labels.machinesNewChat
363
+ })
524
364
  }),
525
365
  listState.machines.map((machine) => /* @__PURE__ */ jsx(MachineRow, {
526
366
  machine,
527
367
  serverVersion: host.serverVersion,
528
368
  now,
369
+ ...newChat ? { onNewChat: () => newChat(host.id, machine.name) } : {},
529
370
  ...machines?.forget && canForget ? { onForget: () => void forget(machine) } : {}
530
371
  }, machine.id)),
372
+ empty && /* @__PURE__ */ jsxs("div", {
373
+ className: "zg-well rounded-zg-lg p-4 mt-2 space-y-3",
374
+ "data-testid": `machines-empty-${host.id}`,
375
+ children: [
376
+ /* @__PURE__ */ jsx("div", {
377
+ className: "text-xs font-medium text-[var(--zg-text)] text-center",
378
+ children: labels.machinesEmptyTitle(host.name)
379
+ }),
380
+ /* @__PURE__ */ jsx(MachinesExplainer, {}),
381
+ openAdd && /* @__PURE__ */ jsx("div", {
382
+ className: "flex justify-center",
383
+ children: /* @__PURE__ */ jsx("button", {
384
+ type: "button",
385
+ className: BTN_PRIMARY,
386
+ onClick: () => openAdd(host.id),
387
+ children: labels.machinesInviteOpen
388
+ })
389
+ })
390
+ ]
391
+ }),
531
392
  listState.error && /* @__PURE__ */ jsx(OpLine, {
532
393
  kind: "error",
533
394
  children: labels.machinesError(listState.error)