@invergent/agent-chat-react 2.8.6 → 2.8.8

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/index.cjs CHANGED
@@ -553,6 +553,13 @@ function BrowserControlBar({
553
553
  )
554
554
  ] });
555
555
  }
556
+ var MIN_ZOOM = 1;
557
+ var MAX_ZOOM = 3;
558
+ var ZOOM_STEP = 0.5;
559
+ function clampZoom(zoom) {
560
+ const bounded = Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, zoom));
561
+ return Math.round(bounded * 100) / 100;
562
+ }
556
563
  function toWsUrl(src) {
557
564
  const url = new URL(src, window.location.href);
558
565
  if (url.protocol === "http:") {
@@ -571,10 +578,12 @@ function BrowserLiveView({
571
578
  const onDisconnectRef = react.useRef(onDisconnect);
572
579
  onDisconnectRef.current = onDisconnect;
573
580
  const [state, setState] = react.useState("connecting");
581
+ const [zoom, setZoom] = react.useState(MIN_ZOOM);
574
582
  react.useEffect(() => {
575
583
  const container = containerRef.current;
576
584
  if (!container) return;
577
585
  setState("connecting");
586
+ setZoom(MIN_ZOOM);
578
587
  let rfb;
579
588
  let disposed = false;
580
589
  const handleConnect = () => setState("connected");
@@ -605,8 +614,68 @@ function BrowserLiveView({
605
614
  }
606
615
  };
607
616
  }, [src]);
617
+ const keepCanvasFocus = (event) => event.preventDefault();
618
+ const zoomTo = (next) => setZoom((current) => clampZoom(next || current));
608
619
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative h-full w-full bg-black", children: [
609
- /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, "data-testid": testId, className: "h-full w-full" }),
620
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "absolute inset-0 overflow-auto", children: /* @__PURE__ */ jsxRuntime.jsx(
621
+ "div",
622
+ {
623
+ ref: containerRef,
624
+ "data-testid": testId,
625
+ style: { width: `${zoom * 100}%`, height: `${zoom * 100}%` }
626
+ }
627
+ ) }),
628
+ state === "connected" && /* @__PURE__ */ jsxRuntime.jsxs(
629
+ "div",
630
+ {
631
+ "data-testid": "browser-rfb-zoom",
632
+ className: "absolute bottom-3 right-3 flex items-center gap-0.5 rounded-md border border-line bg-card/90 p-0.5 text-muted-foreground shadow-sm backdrop-blur",
633
+ children: [
634
+ /* @__PURE__ */ jsxRuntime.jsx(
635
+ "button",
636
+ {
637
+ type: "button",
638
+ tabIndex: -1,
639
+ "aria-label": "Zoom out",
640
+ disabled: zoom <= MIN_ZOOM,
641
+ onMouseDown: keepCanvasFocus,
642
+ onClick: () => zoomTo(zoom - ZOOM_STEP),
643
+ className: "flex size-6 items-center justify-center rounded text-sm leading-none hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-40",
644
+ children: "\u2212"
645
+ }
646
+ ),
647
+ /* @__PURE__ */ jsxRuntime.jsxs(
648
+ "button",
649
+ {
650
+ type: "button",
651
+ tabIndex: -1,
652
+ "aria-label": "Reset zoom",
653
+ disabled: zoom === MIN_ZOOM,
654
+ onMouseDown: keepCanvasFocus,
655
+ onClick: () => setZoom(MIN_ZOOM),
656
+ className: "min-w-10 rounded px-1 text-center text-xs tabular-nums hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none",
657
+ children: [
658
+ Math.round(zoom * 100),
659
+ "%"
660
+ ]
661
+ }
662
+ ),
663
+ /* @__PURE__ */ jsxRuntime.jsx(
664
+ "button",
665
+ {
666
+ type: "button",
667
+ tabIndex: -1,
668
+ "aria-label": "Zoom in",
669
+ disabled: zoom >= MAX_ZOOM,
670
+ onMouseDown: keepCanvasFocus,
671
+ onClick: () => zoomTo(zoom + ZOOM_STEP),
672
+ className: "flex size-6 items-center justify-center rounded text-sm leading-none hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-40",
673
+ children: "+"
674
+ }
675
+ )
676
+ ]
677
+ }
678
+ ),
610
679
  state !== "connected" && /* @__PURE__ */ jsxRuntime.jsx(
611
680
  "div",
612
681
  {
@@ -6694,6 +6763,10 @@ function ChatComposerInner({
6694
6763
  showWorkspace = false,
6695
6764
  onToggleWorkspace,
6696
6765
  canShowBrowser = false,
6766
+ browserProfileId,
6767
+ onSelectBrowserProfile,
6768
+ browserProfilesEnabled = false,
6769
+ browserProfileLocked = false,
6697
6770
  canShowWorkspace = false,
6698
6771
  deepResearchEnabled = false,
6699
6772
  researchEnabled = false,
@@ -6706,6 +6779,20 @@ function ChatComposerInner({
6706
6779
  onViewModeChange
6707
6780
  }) {
6708
6781
  const { adapter } = useAgentChatAdapterContext();
6782
+ const [profileMenuOpen, setProfileMenuOpen] = react.useState(false);
6783
+ const [browserProfiles, setBrowserProfiles] = react.useState([]);
6784
+ react.useEffect(() => {
6785
+ if (!profileMenuOpen || !adapter.listBrowserProfiles) return;
6786
+ let cancelled = false;
6787
+ void adapter.listBrowserProfiles().then((p) => {
6788
+ if (!cancelled) setBrowserProfiles(p);
6789
+ }).catch(() => {
6790
+ if (!cancelled) setBrowserProfiles([]);
6791
+ });
6792
+ return () => {
6793
+ cancelled = true;
6794
+ };
6795
+ }, [profileMenuOpen, adapter]);
6709
6796
  const { textInput, attachments } = usePromptInputController();
6710
6797
  const status = isRunning ? "streaming" : disabled ? "error" : "ready";
6711
6798
  const [adapterCommands, setAdapterCommands] = react.useState([]);
@@ -7045,6 +7132,76 @@ function ChatComposerInner({
7045
7132
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GlobeIcon, { className: "size-4" })
7046
7133
  }
7047
7134
  ),
7135
+ browserProfilesEnabled && onSelectBrowserProfile && adapter.listBrowserProfiles && (browserProfileLocked ? (
7136
+ // A profile is bound at session creation; once a session is
7137
+ // active the choice can't change. Render disabled (via
7138
+ // aria-disabled, not the `disabled` attribute, so the
7139
+ // tooltip still shows on hover) with an explanation.
7140
+ /* @__PURE__ */ jsxRuntime.jsx(
7141
+ PromptInputButton,
7142
+ {
7143
+ "aria-label": "Select browser profile",
7144
+ "aria-disabled": true,
7145
+ "aria-pressed": !!browserProfileId,
7146
+ tooltip: "A browser profile must be chosen before starting the session",
7147
+ className: `cursor-not-allowed opacity-50 ${browserProfileId ? "bg-accent text-foreground" : ""}`,
7148
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.IdCardIcon, { className: "size-4" })
7149
+ }
7150
+ )
7151
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
7152
+ Popover,
7153
+ {
7154
+ open: profileMenuOpen,
7155
+ onOpenChange: setProfileMenuOpen,
7156
+ children: [
7157
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
7158
+ PromptInputButton,
7159
+ {
7160
+ "aria-label": "Select browser profile",
7161
+ "aria-pressed": !!browserProfileId,
7162
+ className: browserProfileId ? "bg-accent text-foreground" : void 0,
7163
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.IdCardIcon, { className: "size-4" })
7164
+ }
7165
+ ) }),
7166
+ /* @__PURE__ */ jsxRuntime.jsx(
7167
+ PopoverContent,
7168
+ {
7169
+ side: "top",
7170
+ align: "start",
7171
+ className: "w-64 overflow-hidden rounded-xl p-1",
7172
+ children: /* @__PURE__ */ jsxRuntime.jsx(Command, { children: /* @__PURE__ */ jsxRuntime.jsxs(CommandList, { children: [
7173
+ /* @__PURE__ */ jsxRuntime.jsx(
7174
+ CommandItem,
7175
+ {
7176
+ onSelect: () => {
7177
+ setProfileMenuOpen(false);
7178
+ onSelectBrowserProfile(null);
7179
+ },
7180
+ className: "gap-3 rounded-md px-3 py-2",
7181
+ children: "No profile"
7182
+ }
7183
+ ),
7184
+ browserProfiles.map((p) => /* @__PURE__ */ jsxRuntime.jsxs(
7185
+ CommandItem,
7186
+ {
7187
+ onSelect: () => {
7188
+ setProfileMenuOpen(false);
7189
+ onSelectBrowserProfile(p.id);
7190
+ },
7191
+ className: "gap-3 rounded-md px-3 py-2",
7192
+ children: [
7193
+ p.name,
7194
+ browserProfileId === p.id && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-auto text-xs text-muted-foreground", children: "Active" })
7195
+ ]
7196
+ },
7197
+ p.id
7198
+ ))
7199
+ ] }) })
7200
+ }
7201
+ )
7202
+ ]
7203
+ }
7204
+ )),
7048
7205
  canShowWorkspace && onToggleWorkspace && /* @__PURE__ */ jsxRuntime.jsx(
7049
7206
  PromptInputButton,
7050
7207
  {
@@ -7764,9 +7921,9 @@ function ArtifactTable({ spec }) {
7764
7921
  init_utils();
7765
7922
  var DEFAULT_HEIGHT_PX = 480;
7766
7923
  var EXPANDED_HEIGHT_PX = 800;
7767
- var MIN_ZOOM = 0.5;
7768
- var MAX_ZOOM = 2;
7769
- var ZOOM_STEP = 0.1;
7924
+ var MIN_ZOOM2 = 0.5;
7925
+ var MAX_ZOOM2 = 2;
7926
+ var ZOOM_STEP2 = 0.1;
7770
7927
  var SAFETY_STYLE_PREAMBLE = "<style>html,body{max-height:100vh}canvas{max-height:80vh!important}</style>";
7771
7928
  function injectSafetyStyle(html) {
7772
7929
  const headCloseIdx = html.search(/<\/head\s*>/i);
@@ -7783,7 +7940,7 @@ function ArtifactHtml({
7783
7940
  const [zoom, setZoom] = react.useState(1);
7784
7941
  const safeSrcdoc = react.useMemo(() => injectSafetyStyle(spec.html), [spec.html]);
7785
7942
  const viewportHeight = expanded ? EXPANDED_HEIGHT_PX : DEFAULT_HEIGHT_PX;
7786
- const clamp = (v) => Math.round(Math.min(MAX_ZOOM, Math.max(MIN_ZOOM, v)) * 100) / 100;
7943
+ const clamp = (v) => Math.round(Math.min(MAX_ZOOM2, Math.max(MIN_ZOOM2, v)) * 100) / 100;
7787
7944
  return /* @__PURE__ */ jsxRuntime.jsxs(
7788
7945
  "div",
7789
7946
  {
@@ -7829,8 +7986,8 @@ function ArtifactHtml({
7829
7986
  "button",
7830
7987
  {
7831
7988
  type: "button",
7832
- onClick: () => setZoom((z) => clamp(z - ZOOM_STEP)),
7833
- disabled: zoom <= MIN_ZOOM,
7989
+ onClick: () => setZoom((z) => clamp(z - ZOOM_STEP2)),
7990
+ disabled: zoom <= MIN_ZOOM2,
7834
7991
  className: "hover:text-foreground disabled:opacity-40 cursor-pointer",
7835
7992
  "aria-label": "zoom out",
7836
7993
  children: "\u2212"
@@ -7853,8 +8010,8 @@ function ArtifactHtml({
7853
8010
  "button",
7854
8011
  {
7855
8012
  type: "button",
7856
- onClick: () => setZoom((z) => clamp(z + ZOOM_STEP)),
7857
- disabled: zoom >= MAX_ZOOM,
8013
+ onClick: () => setZoom((z) => clamp(z + ZOOM_STEP2)),
8014
+ disabled: zoom >= MAX_ZOOM2,
7858
8015
  className: "hover:text-foreground disabled:opacity-40",
7859
8016
  "aria-label": "zoom in",
7860
8017
  children: "+"
@@ -10231,6 +10388,10 @@ function ChatThread({
10231
10388
  showWorkspace = false,
10232
10389
  onToggleWorkspace,
10233
10390
  canShowBrowser = false,
10391
+ browserProfileId,
10392
+ onSelectBrowserProfile,
10393
+ browserProfilesEnabled = false,
10394
+ browserProfileLocked = false,
10234
10395
  canShowWorkspace = false,
10235
10396
  viewMode = "simple",
10236
10397
  onViewModeChange,
@@ -10302,6 +10463,10 @@ function ChatThread({
10302
10463
  showWorkspace,
10303
10464
  onToggleWorkspace,
10304
10465
  canShowBrowser,
10466
+ browserProfileId,
10467
+ onSelectBrowserProfile,
10468
+ browserProfilesEnabled,
10469
+ browserProfileLocked,
10305
10470
  canShowWorkspace,
10306
10471
  viewMode,
10307
10472
  onViewModeChange,
@@ -12819,6 +12984,9 @@ function AgentChat({
12819
12984
  onMessagesChange,
12820
12985
  disabled,
12821
12986
  onComposerError,
12987
+ browserProfileId,
12988
+ onSelectBrowserProfile,
12989
+ browserProfilesEnabled = false,
12822
12990
  deepResearchEnabled = false,
12823
12991
  researchEnabled = false,
12824
12992
  codeAgentsEnabled = false,
@@ -12968,6 +13136,10 @@ function AgentChat({
12968
13136
  tokenUsage: runtime.tokenUsage,
12969
13137
  retryIndicator: runtime.retryIndicator,
12970
13138
  onComposerError,
13139
+ browserProfileId,
13140
+ onSelectBrowserProfile,
13141
+ browserProfilesEnabled,
13142
+ browserProfileLocked: !!sessionId,
12971
13143
  showBrowser,
12972
13144
  onToggleBrowser: handleToggleBrowser,
12973
13145
  showWorkspace,
@@ -13743,7 +13915,7 @@ function InboxDetailActions({
13743
13915
  {
13744
13916
  type: "button",
13745
13917
  size: "sm",
13746
- onClick: () => onSessionSelect?.(item.sessionId),
13918
+ onClick: () => onSessionSelect?.(item.sessionId, item),
13747
13919
  "aria-label": "Open session",
13748
13920
  children: [
13749
13921
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLinkIcon, { className: "size-3.5" }),
@@ -16671,11 +16843,16 @@ function SessionTreePanel({
16671
16843
  limit: sessionListLimit
16672
16844
  }) : Promise.resolve(null);
16673
16845
  const sessionTreePromise = sessionId && adapter.getSessionTree ? adapter.getSessionTree({ sessionId }) : Promise.resolve(null);
16674
- const [sessionList, sessionTree] = await Promise.all([
16846
+ const [listResult, treeResult] = await Promise.allSettled([
16675
16847
  sessionListPromise,
16676
16848
  sessionTreePromise
16677
16849
  ]);
16678
16850
  if (!mounted.current || currentRequestId !== requestId.current) return;
16851
+ if (loadList && listResult.status === "rejected") {
16852
+ throw listResult.reason;
16853
+ }
16854
+ const sessionList = listResult.status === "fulfilled" ? listResult.value : null;
16855
+ const sessionTree = treeResult.status === "fulfilled" ? treeResult.value : null;
16679
16856
  let nextNodes = mergeTreeNodes([
16680
16857
  sessionList?.sessions.map(sessionToTreeNode) ?? [],
16681
16858
  sessionTree?.nodes ?? []
@@ -16833,6 +17010,7 @@ function SessionTreePanel({
16833
17010
  exports.ACTIVE_MISSION_STATUSES = ACTIVE_MISSION_STATUSES;
16834
17011
  exports.AgentChat = AgentChat;
16835
17012
  exports.AgentChatAdapterProvider = AgentChatAdapterProvider;
17013
+ exports.BrowserLiveView = BrowserLiveView;
16836
17014
  exports.CitationText = CitationText;
16837
17015
  exports.CodingAgentsPanel = CodingAgentsPanel;
16838
17016
  exports.ComposioConnectCard = ComposioConnectCard;