@vibesdotdev/terminal-client 0.0.0-stage → 1.0.0-rc.3

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 (201) hide show
  1. package/LICENSE +18 -0
  2. package/README.md +102 -2
  3. package/dist/ansi/index.d.ts +11 -0
  4. package/dist/ansi/index.d.ts.map +1 -0
  5. package/dist/ansi/index.js +11 -0
  6. package/dist/ansi/index.js.map +1 -0
  7. package/dist/ansi/keymap.d.ts +77 -0
  8. package/dist/ansi/keymap.d.ts.map +1 -0
  9. package/dist/ansi/keymap.js +257 -0
  10. package/dist/ansi/keymap.js.map +1 -0
  11. package/dist/api-client-plugin.d.ts +4 -0
  12. package/dist/api-client-plugin.d.ts.map +1 -0
  13. package/dist/api-client-plugin.js +16 -0
  14. package/dist/api-client-plugin.js.map +1 -0
  15. package/dist/client/http-session.d.ts +44 -0
  16. package/dist/client/http-session.d.ts.map +1 -0
  17. package/dist/client/http-session.js +457 -0
  18. package/dist/client/http-session.js.map +1 -0
  19. package/dist/client/index.d.ts +8 -0
  20. package/dist/client/index.d.ts.map +1 -0
  21. package/dist/client/index.js +8 -0
  22. package/dist/client/index.js.map +1 -0
  23. package/dist/client.d.ts +56 -0
  24. package/dist/client.d.ts.map +1 -0
  25. package/dist/client.js +169 -0
  26. package/dist/client.js.map +1 -0
  27. package/dist/index.d.ts +14 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +14 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/pty-bridge/index.d.ts +15 -0
  32. package/dist/pty-bridge/index.d.ts.map +1 -0
  33. package/dist/pty-bridge/index.js +15 -0
  34. package/dist/pty-bridge/index.js.map +1 -0
  35. package/dist/pty-bridge/protocol.d.ts +6 -0
  36. package/dist/pty-bridge/protocol.d.ts.map +1 -0
  37. package/dist/pty-bridge/protocol.js +6 -0
  38. package/dist/pty-bridge/protocol.js.map +1 -0
  39. package/dist/pty-bridge/server.d.ts +7 -0
  40. package/dist/pty-bridge/server.d.ts.map +1 -0
  41. package/dist/pty-bridge/server.js +9 -0
  42. package/dist/pty-bridge/server.js.map +1 -0
  43. package/dist/remote-session.d.ts +51 -0
  44. package/dist/remote-session.d.ts.map +1 -0
  45. package/dist/remote-session.js +326 -0
  46. package/dist/remote-session.js.map +1 -0
  47. package/dist/schemas/http.d.ts +61 -0
  48. package/dist/schemas/http.d.ts.map +1 -0
  49. package/dist/schemas/http.js +45 -0
  50. package/dist/schemas/http.js.map +1 -0
  51. package/dist/schemas/remote.d.ts +25 -0
  52. package/dist/schemas/remote.d.ts.map +1 -0
  53. package/dist/schemas/remote.js +23 -0
  54. package/dist/schemas/remote.js.map +1 -0
  55. package/dist/schemas/session.d.ts +26 -0
  56. package/dist/schemas/session.d.ts.map +1 -0
  57. package/dist/schemas/session.js +17 -0
  58. package/dist/schemas/session.js.map +1 -0
  59. package/dist/schemas/store.d.ts +29 -0
  60. package/dist/schemas/store.d.ts.map +1 -0
  61. package/dist/schemas/store.js +22 -0
  62. package/dist/schemas/store.js.map +1 -0
  63. package/dist/schemas/terminal.d.ts +296 -0
  64. package/dist/schemas/terminal.d.ts.map +1 -0
  65. package/dist/schemas/terminal.js +243 -0
  66. package/dist/schemas/terminal.js.map +1 -0
  67. package/dist/server/index.d.ts +9 -0
  68. package/dist/server/index.d.ts.map +1 -0
  69. package/dist/server/index.js +9 -0
  70. package/dist/server/index.js.map +1 -0
  71. package/dist/server/session-store.d.ts +176 -0
  72. package/dist/server/session-store.d.ts.map +1 -0
  73. package/dist/server/session-store.js +462 -0
  74. package/dist/server/session-store.js.map +1 -0
  75. package/dist/server/sse-stream.d.ts +32 -0
  76. package/dist/server/sse-stream.d.ts.map +1 -0
  77. package/dist/server/sse-stream.js +87 -0
  78. package/dist/server/sse-stream.js.map +1 -0
  79. package/dist/svelte/HostPicker.svelte +263 -0
  80. package/dist/svelte/HostPicker.svelte.d.ts +36 -0
  81. package/dist/svelte/HostPicker.svelte.d.ts.map +1 -0
  82. package/dist/svelte/README.md +75 -0
  83. package/dist/svelte/ReconnectBanner.svelte +88 -0
  84. package/dist/svelte/ReconnectBanner.svelte.d.ts +16 -0
  85. package/dist/svelte/ReconnectBanner.svelte.d.ts.map +1 -0
  86. package/dist/svelte/SessionTabs.svelte +192 -0
  87. package/dist/svelte/SessionTabs.svelte.d.ts +17 -0
  88. package/dist/svelte/SessionTabs.svelte.d.ts.map +1 -0
  89. package/dist/svelte/Terminal.svelte +567 -0
  90. package/dist/svelte/Terminal.svelte.d.ts +25 -0
  91. package/dist/svelte/Terminal.svelte.d.ts.map +1 -0
  92. package/dist/svelte/TerminalHostWorkspace.svelte +264 -0
  93. package/dist/svelte/TerminalHostWorkspace.svelte.d.ts +59 -0
  94. package/dist/svelte/TerminalHostWorkspace.svelte.d.ts.map +1 -0
  95. package/dist/svelte/TerminalMobileToolbar.svelte +56 -0
  96. package/dist/svelte/TerminalMobileToolbar.svelte.d.ts +14 -0
  97. package/dist/svelte/TerminalMobileToolbar.svelte.d.ts.map +1 -0
  98. package/dist/svelte/TerminalWorkspace.svelte +562 -0
  99. package/dist/svelte/TerminalWorkspace.svelte.d.ts +25 -0
  100. package/dist/svelte/TerminalWorkspace.svelte.d.ts.map +1 -0
  101. package/dist/svelte/ambient.d.ts +9 -0
  102. package/dist/svelte/docs/terminal-svelte.docs.descriptor.d.ts +4 -0
  103. package/dist/svelte/docs/terminal-svelte.docs.descriptor.d.ts.map +1 -0
  104. package/dist/svelte/docs/terminal-svelte.docs.descriptor.js +74 -0
  105. package/dist/svelte/index.d.ts +29 -0
  106. package/dist/svelte/index.d.ts.map +1 -0
  107. package/dist/svelte/index.js +28 -0
  108. package/dist/svelte/styles.css +8 -0
  109. package/dist/svelte/terminal-host-workspace.d.ts +152 -0
  110. package/dist/svelte/terminal-host-workspace.d.ts.map +1 -0
  111. package/dist/svelte/terminal-host-workspace.js +335 -0
  112. package/dist/svelte/terminal-session-authority.d.ts +17 -0
  113. package/dist/svelte/terminal-session-authority.d.ts.map +1 -0
  114. package/dist/svelte/terminal-session-authority.js +25 -0
  115. package/dist/svelte/terminal-svelte.plugin.d.ts +21 -0
  116. package/dist/svelte/terminal-svelte.plugin.d.ts.map +1 -0
  117. package/dist/svelte/terminal-svelte.plugin.js +53 -0
  118. package/dist/svelte/terminal-theme.d.ts +11 -0
  119. package/dist/svelte/terminal-theme.d.ts.map +1 -0
  120. package/dist/svelte/terminal-theme.js +116 -0
  121. package/dist/svelte/tool-widgets/TerminalToolCard.svelte +65 -0
  122. package/dist/svelte/tool-widgets/TerminalToolCard.svelte.d.ts +5 -0
  123. package/dist/svelte/tool-widgets/TerminalToolCard.svelte.d.ts.map +1 -0
  124. package/dist/svelte/tool-widgets/terminal-tool-card.d.ts +25 -0
  125. package/dist/svelte/tool-widgets/terminal-tool-card.d.ts.map +1 -0
  126. package/dist/svelte/tool-widgets/terminal-tool-card.js +116 -0
  127. package/dist/svelte/types.d.ts +160 -0
  128. package/dist/svelte/types.d.ts.map +1 -0
  129. package/dist/svelte/types.js +51 -0
  130. package/dist/svelte/workspace.d.ts +3 -0
  131. package/dist/svelte/workspace.d.ts.map +1 -0
  132. package/dist/svelte/workspace.js +2 -0
  133. package/dist/terminal-client.plugin.d.ts +3 -0
  134. package/dist/terminal-client.plugin.d.ts.map +1 -0
  135. package/dist/terminal-client.plugin.js +3 -0
  136. package/dist/terminal-client.plugin.js.map +1 -0
  137. package/dist/terminal-runtime.d.ts +30 -0
  138. package/dist/terminal-runtime.d.ts.map +1 -0
  139. package/dist/terminal-runtime.js +673 -0
  140. package/dist/terminal-runtime.js.map +1 -0
  141. package/dist/terminal.consumer.plugin.d.ts +14 -0
  142. package/dist/terminal.consumer.plugin.d.ts.map +1 -0
  143. package/dist/terminal.consumer.plugin.js +31 -0
  144. package/dist/terminal.consumer.plugin.js.map +1 -0
  145. package/dist/tools/terminal.impl.consumer.d.ts +11 -0
  146. package/dist/tools/terminal.impl.consumer.d.ts.map +1 -0
  147. package/dist/tools/terminal.impl.consumer.js +20 -0
  148. package/dist/tools/terminal.impl.consumer.js.map +1 -0
  149. package/dist/tools/terminal.schemas.d.ts +3 -0
  150. package/dist/tools/terminal.schemas.d.ts.map +1 -0
  151. package/dist/tools/terminal.schemas.js +3 -0
  152. package/dist/tools/terminal.schemas.js.map +1 -0
  153. package/dist/tools/terminal.unified.descriptor.d.ts +218 -0
  154. package/dist/tools/terminal.unified.descriptor.d.ts.map +1 -0
  155. package/dist/tools/terminal.unified.descriptor.js +36 -0
  156. package/dist/tools/terminal.unified.descriptor.js.map +1 -0
  157. package/dist/tui/index.d.ts +9 -0
  158. package/dist/tui/index.d.ts.map +1 -0
  159. package/dist/tui/index.js +8 -0
  160. package/dist/tui/index.js.map +1 -0
  161. package/dist/tui/terminal-open.descriptor.d.ts +9 -0
  162. package/dist/tui/terminal-open.descriptor.d.ts.map +1 -0
  163. package/dist/tui/terminal-open.descriptor.js +18 -0
  164. package/dist/tui/terminal-open.descriptor.js.map +1 -0
  165. package/dist/tui/terminal-open.impl.consumer.d.ts +9 -0
  166. package/dist/tui/terminal-open.impl.consumer.d.ts.map +1 -0
  167. package/dist/tui/terminal-open.impl.consumer.js +13 -0
  168. package/dist/tui/terminal-open.impl.consumer.js.map +1 -0
  169. package/dist/tui/terminal-registry.descriptor.d.ts +11 -0
  170. package/dist/tui/terminal-registry.descriptor.d.ts.map +1 -0
  171. package/dist/tui/terminal-registry.descriptor.js +17 -0
  172. package/dist/tui/terminal-registry.descriptor.js.map +1 -0
  173. package/dist/tui/terminal-registry.impl.consumer.d.ts +71 -0
  174. package/dist/tui/terminal-registry.impl.consumer.d.ts.map +1 -0
  175. package/dist/tui/terminal-registry.impl.consumer.js +190 -0
  176. package/dist/tui/terminal-registry.impl.consumer.js.map +1 -0
  177. package/dist/tui/terminal-tui.plugin.d.ts +10 -0
  178. package/dist/tui/terminal-tui.plugin.d.ts.map +1 -0
  179. package/dist/tui/terminal-tui.plugin.js +32 -0
  180. package/dist/tui/terminal-tui.plugin.js.map +1 -0
  181. package/dist/tui/terminal.descriptor.d.ts +10 -0
  182. package/dist/tui/terminal.descriptor.d.ts.map +1 -0
  183. package/dist/tui/terminal.descriptor.js +25 -0
  184. package/dist/tui/terminal.descriptor.js.map +1 -0
  185. package/dist/tui/terminal.impl.consumer.d.ts +14 -0
  186. package/dist/tui/terminal.impl.consumer.d.ts.map +1 -0
  187. package/dist/tui/terminal.impl.consumer.js +189 -0
  188. package/dist/tui/terminal.impl.consumer.js.map +1 -0
  189. package/dist/tui/terminal.state-key.d.ts +30 -0
  190. package/dist/tui/terminal.state-key.d.ts.map +1 -0
  191. package/dist/tui/terminal.state-key.js +9 -0
  192. package/dist/tui/terminal.state-key.js.map +1 -0
  193. package/dist/tui/terminal.state.d.ts +152 -0
  194. package/dist/tui/terminal.state.d.ts.map +1 -0
  195. package/dist/tui/terminal.state.js +149 -0
  196. package/dist/tui/terminal.state.js.map +1 -0
  197. package/dist/workspace.d.ts +155 -0
  198. package/dist/workspace.d.ts.map +1 -0
  199. package/dist/workspace.js +292 -0
  200. package/dist/workspace.js.map +1 -0
  201. package/package.json +228 -4
@@ -0,0 +1,29 @@
1
+ /**
2
+ * @vibesdotdev/terminal-client/svelte
3
+ *
4
+ * The xterm.js rendering surface for an L3 `ExecutionSession` byte stream.
5
+ * `<Terminal session={…} />` writes keystrokes to the PTY, paints output, and
6
+ * wires resize/signal/exit. Transport-agnostic: the same component renders a
7
+ * local PTY (L9a) and an RPC proxy (L9b).
8
+ *
9
+ * Registers no runtime kinds — Terminal is a view over a session. The
10
+ * `./plugin` entrypoint registers package docs; the shared workspace entrypoint
11
+ * composes session choices through the host's handed runtime.
12
+ *
13
+ * All `@xterm/*` imports are deferred to `onMount` inside the component, so this
14
+ * barrel is import-safe on the server (no eager DOM/WebGL import, no `$state`
15
+ * barrel trap).
16
+ */
17
+ export { default as Terminal } from './Terminal.svelte';
18
+ export { default as TerminalWorkspace } from './TerminalWorkspace.svelte';
19
+ export { default as TerminalHostWorkspace } from './TerminalHostWorkspace.svelte';
20
+ export { default as TerminalMobileToolbar } from './TerminalMobileToolbar.svelte';
21
+ export { default as HostPicker } from './HostPicker.svelte';
22
+ export { default as SessionTabs } from './SessionTabs.svelte';
23
+ export { default as ReconnectBanner } from './ReconnectBanner.svelte';
24
+ export { default as TerminalToolCard } from './tool-widgets/TerminalToolCard.svelte';
25
+ export { TerminalHostWorkspaceController, describeTerminalHostFailure, terminalHostLabel, terminalHostTabBanner, type TerminalHostBanner, type TerminalHostBannerStatus, type TerminalHostErrorDescriber, type TerminalHostOpenedSession, type TerminalHostSessionOpener, type TerminalHostSessionRequest, type TerminalHostTab, type TerminalHostTabStatus, type TerminalHostWorkspaceLayout, type TerminalHostWorkspaceOptions, type TerminalHostWorkspaceSnapshot, type TerminalHostWorkspaceStart } from './terminal-host-workspace.ts';
26
+ export { registerTerminalSvelteToolWidget, terminalSveltePlugin, terminalSvelteWidgetDescriptor, default } from './terminal-svelte.plugin.ts';
27
+ export { openTerminalWorkspaceSession, openTerminalWorkspaceTargetSession, terminalWorkspaceCapabilityBanner, terminalWorkspaceError, terminalWorkspaceExitedEvent, terminalWorkspacePlaceDefaultCapabilities, terminalWorkspacePlaceLabel, terminalWorkspaceStatusLabel, terminalWorkspaceVesselStateSchema, type OpenTerminalWorkspaceSessionInput, type OpenTerminalWorkspaceSessionResult, type OpenTerminalWorkspaceTargetSessionInput, type OpenTerminalWorkspaceTargetSessionResult, type TerminalWorkspaceExitedEvent, type TerminalWorkspaceLifecycleEvent, type TerminalWorkspaceOpenedReceipt, type TerminalWorkspaceRuntimePlaceOption, type TerminalWorkspaceSessionMode, type TerminalWorkspaceStatus } from '../workspace.ts';
28
+ export { DEFAULT_FONT, DEFAULT_TERMINAL_THEME, VIBES_TERMINAL_THEME, type ExecutionSession, type TerminalProps, type TerminalTheme, type HostOption, type TabSession } from './types.ts';
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/svelte/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,0BAA0B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,wCAAwC,CAAC;AACrF,OAAO,EACN,+BAA+B,EAC/B,2BAA2B,EAC3B,iBAAiB,EACjB,qBAAqB,EACrB,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,0BAA0B,EAC/B,KAAK,yBAAyB,EAC9B,KAAK,yBAAyB,EAC9B,KAAK,0BAA0B,EAC/B,KAAK,eAAe,EACpB,KAAK,qBAAqB,EAC1B,KAAK,2BAA2B,EAChC,KAAK,4BAA4B,EACjC,KAAK,6BAA6B,EAClC,KAAK,0BAA0B,EAC/B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACN,gCAAgC,EAChC,oBAAoB,EACpB,8BAA8B,EAC9B,OAAO,EACP,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACN,4BAA4B,EAC5B,kCAAkC,EAClC,iCAAiC,EACjC,sBAAsB,EACtB,4BAA4B,EAC5B,yCAAyC,EACzC,2BAA2B,EAC3B,4BAA4B,EAC5B,kCAAkC,EAClC,KAAK,iCAAiC,EACtC,KAAK,kCAAkC,EACvC,KAAK,uCAAuC,EAC5C,KAAK,wCAAwC,EAC7C,KAAK,4BAA4B,EACjC,KAAK,+BAA+B,EACpC,KAAK,8BAA8B,EACnC,KAAK,mCAAmC,EACxC,KAAK,4BAA4B,EACjC,KAAK,uBAAuB,EAC5B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACN,YAAY,EACZ,sBAAsB,EACtB,oBAAoB,EACpB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,MAAM,YAAY,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * @vibesdotdev/terminal-client/svelte
3
+ *
4
+ * The xterm.js rendering surface for an L3 `ExecutionSession` byte stream.
5
+ * `<Terminal session={…} />` writes keystrokes to the PTY, paints output, and
6
+ * wires resize/signal/exit. Transport-agnostic: the same component renders a
7
+ * local PTY (L9a) and an RPC proxy (L9b).
8
+ *
9
+ * Registers no runtime kinds — Terminal is a view over a session. The
10
+ * `./plugin` entrypoint registers package docs; the shared workspace entrypoint
11
+ * composes session choices through the host's handed runtime.
12
+ *
13
+ * All `@xterm/*` imports are deferred to `onMount` inside the component, so this
14
+ * barrel is import-safe on the server (no eager DOM/WebGL import, no `$state`
15
+ * barrel trap).
16
+ */
17
+ export { default as Terminal } from './Terminal.svelte';
18
+ export { default as TerminalWorkspace } from './TerminalWorkspace.svelte';
19
+ export { default as TerminalHostWorkspace } from './TerminalHostWorkspace.svelte';
20
+ export { default as TerminalMobileToolbar } from './TerminalMobileToolbar.svelte';
21
+ export { default as HostPicker } from './HostPicker.svelte';
22
+ export { default as SessionTabs } from './SessionTabs.svelte';
23
+ export { default as ReconnectBanner } from './ReconnectBanner.svelte';
24
+ export { default as TerminalToolCard } from './tool-widgets/TerminalToolCard.svelte';
25
+ export { TerminalHostWorkspaceController, describeTerminalHostFailure, terminalHostLabel, terminalHostTabBanner } from "./terminal-host-workspace.js";
26
+ export { registerTerminalSvelteToolWidget, terminalSveltePlugin, terminalSvelteWidgetDescriptor, default } from "./terminal-svelte.plugin.js";
27
+ export { openTerminalWorkspaceSession, openTerminalWorkspaceTargetSession, terminalWorkspaceCapabilityBanner, terminalWorkspaceError, terminalWorkspaceExitedEvent, terminalWorkspacePlaceDefaultCapabilities, terminalWorkspacePlaceLabel, terminalWorkspaceStatusLabel, terminalWorkspaceVesselStateSchema } from "../workspace.js";
28
+ export { DEFAULT_FONT, DEFAULT_TERMINAL_THEME, VIBES_TERMINAL_THEME } from "./types.js";
@@ -0,0 +1,8 @@
1
+ /*
2
+ * Tailwind v4 source declaration for consumers — mirrors @vibesdotdev/kit's
3
+ * styles.css pattern. Apps that @import this file get this package's
4
+ * components scanned by their Tailwind build, so utility classes used only
5
+ * inside these components are emitted in the app's CSS bundle (Tailwind
6
+ * never scans node_modules). Paths resolve relative to this file.
7
+ */
8
+ @source "./";
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Rune-free state owner for `<TerminalHostWorkspace>`.
3
+ *
4
+ * The workspace is the embeddable terminal experience: pick a host, open
5
+ * sessions in tabs, see reconnect state, close sessions. This controller owns
6
+ * that state machine so it is the same in a full page, a dialog, or a panel,
7
+ * and so it is testable without a DOM. The component renders a snapshot and
8
+ * forwards user and `<Terminal>` events here.
9
+ *
10
+ * Transport is injected: the host app supplies a `TerminalHostSessionOpener`
11
+ * (typically a closure over `openHttpSession`). The controller never imports
12
+ * an HTTP client and never invents a session — a tab is `live` only after the
13
+ * opener resolves with a real `ExecutionSession`.
14
+ */
15
+ import { CapabilityError } from '@vibesdotdev/kernel';
16
+ import type { ExecutionSession } from '@vibesdotdev/kernel/execution';
17
+ import type { HostOption } from './types.ts';
18
+ /** What the workspace asks the host app's opener for. */
19
+ export interface TerminalHostSessionRequest {
20
+ /** The selected `HostOption.id`. */
21
+ hostId: string;
22
+ /** Command to run; `undefined` means the host's default shell. */
23
+ command?: string;
24
+ /** Fitted (or locked) grid the session must start at. */
25
+ cols: number;
26
+ rows: number;
27
+ /** Aborted when the user closes the tab or the workspace unmounts mid-open. */
28
+ signal: AbortSignal;
29
+ /**
30
+ * Transport reconnect hooks. Forward these to the transport (for
31
+ * `openHttpSession`, as `reconnect.onReconnecting` / `reconnect.onReconnected`)
32
+ * so the workspace can show the reconnect banner.
33
+ */
34
+ reconnect: {
35
+ onReconnecting: (attempt: number, delayMs: number) => void;
36
+ onReconnected: (attempt: number) => void;
37
+ };
38
+ }
39
+ /** A session the opener created. */
40
+ export interface TerminalHostOpenedSession {
41
+ /** Stable session id (shown nowhere; used for tab identity and app hooks). */
42
+ id: string;
43
+ session: ExecutionSession;
44
+ }
45
+ /** Dependency-injected session opener; the workspace owns no transport. */
46
+ export type TerminalHostSessionOpener = (request: TerminalHostSessionRequest) => Promise<TerminalHostOpenedSession>;
47
+ /** Turns an open failure into user-facing copy. */
48
+ export type TerminalHostErrorDescriber = (failure: CapabilityError, hostLabel: string) => string;
49
+ /** Where the workspace is mounted. Only density changes; it always fills its container. */
50
+ export type TerminalHostWorkspaceLayout = 'page' | 'dialog' | 'panel';
51
+ export type TerminalHostTabStatus = 'opening' | 'live' | 'reconnecting' | 'failed' | 'exited';
52
+ export type TerminalHostBannerStatus = 'reconnecting' | 'reconnected' | 'failed' | null;
53
+ export interface TerminalHostTab {
54
+ /** Workspace-local identity (stable before the session id exists). */
55
+ readonly key: string;
56
+ /** Session id once opened. */
57
+ readonly sessionId: string | null;
58
+ readonly hostId: string;
59
+ readonly hostLabel: string;
60
+ /** Requested command; `undefined` = host default shell. */
61
+ readonly command: string | undefined;
62
+ /** Observe mode: keystrokes never reach the session. */
63
+ readonly readonly: boolean;
64
+ readonly status: TerminalHostTabStatus;
65
+ readonly session: ExecutionSession | null;
66
+ readonly inputReady: boolean;
67
+ readonly exitCode: number | null;
68
+ /** User-facing failure copy for `failed`. */
69
+ readonly error: string;
70
+ /** The typed failure behind `failed`, for hosts that log or report it. */
71
+ readonly failure: CapabilityError | null;
72
+ readonly reconnectAttempt: number;
73
+ readonly reconnectDelayMs: number;
74
+ /** True for a short moment after a successful reconnect. */
75
+ readonly reattached: boolean;
76
+ }
77
+ export interface TerminalHostBanner {
78
+ status: TerminalHostBannerStatus;
79
+ attempt: number;
80
+ delayMs: number;
81
+ reason: string;
82
+ }
83
+ export interface TerminalHostWorkspaceSnapshot {
84
+ readonly tabs: readonly TerminalHostTab[];
85
+ readonly activeKey: string;
86
+ readonly pickerOpen: boolean;
87
+ /** The deep-linked host is not in the reachable host list. */
88
+ readonly requestedHostUnavailable: boolean;
89
+ /** Host the picker preselects. */
90
+ readonly pickerHostId: string;
91
+ /** Command the picker pre-fills (never auto-run). */
92
+ readonly pickerCommand: string;
93
+ }
94
+ export interface TerminalHostWorkspaceStart {
95
+ hosts: readonly HostOption[];
96
+ /** Deep-linked host id. Absent → the first host. */
97
+ initialHostId?: string;
98
+ /** Deep-linked command: pre-filled in the picker, never run without a click. */
99
+ initialCommand?: string;
100
+ /** Open a session on the initial host immediately (no picker) when no command is pre-filled. */
101
+ autoOpen?: boolean;
102
+ }
103
+ export interface TerminalHostWorkspaceOptions {
104
+ openSession: TerminalHostSessionOpener;
105
+ /** Observe mode for every session this workspace opens. */
106
+ readonly?: boolean;
107
+ describeError?: TerminalHostErrorDescriber;
108
+ /** How long the "Reattached" banner stays (ms). */
109
+ reattachedNoticeMs?: number;
110
+ }
111
+ /** Product copy for an open failure. Never names transports, kinds, or descriptors. */
112
+ export declare function describeTerminalHostFailure(failure: CapabilityError, hostLabel: string): string;
113
+ /** The banner the active tab shows. Exited and failed tabs offer a new session. */
114
+ export declare function terminalHostTabBanner(tab: TerminalHostTab | undefined): TerminalHostBanner;
115
+ /** Tab label host suffix and picker label for a host. */
116
+ export declare function terminalHostLabel(host: HostOption | undefined, hostId: string): string;
117
+ type Listener = (snapshot: TerminalHostWorkspaceSnapshot) => void;
118
+ export declare class TerminalHostWorkspaceController {
119
+ #private;
120
+ constructor(options: TerminalHostWorkspaceOptions);
121
+ get snapshot(): TerminalHostWorkspaceSnapshot;
122
+ subscribe(listener: Listener): () => void;
123
+ /** The latest reachable hosts (labels for new tabs). */
124
+ setHosts(hosts: readonly HostOption[]): void;
125
+ /** Resolve the deep link once: open on the initial host, or show the picker. */
126
+ start(input: TerminalHostWorkspaceStart): void;
127
+ /** Create a tab on `hostId`. The session opens once its terminal reports a grid. */
128
+ open(hostId: string, command?: string): string;
129
+ /** `<Terminal onReady>` for a tab: start the session at the fitted grid. */
130
+ terminalReady(key: string, dims: {
131
+ cols: number;
132
+ rows: number;
133
+ }): void;
134
+ /** Retry a failed tab in place (same host + command, new session). */
135
+ retry(key: string, dims: {
136
+ cols: number;
137
+ rows: number;
138
+ }): void;
139
+ inputReady(key: string): void;
140
+ exited(key: string, exitCode: number | null): void;
141
+ disconnected(key: string, reason: string): void;
142
+ select(key: string): void;
143
+ /** Close a tab: abort an in-flight open, end a live session. */
144
+ close(key: string): void;
145
+ showPicker(): void;
146
+ /** Dismiss the picker (only when a tab can be shown behind it). */
147
+ hidePicker(): void;
148
+ /** Unmount: abort opens and end every session this workspace started. */
149
+ dispose(): void;
150
+ }
151
+ export {};
152
+ //# sourceMappingURL=terminal-host-workspace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-host-workspace.d.ts","sourceRoot":"","sources":["../../src/svelte/terminal-host-workspace.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,335 @@
1
+ /**
2
+ * Rune-free state owner for `<TerminalHostWorkspace>`.
3
+ *
4
+ * The workspace is the embeddable terminal experience: pick a host, open
5
+ * sessions in tabs, see reconnect state, close sessions. This controller owns
6
+ * that state machine so it is the same in a full page, a dialog, or a panel,
7
+ * and so it is testable without a DOM. The component renders a snapshot and
8
+ * forwards user and `<Terminal>` events here.
9
+ *
10
+ * Transport is injected: the host app supplies a `TerminalHostSessionOpener`
11
+ * (typically a closure over `openHttpSession`). The controller never imports
12
+ * an HTTP client and never invents a session — a tab is `live` only after the
13
+ * opener resolves with a real `ExecutionSession`.
14
+ */
15
+ import * as z from 'zod/v4';
16
+ import { CapabilityError, CaughtFailureSchema } from '@vibesdotdev/kernel';
17
+ const HttpStatusSchema = z.number().int();
18
+ /** Product copy for an open failure. Never names transports, kinds, or descriptors. */
19
+ export function describeTerminalHostFailure(failure, hostLabel) {
20
+ const parsedStatus = HttpStatusSchema.safeParse(failure.details?.status);
21
+ const status = parsedStatus.success ? parsedStatus.data : undefined;
22
+ const text = `${failure.message} ${failure.reason}`.toLowerCase();
23
+ const machine = hostLabel || 'this machine';
24
+ if (status === 401)
25
+ return 'Sign in to open a terminal session.';
26
+ if (status === 403 || failure.code === 'PERMISSION_DENIED') {
27
+ return "Access denied — you don't have permission to open a terminal on this machine.";
28
+ }
29
+ if (status === 429 || /max[_\s-]?sessions|too many sessions/.test(text)) {
30
+ return `Too many sessions are open on ${machine} — close one to start another.`;
31
+ }
32
+ if (status === 503 || status === 502 || /unreachable|unavailable|offline/.test(text)) {
33
+ return `${machine} is not reachable right now. Make sure it is online, then try again.`;
34
+ }
35
+ return `Unable to open a terminal on ${machine}. Check that it is online, then try again.`;
36
+ }
37
+ /** The banner the active tab shows. Exited and failed tabs offer a new session. */
38
+ export function terminalHostTabBanner(tab) {
39
+ const none = { status: null, attempt: 0, delayMs: 0, reason: '' };
40
+ if (!tab)
41
+ return none;
42
+ switch (tab.status) {
43
+ case 'reconnecting':
44
+ return { status: 'reconnecting', attempt: tab.reconnectAttempt, delayMs: tab.reconnectDelayMs, reason: '' };
45
+ case 'failed':
46
+ return { ...none, status: 'failed', reason: tab.error || 'Connection lost and could not be restored.' };
47
+ case 'exited':
48
+ return {
49
+ ...none,
50
+ status: 'failed',
51
+ reason: tab.exitCode === null ? 'Session ended.' : `Session ended — exit code ${tab.exitCode}.`
52
+ };
53
+ case 'live':
54
+ return tab.reattached ? { ...none, status: 'reconnected' } : none;
55
+ default:
56
+ return none;
57
+ }
58
+ }
59
+ /** Tab label host suffix and picker label for a host. */
60
+ export function terminalHostLabel(host, hostId) {
61
+ if (!host)
62
+ return hostId;
63
+ return host.local ? `This machine — ${host.name}` : host.name;
64
+ }
65
+ export class TerminalHostWorkspaceController {
66
+ #options;
67
+ #hosts = [];
68
+ #tabs = [];
69
+ #runtime = new Map();
70
+ #activeKey = '';
71
+ #pickerOpen = true;
72
+ #requestedHostUnavailable = false;
73
+ #pickerHostId = '';
74
+ #pickerCommand = '';
75
+ #nextKey = 0;
76
+ #disposed = false;
77
+ #listeners = new Set();
78
+ #snapshot;
79
+ constructor(options) {
80
+ this.#options = options;
81
+ this.#snapshot = this.#build();
82
+ }
83
+ get snapshot() {
84
+ return this.#snapshot;
85
+ }
86
+ subscribe(listener) {
87
+ this.#listeners.add(listener);
88
+ return () => this.#listeners.delete(listener);
89
+ }
90
+ /** The latest reachable hosts (labels for new tabs). */
91
+ setHosts(hosts) {
92
+ this.#hosts = hosts;
93
+ }
94
+ /** Resolve the deep link once: open on the initial host, or show the picker. */
95
+ start(input) {
96
+ this.#hosts = input.hosts;
97
+ const requested = input.initialHostId?.trim() ?? '';
98
+ const known = requested ? input.hosts.some((host) => host.id === requested) : false;
99
+ this.#requestedHostUnavailable = Boolean(requested) && !known;
100
+ this.#pickerHostId = known ? requested : (input.hosts[0]?.id ?? '');
101
+ this.#pickerCommand = input.initialCommand ?? '';
102
+ const canAutoOpen = input.autoOpen === true &&
103
+ !this.#requestedHostUnavailable &&
104
+ !this.#pickerCommand &&
105
+ this.#pickerHostId !== '';
106
+ if (canAutoOpen)
107
+ this.open(this.#pickerHostId);
108
+ else
109
+ this.#emit();
110
+ }
111
+ /** Create a tab on `hostId`. The session opens once its terminal reports a grid. */
112
+ open(hostId, command) {
113
+ const key = `tab-${++this.#nextKey}`;
114
+ const host = this.#hosts.find((candidate) => candidate.id === hostId);
115
+ this.#tabs = [
116
+ ...this.#tabs,
117
+ {
118
+ key,
119
+ sessionId: null,
120
+ hostId,
121
+ hostLabel: terminalHostLabel(host, hostId),
122
+ command: command?.trim() ? command.trim() : undefined,
123
+ readonly: this.#options.readonly === true,
124
+ status: 'opening',
125
+ session: null,
126
+ inputReady: false,
127
+ exitCode: null,
128
+ error: '',
129
+ failure: null,
130
+ reconnectAttempt: 0,
131
+ reconnectDelayMs: 0,
132
+ reattached: false
133
+ }
134
+ ];
135
+ this.#runtime.set(key, { controller: null, requested: false, reattachTimer: null });
136
+ this.#activeKey = key;
137
+ this.#pickerOpen = false;
138
+ this.#requestedHostUnavailable = false;
139
+ this.#emit();
140
+ return key;
141
+ }
142
+ /** `<Terminal onReady>` for a tab: start the session at the fitted grid. */
143
+ terminalReady(key, dims) {
144
+ const tab = this.#tab(key);
145
+ const runtime = this.#runtime.get(key);
146
+ if (!tab || !runtime || runtime.requested || tab.status !== 'opening')
147
+ return;
148
+ runtime.requested = true;
149
+ void this.#openSession(key, tab, runtime, dims);
150
+ }
151
+ /** Retry a failed tab in place (same host + command, new session). */
152
+ retry(key, dims) {
153
+ const tab = this.#tab(key);
154
+ const runtime = this.#runtime.get(key);
155
+ if (!tab || !runtime || tab.status !== 'failed')
156
+ return;
157
+ tab.session?.kill();
158
+ this.#patch(key, { status: 'opening', session: null, sessionId: null, error: '', inputReady: false });
159
+ runtime.requested = false;
160
+ this.terminalReady(key, dims);
161
+ }
162
+ inputReady(key) {
163
+ const tab = this.#tab(key);
164
+ if (tab && !tab.inputReady && tab.session)
165
+ this.#patch(key, { inputReady: true });
166
+ }
167
+ exited(key, exitCode) {
168
+ const tab = this.#tab(key);
169
+ if (!tab || tab.status === 'exited')
170
+ return;
171
+ this.#patch(key, { status: 'exited', exitCode, inputReady: false, reattached: false });
172
+ }
173
+ disconnected(key, reason) {
174
+ const tab = this.#tab(key);
175
+ if (!tab || tab.status === 'exited')
176
+ return;
177
+ this.#patch(key, {
178
+ status: 'failed',
179
+ inputReady: false,
180
+ reattached: false,
181
+ error: reason ? `Connection lost — ${reason}` : 'Connection lost and could not be restored.'
182
+ });
183
+ }
184
+ select(key) {
185
+ if (!this.#tab(key))
186
+ return;
187
+ this.#activeKey = key;
188
+ this.#pickerOpen = false;
189
+ this.#emit();
190
+ }
191
+ /** Close a tab: abort an in-flight open, end a live session. */
192
+ close(key) {
193
+ const tab = this.#tab(key);
194
+ if (!tab)
195
+ return;
196
+ this.#release(key, tab);
197
+ const index = this.#tabs.findIndex((candidate) => candidate.key === key);
198
+ this.#tabs = this.#tabs.filter((candidate) => candidate.key !== key);
199
+ if (this.#activeKey === key) {
200
+ this.#activeKey = this.#tabs[Math.min(index, this.#tabs.length - 1)]?.key ?? '';
201
+ }
202
+ if (this.#tabs.length === 0)
203
+ this.#pickerOpen = true;
204
+ this.#emit();
205
+ }
206
+ showPicker() {
207
+ this.#pickerOpen = true;
208
+ this.#emit();
209
+ }
210
+ /** Dismiss the picker (only when a tab can be shown behind it). */
211
+ hidePicker() {
212
+ if (this.#tabs.length === 0)
213
+ return;
214
+ this.#pickerOpen = false;
215
+ this.#emit();
216
+ }
217
+ /** Unmount: abort opens and end every session this workspace started. */
218
+ dispose() {
219
+ if (this.#disposed)
220
+ return;
221
+ this.#disposed = true;
222
+ for (const tab of this.#tabs)
223
+ this.#release(tab.key, tab);
224
+ this.#listeners.clear();
225
+ }
226
+ async #openSession(key, tab, runtime, dims) {
227
+ const controller = new AbortController();
228
+ runtime.controller = controller;
229
+ const live = () => !this.#disposed && !controller.signal.aborted && this.#tab(key) !== undefined;
230
+ const outcome = await this.#options
231
+ .openSession({
232
+ hostId: tab.hostId,
233
+ command: tab.command,
234
+ cols: dims.cols,
235
+ rows: dims.rows,
236
+ signal: controller.signal,
237
+ reconnect: {
238
+ onReconnecting: (attempt, delayMs) => {
239
+ if (!live())
240
+ return;
241
+ this.#clearReattached(runtime);
242
+ this.#patch(key, {
243
+ status: 'reconnecting',
244
+ reconnectAttempt: attempt,
245
+ reconnectDelayMs: delayMs,
246
+ reattached: false
247
+ });
248
+ },
249
+ onReconnected: () => {
250
+ if (!live())
251
+ return;
252
+ this.#patch(key, { status: 'live', reconnectAttempt: 0, reconnectDelayMs: 0, reattached: true });
253
+ this.#clearReattached(runtime);
254
+ runtime.reattachTimer = setTimeout(() => {
255
+ runtime.reattachTimer = null;
256
+ if (this.#tab(key))
257
+ this.#patch(key, { reattached: false });
258
+ }, this.#options.reattachedNoticeMs ?? 3000);
259
+ }
260
+ }
261
+ })
262
+ .then((opened) => ({ kind: 'opened', opened }), (cause) => ({ kind: 'failure', failure: CapabilityError.from(CaughtFailureSchema.parse(cause)) }));
263
+ if (runtime.controller === controller)
264
+ runtime.controller = null;
265
+ if (outcome.kind === 'opened' && !live()) {
266
+ outcome.opened.session.kill();
267
+ return;
268
+ }
269
+ if (!live())
270
+ return;
271
+ if (outcome.kind === 'failure') {
272
+ const describe = this.#options.describeError ?? describeTerminalHostFailure;
273
+ this.#patch(key, { status: 'failed', failure: outcome.failure, error: describe(outcome.failure, tab.hostLabel) });
274
+ return;
275
+ }
276
+ this.#patch(key, { status: 'live', session: outcome.opened.session, sessionId: outcome.opened.id });
277
+ void outcome.opened.session.exited().then((result) => this.exited(key, result.exitCode), (cause) => this.#lost(key, CapabilityError.from(CaughtFailureSchema.parse(cause))));
278
+ }
279
+ /** The session's exit wait failed: the transport is gone and could not reconnect. */
280
+ #lost(key, failure) {
281
+ const tab = this.#tab(key);
282
+ if (!tab || tab.status === 'exited')
283
+ return;
284
+ this.#patch(key, {
285
+ status: 'failed',
286
+ inputReady: false,
287
+ reattached: false,
288
+ failure,
289
+ error: 'Connection lost and could not be restored.'
290
+ });
291
+ }
292
+ #release(key, tab) {
293
+ const runtime = this.#runtime.get(key);
294
+ if (runtime) {
295
+ runtime.controller?.abort();
296
+ runtime.controller = null;
297
+ this.#clearReattached(runtime);
298
+ }
299
+ this.#runtime.delete(key);
300
+ if (tab.session && tab.status !== 'exited')
301
+ tab.session.kill();
302
+ }
303
+ #clearReattached(runtime) {
304
+ if (runtime.reattachTimer !== null) {
305
+ clearTimeout(runtime.reattachTimer);
306
+ runtime.reattachTimer = null;
307
+ }
308
+ }
309
+ #tab(key) {
310
+ return this.#tabs.find((tab) => tab.key === key);
311
+ }
312
+ #patch(key, patch) {
313
+ if (this.#disposed)
314
+ return;
315
+ this.#tabs = this.#tabs.map((tab) => (tab.key === key ? { ...tab, ...patch } : tab));
316
+ this.#emit();
317
+ }
318
+ #build() {
319
+ return {
320
+ tabs: this.#tabs,
321
+ activeKey: this.#activeKey,
322
+ pickerOpen: this.#pickerOpen,
323
+ requestedHostUnavailable: this.#requestedHostUnavailable,
324
+ pickerHostId: this.#pickerHostId,
325
+ pickerCommand: this.#pickerCommand
326
+ };
327
+ }
328
+ #emit() {
329
+ if (this.#disposed)
330
+ return;
331
+ this.#snapshot = this.#build();
332
+ for (const listener of this.#listeners)
333
+ listener(this.#snapshot);
334
+ }
335
+ }
@@ -0,0 +1,17 @@
1
+ export interface TerminalSessionLease<T> {
2
+ session: T;
3
+ generation: number;
4
+ }
5
+ /**
6
+ * Keeps async output/exit callbacks scoped to the session attachment that
7
+ * created them. Detaching invalidates every outstanding lease immediately.
8
+ */
9
+ export declare class TerminalSessionAuthority<T> {
10
+ private generation;
11
+ private currentSession;
12
+ get current(): T | null;
13
+ attach(session: T): TerminalSessionLease<T>;
14
+ detach(session?: T): void;
15
+ isCurrent(lease: TerminalSessionLease<T>): boolean;
16
+ }
17
+ //# sourceMappingURL=terminal-session-authority.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-session-authority.d.ts","sourceRoot":"","sources":["../../src/svelte/terminal-session-authority.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oBAAoB,CAAC,CAAC;IACtC,OAAO,EAAE,CAAC,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,qBAAa,wBAAwB,CAAC,CAAC;IACtC,OAAO,CAAC,UAAU,CAAK;IACvB,OAAO,CAAC,cAAc,CAAkB;IAExC,IAAI,OAAO,IAAI,CAAC,GAAG,IAAI,CAEtB;IAED,MAAM,CAAC,OAAO,EAAE,CAAC,GAAG,oBAAoB,CAAC,CAAC,CAAC;IAM3C,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,IAAI;IAMzB,SAAS,CAAC,KAAK,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAAG,OAAO;CAGlD"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Keeps async output/exit callbacks scoped to the session attachment that
3
+ * created them. Detaching invalidates every outstanding lease immediately.
4
+ */
5
+ export class TerminalSessionAuthority {
6
+ generation = 0;
7
+ currentSession = null;
8
+ get current() {
9
+ return this.currentSession;
10
+ }
11
+ attach(session) {
12
+ this.currentSession = session;
13
+ this.generation += 1;
14
+ return { session, generation: this.generation };
15
+ }
16
+ detach(session) {
17
+ if (session !== undefined && this.currentSession !== session)
18
+ return;
19
+ this.currentSession = null;
20
+ this.generation += 1;
21
+ }
22
+ isCurrent(lease) {
23
+ return this.currentSession === lease.session && this.generation === lease.generation;
24
+ }
25
+ }
@@ -0,0 +1,21 @@
1
+ /** Settles after every widget registration this module queued so far (none when Kit is absent). */
2
+ export declare function toolWidgetsRegistered(): Promise<void>;
3
+ /**
4
+ * Designed Kit card for `tools/tool:terminal`. Queued as `tools/ui` when the
5
+ * kind is not yet registered so docs-only hosts can still load this plugin.
6
+ */
7
+ export declare const terminalSvelteWidgetDescriptor: {
8
+ id: string;
9
+ kind: "tools/ui";
10
+ name: string;
11
+ description: string;
12
+ surfaces: readonly ["web"];
13
+ role: "widget";
14
+ targetToolId: string;
15
+ targetId: string;
16
+ enabled: boolean;
17
+ };
18
+ export declare function registerTerminalSvelteToolWidget(): void;
19
+ export declare const terminalSveltePlugin: import("@vibesdotdev/kernel").RuntimePlugin;
20
+ export default terminalSveltePlugin;
21
+ //# sourceMappingURL=terminal-svelte.plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-svelte.plugin.d.ts","sourceRoot":"","sources":["../../src/svelte/terminal-svelte.plugin.ts"],"names":[],"mappings":"AAcA,mGAAmG;AACnG,wBAAgB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAErD;AAED;;;GAGG;AACH,eAAO,MAAM,8BAA8B;;;;;;;;;;CAU1C,CAAC;AAEF,wBAAgB,gCAAgC,IAAI,IAAI,CAEvD;AAID,eAAO,MAAM,oBAAoB,6CAiB/B,CAAC;AAEH,eAAe,oBAAoB,CAAC"}