@marimo-team/frontend 0.15.5 → 0.16.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/dist/assets/{ConnectedDataExplorerComponent-Cn5-l2X1.js → ConnectedDataExplorerComponent-BErMbWvG.js} +1 -1
- package/dist/assets/{ImageComparisonComponent-CEXMKKA4.js → ImageComparisonComponent-fTHv1Ih0.js} +1 -1
- package/dist/assets/{VegaLite-Bt14Ds9k.js → VegaLite-Bdi-TyfY.js} +6 -6
- package/dist/assets/_baseEach-CNBxBxvS.js +1 -0
- package/dist/assets/_baseMap-D1WHjKrd.js +1 -0
- package/dist/assets/_baseUniq-CCgDNtZb.js +1 -0
- package/dist/assets/_createAggregator-DcD0kTA5.js +1 -0
- package/dist/assets/agent-panel-Crv430aI.js +268 -0
- package/dist/assets/agent-panel-D92Mfy1i.css +1 -0
- package/dist/assets/{any-language-editor-DiwNT6zp.js → any-language-editor-CQh552Wu.js} +1 -1
- package/dist/assets/architectureDiagram-W76B3OCA-BAJeBxzt.js +36 -0
- package/dist/assets/{between-horizontal-start-FyewyCGn.js → between-horizontal-start-Boxgxbt_.js} +1 -1
- package/dist/assets/{blockDiagram-QIGZ2CNN-BrOkAf_c.js → blockDiagram-QIGZ2CNN-CL-1svEK.js} +1 -1
- package/dist/assets/{c4Diagram-FPNF74CW-BHPzDxE2.js → c4Diagram-FPNF74CW-BbEqbCTl.js} +5 -5
- package/dist/assets/channel-_2eNSz0n.js +1 -0
- package/dist/assets/chat-panel-CXh5Wl6C.js +3 -0
- package/dist/assets/{chunk-4BX2VUAB-DLxaCNYh.js → chunk-4BX2VUAB-C--8TXeE.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-DdzvO3HR.js → chunk-55IACEB6-Bj00HDqq.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-R5o-nSiG.js → chunk-FMBD7UC4-C-lhB6hN.js} +1 -1
- package/dist/assets/{chunk-K7UQS3LO-DxaMrGgG.js → chunk-K7UQS3LO-B-pGTXPt.js} +1 -1
- package/dist/assets/{chunk-QN33PNHL-DqS9-FYm.js → chunk-QN33PNHL-DqUzGhvm.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-BZ-TzajS.js → chunk-QZHKN3VN-TntJHfSk.js} +1 -1
- package/dist/assets/{chunk-TVAH2DTR-BsgP2dyv.js → chunk-TVAH2DTR-HUJb1psV.js} +1 -1
- package/dist/assets/{chunk-TZMSLE5B-D-h3ahXI.js → chunk-TZMSLE5B-BK3C__t3.js} +1 -1
- package/dist/assets/{circle-play-CQtRZ-rT.js → circle-play-DBLOv1Yu.js} +1 -1
- package/dist/assets/classDiagram-KNZD7YFC-BGmh9POF.js +1 -0
- package/dist/assets/classDiagram-v2-RKCZMP56-BGmh9POF.js +1 -0
- package/dist/assets/{clear-button-BY6Z_ViL.js → clear-button-BeoFbEKH.js} +1 -1
- package/dist/assets/clone-BFDSPAj3.js +1 -0
- package/dist/assets/command-palette-CXZiSv0I.js +1 -0
- package/dist/assets/common-C7oJcmCT.js +1 -0
- package/dist/assets/{compile-Ct_jzdKr.js → compile-7L0MwhyI.js} +1 -1
- package/dist/assets/cose-bilkent-S5V4N54A-BMkGLcVC.js +1 -0
- package/dist/assets/dagre-5GWH7T2D-BJtRienS.js +4 -0
- package/dist/assets/{data-grid-overlay-editor-BN_wulc3.js → data-grid-overlay-editor-DBkmGtNs.js} +1 -1
- package/dist/assets/datasources-panel-B7FbYLiy.js +1 -0
- package/dist/assets/{dependency-graph-panel-BOmSCZf7.js → dependency-graph-panel-DEdOxp2X.js} +4 -4
- package/dist/assets/diagram-N5W7TBWH-CmECY3nb.js +24 -0
- package/dist/assets/diagram-QEK2KX5R-DMOVSNKD.js +43 -0
- package/dist/assets/diagram-S2PKOQOG-BiJ96PNQ.js +24 -0
- package/dist/assets/{documentation-panel-BxjJO_Gw.js → documentation-panel-xULhaEv3.js} +1 -1
- package/dist/assets/edit-page-BrYda9VE.js +129 -0
- package/dist/assets/{ellipsis-vertical-UHbmjI2n.js → ellipsis-vertical-BBqXIlc2.js} +1 -1
- package/dist/assets/{empty-state-BIBXzY_0.js → empty-state-B3dA3G5P.js} +1 -1
- package/dist/assets/{erDiagram-AWTI2OKA-E84mAle_.js → erDiagram-AWTI2OKA-MP1DiFRo.js} +1 -1
- package/dist/assets/{error-panel-MEvQ6K7h.js → error-panel-Cc1sv-Ag.js} +1 -1
- package/dist/assets/file-explorer-panel-Bw59Kva1.js +1 -0
- package/dist/assets/{flowDiagram-PVAE7QVJ-DfbIRSAW.js → flowDiagram-PVAE7QVJ-BX7caPp7.js} +1 -1
- package/dist/assets/{ganttDiagram-OWAHRB6G-DR4HZ1z_.js → ganttDiagram-OWAHRB6G-B462g4Yf.js} +3 -3
- package/dist/assets/gitGraphDiagram-NY62KEGX-CGgvZ9-9.js +65 -0
- package/dist/assets/{glide-data-editor-nNmo1lPq.js → glide-data-editor-C0gUFZON.js} +4 -4
- package/dist/assets/graph-CHRVBzY5.js +1 -0
- package/dist/assets/{home-page-9eW6qida.js → home-page-Fb2osjys.js} +3 -3
- package/dist/assets/{index-DMomwMcN.js → index-BVgAenPd.js} +1 -1
- package/dist/assets/{index-B8llrTSo.js → index-BY93Ejhl.js} +1 -1
- package/dist/assets/{index-BFSnz7iM.js → index-C-8WADat.js} +1 -1
- package/dist/assets/{index-CPN7TRA1.js → index-C-GhZ7ti.js} +1 -1
- package/dist/assets/{index-DyLSuOH1.js → index-C1v_Z9et.js} +1 -1
- package/dist/assets/{index-VPWqq2Pg.js → index-C4Tn5NvJ.js} +1 -1
- package/dist/assets/{index-BAH034Ue.js → index-C77h_TXN.js} +1 -1
- package/dist/assets/{index-Dt9UWeWn.js → index-CQDrxQ0j.js} +1 -1
- package/dist/assets/{index-DWOaniGT.js → index-CWMgowgL.js} +1 -1
- package/dist/assets/{index-B1_GXGaP.js → index-Clbi_Yaq.js} +1 -1
- package/dist/assets/{index-B7yXbrLa.js → index-CpTPJo4k.js} +1 -1
- package/dist/assets/{index-CknhX2Vy.css → index-Cx0bsY1w.css} +1 -1
- package/dist/assets/{index-DqzMPAC8.js → index-D1vmG6DS.js} +2 -2
- package/dist/assets/{index-c6If577Q.js → index-D9UKkrr2.js} +1 -1
- package/dist/assets/{index-CB2pnVQG.js → index-DEQvTChO.js} +1 -1
- package/dist/assets/{index-OC46250R.js → index-DKEudB02.js} +205 -197
- package/dist/assets/{index-CSgxTUzD.js → index-DRMm6SNo.js} +1 -1
- package/dist/assets/{index-Bq516OmX.js → index-DoRmcrKM.js} +1 -1
- package/dist/assets/{index-DSU75csX.js → index-lYa_leQE.js} +1 -1
- package/dist/assets/{index-BLu5CX6z.js → index-vmICa5KN.js} +1 -1
- package/dist/assets/{index-uacyUula.js → index-z9bohSQJ.js} +1 -1
- package/dist/assets/infoDiagram-STP46IZ2-CVyrdLc8.js +2 -0
- package/dist/assets/isEmpty-DU_ogP_D.js +1 -0
- package/dist/assets/{journeyDiagram-BIP6EPQ6-BBiFyygf.js → journeyDiagram-BIP6EPQ6-C6EgLP_Q.js} +1 -1
- package/dist/assets/{kanban-definition-6OIFK2YF-DhgA6Nt6.js → kanban-definition-6OIFK2YF-BXzYO1yj.js} +4 -4
- package/dist/assets/layout-jihVw5-i.js +1 -0
- package/dist/assets/linear-C4blANlC.js +1 -0
- package/dist/assets/{links-CbvGxbsJ.js → links-D59GIweI.js} +3 -3
- package/dist/assets/{logs-panel-B9SmTZAW.js → logs-panel-D401qzZh.js} +1 -1
- package/dist/assets/markdown-renderer-Cd9eYyaL.js +263 -0
- package/dist/assets/{agent-panel-DpQ6muj-.css → markdown-renderer-ClyzDMmG.css} +1 -1
- package/dist/assets/mermaid-BEVuRz_O.js +1 -0
- package/dist/assets/{mermaid.core-4nVOEVX3.js → mermaid.core-CaSnaLH0.js} +41 -41
- package/dist/assets/min-DUMu_zeK.js +1 -0
- package/dist/assets/{mindmap-definition-Q6HEUPPD-CVLQNn1q.js → mindmap-definition-Q6HEUPPD-BXUM5MT2.js} +2 -2
- package/dist/assets/{number-overlay-editor-CzRzXLcd.js → number-overlay-editor-4uWXGlPG.js} +1 -1
- package/dist/assets/{outline-panel-uvsS-YEQ.js → outline-panel-DIzkvm2I.js} +1 -1
- package/dist/assets/packages-panel-CJL0MVlj.js +1 -0
- package/dist/assets/{pieDiagram-ADFJNKIX-C5IQ5DBZ.js → pieDiagram-ADFJNKIX-Dxt5PVNo.js} +3 -3
- package/dist/assets/{quadrantDiagram-LMRXKWRM-CFXFnQxx.js → quadrantDiagram-LMRXKWRM-D4pUaA31.js} +1 -1
- package/dist/assets/{react-plotly-mzdv02_Y.js → react-plotly-cJZ0VWBq.js} +1 -1
- package/dist/assets/{requirementDiagram-4UW4RH46-D9bPC89T.js → requirementDiagram-4UW4RH46-DVRTjgas.js} +1 -1
- package/dist/assets/run-page-BUEnMC9w.js +1 -0
- package/dist/assets/sankeyDiagram-GR3RE2ED-CVFnD9C-.js +10 -0
- package/dist/assets/scratchpad-panel-BIgRENkI.js +1 -0
- package/dist/assets/secrets-panel-xY5-V_BD.js +1 -0
- package/dist/assets/{sequenceDiagram-C3RYC4MD-6N7_hY4k.js → sequenceDiagram-C3RYC4MD-_lY4ZN_S.js} +4 -4
- package/dist/assets/{slides-component-EcjC8sDK.js → slides-component-Xjymwj7X.js} +1 -1
- package/dist/assets/snippets-panel-CTPYW41n.js +1 -0
- package/dist/assets/sortBy-BNZKwiq_.js +1 -0
- package/dist/assets/state-C4NiC9tO.js +1 -0
- package/dist/assets/stateDiagram-KXAO66HF-Da0JQWCn.js +1 -0
- package/dist/assets/stateDiagram-v2-UMBNRL4Z-D5lYZOOt.js +1 -0
- package/dist/assets/storage-CMdLzB_c.js +26 -0
- package/dist/assets/terminal-BPwTkXae.js +10 -0
- package/dist/assets/time-Dv5_Ouz_.js +1 -0
- package/dist/assets/{timeline-definition-XQNQX7LJ-BEaynAiY.js → timeline-definition-XQNQX7LJ-Dxh5Zu2e.js} +1 -1
- package/dist/assets/tracing-BCIurUfa.js +2 -0
- package/dist/assets/{tracing-panel-BmuHLPrY.js → tracing-panel-DAzrzNmm.js} +2 -2
- package/dist/assets/{trash-UBqfK4mR.js → trash-Dc6DSjz_.js} +1 -1
- package/dist/assets/{tree-XiEycetl.js → tree-jheoerAX.js} +1 -1
- package/dist/assets/{treemap-75Q7IDZK-CnuVFbBG.js → treemap-75Q7IDZK-IgpxeGaf.js} +21 -21
- package/dist/assets/{ts-tags-CloPe9IY.js → ts-tags-DxCDHihD.js} +1 -1
- package/dist/assets/variable-panel-DYAiLBmF.js +1 -0
- package/dist/assets/{vega-component-DsTH4tuX.js → vega-component-BpfpiPKI.js} +1 -1
- package/dist/assets/{xychartDiagram-6GGTOJPD-Dcz3O-A3.js → xychartDiagram-6GGTOJPD-CmNigJ31.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +8 -4
- package/src/__tests__/mocks.ts +43 -0
- package/src/components/app-config/user-config-form.tsx +32 -0
- package/src/components/chat/acp/__tests__/__snapshots__/prompt.test.ts.snap +55 -23
- package/src/components/chat/acp/__tests__/context-utils.test.ts +222 -0
- package/src/components/chat/acp/__tests__/prompt.test.ts +1 -1
- package/src/components/chat/acp/__tests__/state.test.ts +2 -6
- package/src/components/chat/acp/agent-docs.tsx +33 -6
- package/src/components/chat/acp/agent-panel.css +0 -18
- package/src/components/chat/acp/agent-panel.tsx +397 -72
- package/src/components/chat/acp/agent-selector.tsx +7 -1
- package/src/components/chat/acp/blocks.tsx +40 -10
- package/src/components/chat/acp/common.tsx +10 -2
- package/src/components/chat/acp/context-utils.ts +127 -0
- package/src/components/chat/acp/prompt.ts +34 -10
- package/src/components/chat/acp/state.ts +1 -1
- package/src/components/chat/acp/types.ts +8 -0
- package/src/components/chat/chat-panel.tsx +23 -88
- package/src/components/chat/chat-utils.ts +127 -1
- package/src/components/chat/markdown-renderer.css +39 -0
- package/src/components/chat/markdown-renderer.tsx +7 -38
- package/src/components/chat/tool-call-accordion.tsx +113 -23
- package/src/components/editor/Cell.tsx +6 -0
- package/src/components/editor/actions/name-cell-input.tsx +6 -1
- package/src/components/editor/actions/useCellActionButton.tsx +3 -1
- package/src/components/editor/ai/__tests__/completion-utils.test.ts +178 -1
- package/src/components/editor/ai/add-cell-with-ai.tsx +68 -66
- package/src/components/editor/ai/ai-completion-editor.tsx +29 -26
- package/src/components/editor/ai/completion-handlers.tsx +44 -6
- package/src/components/editor/ai/completion-utils.ts +92 -0
- package/src/components/editor/ai/transport/chat-transport.tsx +36 -0
- package/src/components/editor/cell/StagedAICell.tsx +51 -0
- package/src/components/editor/cell/cell-actions.tsx +2 -1
- package/src/components/terminal/__tests__/state.test.ts +207 -0
- package/src/components/terminal/hooks.ts +41 -0
- package/src/components/terminal/state.ts +75 -0
- package/src/components/terminal/terminal.tsx +334 -13
- package/src/components/terminal/theme.tsx +56 -0
- package/src/core/ai/__tests__/staged-cells.test.ts +356 -0
- package/src/core/ai/staged-cells.ts +208 -0
- package/src/core/cells/cells.ts +1 -1
- package/src/core/codemirror/lsp/federated-lsp.ts +1 -1
- package/src/core/islands/main.ts +2 -2
- package/src/core/kernel/messages.ts +8 -12
- package/src/core/saving/__tests__/filename.test.ts +37 -0
- package/src/core/static/__tests__/download-html.test.ts +43 -1
- package/src/core/websocket/useMarimoWebSocket.tsx +2 -2
- package/src/css/app/Cell.css +11 -0
- package/src/plugins/core/RenderHTML.tsx +36 -2
- package/src/plugins/core/__test__/RenderHTML.test.ts +72 -0
- package/src/plugins/core/registerReactComponent.tsx +28 -0
- package/src/plugins/impl/FileBrowserPlugin.tsx +8 -2
- package/src/stories/cell.stories.tsx +1 -1
- package/src/stories/layout/vertical/one-column.stories.tsx +1 -1
- package/src/utils/__tests__/cell-urls.test.ts +29 -0
- package/src/utils/__tests__/filenames.test.ts +18 -0
- package/src/utils/__tests__/path.test.ts +38 -0
- package/src/utils/__tests__/urls.test.ts +56 -1
- package/src/utils/errors.ts +9 -0
- package/dist/assets/_baseEach-C1FLm7WW.js +0 -1
- package/dist/assets/_baseMap-DBVArUYD.js +0 -1
- package/dist/assets/_baseUniq-Dk7ZPJ3N.js +0 -1
- package/dist/assets/_createAggregator-Bn38fDd3.js +0 -1
- package/dist/assets/agent-panel-COUYnuIK.js +0 -475
- package/dist/assets/architectureDiagram-W76B3OCA-DBzWQKKu.js +0 -36
- package/dist/assets/channel-CjhbjOv4.js +0 -1
- package/dist/assets/chat-panel-BPXKoTnZ.js +0 -7
- package/dist/assets/chat-panel-Brrs_eeH.css +0 -1
- package/dist/assets/classDiagram-KNZD7YFC-DHs5cFzy.js +0 -1
- package/dist/assets/classDiagram-v2-RKCZMP56-DHs5cFzy.js +0 -1
- package/dist/assets/clone-DM1YNjEn.js +0 -1
- package/dist/assets/command-palette-S0bzQp7v.js +0 -1
- package/dist/assets/common-B8U9k2Ly.js +0 -1
- package/dist/assets/cose-bilkent-S5V4N54A-wz1Sfx7j.js +0 -1
- package/dist/assets/dagre-5GWH7T2D-BfpcVBgq.js +0 -4
- package/dist/assets/datasources-panel-DfuURLJw.js +0 -1
- package/dist/assets/diagram-N5W7TBWH-Bf0oqqQh.js +0 -24
- package/dist/assets/diagram-QEK2KX5R-ZTc3qikh.js +0 -43
- package/dist/assets/diagram-S2PKOQOG-tLScBy7Z.js +0 -24
- package/dist/assets/edit-page-DJ8kJZ9w.js +0 -129
- package/dist/assets/file-explorer-panel-CzNUJ63G.js +0 -1
- package/dist/assets/gitGraphDiagram-NY62KEGX-C1t6QtVa.js +0 -65
- package/dist/assets/graph-CssCVWIq.js +0 -1
- package/dist/assets/index-DcCIe7np.js +0 -28
- package/dist/assets/infoDiagram-STP46IZ2-CwiAoz9f.js +0 -2
- package/dist/assets/layout-DpQrxGW-.js +0 -1
- package/dist/assets/linear-NsreOeBF.js +0 -1
- package/dist/assets/mermaid-DSt0r6IQ.js +0 -1
- package/dist/assets/min-D259kI3t.js +0 -1
- package/dist/assets/packages-panel-xMz9W2hW.js +0 -1
- package/dist/assets/run-page-Bb68qdhQ.js +0 -1
- package/dist/assets/sankeyDiagram-GR3RE2ED-BSJOau8E.js +0 -10
- package/dist/assets/scratchpad-panel-BF4BO-U4.js +0 -1
- package/dist/assets/secrets-panel-CdIX44dQ.js +0 -1
- package/dist/assets/snippets-panel-Dco9h0rb.js +0 -1
- package/dist/assets/sortBy-aLGA-PGK.js +0 -1
- package/dist/assets/stateDiagram-KXAO66HF-Bd68WT3b.js +0 -1
- package/dist/assets/stateDiagram-v2-UMBNRL4Z-BXz_GSwb.js +0 -1
- package/dist/assets/storage-CGlP4lCF.js +0 -26
- package/dist/assets/terminal-CxkHubcu.js +0 -9
- package/dist/assets/time-D2nr1UgQ.js +0 -1
- package/dist/assets/tracing-kTqHxa7q.js +0 -2
- package/dist/assets/variable-panel-noTnH-AQ.js +0 -1
|
@@ -1,23 +1,32 @@
|
|
|
1
1
|
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import { useAtom } from "jotai";
|
|
3
|
+
import { useAtom, useAtomValue } from "jotai";
|
|
4
4
|
import { capitalize } from "lodash-es";
|
|
5
5
|
import {
|
|
6
|
+
AtSignIcon,
|
|
6
7
|
BotMessageSquareIcon,
|
|
8
|
+
PaperclipIcon,
|
|
7
9
|
RefreshCwIcon,
|
|
10
|
+
SendIcon,
|
|
11
|
+
SquareIcon,
|
|
8
12
|
StopCircleIcon,
|
|
9
13
|
} from "lucide-react";
|
|
10
|
-
import React, { memo, useEffect, useRef, useState } from "react";
|
|
14
|
+
import React, { memo, useEffect, useMemo, useRef, useState } from "react";
|
|
11
15
|
import useEvent from "react-use-event-hook";
|
|
12
16
|
import { useAcpClient } from "use-acp";
|
|
13
17
|
import {
|
|
14
18
|
ConnectionStatus,
|
|
15
19
|
PermissionRequest,
|
|
16
20
|
} from "@/components/chat/acp/common";
|
|
17
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
type AdditionalCompletions,
|
|
23
|
+
PromptInput,
|
|
24
|
+
} from "@/components/editor/ai/add-cell-with-ai";
|
|
18
25
|
import { PanelEmptyState } from "@/components/editor/chrome/panels/empty-state";
|
|
19
26
|
import { Spinner } from "@/components/icons/spinner";
|
|
20
27
|
import { Button } from "@/components/ui/button";
|
|
28
|
+
import { Input } from "@/components/ui/input";
|
|
29
|
+
import { Tooltip, TooltipProvider } from "@/components/ui/tooltip";
|
|
21
30
|
import { cn } from "@/utils/cn";
|
|
22
31
|
import { Logger } from "@/utils/Logger";
|
|
23
32
|
import { AgentDocs } from "./agent-docs";
|
|
@@ -34,27 +43,50 @@ import {
|
|
|
34
43
|
} from "./state";
|
|
35
44
|
import { AgentThread } from "./thread";
|
|
36
45
|
import "./agent-panel.css";
|
|
46
|
+
import type { Completion } from "@codemirror/autocomplete";
|
|
47
|
+
import type { ReactCodeMirrorRef } from "@uiw/react-codemirror";
|
|
37
48
|
import type {
|
|
38
|
-
|
|
49
|
+
ContentBlock,
|
|
39
50
|
RequestPermissionResponse,
|
|
40
|
-
WriteTextFileResponse,
|
|
41
51
|
} from "@zed-industries/agent-client-protocol";
|
|
52
|
+
import {
|
|
53
|
+
addContextCompletion,
|
|
54
|
+
CONTEXT_TRIGGER,
|
|
55
|
+
} from "@/components/editor/ai/completion-utils";
|
|
56
|
+
import {
|
|
57
|
+
Select,
|
|
58
|
+
SelectContent,
|
|
59
|
+
SelectGroup,
|
|
60
|
+
SelectItem,
|
|
61
|
+
SelectLabel,
|
|
62
|
+
SelectTrigger,
|
|
63
|
+
} from "@/components/ui/select";
|
|
42
64
|
import { toast } from "@/components/ui/use-toast";
|
|
43
65
|
import { useRequestClient } from "@/core/network/requests";
|
|
44
66
|
import { filenameAtom } from "@/core/saving/file-state";
|
|
45
67
|
import { store } from "@/core/state/jotai";
|
|
46
68
|
import { Functions } from "@/utils/functions";
|
|
47
69
|
import { Paths } from "@/utils/paths";
|
|
70
|
+
import { FileAttachmentPill } from "../chat-components";
|
|
71
|
+
import {
|
|
72
|
+
convertFilesToResourceLinks,
|
|
73
|
+
parseContextFromPrompt,
|
|
74
|
+
} from "./context-utils";
|
|
48
75
|
import { getAgentPrompt } from "./prompt";
|
|
49
76
|
import type {
|
|
50
77
|
AgentConnectionState,
|
|
51
78
|
AgentPendingPermission,
|
|
79
|
+
AvailableCommands,
|
|
52
80
|
ExternalAgentSessionId,
|
|
53
81
|
NotificationEvent,
|
|
82
|
+
SessionMode,
|
|
54
83
|
} from "./types";
|
|
55
84
|
|
|
56
85
|
const logger = Logger.get("agents");
|
|
57
86
|
|
|
87
|
+
// File attachment constants
|
|
88
|
+
const SUPPORTED_ATTACHMENT_TYPES = ["image/*", "text/*"];
|
|
89
|
+
|
|
58
90
|
interface AgentTitleProps {
|
|
59
91
|
currentAgentId?: ExternalAgentId;
|
|
60
92
|
}
|
|
@@ -171,35 +203,45 @@ interface EmptyStateProps {
|
|
|
171
203
|
}
|
|
172
204
|
|
|
173
205
|
const EmptyState = memo<EmptyStateProps>(
|
|
174
|
-
({ currentAgentId, connectionState, onConnect, onDisconnect }) =>
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
<
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
<AgentDocs
|
|
194
|
-
className="border-t pt-6"
|
|
195
|
-
title="Connect to an agent"
|
|
196
|
-
description="Start agents by running these commands in your terminal:"
|
|
206
|
+
({ currentAgentId, connectionState, onConnect, onDisconnect }) => {
|
|
207
|
+
const filename = useAtomValue(filenameAtom);
|
|
208
|
+
return (
|
|
209
|
+
<div className="flex flex-col h-full">
|
|
210
|
+
<AgentPanelHeader
|
|
211
|
+
connectionState={connectionState}
|
|
212
|
+
currentAgentId={currentAgentId}
|
|
213
|
+
onConnect={onConnect}
|
|
214
|
+
onDisconnect={onDisconnect}
|
|
215
|
+
hasActiveSession={false}
|
|
216
|
+
/>
|
|
217
|
+
<SessionTabs />
|
|
218
|
+
<div className="flex-1 flex items-center justify-center p-6">
|
|
219
|
+
<div className="max-w-md w-full space-y-6">
|
|
220
|
+
<PanelEmptyState
|
|
221
|
+
title="No Agent Sessions"
|
|
222
|
+
description="Create a new session to start a conversation"
|
|
223
|
+
action={<AgentSelector className="border-y-1 rounded" />}
|
|
224
|
+
icon={<BotMessageSquareIcon />}
|
|
197
225
|
/>
|
|
198
|
-
|
|
226
|
+
{connectionState.status === "disconnected" && (
|
|
227
|
+
<AgentDocs
|
|
228
|
+
className="border-t pt-6"
|
|
229
|
+
title="Connect to an agent"
|
|
230
|
+
description={
|
|
231
|
+
<>
|
|
232
|
+
Start agents by running these commands in your terminal:
|
|
233
|
+
<br />
|
|
234
|
+
Note: This must be in the directory{" "}
|
|
235
|
+
{Paths.dirname(filename ?? "")}
|
|
236
|
+
</>
|
|
237
|
+
}
|
|
238
|
+
/>
|
|
239
|
+
)}
|
|
240
|
+
</div>
|
|
199
241
|
</div>
|
|
200
242
|
</div>
|
|
201
|
-
|
|
202
|
-
|
|
243
|
+
);
|
|
244
|
+
},
|
|
203
245
|
);
|
|
204
246
|
EmptyState.displayName = "EmptyState";
|
|
205
247
|
|
|
@@ -248,8 +290,14 @@ interface PromptAreaProps {
|
|
|
248
290
|
isLoading: boolean;
|
|
249
291
|
activeSessionId: ExternalAgentSessionId | null;
|
|
250
292
|
promptValue: string;
|
|
293
|
+
commands: AvailableCommands | undefined;
|
|
251
294
|
onPromptValueChange: (value: string) => void;
|
|
252
295
|
onPromptSubmit: (e: KeyboardEvent | undefined, prompt: string) => void;
|
|
296
|
+
onAddFiles: (files: File[]) => void;
|
|
297
|
+
onStop: () => void;
|
|
298
|
+
fileInputRef: React.RefObject<HTMLInputElement | null>;
|
|
299
|
+
sessionMode?: SessionMode;
|
|
300
|
+
onModeChange?: (mode: string) => void;
|
|
253
301
|
}
|
|
254
302
|
|
|
255
303
|
const PromptArea = memo<PromptAreaProps>(
|
|
@@ -257,36 +305,193 @@ const PromptArea = memo<PromptAreaProps>(
|
|
|
257
305
|
isLoading,
|
|
258
306
|
activeSessionId,
|
|
259
307
|
promptValue,
|
|
308
|
+
commands,
|
|
260
309
|
onPromptValueChange,
|
|
261
310
|
onPromptSubmit,
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
>
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
311
|
+
onAddFiles,
|
|
312
|
+
onStop,
|
|
313
|
+
fileInputRef,
|
|
314
|
+
sessionMode,
|
|
315
|
+
onModeChange,
|
|
316
|
+
}) => {
|
|
317
|
+
const inputRef = useRef<ReactCodeMirrorRef | null>(null);
|
|
318
|
+
const promptCompletions: AdditionalCompletions | undefined = useMemo(() => {
|
|
319
|
+
if (!commands) {
|
|
320
|
+
return undefined;
|
|
321
|
+
}
|
|
322
|
+
// sentence has to begin with '/' to trigger autocomplete
|
|
323
|
+
return {
|
|
324
|
+
triggerCompletionRegex: /^\/(\w+)?/,
|
|
325
|
+
completions: commands.map(
|
|
326
|
+
(prompt): Completion => ({
|
|
327
|
+
label: `/${prompt.name}`,
|
|
328
|
+
info: prompt.description,
|
|
329
|
+
}),
|
|
330
|
+
),
|
|
331
|
+
};
|
|
332
|
+
}, [commands]);
|
|
333
|
+
|
|
334
|
+
const handleSendClick = useEvent(() => {
|
|
335
|
+
if (promptValue.trim()) {
|
|
336
|
+
onPromptSubmit(undefined, promptValue);
|
|
337
|
+
}
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
const handleAddContext = useEvent(() => {
|
|
341
|
+
// For now, just append @ to the current value
|
|
342
|
+
addContextCompletion(inputRef);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
return (
|
|
346
|
+
<div className="border-t bg-background flex-shrink-0">
|
|
347
|
+
<div
|
|
348
|
+
className={cn(
|
|
349
|
+
"px-3 py-2 min-h-[80px]",
|
|
350
|
+
(isLoading || !activeSessionId) && "opacity-50 pointer-events-none",
|
|
351
|
+
)}
|
|
352
|
+
>
|
|
353
|
+
<PromptInput
|
|
354
|
+
inputRef={inputRef}
|
|
355
|
+
value={promptValue}
|
|
356
|
+
onChange={isLoading ? Functions.NOOP : onPromptValueChange}
|
|
357
|
+
onSubmit={onPromptSubmit}
|
|
358
|
+
additionalCompletions={promptCompletions}
|
|
359
|
+
onClose={Functions.NOOP}
|
|
360
|
+
onAddFiles={onAddFiles}
|
|
361
|
+
placeholder={
|
|
362
|
+
isLoading
|
|
363
|
+
? "Processing..."
|
|
364
|
+
: `Ask anything, ${CONTEXT_TRIGGER} to include context about tables or dataframes`
|
|
365
|
+
}
|
|
366
|
+
className={isLoading ? "opacity-50 pointer-events-none" : ""}
|
|
367
|
+
maxHeight="120px"
|
|
368
|
+
/>
|
|
369
|
+
</div>
|
|
370
|
+
<TooltipProvider>
|
|
371
|
+
<div className="px-3 py-2 border-t border-border/20 flex flex-row items-center justify-between">
|
|
372
|
+
<div className="flex items-center gap-2">
|
|
373
|
+
{sessionMode && onModeChange && (
|
|
374
|
+
<ModeSelector
|
|
375
|
+
sessionMode={sessionMode}
|
|
376
|
+
onModeChange={onModeChange}
|
|
377
|
+
/>
|
|
378
|
+
)}
|
|
379
|
+
</div>
|
|
380
|
+
<div className="flex flex-row">
|
|
381
|
+
<Tooltip content="Add context">
|
|
382
|
+
<Button variant="text" size="icon" onClick={handleAddContext}>
|
|
383
|
+
<AtSignIcon className="h-3.5 w-3.5" />
|
|
384
|
+
</Button>
|
|
385
|
+
</Tooltip>
|
|
386
|
+
<>
|
|
387
|
+
<Tooltip content="Attach a file">
|
|
388
|
+
<Button
|
|
389
|
+
variant="text"
|
|
390
|
+
size="icon"
|
|
391
|
+
className="cursor-pointer"
|
|
392
|
+
onClick={() => fileInputRef.current?.click()}
|
|
393
|
+
title="Attach a file"
|
|
394
|
+
>
|
|
395
|
+
<PaperclipIcon className="h-3.5 w-3.5" />
|
|
396
|
+
</Button>
|
|
397
|
+
</Tooltip>
|
|
398
|
+
<Input
|
|
399
|
+
ref={fileInputRef}
|
|
400
|
+
type="file"
|
|
401
|
+
multiple={true}
|
|
402
|
+
hidden={true}
|
|
403
|
+
onChange={(event) => {
|
|
404
|
+
if (event.target.files) {
|
|
405
|
+
onAddFiles([...event.target.files]);
|
|
406
|
+
}
|
|
407
|
+
}}
|
|
408
|
+
accept={SUPPORTED_ATTACHMENT_TYPES.join(",")}
|
|
409
|
+
/>
|
|
410
|
+
</>
|
|
411
|
+
<Tooltip content={isLoading ? "Stop" : "Submit"}>
|
|
412
|
+
<Button
|
|
413
|
+
variant="text"
|
|
414
|
+
size="sm"
|
|
415
|
+
className="h-6 w-6 p-0 hover:bg-muted/30 cursor-pointer"
|
|
416
|
+
onClick={isLoading ? onStop : handleSendClick}
|
|
417
|
+
disabled={isLoading ? false : !promptValue.trim()}
|
|
418
|
+
>
|
|
419
|
+
{isLoading ? (
|
|
420
|
+
<SquareIcon className="h-3 w-3 fill-current" />
|
|
421
|
+
) : (
|
|
422
|
+
<SendIcon className="h-3 w-3" />
|
|
423
|
+
)}
|
|
424
|
+
</Button>
|
|
425
|
+
</Tooltip>
|
|
426
|
+
</div>
|
|
427
|
+
</div>
|
|
428
|
+
</TooltipProvider>
|
|
429
|
+
</div>
|
|
430
|
+
);
|
|
431
|
+
},
|
|
280
432
|
);
|
|
281
433
|
PromptArea.displayName = "PromptArea";
|
|
282
434
|
|
|
435
|
+
interface ModeSelectorProps {
|
|
436
|
+
sessionMode: SessionMode;
|
|
437
|
+
onModeChange: (mode: string) => void;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const ModeSelector = memo<ModeSelectorProps>(
|
|
441
|
+
({ sessionMode, onModeChange }) => {
|
|
442
|
+
const availableModes = sessionMode?.availableModes || [];
|
|
443
|
+
const currentModeId = sessionMode?.currentModeId;
|
|
444
|
+
if (availableModes.length === 0) {
|
|
445
|
+
return null;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const modeOptions = availableModes.map((mode) => ({
|
|
449
|
+
value: mode.id,
|
|
450
|
+
label: mode.name,
|
|
451
|
+
subtitle: mode.description ?? "",
|
|
452
|
+
}));
|
|
453
|
+
const currentMode = modeOptions.find((opt) => opt.value === currentModeId);
|
|
454
|
+
|
|
455
|
+
return (
|
|
456
|
+
<Select value={currentModeId} onValueChange={onModeChange}>
|
|
457
|
+
<SelectTrigger className="h-6 text-xs border-border shadow-none! ring-0! bg-muted hover:bg-muted/30 py-0 px-2 gap-1 capitalize">
|
|
458
|
+
{currentMode?.label ?? currentModeId}
|
|
459
|
+
</SelectTrigger>
|
|
460
|
+
<SelectContent>
|
|
461
|
+
<SelectGroup>
|
|
462
|
+
<SelectLabel>Agent Mode</SelectLabel>
|
|
463
|
+
{modeOptions.map((option) => (
|
|
464
|
+
<SelectItem
|
|
465
|
+
key={option.value}
|
|
466
|
+
value={option.value}
|
|
467
|
+
className="text-xs"
|
|
468
|
+
>
|
|
469
|
+
<div className="flex flex-col">
|
|
470
|
+
{option.label}
|
|
471
|
+
{option.subtitle && (
|
|
472
|
+
<div className="text-muted-foreground text-xs pt-1 block">
|
|
473
|
+
{option.subtitle}
|
|
474
|
+
</div>
|
|
475
|
+
)}
|
|
476
|
+
</div>
|
|
477
|
+
</SelectItem>
|
|
478
|
+
))}
|
|
479
|
+
</SelectGroup>
|
|
480
|
+
</SelectContent>
|
|
481
|
+
</Select>
|
|
482
|
+
);
|
|
483
|
+
},
|
|
484
|
+
);
|
|
485
|
+
ModeSelector.displayName = "ModeSelector";
|
|
486
|
+
|
|
283
487
|
interface ChatContentProps {
|
|
284
488
|
hasNotifications: boolean;
|
|
489
|
+
agentId: ExternalAgentId | undefined;
|
|
285
490
|
connectionState: AgentConnectionState;
|
|
286
491
|
sessionId: ExternalAgentSessionId | null;
|
|
287
492
|
notifications: NotificationEvent[];
|
|
288
493
|
pendingPermission: AgentPendingPermission;
|
|
289
|
-
onResolvePermission: (option:
|
|
494
|
+
onResolvePermission: (option: RequestPermissionResponse) => void;
|
|
290
495
|
onRetryConnection?: () => void;
|
|
291
496
|
onRetryLastAction?: () => void;
|
|
292
497
|
onDismissError?: (errorId: string) => void;
|
|
@@ -295,6 +500,7 @@ interface ChatContentProps {
|
|
|
295
500
|
const ChatContent = memo<ChatContentProps>(
|
|
296
501
|
({
|
|
297
502
|
hasNotifications,
|
|
503
|
+
agentId,
|
|
298
504
|
connectionState,
|
|
299
505
|
notifications,
|
|
300
506
|
pendingPermission,
|
|
@@ -306,6 +512,7 @@ const ChatContent = memo<ChatContentProps>(
|
|
|
306
512
|
}) => {
|
|
307
513
|
const [isScrolledToBottom, setIsScrolledToBottom] = useState(true);
|
|
308
514
|
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
|
515
|
+
const isDisconnected = connectionState.status === "disconnected";
|
|
309
516
|
|
|
310
517
|
// Scroll handler to determine if we're at the bottom of the chat
|
|
311
518
|
const handleScroll = useEvent(() => {
|
|
@@ -375,12 +582,30 @@ const ChatContent = memo<ChatContentProps>(
|
|
|
375
582
|
/>
|
|
376
583
|
</div>
|
|
377
584
|
) : (
|
|
378
|
-
<div className="flex items-center justify-center h-full min-h-[200px]">
|
|
585
|
+
<div className="flex items-center justify-center h-full min-h-[200px] flex-col">
|
|
379
586
|
<PanelEmptyState
|
|
380
587
|
title="Waiting for agent"
|
|
381
588
|
description="Your AI agent will appear here when active"
|
|
382
589
|
icon={<BotMessageSquareIcon />}
|
|
383
590
|
/>
|
|
591
|
+
{isDisconnected && agentId && (
|
|
592
|
+
<AgentDocs
|
|
593
|
+
className="border-t pt-6 px-5"
|
|
594
|
+
title="Make sure you're connected to an agent"
|
|
595
|
+
description="Run this command in your terminal:"
|
|
596
|
+
agents={[agentId]}
|
|
597
|
+
/>
|
|
598
|
+
)}
|
|
599
|
+
{isDisconnected && (
|
|
600
|
+
<Button
|
|
601
|
+
variant="outline"
|
|
602
|
+
onClick={onRetryConnection}
|
|
603
|
+
type="button"
|
|
604
|
+
className="mt-4"
|
|
605
|
+
>
|
|
606
|
+
Retry
|
|
607
|
+
</Button>
|
|
608
|
+
)}
|
|
384
609
|
</div>
|
|
385
610
|
)}
|
|
386
611
|
</div>
|
|
@@ -408,6 +633,8 @@ function getCwd() {
|
|
|
408
633
|
const AgentPanel: React.FC = () => {
|
|
409
634
|
const [isLoading, setIsLoading] = useState(false);
|
|
410
635
|
const [promptValue, setPromptValue] = useState("");
|
|
636
|
+
const [files, setFiles] = useState<File[]>();
|
|
637
|
+
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
411
638
|
|
|
412
639
|
const [selectedTab] = useAtom(selectedTabAtom);
|
|
413
640
|
const [sessionState, setSessionState] = useAtom(agentSessionStateAtom);
|
|
@@ -421,7 +648,7 @@ const AgentPanel: React.FC = () => {
|
|
|
421
648
|
const acpClient = useAcpClient({
|
|
422
649
|
wsUrl,
|
|
423
650
|
clientOptions: {
|
|
424
|
-
readTextFile: (request)
|
|
651
|
+
readTextFile: (request) => {
|
|
425
652
|
logger.debug("Agent requesting file read", {
|
|
426
653
|
path: request.path,
|
|
427
654
|
});
|
|
@@ -429,7 +656,7 @@ const AgentPanel: React.FC = () => {
|
|
|
429
656
|
content: response.contents || "",
|
|
430
657
|
}));
|
|
431
658
|
},
|
|
432
|
-
writeTextFile: (request)
|
|
659
|
+
writeTextFile: (request) => {
|
|
433
660
|
logger.debug("Agent requesting file write", {
|
|
434
661
|
path: request.path,
|
|
435
662
|
contentLength: request.content.length,
|
|
@@ -437,7 +664,7 @@ const AgentPanel: React.FC = () => {
|
|
|
437
664
|
return sendUpdateFile({
|
|
438
665
|
path: request.path,
|
|
439
666
|
contents: request.content,
|
|
440
|
-
}).then(() =>
|
|
667
|
+
}).then(() => ({}));
|
|
441
668
|
},
|
|
442
669
|
},
|
|
443
670
|
autoConnect: false, // We'll manage connection manually based on active session
|
|
@@ -449,11 +676,25 @@ const AgentPanel: React.FC = () => {
|
|
|
449
676
|
connectionState,
|
|
450
677
|
notifications,
|
|
451
678
|
pendingPermission,
|
|
679
|
+
availableCommands,
|
|
452
680
|
resolvePermission,
|
|
681
|
+
sessionMode,
|
|
453
682
|
activeSessionId,
|
|
454
683
|
agent,
|
|
455
684
|
} = acpClient;
|
|
456
685
|
|
|
686
|
+
useEffect(() => {
|
|
687
|
+
agent?.initialize({
|
|
688
|
+
protocolVersion: 1,
|
|
689
|
+
clientCapabilities: {
|
|
690
|
+
fs: {
|
|
691
|
+
readTextFile: true,
|
|
692
|
+
writeTextFile: true,
|
|
693
|
+
},
|
|
694
|
+
},
|
|
695
|
+
});
|
|
696
|
+
}, [agent]);
|
|
697
|
+
|
|
457
698
|
// Auto-connect to agent when we have an active session, but only once per session
|
|
458
699
|
useEffect(() => {
|
|
459
700
|
if (wsUrl === NO_WS_SET) {
|
|
@@ -581,6 +822,7 @@ const AgentPanel: React.FC = () => {
|
|
|
581
822
|
});
|
|
582
823
|
setIsLoading(true);
|
|
583
824
|
setPromptValue("");
|
|
825
|
+
setFiles(undefined);
|
|
584
826
|
|
|
585
827
|
// Update session title with first message if it's still the default
|
|
586
828
|
if (selectedTab?.title.startsWith("New ")) {
|
|
@@ -597,26 +839,48 @@ const AgentPanel: React.FC = () => {
|
|
|
597
839
|
return;
|
|
598
840
|
}
|
|
599
841
|
|
|
842
|
+
const promptBlocks: ContentBlock[] = [{ type: "text", text: prompt }];
|
|
843
|
+
|
|
844
|
+
// Parse context from the prompt
|
|
845
|
+
const { contextBlocks, attachmentBlocks } =
|
|
846
|
+
await parseContextFromPrompt(prompt);
|
|
847
|
+
promptBlocks.push(...contextBlocks);
|
|
848
|
+
promptBlocks.push(...attachmentBlocks);
|
|
849
|
+
|
|
850
|
+
// Add manually uploaded files as resource links
|
|
851
|
+
if (files && files.length > 0) {
|
|
852
|
+
const fileResourceLinks = await convertFilesToResourceLinks(files);
|
|
853
|
+
promptBlocks.push(...fileResourceLinks);
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
const hasGivenRules = notifications.some(
|
|
857
|
+
(notification) =>
|
|
858
|
+
notification.type === "session_notification" &&
|
|
859
|
+
notification.data.update.sessionUpdate === "user_message_chunk",
|
|
860
|
+
);
|
|
861
|
+
if (!hasGivenRules) {
|
|
862
|
+
promptBlocks.push(
|
|
863
|
+
{
|
|
864
|
+
type: "resource_link",
|
|
865
|
+
uri: filename,
|
|
866
|
+
mimeType: "text/x-python",
|
|
867
|
+
name: filename,
|
|
868
|
+
},
|
|
869
|
+
{
|
|
870
|
+
type: "resource",
|
|
871
|
+
resource: {
|
|
872
|
+
uri: "marimo_rules.md",
|
|
873
|
+
mimeType: "text/markdown",
|
|
874
|
+
text: getAgentPrompt(filename),
|
|
875
|
+
},
|
|
876
|
+
},
|
|
877
|
+
);
|
|
878
|
+
}
|
|
879
|
+
|
|
600
880
|
try {
|
|
601
881
|
await agent.prompt({
|
|
602
882
|
sessionId: activeSessionId,
|
|
603
|
-
prompt:
|
|
604
|
-
{ type: "text", text: prompt },
|
|
605
|
-
{
|
|
606
|
-
type: "resource_link",
|
|
607
|
-
uri: filename,
|
|
608
|
-
mimeType: "text/x-python",
|
|
609
|
-
name: filename,
|
|
610
|
-
},
|
|
611
|
-
{
|
|
612
|
-
type: "resource",
|
|
613
|
-
resource: {
|
|
614
|
-
uri: "marimo_rules.md",
|
|
615
|
-
mimeType: "text/markdown",
|
|
616
|
-
text: getAgentPrompt(filename),
|
|
617
|
-
},
|
|
618
|
-
},
|
|
619
|
-
],
|
|
883
|
+
prompt: promptBlocks,
|
|
620
884
|
});
|
|
621
885
|
} catch (error) {
|
|
622
886
|
logger.error("Failed to send prompt", { error });
|
|
@@ -635,6 +899,21 @@ const AgentPanel: React.FC = () => {
|
|
|
635
899
|
setIsLoading(false);
|
|
636
900
|
});
|
|
637
901
|
|
|
902
|
+
// Handler for adding files
|
|
903
|
+
const handleAddFiles = useEvent((newFiles: File[]) => {
|
|
904
|
+
if (newFiles.length === 0) {
|
|
905
|
+
return;
|
|
906
|
+
}
|
|
907
|
+
setFiles((prev) => [...(prev ?? []), ...newFiles]);
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
// Handler for removing files
|
|
911
|
+
const handleRemoveFile = useEvent((fileToRemove: File) => {
|
|
912
|
+
if (files) {
|
|
913
|
+
setFiles(files.filter((f) => f !== fileToRemove));
|
|
914
|
+
}
|
|
915
|
+
});
|
|
916
|
+
|
|
638
917
|
// Handler for manual connect
|
|
639
918
|
const handleManualConnect = useEvent(() => {
|
|
640
919
|
logger.debug("Manual connect requested", {
|
|
@@ -652,6 +931,33 @@ const AgentPanel: React.FC = () => {
|
|
|
652
931
|
disconnect();
|
|
653
932
|
});
|
|
654
933
|
|
|
934
|
+
const handleModeChange = useEvent((mode: string) => {
|
|
935
|
+
logger.debug("Mode change requested", {
|
|
936
|
+
sessionId: activeSessionId,
|
|
937
|
+
mode,
|
|
938
|
+
});
|
|
939
|
+
if (!agent) {
|
|
940
|
+
toast({
|
|
941
|
+
title: "Agent not connected",
|
|
942
|
+
description: "Please connect to an agent to change the mode",
|
|
943
|
+
variant: "danger",
|
|
944
|
+
});
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
if (!agent.setSessionMode) {
|
|
948
|
+
toast({
|
|
949
|
+
title: "Mode change not supported",
|
|
950
|
+
description: "The agent does not support mode changes",
|
|
951
|
+
variant: "danger",
|
|
952
|
+
});
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
void agent.setSessionMode?.({
|
|
956
|
+
sessionId: activeSessionId as string,
|
|
957
|
+
modeId: mode,
|
|
958
|
+
});
|
|
959
|
+
});
|
|
960
|
+
|
|
655
961
|
const hasNotifications = notifications.length > 0;
|
|
656
962
|
const hasActiveSessions = sessionState.sessions.length > 0;
|
|
657
963
|
|
|
@@ -680,6 +986,7 @@ const AgentPanel: React.FC = () => {
|
|
|
680
986
|
<SessionTabs />
|
|
681
987
|
|
|
682
988
|
<ChatContent
|
|
989
|
+
agentId={selectedTab?.agentId}
|
|
683
990
|
sessionId={activeSessionId}
|
|
684
991
|
hasNotifications={hasNotifications}
|
|
685
992
|
connectionState={connectionState}
|
|
@@ -690,7 +997,7 @@ const AgentPanel: React.FC = () => {
|
|
|
690
997
|
sessionId: activeSessionId,
|
|
691
998
|
option,
|
|
692
999
|
});
|
|
693
|
-
resolvePermission(option
|
|
1000
|
+
resolvePermission(option);
|
|
694
1001
|
}}
|
|
695
1002
|
onRetryConnection={handleManualConnect}
|
|
696
1003
|
/>
|
|
@@ -701,12 +1008,30 @@ const AgentPanel: React.FC = () => {
|
|
|
701
1008
|
onStop={handleStop}
|
|
702
1009
|
/>
|
|
703
1010
|
|
|
1011
|
+
{files && files.length > 0 && (
|
|
1012
|
+
<div className="flex flex-row gap-1 flex-wrap p-3 border-t">
|
|
1013
|
+
{files.map((file) => (
|
|
1014
|
+
<FileAttachmentPill
|
|
1015
|
+
file={file}
|
|
1016
|
+
key={file.name}
|
|
1017
|
+
onRemove={() => handleRemoveFile(file)}
|
|
1018
|
+
/>
|
|
1019
|
+
))}
|
|
1020
|
+
</div>
|
|
1021
|
+
)}
|
|
1022
|
+
|
|
704
1023
|
<PromptArea
|
|
705
1024
|
isLoading={isLoading}
|
|
706
1025
|
activeSessionId={activeSessionId}
|
|
707
1026
|
promptValue={promptValue}
|
|
708
1027
|
onPromptValueChange={setPromptValue}
|
|
709
1028
|
onPromptSubmit={handlePromptSubmit}
|
|
1029
|
+
onAddFiles={handleAddFiles}
|
|
1030
|
+
onStop={handleStop}
|
|
1031
|
+
fileInputRef={fileInputRef}
|
|
1032
|
+
commands={availableCommands}
|
|
1033
|
+
sessionMode={sessionMode}
|
|
1034
|
+
onModeChange={handleModeChange}
|
|
710
1035
|
/>
|
|
711
1036
|
</div>
|
|
712
1037
|
);
|
|
@@ -13,7 +13,9 @@ import {
|
|
|
13
13
|
DropdownMenuSeparator,
|
|
14
14
|
DropdownMenuTrigger,
|
|
15
15
|
} from "@/components/ui/dropdown-menu";
|
|
16
|
+
import { useFilename } from "@/core/saving/filename";
|
|
16
17
|
import { cn } from "@/utils/cn";
|
|
18
|
+
import { Paths } from "@/utils/paths";
|
|
17
19
|
import { AgentDocs } from "./agent-docs";
|
|
18
20
|
import {
|
|
19
21
|
type AgentSession,
|
|
@@ -79,6 +81,7 @@ AgentMenuItem.displayName = "AgentMenuItem";
|
|
|
79
81
|
|
|
80
82
|
export const AgentSelector: React.FC<AgentSelectorProps> = memo(
|
|
81
83
|
({ onSessionCreated, className }) => {
|
|
84
|
+
const filename = useFilename();
|
|
82
85
|
const [sessionState, setSessionState] = useAtom(agentSessionStateAtom);
|
|
83
86
|
const setActiveTab = useSetAtom(selectedTabAtom);
|
|
84
87
|
const [isOpen, setIsOpen] = useState(false);
|
|
@@ -123,7 +126,10 @@ export const AgentSelector: React.FC<AgentSelectorProps> = memo(
|
|
|
123
126
|
<div className="px-2 py-2">
|
|
124
127
|
<div className="text-xs font-medium text-muted-foreground mb-3">
|
|
125
128
|
To start an external agent, run the following command in your
|
|
126
|
-
terminal
|
|
129
|
+
terminal.
|
|
130
|
+
<br />
|
|
131
|
+
Note: This must be in the directory{" "}
|
|
132
|
+
{Paths.dirname(filename ?? "")}
|
|
127
133
|
</div>
|
|
128
134
|
<AgentDocs
|
|
129
135
|
agents={AVAILABLE_AGENTS.map((agent) => agent.id)}
|