@tangle-network/sandbox-ui 0.91.0 → 0.91.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/chat.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  snapHarnessToModel,
20
20
  snapModelToHarness,
21
21
  validateComposerFiles
22
- } from "./chunk-GHG4TCDS.js";
22
+ } from "./chunk-QMY3XVH3.js";
23
23
  import "./chunk-TK46XFLM.js";
24
24
  import {
25
25
  MENTION_PILL_CLASS
@@ -676,88 +676,96 @@ function HarnessDropdown({
676
676
  }
677
677
  );
678
678
  }
679
- return /* @__PURE__ */ jsxs3(DropdownMenu2.Root, { children: [
680
- /* @__PURE__ */ jsx3(DropdownMenu2.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs3(
681
- "button",
682
- {
683
- type: "button",
684
- disabled: disabled || locked,
685
- title: locked ? lockReason : void 0,
686
- className: cn(
687
- "inline-flex h-8 items-center gap-1.5 rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-2.5",
688
- "text-xs font-medium text-foreground shadow-sm transition-colors",
689
- "hover:border-[var(--md3-outline-variant)] hover:bg-surface-container-high focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
690
- "data-[state=open]:border-[var(--md3-outline-variant)] data-[state=open]:bg-surface-container-high",
691
- "disabled:cursor-not-allowed disabled:opacity-60",
692
- triggerClassName
693
- ),
694
- "aria-label": "Agent harness",
695
- children: [
696
- /* @__PURE__ */ jsx3(HarnessLogo, { type: value, size: 16, className: "shrink-0" }),
697
- /* @__PURE__ */ jsx3("span", { className: "truncate", children: selected?.label ?? value }),
698
- !locked && /* @__PURE__ */ jsx3(ChevronDown3, { className: "ml-auto h-3.5 w-3.5 shrink-0 text-muted-foreground" })
699
- ]
700
- }
701
- ) }),
702
- /* @__PURE__ */ jsx3(DropdownMenu2.Portal, { children: /* @__PURE__ */ jsxs3(
703
- DropdownMenu2.Content,
704
- {
705
- side,
706
- align,
707
- avoidCollisions,
708
- collisionPadding: 24,
709
- sideOffset: 6,
710
- className: pickerMenuContentClass,
711
- children: [
712
- showSearch && /* @__PURE__ */ jsx3(
713
- PickerMenuSearch,
714
- {
715
- value: query,
716
- onChange: setQuery,
717
- placeholder: "Search agents..."
718
- }
719
- ),
720
- /* @__PURE__ */ jsx3("div", { className: pickerMenuBodyClass, children: sections.length === 0 ? /* @__PURE__ */ jsx3("div", { className: "px-3 py-8 text-center text-xs text-muted-foreground", children: "No agents match." }) : sections.map((section) => /* @__PURE__ */ jsx3(PickerMenuSection, { label: section.label, children: section.items.map((option) => /* @__PURE__ */ jsxs3(
721
- DropdownMenu2.Item,
722
- {
723
- onSelect: (event) => {
724
- event.preventDefault();
725
- onChange(option.type);
726
- },
727
- className: pickerMenuItemClass({
728
- active: option.type === value
729
- }),
730
- children: [
731
- /* @__PURE__ */ jsx3(
732
- HarnessLogo,
733
- {
734
- type: option.type,
735
- size: 20,
736
- className: "mt-0.5"
737
- }
738
- ),
739
- /* @__PURE__ */ jsxs3("div", { className: "min-w-0 flex-1", children: [
740
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1.5", children: [
741
- /* @__PURE__ */ jsx3("span", { className: "min-w-0 truncate text-sm font-medium", children: option.label }),
742
- harnessAutonomyNote(option.type) && /* @__PURE__ */ jsx3(PickerMenuTag, { children: harnessAutonomyNote(option.type) })
743
- ] }),
744
- /* @__PURE__ */ jsx3(PickerMenuMeta, { parts: [option.description] })
745
- ] })
746
- ]
747
- },
748
- option.type
749
- )) }, section.key)) }),
750
- showSearch && /* @__PURE__ */ jsxs3(PickerMenuFooter, { children: [
751
- filtered.length,
752
- " of ",
753
- options.length,
754
- " agent",
755
- options.length === 1 ? "" : "s"
756
- ] })
757
- ]
758
- }
759
- ) })
760
- ] });
679
+ return /* @__PURE__ */ jsxs3(
680
+ DropdownMenu2.Root,
681
+ {
682
+ onOpenChange: (nextOpen) => {
683
+ if (!nextOpen) setQuery("");
684
+ },
685
+ children: [
686
+ /* @__PURE__ */ jsx3(DropdownMenu2.Trigger, { asChild: true, children: /* @__PURE__ */ jsxs3(
687
+ "button",
688
+ {
689
+ type: "button",
690
+ disabled: disabled || locked,
691
+ title: locked ? lockReason : void 0,
692
+ className: cn(
693
+ "inline-flex h-8 items-center gap-1.5 rounded-lg border border-[var(--md3-outline-variant)] bg-surface-container px-2.5",
694
+ "text-xs font-medium text-foreground shadow-sm transition-colors",
695
+ "hover:border-[var(--md3-outline-variant)] hover:bg-surface-container-high focus:outline-none focus-visible:ring-1 focus-visible:ring-ring",
696
+ "data-[state=open]:border-[var(--md3-outline-variant)] data-[state=open]:bg-surface-container-high",
697
+ "disabled:cursor-not-allowed disabled:opacity-60",
698
+ triggerClassName
699
+ ),
700
+ "aria-label": "Agent harness",
701
+ children: [
702
+ /* @__PURE__ */ jsx3(HarnessLogo, { type: value, size: 16, className: "shrink-0" }),
703
+ /* @__PURE__ */ jsx3("span", { className: "truncate", children: selected?.label ?? value }),
704
+ !locked && /* @__PURE__ */ jsx3(ChevronDown3, { className: "ml-auto h-3.5 w-3.5 shrink-0 text-muted-foreground" })
705
+ ]
706
+ }
707
+ ) }),
708
+ /* @__PURE__ */ jsx3(DropdownMenu2.Portal, { children: /* @__PURE__ */ jsxs3(
709
+ DropdownMenu2.Content,
710
+ {
711
+ side,
712
+ align,
713
+ avoidCollisions,
714
+ collisionPadding: 24,
715
+ sideOffset: 6,
716
+ className: pickerMenuContentClass,
717
+ children: [
718
+ showSearch && /* @__PURE__ */ jsx3(
719
+ PickerMenuSearch,
720
+ {
721
+ value: query,
722
+ onChange: setQuery,
723
+ placeholder: "Search agents..."
724
+ }
725
+ ),
726
+ /* @__PURE__ */ jsx3("div", { className: pickerMenuBodyClass, children: sections.length === 0 ? /* @__PURE__ */ jsx3("div", { className: "px-3 py-8 text-center text-xs text-muted-foreground", children: "No agents match." }) : sections.map((section) => /* @__PURE__ */ jsx3(PickerMenuSection, { label: section.label, children: section.items.map((option) => /* @__PURE__ */ jsxs3(
727
+ DropdownMenu2.Item,
728
+ {
729
+ onSelect: (event) => {
730
+ event.preventDefault();
731
+ onChange(option.type);
732
+ },
733
+ className: pickerMenuItemClass({
734
+ active: option.type === value
735
+ }),
736
+ children: [
737
+ /* @__PURE__ */ jsx3(
738
+ HarnessLogo,
739
+ {
740
+ type: option.type,
741
+ size: 20,
742
+ className: "mt-0.5"
743
+ }
744
+ ),
745
+ /* @__PURE__ */ jsxs3("div", { className: "min-w-0 flex-1", children: [
746
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1.5", children: [
747
+ /* @__PURE__ */ jsx3("span", { className: "min-w-0 truncate text-sm font-medium", children: option.label }),
748
+ harnessAutonomyNote(option.type) && /* @__PURE__ */ jsx3(PickerMenuTag, { children: harnessAutonomyNote(option.type) })
749
+ ] }),
750
+ /* @__PURE__ */ jsx3(PickerMenuMeta, { parts: [option.description] })
751
+ ] })
752
+ ]
753
+ },
754
+ option.type
755
+ )) }, section.key)) }),
756
+ showSearch && /* @__PURE__ */ jsxs3(PickerMenuFooter, { children: [
757
+ filtered.length,
758
+ " of ",
759
+ options.length,
760
+ " agent",
761
+ options.length === 1 ? "" : "s"
762
+ ] })
763
+ ]
764
+ }
765
+ ) })
766
+ ]
767
+ }
768
+ );
761
769
  }
762
770
  function modelReasoningCapability(models, canonicalId) {
763
771
  if (!models || !canonicalId) return null;
@@ -79,7 +79,12 @@ function useSessionStream({
79
79
  const reconnectTimerRef = useRef(null);
80
80
  const streamingMsgIdRef = useRef(null);
81
81
  const insertionCounterRef = useRef(0);
82
+ const echoCounterRef = useRef(0);
83
+ const pendingEchoesRef = useRef(/* @__PURE__ */ new Map());
82
84
  const handleSSEEventRef = useRef(null);
85
+ useEffect(() => {
86
+ pendingEchoesRef.current.clear();
87
+ }, [sessionId]);
83
88
  const refetch = useCallback(async () => {
84
89
  if (!token || !sessionId || !apiUrl) return;
85
90
  try {
@@ -93,8 +98,12 @@ function useSessionStream({
93
98
  newMessages.push(message);
94
99
  newPartMap[message.id] = parts;
95
100
  }
96
- setMessages(newMessages);
97
- setPartMap(newPartMap);
101
+ const echoes = [...pendingEchoesRef.current.values()];
102
+ setMessages([...newMessages, ...echoes.map((echo) => echo.message)]);
103
+ setPartMap({
104
+ ...newPartMap,
105
+ ...Object.fromEntries(echoes.map((echo) => [echo.message.id, echo.parts]))
106
+ });
98
107
  streamingMsgIdRef.current = null;
99
108
  } catch (err) {
100
109
  const msg = err instanceof Error ? err.message : "Failed to fetch messages";
@@ -240,18 +249,34 @@ function useSessionStream({
240
249
  } else if (type === "session.idle") {
241
250
  setIsStreaming(false);
242
251
  streamingMsgIdRef.current = null;
252
+ pendingEchoesRef.current.clear();
243
253
  refetch();
244
254
  } else if (type === "session.error") {
245
255
  setIsStreaming(false);
246
256
  streamingMsgIdRef.current = null;
247
257
  const errorMsg = props.error ?? props.message ?? "Agent error";
248
258
  setError(errorMsg);
259
+ pendingEchoesRef.current.clear();
249
260
  refetch();
250
261
  }
251
262
  }, [refetch]);
252
263
  handleSSEEventRef.current = handleSSEEvent;
253
264
  const send = useCallback(async (text, options) => {
254
265
  if (!token || !sessionId || !apiUrl) return;
266
+ const echoId = `local-echo-${echoCounterRef.current++}`;
267
+ const echo = {
268
+ message: {
269
+ id: echoId,
270
+ role: "user",
271
+ time: { created: Date.now() },
272
+ _insertionIndex: insertionCounterRef.current++
273
+ },
274
+ parts: [{ type: "text", text }]
275
+ };
276
+ pendingEchoesRef.current.set(echoId, echo);
277
+ setMessages((prev) => [...prev, echo.message]);
278
+ setPartMap((prev) => ({ ...prev, [echoId]: echo.parts }));
279
+ setIsStreaming(true);
255
280
  try {
256
281
  const url = `${apiUrl}/session/sessions/${encodeURIComponent(sessionId)}/messages`;
257
282
  await fetchJson(url, token, {
@@ -266,8 +291,18 @@ function useSessionStream({
266
291
  ...options?.reasoningEffort ? { reasoningEffort: options.reasoningEffort } : {}
267
292
  })
268
293
  });
269
- setIsStreaming(true);
270
294
  } catch (err) {
295
+ pendingEchoesRef.current.delete(echoId);
296
+ setMessages((prev) => prev.filter((message) => message.id !== echoId));
297
+ setPartMap((prev) => {
298
+ if (!(echoId in prev)) return prev;
299
+ const next = { ...prev };
300
+ delete next[echoId];
301
+ return next;
302
+ });
303
+ if (pendingEchoesRef.current.size === 0 && !streamingMsgIdRef.current) {
304
+ setIsStreaming(false);
305
+ }
271
306
  const msg = err instanceof Error ? err.message : "Failed to send message";
272
307
  setError(msg);
273
308
  }
package/dist/hooks.js CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  import {
22
22
  useSessionStream,
23
23
  useSidecarAuth
24
- } from "./chunk-QORIZARI.js";
24
+ } from "./chunk-TIEOKIMX.js";
25
25
  import {
26
26
  usePtySession
27
27
  } from "./chunk-KEYPUGS3.js";
package/dist/index.js CHANGED
@@ -21,7 +21,7 @@ import {
21
21
  import {
22
22
  useSessionStream,
23
23
  useSidecarAuth
24
- } from "./chunk-QORIZARI.js";
24
+ } from "./chunk-TIEOKIMX.js";
25
25
  import {
26
26
  activeSessionsAtom,
27
27
  addMessage,
@@ -91,7 +91,7 @@ import {
91
91
  snapHarnessToModel,
92
92
  snapModelToHarness,
93
93
  validateComposerFiles
94
- } from "./chunk-GHG4TCDS.js";
94
+ } from "./chunk-QMY3XVH3.js";
95
95
  import {
96
96
  TOOL_CATEGORY_ICONS,
97
97
  cn,
package/dist/sdk-hooks.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  useSessionStream,
3
3
  useSidecarAuth
4
- } from "./chunk-QORIZARI.js";
4
+ } from "./chunk-TIEOKIMX.js";
5
5
  import "./chunk-CZ7CSFO4.js";
6
6
 
7
7
  // src/sdk-hooks.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tangle-network/sandbox-ui",
3
- "version": "0.91.0",
3
+ "version": "0.91.2",
4
4
  "description": "Unified UI component library for Tangle Sandbox — primitives, chat, dashboard, terminal, editor, and workspace components",
5
5
  "repository": {
6
6
  "type": "git",