@tangle-network/sandbox-ui 0.3.12 → 0.4.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 CHANGED
@@ -222,6 +222,12 @@ For that, compose directly from:
222
222
 
223
223
  Retheming is absolutely supported, but the documentation was thinner than it should be. The token layer is strong; the higher-level surfaces are themeable, but more opinionated. For a radically different product look, prefer keeping the token contract and wrapping the higher-level workbench/chat surfaces rather than fighting every internal class.
224
224
 
225
+ ## Docs
226
+
227
+ | Guide | Description |
228
+ |-------|-------------|
229
+ | [Sidebar](./docs/sidebar.md) | Composable Rail + Panel sidebar system (architecture, components, full API) |
230
+
225
231
  ## Subpath Exports
226
232
 
227
233
  | Subpath | Description |
@@ -232,7 +238,7 @@ Retheming is absolutely supported, but the documentation was thinner than it sho
232
238
  | `/workspace` | SandboxWorkbench, WorkspaceLayout, DirectoryPane, RuntimePane, StatusBar |
233
239
  | `/openui` | OpenUIArtifactRenderer and schema types for structured artifact rendering |
234
240
  | `/files` | FileTree, FilePreview, FileTabs, FileArtifactPane |
235
- | `/dashboard` | DashboardLayout, BillingDashboard, UsageChart, ProfileSelector |
241
+ | `/dashboard` | [Sidebar](./docs/sidebar.md), DashboardLayout, BillingDashboard, UsageChart, ProfileSelector |
236
242
  | `/editor` | TipTap collaborative editor (requires optional peers) |
237
243
  | `/terminal` | xterm.js terminal view (requires optional peers) |
238
244
  | `/markdown` | Markdown renderer with GFM, code blocks, copy button |
package/dist/auth.js CHANGED
@@ -3,9 +3,8 @@ import {
3
3
  GitHubLoginButton,
4
4
  LoginLayout,
5
5
  UserMenu
6
- } from "./chunk-PCTEG6HR.js";
7
- import "./chunk-B26TQ7SA.js";
8
- import "./chunk-MCGKDCOR.js";
6
+ } from "./chunk-OHMO7NUX.js";
7
+ import "./chunk-IW2JZCOC.js";
9
8
  import "./chunk-HWLX5NME.js";
10
9
  import "./chunk-RQHJBTEU.js";
11
10
  export {
@@ -8,11 +8,13 @@ import { R as Run } from './run-CtFZ6s-D.js';
8
8
  import { OpenUIAction } from './openui.js';
9
9
 
10
10
  /**
11
- * ChatInput — message input bar with file attach, send/cancel, model selector.
11
+ * ChatInput — message input bar with file attach, drag-and-drop, send/cancel.
12
12
  *
13
13
  * - Auto-resizing textarea (up to max height)
14
14
  * - Enter to send, Shift+Enter for newline
15
- * - File attachment button with pending file chips
15
+ * - Drag-and-drop files onto the input with styled overlay
16
+ * - File attachment button (files) + folder attachment button
17
+ * - Pending file/folder chips
16
18
  * - Cancel button when streaming
17
19
  * - Optional model selector pill
18
20
  */
@@ -20,6 +22,9 @@ interface PendingFile {
20
22
  id: string;
21
23
  name: string;
22
24
  size: number;
25
+ type: "file" | "folder";
26
+ /** Number of files inside (for folders) */
27
+ fileCount?: number;
23
28
  status: "pending" | "uploading" | "ready" | "error";
24
29
  }
25
30
  interface ChatInputProps {
@@ -34,10 +39,19 @@ interface ChatInputProps {
34
39
  /** Pending uploaded files */
35
40
  pendingFiles?: PendingFile[];
36
41
  onRemoveFile?: (id: string) => void;
42
+ /** Called when files are attached (via button or drag-and-drop) */
37
43
  onAttach?: (files: FileList) => void;
44
+ /** Called when a folder is selected via the folder button */
45
+ onAttachFolder?: (files: FileList) => void;
46
+ /** Accepted file types for the file input (e.g. ".pdf,.csv") */
47
+ accept?: string;
48
+ /** Drop zone overlay title */
49
+ dropTitle?: string;
50
+ /** Drop zone overlay description */
51
+ dropDescription?: string;
38
52
  className?: string;
39
53
  }
40
- declare function ChatInput({ onSend, onCancel, isStreaming, disabled, placeholder, modelLabel, onModelClick, pendingFiles, onRemoveFile, onAttach, className, }: ChatInputProps): react_jsx_runtime.JSX.Element;
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;
41
55
 
42
56
  interface ChatContainerProps {
43
57
  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-Dn1jWtWo.js';
1
+ export { C as ChatContainer, a as ChatContainerProps, b as ChatInput, c as ChatInputProps, P as PendingFile } from './chat-container-Cg-GwyiK.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';
package/dist/chat.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  MessageList,
7
7
  ThinkingIndicator,
8
8
  UserMessage
9
- } from "./chunk-6H3EFUUC.js";
9
+ } from "./chunk-GVUW4VDD.js";
10
10
  import "./chunk-CNWVHQFY.js";
11
11
  import "./chunk-72UEKFZ2.js";
12
12
  import "./chunk-HRMUF35V.js";
@@ -10,7 +10,7 @@ import {
10
10
  } from "./chunk-MUOL44AE.js";
11
11
  import {
12
12
  ChatContainer
13
- } from "./chunk-6H3EFUUC.js";
13
+ } from "./chunk-GVUW4VDD.js";
14
14
  import {
15
15
  OpenUIArtifactRenderer
16
16
  } from "./chunk-YDBXQQLC.js";
@@ -18,7 +18,7 @@ import {
18
18
  FileArtifactPane,
19
19
  FileTree,
20
20
  filterFileTree
21
- } from "./chunk-IAIJUFM6.js";
21
+ } from "./chunk-HYLTXGOI.js";
22
22
  import {
23
23
  ArtifactPane
24
24
  } from "./chunk-W4LM3QYZ.js";
@@ -121,6 +121,43 @@ function ResizeHandle({ label, onDragStart, onStep, className }) {
121
121
  }
122
122
  );
123
123
  }
124
+ function HorizontalResizeHandle({ label, onDragStart, onStep, className }) {
125
+ const handlePointerDown = (event) => {
126
+ event.preventDefault();
127
+ onDragStart(event.clientY);
128
+ };
129
+ const handleKeyDown = (event) => {
130
+ if (event.key === "ArrowUp") {
131
+ event.preventDefault();
132
+ onStep(24);
133
+ }
134
+ if (event.key === "ArrowDown") {
135
+ event.preventDefault();
136
+ onStep(-24);
137
+ }
138
+ };
139
+ return /* @__PURE__ */ jsxs(
140
+ "button",
141
+ {
142
+ type: "button",
143
+ "aria-label": label,
144
+ role: "separator",
145
+ "aria-orientation": "horizontal",
146
+ onPointerDown: handlePointerDown,
147
+ onKeyDown: handleKeyDown,
148
+ className: cn(
149
+ "relative hidden h-3 shrink-0 cursor-row-resize lg:flex",
150
+ "items-center justify-center bg-transparent touch-none w-full",
151
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--brand-cool)]/60",
152
+ className
153
+ ),
154
+ children: [
155
+ /* @__PURE__ */ jsx("span", { className: "absolute inset-x-0 top-1/2 h-px -translate-y-1/2 bg-[var(--border-subtle)] transition-colors" }),
156
+ /* @__PURE__ */ jsx("span", { className: "absolute inset-x-0 top-1/2 h-[3px] -translate-y-1/2 rounded-full bg-transparent hover:bg-[var(--brand-cool)]/30 focus-visible:bg-[var(--brand-cool)]/40" })
157
+ ]
158
+ }
159
+ );
160
+ }
124
161
  function MobileDrawer({ side, title, header, onClose, children }) {
125
162
  return /* @__PURE__ */ jsxs("div", { className: "fixed inset-0 z-50 flex lg:hidden", "aria-modal": "true", role: "dialog", "aria-label": title, children: [
126
163
  /* @__PURE__ */ jsx(
@@ -174,10 +211,13 @@ function WorkspaceLayout({
174
211
  defaultBottomOpen = false,
175
212
  defaultLeftWidth = 280,
176
213
  defaultRightWidth = 480,
214
+ defaultBottomHeight = 224,
177
215
  minLeftWidth = 220,
178
216
  maxLeftWidth = 420,
179
217
  minRightWidth = 320,
180
218
  maxRightWidth = 720,
219
+ minBottomHeight = 100,
220
+ maxBottomHeight = 500,
181
221
  persistenceKey,
182
222
  resizable = true,
183
223
  theme = "operator",
@@ -202,6 +242,9 @@ function WorkspaceLayout({
202
242
  const [rightWidth, setRightWidth] = useState(
203
243
  clamp(storedLayout?.rightWidth ?? defaultRightWidth, minRightWidth, maxRightWidth)
204
244
  );
245
+ const [bottomHeight, setBottomHeight] = useState(
246
+ clamp(storedLayout?.bottomHeight ?? defaultBottomHeight, minBottomHeight, maxBottomHeight)
247
+ );
205
248
  useEffect(() => {
206
249
  if (!persistenceKey || typeof window === "undefined") return;
207
250
  const payload = {
@@ -209,16 +252,20 @@ function WorkspaceLayout({
209
252
  rightOpen,
210
253
  bottomOpen,
211
254
  leftWidth,
212
- rightWidth
255
+ rightWidth,
256
+ bottomHeight
213
257
  };
214
258
  window.localStorage.setItem(persistenceKey, JSON.stringify(payload));
215
- }, [bottomOpen, leftOpen, leftWidth, persistenceKey, rightOpen, rightWidth]);
259
+ }, [bottomHeight, bottomOpen, leftOpen, leftWidth, persistenceKey, rightOpen, rightWidth]);
216
260
  useEffect(() => {
217
261
  if (!desktop) return;
218
262
  const handlePointerMove = (event) => {
219
263
  const dragState = dragStateRef.current;
220
264
  if (!dragState) return;
221
- if (dragState.side === "left") {
265
+ if (dragState.side === "bottom") {
266
+ const delta = dragState.pointerStartY - event.clientY;
267
+ setBottomHeight(clamp(dragState.heightStart + delta, minBottomHeight, maxBottomHeight));
268
+ } else if (dragState.side === "left") {
222
269
  const delta = event.clientX - dragState.pointerStartX;
223
270
  setLeftWidth(clamp(dragState.widthStart + delta, minLeftWidth, maxLeftWidth));
224
271
  } else {
@@ -237,14 +284,25 @@ function WorkspaceLayout({
237
284
  window.removeEventListener("pointerup", handlePointerUp);
238
285
  window.removeEventListener("pointercancel", handlePointerUp);
239
286
  };
240
- }, [desktop, maxLeftWidth, maxRightWidth, minLeftWidth, minRightWidth]);
287
+ }, [desktop, maxBottomHeight, maxLeftWidth, maxRightWidth, minBottomHeight, minLeftWidth, minRightWidth]);
241
288
  const leftStyle = useMemo(() => ({ width: `${leftWidth}px` }), [leftWidth]);
242
289
  const rightStyle = useMemo(() => ({ width: `${rightWidth}px` }), [rightWidth]);
243
290
  const startResize = (side, pointerStartX) => {
244
291
  dragStateRef.current = {
245
292
  side,
246
293
  pointerStartX,
247
- widthStart: side === "left" ? leftWidth : rightWidth
294
+ pointerStartY: 0,
295
+ widthStart: side === "left" ? leftWidth : rightWidth,
296
+ heightStart: 0
297
+ };
298
+ };
299
+ const startBottomResize = (pointerStartY) => {
300
+ dragStateRef.current = {
301
+ side: "bottom",
302
+ pointerStartX: 0,
303
+ pointerStartY,
304
+ widthStart: 0,
305
+ heightStart: bottomHeight
248
306
  };
249
307
  };
250
308
  const stepLeftWidth = (delta) => {
@@ -253,6 +311,9 @@ function WorkspaceLayout({
253
311
  const stepRightWidth = (delta) => {
254
312
  setRightWidth((current) => clamp(current + delta, minRightWidth, maxRightWidth));
255
313
  };
314
+ const stepBottomHeight = (delta) => {
315
+ setBottomHeight((current) => clamp(current + delta, minBottomHeight, maxBottomHeight));
316
+ };
256
317
  return /* @__PURE__ */ jsxs(
257
318
  "div",
258
319
  {
@@ -334,29 +395,40 @@ function WorkspaceLayout({
334
395
  )
335
396
  ] }),
336
397
  /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-auto", children: center }),
337
- bottom && bottomOpen && /* @__PURE__ */ jsxs(
338
- "section",
339
- {
340
- "aria-label": bottomLabel,
341
- className: "border-t border-[var(--border-subtle)] bg-[var(--bg-card)]",
342
- children: [
343
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 border-b border-[var(--border-subtle)] px-3 py-2", children: [
344
- /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: bottomHeader ?? /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold uppercase tracking-[0.12em] text-[var(--text-muted)]", children: "Runtime" }) }),
345
- /* @__PURE__ */ jsx(
346
- "button",
347
- {
348
- type: "button",
349
- "aria-label": "Collapse bottom panel",
350
- onClick: () => setBottomOpen(false),
351
- className: "rounded-[var(--radius-sm)] p-1.5 text-[var(--text-muted)] transition-colors hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--brand-cool)]/60",
352
- children: /* @__PURE__ */ jsx(PanelBottomClose, { className: "h-4 w-4" })
353
- }
354
- )
355
- ] }),
356
- /* @__PURE__ */ jsx("div", { className: "max-h-56 overflow-auto", children: bottom })
357
- ]
358
- }
359
- ),
398
+ bottom && bottomOpen && /* @__PURE__ */ jsxs(Fragment, { children: [
399
+ resizable && /* @__PURE__ */ jsx(
400
+ HorizontalResizeHandle,
401
+ {
402
+ label: "Resize bottom panel",
403
+ onDragStart: startBottomResize,
404
+ onStep: stepBottomHeight
405
+ }
406
+ ),
407
+ /* @__PURE__ */ jsx(
408
+ "section",
409
+ {
410
+ "aria-label": bottomLabel,
411
+ className: "border-t border-[var(--border-subtle)] bg-[var(--bg-card)] shrink-0",
412
+ style: { height: `${bottomHeight}px` },
413
+ children: /* @__PURE__ */ jsxs("div", { className: "flex h-full flex-col", children: [
414
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 border-b border-[var(--border-subtle)] px-3 py-2 shrink-0", children: [
415
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: bottomHeader ?? /* @__PURE__ */ jsx("span", { className: "text-xs font-semibold uppercase tracking-[0.12em] text-[var(--text-muted)]", children: "Runtime" }) }),
416
+ /* @__PURE__ */ jsx(
417
+ "button",
418
+ {
419
+ type: "button",
420
+ "aria-label": "Collapse bottom panel",
421
+ onClick: () => setBottomOpen(false),
422
+ className: "rounded-[var(--radius-sm)] p-1.5 text-[var(--text-muted)] transition-colors hover:bg-[var(--bg-hover)] hover:text-[var(--text-secondary)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--brand-cool)]/60",
423
+ children: /* @__PURE__ */ jsx(PanelBottomClose, { className: "h-4 w-4" })
424
+ }
425
+ )
426
+ ] }),
427
+ /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-auto", children: bottom })
428
+ ] })
429
+ }
430
+ )
431
+ ] }),
360
432
  centerFooter && /* @__PURE__ */ jsx("div", { className: "shrink-0 border-t border-[var(--border-subtle)] bg-[var(--bg-dark)]", children: centerFooter })
361
433
  ] }),
362
434
  desktop && right && rightOpen && /* @__PURE__ */ jsxs(Fragment, { children: [