@tangle-network/sandbox-ui 0.6.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -6
- package/dist/auth.d.ts +10 -6
- package/dist/auth.js +3 -3
- package/dist/{chat-container-Cg-GwyiK.d.ts → chat-container-f4yEs6KN.d.ts} +9 -1
- package/dist/chat.d.ts +12 -2
- package/dist/chat.js +10 -10
- package/dist/{chunk-WBQ7VULC.js → chunk-34A66VBG.js} +7 -7
- package/dist/{chunk-JP725R4W.js → chunk-34I7UFSX.js} +2 -2
- package/dist/{chunk-CNWVHQFY.js → chunk-54SQQMMM.js} +6 -24
- package/dist/{chunk-DLCFZDGX.js → chunk-5UM2XMEJ.js} +39 -14
- package/dist/{chunk-YYGECNZZ.js → chunk-66EZOYZR.js} +3 -3
- package/dist/{chunk-6V4XVKFY.js → chunk-7YWFOGKQ.js} +344 -338
- package/dist/{chunk-DCPYTL4W.js → chunk-D4CZWJCD.js} +72 -148
- package/dist/{chunk-XTPAWK7L.js → chunk-DI3NZ5ZX.js} +15 -51
- package/dist/{chunk-MXRQ4MJE.js → chunk-DXMIEK4K.js} +34 -23
- package/dist/{chunk-ZMWWE5RF.js → chunk-EXSOPXIY.js} +141 -123
- package/dist/{chunk-GW4GRAWJ.js → chunk-GSZA3TSY.js} +18 -12
- package/dist/{chunk-W4LM3QYZ.js → chunk-HB5Y37YU.js} +8 -8
- package/dist/{chunk-RKXIRRKQ.js → chunk-JLKYXLFN.js} +70 -66
- package/dist/{chunk-BRBTD7RH.js → chunk-MA7YKRUP.js} +28 -18
- package/dist/{chunk-TSE423UF.js → chunk-MKTSMWVD.js} +6 -6
- package/dist/{chunk-MJUDMVRU.js → chunk-MT5FJ3ZT.js} +17 -17
- package/dist/{chunk-565V6JTN.js → chunk-OHPW55EV.js} +60 -99
- package/dist/chunk-OKLQVY3Y.js +139 -0
- package/dist/{chunk-OVNLOE3Y.js → chunk-PLTZB5BC.js} +41 -41
- package/dist/{chunk-E2XT3G52.js → chunk-QC4BJEG6.js} +136 -137
- package/dist/{chunk-KH5UDAJ2.js → chunk-QDH5GEGY.js} +58 -54
- package/dist/{chunk-33W2TLUL.js → chunk-QID2OOMG.js} +12 -3
- package/dist/{chunk-FJSVPBKY.js → chunk-S7OXQTST.js} +17 -3
- package/dist/chunk-T7HMZEVO.js +216 -0
- package/dist/{chunk-FNYJFCGU.js → chunk-U6QTHMY6.js} +145 -256
- package/dist/{chunk-YS66Q3RC.js → chunk-US6JKJKH.js} +2 -2
- package/dist/chunk-VX3XOUEB.js +63 -0
- package/dist/{chunk-TDYQBLL5.js → chunk-ZMNSRDMH.js} +6 -6
- package/dist/dashboard.d.ts +156 -4
- package/dist/dashboard.js +885 -8
- package/dist/{document-editor-pane-DWWUTTTZ.js → document-editor-pane-TLPVRBBU.js} +3 -3
- package/dist/editor.d.ts +9 -8
- package/dist/editor.js +3 -3
- package/dist/files.js +3 -3
- package/dist/globals.css +4787 -69
- package/dist/hooks.d.ts +1 -1
- package/dist/hooks.js +7 -7
- package/dist/index.d.ts +4 -4
- package/dist/index.js +28 -28
- package/dist/markdown.js +1 -1
- package/dist/openui.js +5 -5
- package/dist/pages.d.ts +114 -5
- package/dist/pages.js +1978 -365
- package/dist/primitives.d.ts +5 -2
- package/dist/primitives.js +10 -10
- package/dist/run.js +4 -4
- package/dist/sdk-hooks.d.ts +2 -3
- package/dist/sdk-hooks.js +5 -5
- package/dist/styles.css +4787 -69
- package/dist/template-card-BAtvcAkU.d.ts +18 -0
- package/dist/terminal.d.ts +3 -1
- package/dist/terminal.js +66 -32
- package/dist/tokens.css +289 -237
- package/dist/{usage-chart-XCoB_7Xu.d.ts → usage-chart-SSiOgeQI.d.ts} +3 -1
- package/dist/{use-pty-session-COzVkhtc.d.ts → use-pty-session-0AOuwXgq.d.ts} +2 -0
- package/dist/{index-BT_-ecpc.d.ts → variant-list-CsS6ydgm.d.ts} +16 -7
- package/dist/workspace.d.ts +2 -2
- package/dist/workspace.js +13 -13
- package/package.json +18 -3
- package/tailwind.config.cjs +3 -2
- package/dist/chunk-3HW53XTH.js +0 -228
- package/dist/chunk-OKCIKTXQ.js +0 -63
package/README.md
CHANGED
|
@@ -133,16 +133,15 @@ There is a built-in Tangle default theme, but consumers can restyle the library
|
|
|
133
133
|
|
|
134
134
|
`WorkspaceLayout` and `SandboxWorkbench` support:
|
|
135
135
|
|
|
136
|
-
- `theme="
|
|
137
|
-
-
|
|
138
|
-
- `theme="consumer"`
|
|
136
|
+
- `theme="vault"` — light theme with solid surfaces
|
|
137
|
+
- No theme prop — default dark theme
|
|
139
138
|
|
|
140
139
|
They also support `density="comfortable"` and `density="compact"`.
|
|
141
140
|
|
|
142
141
|
```tsx
|
|
143
142
|
<SandboxWorkbench
|
|
144
143
|
layout={{
|
|
145
|
-
theme: "
|
|
144
|
+
theme: "vault",
|
|
146
145
|
density: "comfortable",
|
|
147
146
|
}}
|
|
148
147
|
session={{ ... }}
|
|
@@ -152,7 +151,7 @@ They also support `density="comfortable"` and `density="compact"`.
|
|
|
152
151
|
If you are not using `SandboxWorkbench`, you can set the same attributes yourself:
|
|
153
152
|
|
|
154
153
|
```tsx
|
|
155
|
-
<div data-sandbox-ui data-sandbox-theme="
|
|
154
|
+
<div data-sandbox-ui data-sandbox-theme="vault" data-density="compact">
|
|
156
155
|
<YourSandboxApp />
|
|
157
156
|
</div>
|
|
158
157
|
```
|
|
@@ -257,7 +256,7 @@ Retheming is absolutely supported, but the documentation was thinner than it sho
|
|
|
257
256
|
- [Tailwind CSS](https://tailwindcss.com/) v4
|
|
258
257
|
- [Lucide](https://lucide.dev/) icons
|
|
259
258
|
- [CVA](https://cva.style/) for variant management
|
|
260
|
-
- Shared semantic tokens for
|
|
259
|
+
- Shared semantic tokens for default dark and `vault` light sandbox themes
|
|
261
260
|
- ESM-only, tree-shakeable, fully typed
|
|
262
261
|
|
|
263
262
|
## License
|
package/dist/auth.d.ts
CHANGED
|
@@ -54,17 +54,21 @@ interface AuthHeaderProps {
|
|
|
54
54
|
declare function AuthHeader({ user, loading, variant, apiBaseUrl, menuLinks, className, }: AuthHeaderProps): react_jsx_runtime.JSX.Element;
|
|
55
55
|
|
|
56
56
|
interface LoginLayoutProps {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
title?: React.ReactNode;
|
|
58
|
+
subtitle?: React.ReactNode;
|
|
59
|
+
brandIcon?: React.ReactNode;
|
|
60
|
+
children: React.ReactNode;
|
|
61
|
+
className?: string;
|
|
62
|
+
/** (Deprecated) terminal lines from legacy layout, kept for backwards compatibility */
|
|
60
63
|
terminalLines?: string[];
|
|
64
|
+
/** (Deprecated) tagline, kept for backwards compatibility */
|
|
65
|
+
tagline?: React.ReactNode;
|
|
66
|
+
/** (Deprecated) footerLinks, kept for backwards compatibility */
|
|
61
67
|
footerLinks?: {
|
|
62
68
|
label: string;
|
|
63
69
|
href: string;
|
|
64
70
|
}[];
|
|
65
|
-
children: React.ReactNode;
|
|
66
|
-
className?: string;
|
|
67
71
|
}
|
|
68
|
-
declare function LoginLayout({
|
|
72
|
+
declare function LoginLayout({ title, subtitle, brandIcon, children, className, }: LoginLayoutProps): react_jsx_runtime.JSX.Element;
|
|
69
73
|
|
|
70
74
|
export { AuthHeader, type AuthHeaderProps, GitHubLoginButton, type GitHubLoginButtonProps, LoginLayout, type LoginLayoutProps, type SessionUser, UserMenu, type UserMenuProps };
|
package/dist/auth.js
CHANGED
|
@@ -3,9 +3,9 @@ import {
|
|
|
3
3
|
GitHubLoginButton,
|
|
4
4
|
LoginLayout,
|
|
5
5
|
UserMenu
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
6
|
+
} from "./chunk-DI3NZ5ZX.js";
|
|
7
|
+
import "./chunk-34A66VBG.js";
|
|
8
|
+
import "./chunk-MKTSMWVD.js";
|
|
9
9
|
import "./chunk-RQHJBTEU.js";
|
|
10
10
|
export {
|
|
11
11
|
AuthHeader,
|
|
@@ -50,8 +50,16 @@ interface ChatInputProps {
|
|
|
50
50
|
/** Drop zone overlay description */
|
|
51
51
|
dropDescription?: string;
|
|
52
52
|
className?: string;
|
|
53
|
+
/** Label above the input. Set to null to hide. Default: "Agent Command Deck" */
|
|
54
|
+
inputLabel?: string | null;
|
|
55
|
+
/** Status text shown when idle. Set to null to hide. Default: "Ready for next instruction" */
|
|
56
|
+
idleStatus?: string | null;
|
|
57
|
+
/** Status text shown when streaming. Set to null to hide. Default: "Streaming response" */
|
|
58
|
+
streamingStatus?: string | null;
|
|
59
|
+
/** Hide the Cmd+L focus shortcut hint */
|
|
60
|
+
hideShortcutHint?: boolean;
|
|
53
61
|
}
|
|
54
|
-
declare function ChatInput({ onSend, onCancel, isStreaming, disabled, placeholder, modelLabel, onModelClick, pendingFiles, onRemoveFile, onAttach, onAttachFolder, accept, dropTitle, dropDescription, className, }: ChatInputProps): react_jsx_runtime.JSX.Element;
|
|
62
|
+
declare function ChatInput({ onSend, onCancel, isStreaming, disabled, placeholder, modelLabel, onModelClick, pendingFiles, onRemoveFile, onAttach, onAttachFolder, accept, dropTitle, dropDescription, className, inputLabel, idleStatus, streamingStatus, hideShortcutHint, }: ChatInputProps): react_jsx_runtime.JSX.Element;
|
|
55
63
|
|
|
56
64
|
interface ChatContainerProps {
|
|
57
65
|
messages: SessionMessage[];
|
package/dist/chat.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { C as ChatContainer, a as ChatContainerProps, b as ChatInput, c as ChatInputProps, P as PendingFile } from './chat-container-
|
|
1
|
+
export { C as ChatContainer, a as ChatContainerProps, b as ChatInput, c as ChatInputProps, P as PendingFile } from './chat-container-f4yEs6KN.js';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { ReactNode } from 'react';
|
|
4
4
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
@@ -20,8 +20,18 @@ interface ChatMessageProps {
|
|
|
20
20
|
/** Timestamp */
|
|
21
21
|
timestamp?: Date;
|
|
22
22
|
className?: string;
|
|
23
|
+
/** Custom user label. Default: "You" */
|
|
24
|
+
userLabel?: string;
|
|
25
|
+
/** Custom assistant label. Default: "Agent" */
|
|
26
|
+
assistantLabel?: string;
|
|
27
|
+
/** Hide the role label row entirely */
|
|
28
|
+
hideRoleLabel?: boolean;
|
|
29
|
+
/** Hide the avatar icon */
|
|
30
|
+
hideAvatar?: boolean;
|
|
31
|
+
/** Custom avatar element (replaces default User/Bot icon) */
|
|
32
|
+
avatar?: ReactNode;
|
|
23
33
|
}
|
|
24
|
-
declare function ChatMessage({ role, content, toolCalls, isStreaming, timestamp, className, }: ChatMessageProps): react_jsx_runtime.JSX.Element;
|
|
34
|
+
declare function ChatMessage({ role, content, toolCalls, isStreaming, timestamp, className, userLabel, assistantLabel, hideRoleLabel, hideAvatar, avatar, }: ChatMessageProps): react_jsx_runtime.JSX.Element;
|
|
25
35
|
|
|
26
36
|
interface MessageListProps {
|
|
27
37
|
groups: GroupedMessage[];
|
package/dist/chat.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ChatMessage
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-GSZA3TSY.js";
|
|
4
4
|
import {
|
|
5
5
|
AgentTimeline,
|
|
6
6
|
ChatContainer,
|
|
@@ -8,17 +8,17 @@ import {
|
|
|
8
8
|
MessageList,
|
|
9
9
|
ThinkingIndicator,
|
|
10
10
|
UserMessage
|
|
11
|
-
} from "./chunk-
|
|
12
|
-
import "./chunk-
|
|
13
|
-
import "./chunk-
|
|
11
|
+
} from "./chunk-JLKYXLFN.js";
|
|
12
|
+
import "./chunk-54SQQMMM.js";
|
|
13
|
+
import "./chunk-EXSOPXIY.js";
|
|
14
14
|
import "./chunk-HRMUF35V.js";
|
|
15
|
-
import "./chunk-
|
|
15
|
+
import "./chunk-MT5FJ3ZT.js";
|
|
16
16
|
import "./chunk-BX6AQMUS.js";
|
|
17
|
-
import "./chunk-
|
|
18
|
-
import "./chunk-
|
|
19
|
-
import "./chunk-
|
|
20
|
-
import "./chunk-
|
|
21
|
-
import "./chunk-
|
|
17
|
+
import "./chunk-PLTZB5BC.js";
|
|
18
|
+
import "./chunk-34I7UFSX.js";
|
|
19
|
+
import "./chunk-T7HMZEVO.js";
|
|
20
|
+
import "./chunk-ZMNSRDMH.js";
|
|
21
|
+
import "./chunk-MKTSMWVD.js";
|
|
22
22
|
import "./chunk-RQHJBTEU.js";
|
|
23
23
|
export {
|
|
24
24
|
AgentTimeline,
|
|
@@ -57,7 +57,7 @@ var DropdownMenuSubTrigger = React2.forwardRef(({ className, inset, children, ..
|
|
|
57
57
|
ref,
|
|
58
58
|
className: cn(
|
|
59
59
|
"flex cursor-default select-none items-center rounded-md px-2 py-1.5 text-sm outline-none",
|
|
60
|
-
"focus:bg-accent data-[state=open]:bg-
|
|
60
|
+
"focus:bg-accent data-[state=open]:bg-muted/50",
|
|
61
61
|
inset && "pl-8",
|
|
62
62
|
className
|
|
63
63
|
),
|
|
@@ -74,7 +74,7 @@ var DropdownMenuSubContent = React2.forwardRef(({ className, ...props }, ref) =>
|
|
|
74
74
|
{
|
|
75
75
|
ref,
|
|
76
76
|
className: cn(
|
|
77
|
-
"z-50 min-w-[8rem] overflow-hidden rounded-xl border border-
|
|
77
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-xl border border-border bg-card p-1 text-foreground shadow-[var(--shadow-card)]",
|
|
78
78
|
"data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
79
79
|
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
80
80
|
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
@@ -92,7 +92,7 @@ var DropdownMenuContent = React2.forwardRef(({ className, sideOffset = 4, ...pro
|
|
|
92
92
|
ref,
|
|
93
93
|
sideOffset,
|
|
94
94
|
className: cn(
|
|
95
|
-
"z-50 min-w-[8rem] overflow-hidden rounded-xl border border-
|
|
95
|
+
"z-50 min-w-[8rem] overflow-hidden rounded-xl border border-border bg-card p-1 text-foreground shadow-[var(--shadow-card)]",
|
|
96
96
|
"data-[state=closed]:animate-out data-[state=open]:animate-in",
|
|
97
97
|
"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
|
98
98
|
"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
|
|
@@ -110,7 +110,7 @@ var DropdownMenuItem = React2.forwardRef(({ className, inset, ...props }, ref) =
|
|
|
110
110
|
ref,
|
|
111
111
|
className: cn(
|
|
112
112
|
"relative flex cursor-pointer select-none items-center rounded-md px-2 py-1.5 text-sm outline-none transition-colors",
|
|
113
|
-
"focus:bg-
|
|
113
|
+
"focus:bg-muted/50 focus:text-foreground",
|
|
114
114
|
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
115
115
|
inset && "pl-8",
|
|
116
116
|
className
|
|
@@ -125,7 +125,7 @@ var DropdownMenuCheckboxItem = React2.forwardRef(({ className, children, checked
|
|
|
125
125
|
ref,
|
|
126
126
|
className: cn(
|
|
127
127
|
"relative flex cursor-pointer select-none items-center rounded-md py-1.5 pr-2 pl-8 text-sm outline-none transition-colors",
|
|
128
|
-
"focus:bg-
|
|
128
|
+
"focus:bg-muted/50 focus:text-foreground",
|
|
129
129
|
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
130
130
|
className
|
|
131
131
|
),
|
|
@@ -144,7 +144,7 @@ var DropdownMenuRadioItem = React2.forwardRef(({ className, children, ...props }
|
|
|
144
144
|
ref,
|
|
145
145
|
className: cn(
|
|
146
146
|
"relative flex cursor-pointer select-none items-center rounded-md py-1.5 pr-2 pl-8 text-sm outline-none transition-colors",
|
|
147
|
-
"focus:bg-
|
|
147
|
+
"focus:bg-muted/50 focus:text-foreground",
|
|
148
148
|
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
149
149
|
className
|
|
150
150
|
),
|
|
@@ -173,7 +173,7 @@ var DropdownMenuSeparator = React2.forwardRef(({ className, ...props }, ref) =>
|
|
|
173
173
|
DropdownMenuPrimitive.Separator,
|
|
174
174
|
{
|
|
175
175
|
ref,
|
|
176
|
-
className: cn("-mx-1 my-1 h-px bg-
|
|
176
|
+
className: cn("-mx-1 my-1 h-px bg-border", className),
|
|
177
177
|
...props
|
|
178
178
|
}
|
|
179
179
|
));
|
|
@@ -30,7 +30,7 @@ var TableFooter = React.forwardRef(({ className, ...props }, ref) => /* @__PURE_
|
|
|
30
30
|
{
|
|
31
31
|
ref,
|
|
32
32
|
className: cn(
|
|
33
|
-
"border-t bg-
|
|
33
|
+
"border-t bg-card font-medium [&>tr]:last:border-b-0",
|
|
34
34
|
className
|
|
35
35
|
),
|
|
36
36
|
...props
|
|
@@ -42,7 +42,7 @@ var TableRow = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ *
|
|
|
42
42
|
{
|
|
43
43
|
ref,
|
|
44
44
|
className: cn(
|
|
45
|
-
"border-border border-b transition-colors hover:bg-
|
|
45
|
+
"border-border border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted/50",
|
|
46
46
|
className
|
|
47
47
|
),
|
|
48
48
|
...props
|
|
@@ -91,26 +91,9 @@ function useRunGroups({
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
// src/hooks/use-run-collapse-state.ts
|
|
94
|
-
import { useCallback,
|
|
95
|
-
var AUTO_COLLAPSE_DELAY = 1e3;
|
|
94
|
+
import { useCallback, useState } from "react";
|
|
96
95
|
function useRunCollapseState(runs) {
|
|
97
96
|
const [collapsedMap, setCollapsedMap] = useState({});
|
|
98
|
-
const userOverrides = useRef(/* @__PURE__ */ new Set());
|
|
99
|
-
const completedRuns = useRef(/* @__PURE__ */ new Set());
|
|
100
|
-
useEffect(() => {
|
|
101
|
-
const timers = [];
|
|
102
|
-
for (const run of runs) {
|
|
103
|
-
if (run.isComplete && !completedRuns.current.has(run.id)) {
|
|
104
|
-
completedRuns.current.add(run.id);
|
|
105
|
-
if (userOverrides.current.has(run.id)) continue;
|
|
106
|
-
const timer = setTimeout(() => {
|
|
107
|
-
setCollapsedMap((prev) => ({ ...prev, [run.id]: true }));
|
|
108
|
-
}, AUTO_COLLAPSE_DELAY);
|
|
109
|
-
timers.push(timer);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return () => timers.forEach(clearTimeout);
|
|
113
|
-
}, [runs]);
|
|
114
97
|
const isCollapsed = useCallback(
|
|
115
98
|
(runId) => {
|
|
116
99
|
return collapsedMap[runId] ?? false;
|
|
@@ -118,7 +101,6 @@ function useRunCollapseState(runs) {
|
|
|
118
101
|
[collapsedMap]
|
|
119
102
|
);
|
|
120
103
|
const toggleCollapse = useCallback((runId) => {
|
|
121
|
-
userOverrides.current.add(runId);
|
|
122
104
|
setCollapsedMap((prev) => ({ ...prev, [runId]: !prev[runId] }));
|
|
123
105
|
}, []);
|
|
124
106
|
return { isCollapsed, toggleCollapse };
|
|
@@ -127,20 +109,20 @@ function useRunCollapseState(runs) {
|
|
|
127
109
|
// src/hooks/use-auto-scroll.ts
|
|
128
110
|
import {
|
|
129
111
|
useCallback as useCallback2,
|
|
130
|
-
useEffect
|
|
131
|
-
useRef
|
|
112
|
+
useEffect,
|
|
113
|
+
useRef,
|
|
132
114
|
useState as useState2
|
|
133
115
|
} from "react";
|
|
134
116
|
var BOTTOM_THRESHOLD = 40;
|
|
135
117
|
function useAutoScroll(containerRef, deps = []) {
|
|
136
118
|
const [isAtBottom, setIsAtBottom] = useState2(true);
|
|
137
|
-
const userScrolledUp =
|
|
119
|
+
const userScrolledUp = useRef(false);
|
|
138
120
|
const checkBottom = useCallback2(() => {
|
|
139
121
|
const el = containerRef.current;
|
|
140
122
|
if (!el) return true;
|
|
141
123
|
return el.scrollHeight - el.scrollTop - el.clientHeight < BOTTOM_THRESHOLD;
|
|
142
124
|
}, [containerRef]);
|
|
143
|
-
|
|
125
|
+
useEffect(() => {
|
|
144
126
|
const el = containerRef.current;
|
|
145
127
|
if (!el) return;
|
|
146
128
|
const onScroll = () => {
|
|
@@ -151,7 +133,7 @@ function useAutoScroll(containerRef, deps = []) {
|
|
|
151
133
|
el.addEventListener("scroll", onScroll, { passive: true });
|
|
152
134
|
return () => el.removeEventListener("scroll", onScroll);
|
|
153
135
|
}, [containerRef, checkBottom]);
|
|
154
|
-
|
|
136
|
+
useEffect(() => {
|
|
155
137
|
if (userScrolledUp.current) return;
|
|
156
138
|
const el = containerRef.current;
|
|
157
139
|
if (!el) return;
|
|
@@ -9,7 +9,6 @@ function usePtySession({ apiUrl, token, onData }) {
|
|
|
9
9
|
const retryCountRef = useRef(0);
|
|
10
10
|
const mountedRef = useRef(true);
|
|
11
11
|
const onDataRef = useRef(onData);
|
|
12
|
-
onDataRef.current = onData;
|
|
13
12
|
const connectStreamRef = useRef(null);
|
|
14
13
|
const abortStream = useCallback(() => {
|
|
15
14
|
if (retryTimerRef.current) {
|
|
@@ -117,6 +116,7 @@ function usePtySession({ apiUrl, token, onData }) {
|
|
|
117
116
|
}
|
|
118
117
|
}
|
|
119
118
|
}, [apiUrl, token, abortStream]);
|
|
119
|
+
onDataRef.current = onData;
|
|
120
120
|
connectStreamRef.current = connectStream;
|
|
121
121
|
const connect = useCallback(async () => {
|
|
122
122
|
cleanup();
|
|
@@ -149,21 +149,46 @@ function usePtySession({ apiUrl, token, onData }) {
|
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
}, [apiUrl, token, cleanup, connectStream]);
|
|
152
|
+
const resizeTerminal = useCallback(async (cols, rows) => {
|
|
153
|
+
const sid = sessionIdRef.current;
|
|
154
|
+
if (!sid || cols <= 0 || rows <= 0) return;
|
|
155
|
+
try {
|
|
156
|
+
const res = await fetch(`${apiUrl}/terminals/${sid}`, {
|
|
157
|
+
method: "PATCH",
|
|
158
|
+
headers: {
|
|
159
|
+
Authorization: `Bearer ${token}`,
|
|
160
|
+
"Content-Type": "application/json"
|
|
161
|
+
},
|
|
162
|
+
credentials: "include",
|
|
163
|
+
body: JSON.stringify({ cols, rows })
|
|
164
|
+
});
|
|
165
|
+
if (!res.ok) {
|
|
166
|
+
console.error("Failed to resize terminal:", res.status);
|
|
167
|
+
}
|
|
168
|
+
} catch (err) {
|
|
169
|
+
console.error("Failed to resize terminal", err);
|
|
170
|
+
}
|
|
171
|
+
}, [apiUrl, token]);
|
|
152
172
|
const sendCommand = useCallback(async (command) => {
|
|
153
173
|
const sid = sessionIdRef.current;
|
|
154
174
|
if (!sid) return;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
175
|
+
try {
|
|
176
|
+
const res = await fetch(`${apiUrl}/terminals/${sid}/input`, {
|
|
177
|
+
method: "POST",
|
|
178
|
+
headers: {
|
|
179
|
+
Authorization: `Bearer ${token}`,
|
|
180
|
+
"Content-Type": "application/json"
|
|
181
|
+
},
|
|
182
|
+
credentials: "include",
|
|
183
|
+
body: JSON.stringify({ data: command })
|
|
184
|
+
});
|
|
185
|
+
if (!res.ok) {
|
|
186
|
+
const text = await res.text();
|
|
187
|
+
throw new Error(text || `Input failed: ${res.status}`);
|
|
188
|
+
}
|
|
189
|
+
} catch (err) {
|
|
190
|
+
console.error("Failed to send command", err);
|
|
191
|
+
throw err;
|
|
167
192
|
}
|
|
168
193
|
}, [apiUrl, token]);
|
|
169
194
|
useEffect(() => {
|
|
@@ -174,7 +199,7 @@ function usePtySession({ apiUrl, token, onData }) {
|
|
|
174
199
|
cleanup();
|
|
175
200
|
};
|
|
176
201
|
}, [connect, cleanup]);
|
|
177
|
-
return { isConnected, error, sendCommand, reconnect: connect };
|
|
202
|
+
return { isConnected, error, sendCommand, resizeTerminal, reconnect: connect };
|
|
178
203
|
}
|
|
179
204
|
|
|
180
205
|
export {
|
|
@@ -52,7 +52,7 @@ function Skeleton({
|
|
|
52
52
|
return /* @__PURE__ */ jsx2(
|
|
53
53
|
"div",
|
|
54
54
|
{
|
|
55
|
-
className: cn("animate-pulse rounded-lg bg-
|
|
55
|
+
className: cn("animate-pulse rounded-lg bg-muted/50", className),
|
|
56
56
|
...props
|
|
57
57
|
}
|
|
58
58
|
);
|
|
@@ -62,7 +62,7 @@ function SkeletonCard({ className }) {
|
|
|
62
62
|
"div",
|
|
63
63
|
{
|
|
64
64
|
className: cn(
|
|
65
|
-
"space-y-4 rounded-xl border border-
|
|
65
|
+
"space-y-4 rounded-xl border border-border bg-card p-6",
|
|
66
66
|
className
|
|
67
67
|
),
|
|
68
68
|
children: [
|
|
@@ -79,7 +79,7 @@ function SkeletonCard({ className }) {
|
|
|
79
79
|
}
|
|
80
80
|
function SkeletonTable({ rows = 5 }) {
|
|
81
81
|
return /* @__PURE__ */ jsxs2("div", { className: "space-y-3", children: [
|
|
82
|
-
/* @__PURE__ */ jsxs2("div", { className: "flex gap-4 border-
|
|
82
|
+
/* @__PURE__ */ jsxs2("div", { className: "flex gap-4 border-border border-b pb-2", children: [
|
|
83
83
|
/* @__PURE__ */ jsx2(Skeleton, { className: "h-4 w-1/4" }),
|
|
84
84
|
/* @__PURE__ */ jsx2(Skeleton, { className: "h-4 w-1/4" }),
|
|
85
85
|
/* @__PURE__ */ jsx2(Skeleton, { className: "h-4 w-1/4" }),
|