@marimo-team/islands 0.15.4 → 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/{ConnectedDataExplorerComponent-B68gXlbY.js → ConnectedDataExplorerComponent-DXzE2Rlw.js} +2 -2
- package/dist/{ImageComparisonComponent-Cw1oA8Tn.js → ImageComparisonComponent-C-eUz3jJ.js} +1 -1
- package/dist/{_baseUniq-CQrhBg_9.js → _baseUniq-DQHjMSO_.js} +1 -1
- package/dist/{any-language-editor-pzUl6lxp.js → any-language-editor-DdvqlgsG.js} +1 -1
- package/dist/{architectureDiagram-W76B3OCA-DdYf2VnU.js → architectureDiagram-W76B3OCA-BhiWK5Po.js} +4 -4
- package/dist/{blockDiagram-QIGZ2CNN-DmmYotkP.js → blockDiagram-QIGZ2CNN-BsxfyKOq.js} +5 -5
- package/dist/{c4Diagram-FPNF74CW-Dyz4zMHJ.js → c4Diagram-FPNF74CW-DT6wxSq_.js} +2 -2
- package/dist/{channel-CCL8jXAe.js → channel-Cpc6YsEQ.js} +1 -1
- package/dist/{chunk-4BX2VUAB-BfKwWLfJ.js → chunk-4BX2VUAB-DqO0lgY1.js} +1 -1
- package/dist/{chunk-55IACEB6-CFQU7zSp.js → chunk-55IACEB6-9YHCYHRD.js} +1 -1
- package/dist/{chunk-FMBD7UC4-DDjZzUcl.js → chunk-FMBD7UC4-us9pmTom.js} +1 -1
- package/dist/{chunk-K7UQS3LO-nBRjBU1H.js → chunk-K7UQS3LO-iRfgC4yf.js} +4 -4
- package/dist/{chunk-QN33PNHL-B-g8sJzl.js → chunk-QN33PNHL-C8H89KNK.js} +1 -1
- package/dist/{chunk-QZHKN3VN-B7QSJS3J.js → chunk-QZHKN3VN-DrUqnrvD.js} +1 -1
- package/dist/{chunk-TVAH2DTR-pGXll4d1.js → chunk-TVAH2DTR-Ba-bSegS.js} +3 -3
- package/dist/{chunk-TZMSLE5B-Dx9h-1mv.js → chunk-TZMSLE5B-C5yvoyVQ.js} +1 -1
- package/dist/{classDiagram-v2-RKCZMP56-BXryI7DY.js → classDiagram-KNZD7YFC--YosU2wQ.js} +2 -2
- package/dist/{classDiagram-KNZD7YFC-BXryI7DY.js → classDiagram-v2-RKCZMP56--YosU2wQ.js} +2 -2
- package/dist/{clone-DqwV7ges.js → clone-B3q5i6e_.js} +1 -1
- package/dist/{cose-bilkent-S5V4N54A-Di6FNMXz.js → cose-bilkent-S5V4N54A-DgXfeDAu.js} +2 -2
- package/dist/{dagre-5GWH7T2D-BTZPMTey.js → dagre-5GWH7T2D-CSZyPlXn.js} +6 -6
- package/dist/{data-grid-overlay-editor-ryatXXby.js → data-grid-overlay-editor-3IPfgtVA.js} +2 -2
- package/dist/{diagram-N5W7TBWH-D79_zdOu.js → diagram-N5W7TBWH-Bk2EPtkl.js} +5 -5
- package/dist/{diagram-QEK2KX5R-DX2A_SD0.js → diagram-QEK2KX5R-CmUzAfqR.js} +3 -3
- package/dist/{diagram-S2PKOQOG-DM6VMTrJ.js → diagram-S2PKOQOG-3ajjAXEO.js} +3 -3
- package/dist/{dockerfile-BoowzQlp.js → dockerfile-C1fNsrIG.js} +1 -1
- package/dist/{erDiagram-AWTI2OKA-BBirxtlI.js → erDiagram-AWTI2OKA-B8Qsnrsf.js} +4 -4
- package/dist/{flowDiagram-PVAE7QVJ-DyVweEMs.js → flowDiagram-PVAE7QVJ-DSj30qa8.js} +5 -5
- package/dist/{ganttDiagram-OWAHRB6G-DTB7FX7r.js → ganttDiagram-OWAHRB6G-JbAchSao.js} +4 -4
- package/dist/{gitGraphDiagram-NY62KEGX-BrbIb5pD.js → gitGraphDiagram-NY62KEGX-HN8sfxnX.js} +4 -4
- package/dist/{glide-data-editor-DhMX4nmM.js → glide-data-editor-DxLZKoAV.js} +3 -3
- package/dist/{graph-CuLSrclI.js → graph-CnSqa440.js} +3 -3
- package/dist/{index-DIy6LHLJ.js → index-CN5rQ0L7.js} +1 -1
- package/dist/{index-Df2dsx1t.js → index-D-c83Zu2.js} +1 -1
- package/dist/{index-BNgdUQ2e.js → index-DFWnpmwy.js} +1 -1
- package/dist/{index-cz_xaKvT.js → index-a9F3H4C8.js} +3 -3
- package/dist/{infoDiagram-STP46IZ2-CCBHc7-K.js → infoDiagram-STP46IZ2-sRlbbIPE.js} +2 -2
- package/dist/{journeyDiagram-BIP6EPQ6-LhGSj54j.js → journeyDiagram-BIP6EPQ6-Ggg6NzoC.js} +3 -3
- package/dist/{kanban-definition-6OIFK2YF-aegTMFS6.js → kanban-definition-6OIFK2YF-C2bLuMPD.js} +2 -2
- package/dist/{layout-BEARWMhl.js → layout-Yxizer3A.js} +4 -4
- package/dist/{linear-fbJq6cdO.js → linear-DafxvwEV.js} +1 -1
- package/dist/{main-HerZgEhd.js → main-mtVsqZY6.js} +78656 -78608
- package/dist/main.js +1 -1
- package/dist/{mermaid-DxPYK0KX.js → mermaid-DUnuxwY7.js} +30 -30
- package/dist/{min-DBJkhObB.js → min-BLUnrJi8.js} +2 -2
- package/dist/{mindmap-definition-Q6HEUPPD-A3Fh5XDZ.js → mindmap-definition-Q6HEUPPD-Vk72Jw1R.js} +3 -3
- package/dist/{number-overlay-editor-USMrY6k3.js → number-overlay-editor-COu53LIm.js} +2 -2
- package/dist/{pieDiagram-ADFJNKIX-Q9uFlCV0.js → pieDiagram-ADFJNKIX-DQIJVPDY.js} +3 -3
- package/dist/{quadrantDiagram-LMRXKWRM-BuPh-qpK.js → quadrantDiagram-LMRXKWRM-2UdNzO_J.js} +2 -2
- package/dist/{react-plotly-HSqJPRfa.js → react-plotly-DUl5VnGd.js} +1 -1
- package/dist/{requirementDiagram-4UW4RH46-CHROYNU_.js → requirementDiagram-4UW4RH46-QhCxm6IF.js} +3 -3
- package/dist/{sankeyDiagram-GR3RE2ED-DkUqHP2d.js → sankeyDiagram-GR3RE2ED-BdUAKmBf.js} +1 -1
- package/dist/{sequenceDiagram-C3RYC4MD-YoPTMplP.js → sequenceDiagram-C3RYC4MD-fSAzFvC4.js} +3 -3
- package/dist/{slides-component-D7CHSR00.js → slides-component-BT22bKwF.js} +1 -1
- package/dist/{stateDiagram-KXAO66HF-DEN00mVU.js → stateDiagram-KXAO66HF-BCY34H2w.js} +4 -4
- package/dist/{stateDiagram-v2-UMBNRL4Z-DlQqSUAa.js → stateDiagram-v2-UMBNRL4Z-CXpFdDan.js} +2 -2
- package/dist/style.css +1 -1
- package/dist/{time-BtVcKqeD.js → time-Cp5uV45N.js} +2 -2
- package/dist/{timeline-definition-XQNQX7LJ-DEteLt8D.js → timeline-definition-XQNQX7LJ-BMz0bhXy.js} +1 -1
- package/dist/{treemap-75Q7IDZK-8S6podme.js → treemap-75Q7IDZK-Dy3GswCD.js} +5 -5
- package/dist/{vega-component-D35L45kI.js → vega-component-D-4UjbYh.js} +2 -2
- package/dist/{xychartDiagram-6GGTOJPD-DKwGThyy.js → xychartDiagram-6GGTOJPD-D-zWFjme.js} +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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { R as q, r as ln, o as P, q as X, C as Z, u as D, v as fn } from "./timer-B0-z63CM.js";
|
|
2
|
-
import { c as hn, a as on } from "./linear-
|
|
2
|
+
import { c as hn, a as on } from "./linear-DafxvwEV.js";
|
|
3
3
|
import { i as bn } from "./init-DxzjmxYy.js";
|
|
4
|
-
import { W as gn, X as d, Y as v, Z as k, _ as x, $ as L, a0 as yn, a1 as R, a2 as H, a3 as W, a4 as pn, a5 as mn, a6 as wn, a7 as Mn, a8 as dn, a9 as vn, aa as kn, ab as $, ac as A, ad as B, ae as K, af as _, ag as j, ah as xn } from "./main-
|
|
4
|
+
import { W as gn, X as d, Y as v, Z as k, _ as x, $ as L, a0 as yn, a1 as R, a2 as H, a3 as W, a4 as pn, a5 as mn, a6 as wn, a7 as Mn, a8 as dn, a9 as vn, aa as kn, ab as $, ac as A, ad as B, ae as K, af as _, ag as j, ah as xn } from "./main-mtVsqZY6.js";
|
|
5
5
|
function Pn(n, t) {
|
|
6
6
|
let i;
|
|
7
7
|
if (t === void 0)
|
package/dist/{timeline-definition-XQNQX7LJ-DEteLt8D.js → timeline-definition-XQNQX7LJ-BMz0bhXy.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { _ as s, c as xt, l as E, d as j, V as kt, W as vt, X as _t, Y as bt, D as wt, $ as St, z as Et } from "./mermaid-
|
|
1
|
+
import { _ as s, c as xt, l as E, d as j, V as kt, W as vt, X as _t, Y as bt, D as wt, $ as St, z as Et } from "./mermaid-DUnuxwY7.js";
|
|
2
2
|
import { d as nt } from "./arc-BOhn-m2C.js";
|
|
3
3
|
var Q = (function() {
|
|
4
4
|
var n = /* @__PURE__ */ s(function(x, r, a, c) {
|
|
@@ -2,11 +2,11 @@ var __defProp = Object.defineProperty;
|
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
4
|
var _a2, _b, _c2, _d2, _e, _f2, _g, _h2, _i2, _j, _k, _l2, _m2, _n2, _o2, _p2, _q;
|
|
5
|
-
import { dY as Kl, bO as Vl, al as Ma, aT as Hl, u as _t, aO as Wl, ao as Q, bk as jl, bl as zl, k as ql, n as Ni, m as Da, am as Qs, dZ as Fa, aK as Yl, b0 as Ga, d_ as Ua, aM as Xl, b2 as wi, an as Jl, d$ as Zl, bg as Ql, e0 as eu, aP as Ie, q as vt, ar as _i, b3 as tu, e1 as H, e2 as Re } from "./main-
|
|
6
|
-
import { g as ei, d as bt, k as nu, v as W, l as Ba, m as ru, n as su, a as Ka, c as x, i as qe, r as ae, f as ke, o as z } from "./_baseUniq-
|
|
7
|
-
import { m as k, f as Lt, h as I, e as ti, l as Ot, d as iu } from "./min-
|
|
8
|
-
import { ad as P } from "./mermaid-
|
|
9
|
-
import { c as te } from "./clone-
|
|
5
|
+
import { dY as Kl, bO as Vl, al as Ma, aT as Hl, u as _t, aO as Wl, ao as Q, bk as jl, bl as zl, k as ql, n as Ni, m as Da, am as Qs, dZ as Fa, aK as Yl, b0 as Ga, d_ as Ua, aM as Xl, b2 as wi, an as Jl, d$ as Zl, bg as Ql, e0 as eu, aP as Ie, q as vt, ar as _i, b3 as tu, e1 as H, e2 as Re } from "./main-mtVsqZY6.js";
|
|
6
|
+
import { g as ei, d as bt, k as nu, v as W, l as Ba, m as ru, n as su, a as Ka, c as x, i as qe, r as ae, f as ke, o as z } from "./_baseUniq-DQHjMSO_.js";
|
|
7
|
+
import { m as k, f as Lt, h as I, e as ti, l as Ot, d as iu } from "./min-BLUnrJi8.js";
|
|
8
|
+
import { ad as P } from "./mermaid-DUnuxwY7.js";
|
|
9
|
+
import { c as te } from "./clone-B3q5i6e_.js";
|
|
10
10
|
var au = Object.prototype, ou = au.hasOwnProperty, Ee = Kl(function(n, e) {
|
|
11
11
|
if (Vl(e) || Ma(e)) {
|
|
12
12
|
Hl(e, _t(e), n);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { D as V, F as K, A as Z, G as z, H as R, I as G, j as d, E as W, J as U, r as k, K as j, M, N as $, L as x, O as _, P as B, Q, S as q, T as J, U as Y, V as X } from "./main-
|
|
2
|
-
import { M as m, V as ee } from "./index-
|
|
1
|
+
import { D as V, F as K, A as Z, G as z, H as R, I as G, j as d, E as W, J as U, r as k, K as j, M, N as $, L as x, O as _, P as B, Q, S as q, T as J, U as Y, V as X } from "./main-mtVsqZY6.js";
|
|
2
|
+
import { M as m, V as ee } from "./index-a9F3H4C8.js";
|
|
3
3
|
function te(e) {
|
|
4
4
|
return e.data && "url" in e.data && (e.data.url = V(e.data.url).href), e;
|
|
5
5
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i2, _j;
|
|
2
|
-
import { _ as a, s as ei, g as si, t as Lt, q as ni, a as ai, b as oi, l as Et, K as ri, e as hi, z as li, G as xt, F as It, H as ci, M as ui, i as gi, ac as xi } from "./mermaid-
|
|
2
|
+
import { _ as a, s as ei, g as si, t as Lt, q as ni, a as ai, b as oi, l as Et, K as ri, e as hi, z as li, G as xt, F as It, H as ci, M as ui, i as gi, ac as xi } from "./mermaid-DUnuxwY7.js";
|
|
3
3
|
import { i as di } from "./init-DxzjmxYy.js";
|
|
4
4
|
import { o as fi } from "./ordinal-CYN5qNoq.js";
|
|
5
5
|
import { r as pi } from "./range-DdOGybNB.js";
|
|
6
|
-
import { l as Tt } from "./linear-
|
|
6
|
+
import { l as Tt } from "./linear-DafxvwEV.js";
|
|
7
7
|
import { l as Dt } from "./timer-B0-z63CM.js";
|
|
8
8
|
function ht() {
|
|
9
9
|
var t = fi().unknown(void 0), i = t.domain, e = t.range, s = 0, n = 1, d, g, m = false, S = 0, D = 0, v = 0.5;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marimo-team/islands",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"main": "dist/main.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -100,9 +100,13 @@
|
|
|
100
100
|
"@uwdata/flechette": "^1.1.2",
|
|
101
101
|
"@valtown/codemirror-codeium": "^1.1.1",
|
|
102
102
|
"@xterm/addon-attach": "^0.11.0",
|
|
103
|
+
"@xterm/addon-canvas": "^0.7.0",
|
|
103
104
|
"@xterm/addon-fit": "^0.10.0",
|
|
105
|
+
"@xterm/addon-search": "^0.15.0",
|
|
106
|
+
"@xterm/addon-unicode11": "^0.8.0",
|
|
107
|
+
"@xterm/addon-web-links": "^0.11.0",
|
|
104
108
|
"@xterm/xterm": "^5.5.0",
|
|
105
|
-
"@zed-industries/agent-client-protocol": "^0.1
|
|
109
|
+
"@zed-industries/agent-client-protocol": "^0.3.1",
|
|
106
110
|
"ai": "^5.0.30",
|
|
107
111
|
"ansi_up": "^6.0.6",
|
|
108
112
|
"class-variance-authority": "^0.7.1",
|
|
@@ -151,7 +155,7 @@
|
|
|
151
155
|
"remark-gfm": "^4.0.1",
|
|
152
156
|
"rpc-anywhere": "^1.7.0",
|
|
153
157
|
"sql-formatter": "^15.6.6",
|
|
154
|
-
"streamdown": "^1.
|
|
158
|
+
"streamdown": "^1.3.0",
|
|
155
159
|
"string-dedent": "^3.0.2",
|
|
156
160
|
"swiper": "^11.2.10",
|
|
157
161
|
"tailwind-merge": "^2.6.0",
|
|
@@ -159,7 +163,7 @@
|
|
|
159
163
|
"thememirror": "^2.0.1",
|
|
160
164
|
"timestring": "^7.0.0",
|
|
161
165
|
"typescript-memoize": "^1.1.1",
|
|
162
|
-
"use-acp": "^0.
|
|
166
|
+
"use-acp": "^0.2.3",
|
|
163
167
|
"use-resize-observer": "^9.1.0",
|
|
164
168
|
"vega-lite": "^5.23.0",
|
|
165
169
|
"vega-loader": "^4.5.3",
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
// Edge case filenames for testing unicode, spaces, and special characters
|
|
4
|
+
export const EDGE_CASE_FILENAMES = [
|
|
5
|
+
// Unicode
|
|
6
|
+
"tést.py",
|
|
7
|
+
"café.py",
|
|
8
|
+
"测试.py",
|
|
9
|
+
// Emojis
|
|
10
|
+
"🚀notebook.py",
|
|
11
|
+
// Spaces
|
|
12
|
+
"test file.py",
|
|
13
|
+
"file with spaces.py",
|
|
14
|
+
// Multiple
|
|
15
|
+
"café & 测试.py",
|
|
16
|
+
"café notebook.py",
|
|
17
|
+
// URL characters
|
|
18
|
+
"test-file.py",
|
|
19
|
+
"test_file.py",
|
|
20
|
+
"test_file.backup.py",
|
|
21
|
+
"file&with&ersands.py",
|
|
22
|
+
"file=with=equals.py",
|
|
23
|
+
"file?with?questions.py",
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
// Cell names with unicode and special characters for frontend tests
|
|
27
|
+
export const EDGE_CASE_CELL_NAMES = [
|
|
28
|
+
"tést_cell",
|
|
29
|
+
"café_notebook",
|
|
30
|
+
"测试_cell",
|
|
31
|
+
"🚀_my_cell",
|
|
32
|
+
"cell with spaces",
|
|
33
|
+
"café notebook cell",
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
// URL test cases with special characters that could break query parameters
|
|
37
|
+
export const URL_SPECIAL_CHAR_FILENAMES = [
|
|
38
|
+
"file with spaces.py",
|
|
39
|
+
"file&with&ersands.py",
|
|
40
|
+
"file=with=equals.py",
|
|
41
|
+
"file?with?questions.py",
|
|
42
|
+
"café & 测试.py",
|
|
43
|
+
];
|
|
@@ -1283,6 +1283,38 @@ export const UserConfigForm: React.FC = () => {
|
|
|
1283
1283
|
</div>
|
|
1284
1284
|
)}
|
|
1285
1285
|
/>
|
|
1286
|
+
<FormField
|
|
1287
|
+
control={form.control}
|
|
1288
|
+
name="experimental.external_agents"
|
|
1289
|
+
render={({ field }) => (
|
|
1290
|
+
<div className="flex flex-col gap-y-1">
|
|
1291
|
+
<FormItem className={formItemClasses}>
|
|
1292
|
+
<FormLabel className="font-normal">
|
|
1293
|
+
External Agents
|
|
1294
|
+
</FormLabel>
|
|
1295
|
+
<FormControl>
|
|
1296
|
+
<Checkbox
|
|
1297
|
+
data-testid="external-agents-checkbox"
|
|
1298
|
+
checked={field.value === true}
|
|
1299
|
+
onCheckedChange={field.onChange}
|
|
1300
|
+
/>
|
|
1301
|
+
</FormControl>
|
|
1302
|
+
</FormItem>
|
|
1303
|
+
<IsOverridden
|
|
1304
|
+
userConfig={config}
|
|
1305
|
+
name="experimental.external_agents"
|
|
1306
|
+
/>
|
|
1307
|
+
<FormDescription>
|
|
1308
|
+
Enable experimental external agents such as Claude Code and
|
|
1309
|
+
Gemini CLI. Learn more in the{" "}
|
|
1310
|
+
<ExternalLink href="https://docs.marimo.io/guides/editor_features/agents/">
|
|
1311
|
+
docs
|
|
1312
|
+
</ExternalLink>
|
|
1313
|
+
.
|
|
1314
|
+
</FormDescription>
|
|
1315
|
+
</div>
|
|
1316
|
+
)}
|
|
1317
|
+
/>
|
|
1286
1318
|
</SettingGroup>
|
|
1287
1319
|
);
|
|
1288
1320
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
exports[`getAgentPrompt > should generate complete agent prompt with default filename 1`] = `
|
|
4
4
|
"
|
|
5
5
|
I am currently editing a marimo notebook.
|
|
6
|
-
You can read or write to the notebook at
|
|
6
|
+
You can read or write to the notebook at test-notebook.py
|
|
7
7
|
|
|
8
8
|
If you make edits to the notebook, only edit the contents inside the function decorator with @app.cell.
|
|
9
9
|
marimo will automatically handle adding the parameters and return statement of the function. For example,
|
|
@@ -11,7 +11,7 @@ exports[`getAgentPrompt > should generate complete agent prompt with default fil
|
|
|
11
11
|
|
|
12
12
|
\`\`\`
|
|
13
13
|
@app.cell
|
|
14
|
-
def
|
|
14
|
+
def _():
|
|
15
15
|
<your code here>
|
|
16
16
|
return
|
|
17
17
|
\`\`\`
|
|
@@ -37,6 +37,7 @@ exports[`getAgentPrompt > should generate complete agent prompt with default fil
|
|
|
37
37
|
7. The last expression in a cell is automatically displayed, just like in Jupyter notebooks.
|
|
38
38
|
8. Don't include comments in markdown cells
|
|
39
39
|
9. Don't include comments in SQL cells
|
|
40
|
+
10. Never define anything using \`global\`.
|
|
40
41
|
|
|
41
42
|
## Reactivity
|
|
42
43
|
|
|
@@ -46,6 +47,7 @@ exports[`getAgentPrompt > should generate complete agent prompt with default fil
|
|
|
46
47
|
- UI elements trigger updates when their values change without explicit callbacks
|
|
47
48
|
- UI element values are accessed through \`.value\` attribute
|
|
48
49
|
- You cannot access a UI element's value in the same cell where it's defined
|
|
50
|
+
- Cells prefixed with an underscore (e.g. _my_var) are local to the cell and cannot be accessed by other cells
|
|
49
51
|
|
|
50
52
|
## Best Practices
|
|
51
53
|
|
|
@@ -115,34 +117,50 @@ exports[`getAgentPrompt > should generate complete agent prompt with default fil
|
|
|
115
117
|
|
|
116
118
|
- \`mo.md(text)\` - display markdown
|
|
117
119
|
- \`mo.stop(predicate, output=None)\` - stop execution conditionally
|
|
120
|
+
- \`mo.output.append(value)\` - append to the output when it is not the last expression
|
|
121
|
+
- \`mo.output.replace(value)\` - replace the output when it is not the last expression
|
|
118
122
|
- \`mo.Html(html)\` - display HTML
|
|
119
123
|
- \`mo.image(image)\` - display an image
|
|
120
124
|
- \`mo.hstack(elements)\` - stack elements horizontally
|
|
121
125
|
- \`mo.vstack(elements)\` - stack elements vertically
|
|
122
126
|
- \`mo.tabs(elements)\` - create a tabbed interface
|
|
123
127
|
|
|
128
|
+
|
|
129
|
+
|
|
124
130
|
## Examples
|
|
125
131
|
|
|
132
|
+
<example title="Markdown ccell">
|
|
133
|
+
|
|
134
|
+
@app.cell
|
|
135
|
+
def _():
|
|
136
|
+
mo.md("""
|
|
137
|
+
# Hello world
|
|
138
|
+
This is a _markdown_ **cell**.
|
|
139
|
+
""")
|
|
140
|
+
return
|
|
141
|
+
|
|
142
|
+
</example>
|
|
143
|
+
|
|
126
144
|
<example title="Basic UI with reactivity">
|
|
127
145
|
|
|
128
146
|
@app.cell
|
|
129
|
-
def
|
|
147
|
+
def _():
|
|
130
148
|
import marimo as mo
|
|
131
149
|
import matplotlib.pyplot as plt
|
|
132
150
|
import numpy as np
|
|
133
151
|
return
|
|
134
152
|
|
|
135
153
|
@app.cell
|
|
136
|
-
def
|
|
154
|
+
def _():
|
|
137
155
|
n_points = mo.ui.slider(10, 100, value=50, label="Number of points")
|
|
138
156
|
n_points
|
|
139
157
|
return
|
|
140
158
|
|
|
141
159
|
@app.cell
|
|
142
|
-
def
|
|
160
|
+
def _():
|
|
143
161
|
x = np.random.rand(n_points.value)
|
|
144
162
|
y = np.random.rand(n_points.value)
|
|
145
|
-
|
|
163
|
+
|
|
146
164
|
plt.figure(figsize=(8, 6))
|
|
147
165
|
plt.scatter(x, y, alpha=0.7)
|
|
148
166
|
plt.title(f"Scatter plot with {n_points.value} points")
|
|
@@ -156,14 +174,14 @@ exports[`getAgentPrompt > should generate complete agent prompt with default fil
|
|
|
156
174
|
<example title="Data explorer">
|
|
157
175
|
|
|
158
176
|
@app.cell
|
|
159
|
-
def
|
|
177
|
+
def _():
|
|
160
178
|
import marimo as mo
|
|
161
179
|
import pandas as pd
|
|
162
180
|
from vega_datasets import data
|
|
163
181
|
return
|
|
164
182
|
|
|
165
183
|
@app.cell
|
|
166
|
-
def
|
|
184
|
+
def _():
|
|
167
185
|
cars_df = data.cars()
|
|
168
186
|
mo.ui.data_explorer(cars_df)
|
|
169
187
|
return
|
|
@@ -173,7 +191,7 @@ exports[`getAgentPrompt > should generate complete agent prompt with default fil
|
|
|
173
191
|
<example title="Multiple UI elements">
|
|
174
192
|
|
|
175
193
|
@app.cell
|
|
176
|
-
def
|
|
194
|
+
def _():
|
|
177
195
|
import marimo as mo
|
|
178
196
|
import pandas as pd
|
|
179
197
|
import matplotlib.pyplot as plt
|
|
@@ -181,12 +199,12 @@ exports[`getAgentPrompt > should generate complete agent prompt with default fil
|
|
|
181
199
|
return
|
|
182
200
|
|
|
183
201
|
@app.cell
|
|
184
|
-
def
|
|
202
|
+
def _():
|
|
185
203
|
iris = sns.load_dataset('iris')
|
|
186
204
|
return
|
|
187
205
|
|
|
188
206
|
@app.cell
|
|
189
|
-
def
|
|
207
|
+
def _():
|
|
190
208
|
species_selector = mo.ui.dropdown(
|
|
191
209
|
options=["All"] + iris["species"].unique().tolist(),
|
|
192
210
|
value="All",
|
|
@@ -206,9 +224,9 @@ exports[`getAgentPrompt > should generate complete agent prompt with default fil
|
|
|
206
224
|
return
|
|
207
225
|
|
|
208
226
|
@app.cell
|
|
209
|
-
def
|
|
227
|
+
def _():
|
|
210
228
|
filtered_data = iris if species_selector.value == "All" else iris[iris["species"] == species_selector.value]
|
|
211
|
-
|
|
229
|
+
|
|
212
230
|
plt.figure(figsize=(10, 6))
|
|
213
231
|
sns.scatterplot(
|
|
214
232
|
data=filtered_data,
|
|
@@ -222,17 +240,31 @@ exports[`getAgentPrompt > should generate complete agent prompt with default fil
|
|
|
222
240
|
|
|
223
241
|
</example>
|
|
224
242
|
|
|
243
|
+
<example title="Conditional Outputs">
|
|
244
|
+
|
|
245
|
+
@app.cell
|
|
246
|
+
def _():
|
|
247
|
+
mo.stop(not data.value, mo.md("No data to display"))
|
|
248
|
+
|
|
249
|
+
if mode.value == "scatter":
|
|
250
|
+
mo.output.replace(render_scatter(data.value))
|
|
251
|
+
else:
|
|
252
|
+
mo.output.replace(render_bar_chart(data.value))
|
|
253
|
+
return
|
|
254
|
+
|
|
255
|
+
</example>
|
|
256
|
+
|
|
225
257
|
<example title="Interactive chart with Altair">
|
|
226
258
|
|
|
227
259
|
@app.cell
|
|
228
|
-
def
|
|
260
|
+
def _():
|
|
229
261
|
import marimo as mo
|
|
230
262
|
import altair as alt
|
|
231
263
|
import pandas as pd
|
|
232
264
|
return
|
|
233
265
|
|
|
234
266
|
@app.cell
|
|
235
|
-
def
|
|
267
|
+
def _():
|
|
236
268
|
# Load dataset
|
|
237
269
|
cars_df = pd.read_csv('<https://raw.githubusercontent.com/vega/vega-datasets/master/data/cars.json>')
|
|
238
270
|
_chart = alt.Chart(cars_df).mark_point().encode(
|
|
@@ -243,13 +275,13 @@ exports[`getAgentPrompt > should generate complete agent prompt with default fil
|
|
|
243
275
|
return
|
|
244
276
|
|
|
245
277
|
@app.cell
|
|
246
|
-
def
|
|
278
|
+
def _():
|
|
247
279
|
chart = mo.ui.altair_chart(_chart)
|
|
248
280
|
chart
|
|
249
281
|
return
|
|
250
282
|
|
|
251
283
|
@app.cell
|
|
252
|
-
def
|
|
284
|
+
def _():
|
|
253
285
|
# Display the selection
|
|
254
286
|
chart.value
|
|
255
287
|
return
|
|
@@ -259,19 +291,19 @@ exports[`getAgentPrompt > should generate complete agent prompt with default fil
|
|
|
259
291
|
<example title="Run Button Example">
|
|
260
292
|
|
|
261
293
|
@app.cell
|
|
262
|
-
def
|
|
294
|
+
def _():
|
|
263
295
|
import marimo as mo
|
|
264
296
|
return
|
|
265
297
|
|
|
266
298
|
@app.cell
|
|
267
|
-
def
|
|
299
|
+
def _():
|
|
268
300
|
first_button = mo.ui.run_button(label="Option 1")
|
|
269
301
|
second_button = mo.ui.run_button(label="Option 2")
|
|
270
302
|
[first_button, second_button]
|
|
271
303
|
return
|
|
272
304
|
|
|
273
305
|
@app.cell
|
|
274
|
-
def
|
|
306
|
+
def _():
|
|
275
307
|
if first_button.value:
|
|
276
308
|
print("You chose option 1!")
|
|
277
309
|
elif second_button.value:
|
|
@@ -285,18 +317,18 @@ exports[`getAgentPrompt > should generate complete agent prompt with default fil
|
|
|
285
317
|
<example title="SQL with duckdb">
|
|
286
318
|
|
|
287
319
|
@app.cell
|
|
288
|
-
def
|
|
320
|
+
def _():
|
|
289
321
|
import marimo as mo
|
|
290
322
|
import pandas as pd
|
|
291
323
|
return
|
|
292
324
|
|
|
293
325
|
@app.cell
|
|
294
|
-
def
|
|
326
|
+
def _():
|
|
295
327
|
cars_df = pd.read_csv('<https://raw.githubusercontent.com/vega/vega-datasets/master/data/cars.json>')
|
|
296
328
|
return
|
|
297
329
|
|
|
298
330
|
@app.cell
|
|
299
|
-
def
|
|
331
|
+
def _():
|
|
300
332
|
_df = mo.sql("SELECT * from cars_df WHERE Miles_per_Gallon > 20")
|
|
301
333
|
return
|
|
302
334
|
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { beforeEach, describe, expect, it, type Mocked, vi } from "vitest";
|
|
4
|
+
import {
|
|
5
|
+
convertFilesToResourceLinks,
|
|
6
|
+
parseContextFromPrompt,
|
|
7
|
+
} from "../context-utils";
|
|
8
|
+
|
|
9
|
+
// Mock dependencies
|
|
10
|
+
vi.mock("@/utils/fileToBase64", () => ({
|
|
11
|
+
blobToString: vi.fn(),
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
vi.mock("@/core/ai/context/context", () => ({
|
|
15
|
+
getAIContextRegistry: vi.fn(),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
vi.mock("@/core/state/jotai", () => ({
|
|
19
|
+
store: {},
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
vi.mock("@/utils/Logger", () => ({
|
|
23
|
+
Logger: {
|
|
24
|
+
error: vi.fn(),
|
|
25
|
+
debug: vi.fn(),
|
|
26
|
+
},
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
import { getAIContextRegistry } from "@/core/ai/context/context";
|
|
30
|
+
import type {
|
|
31
|
+
AIContextItem,
|
|
32
|
+
AIContextRegistry,
|
|
33
|
+
ContextLocatorId,
|
|
34
|
+
} from "@/core/ai/context/registry";
|
|
35
|
+
import { blobToString } from "@/utils/fileToBase64";
|
|
36
|
+
|
|
37
|
+
const CONTEXT_ID = "context1" as ContextLocatorId;
|
|
38
|
+
|
|
39
|
+
describe("convertFilesToResourceLinks", () => {
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
vi.clearAllMocks();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("should convert files to resource links", async () => {
|
|
45
|
+
const mockFile = new File(["content"], "test.txt", { type: "text/plain" });
|
|
46
|
+
vi.mocked(blobToString).mockResolvedValue(
|
|
47
|
+
"data:text/plain;base64,Y29udGVudA==",
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const result = await convertFilesToResourceLinks([mockFile]);
|
|
51
|
+
|
|
52
|
+
expect(result).toEqual([
|
|
53
|
+
{
|
|
54
|
+
type: "resource_link",
|
|
55
|
+
uri: "data:text/plain;base64,Y29udGVudA==",
|
|
56
|
+
mimeType: "text/plain",
|
|
57
|
+
name: "test.txt",
|
|
58
|
+
},
|
|
59
|
+
]);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("should handle empty file array", async () => {
|
|
63
|
+
const result = await convertFilesToResourceLinks([]);
|
|
64
|
+
expect(result).toEqual([]);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("should handle file conversion errors gracefully", async () => {
|
|
68
|
+
const mockFile = new File(["content"], "test.txt", { type: "text/plain" });
|
|
69
|
+
vi.mocked(blobToString).mockRejectedValue(new Error("Conversion failed"));
|
|
70
|
+
|
|
71
|
+
const result = await convertFilesToResourceLinks([mockFile]);
|
|
72
|
+
|
|
73
|
+
expect(result).toEqual([]);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it("should process multiple files", async () => {
|
|
77
|
+
const file1 = new File(["content1"], "test1.txt", { type: "text/plain" });
|
|
78
|
+
const file2 = new File(["content2"], "test2.txt", { type: "text/plain" });
|
|
79
|
+
|
|
80
|
+
vi.mocked(blobToString)
|
|
81
|
+
.mockResolvedValueOnce("data:text/plain;base64,Y29udGVudDE=")
|
|
82
|
+
.mockResolvedValueOnce("data:text/plain;base64,Y29udGVudDI=");
|
|
83
|
+
|
|
84
|
+
const result = await convertFilesToResourceLinks([file1, file2]);
|
|
85
|
+
|
|
86
|
+
expect(result).toHaveLength(2);
|
|
87
|
+
expect((result[0] as { name: string }).name).toBe("test1.txt");
|
|
88
|
+
expect((result[1] as { name: string }).name).toBe("test2.txt");
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe("parseContextFromPrompt", () => {
|
|
93
|
+
const mockRegistry = {
|
|
94
|
+
parseAllContextIds: vi.fn(),
|
|
95
|
+
formatContextForAI: vi.fn(),
|
|
96
|
+
getAttachmentsForContext: vi.fn(),
|
|
97
|
+
} as unknown as Mocked<AIContextRegistry<AIContextItem>>;
|
|
98
|
+
|
|
99
|
+
beforeEach(() => {
|
|
100
|
+
vi.clearAllMocks();
|
|
101
|
+
vi.mocked(getAIContextRegistry).mockReturnValue(mockRegistry);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("should return empty blocks when no @ symbol in prompt", async () => {
|
|
105
|
+
const result = await parseContextFromPrompt("simple prompt");
|
|
106
|
+
|
|
107
|
+
expect(result).toEqual({
|
|
108
|
+
contextBlocks: [],
|
|
109
|
+
attachmentBlocks: [],
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("should return empty blocks when no context IDs found", async () => {
|
|
114
|
+
mockRegistry.parseAllContextIds.mockReturnValue([]);
|
|
115
|
+
|
|
116
|
+
const result = await parseContextFromPrompt("prompt with @ but no context");
|
|
117
|
+
|
|
118
|
+
expect(result).toEqual({
|
|
119
|
+
contextBlocks: [],
|
|
120
|
+
attachmentBlocks: [],
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("should create context blocks when context IDs are found", async () => {
|
|
125
|
+
mockRegistry.parseAllContextIds.mockReturnValue([CONTEXT_ID]);
|
|
126
|
+
mockRegistry.formatContextForAI.mockReturnValue("formatted context");
|
|
127
|
+
mockRegistry.getAttachmentsForContext.mockResolvedValue([]);
|
|
128
|
+
|
|
129
|
+
const result = await parseContextFromPrompt("prompt with @context1");
|
|
130
|
+
|
|
131
|
+
expect(result.contextBlocks).toHaveLength(1);
|
|
132
|
+
expect(result.contextBlocks[0]).toEqual({
|
|
133
|
+
type: "resource",
|
|
134
|
+
resource: {
|
|
135
|
+
uri: "context.md",
|
|
136
|
+
mimeType: "text/markdown",
|
|
137
|
+
text: "formatted context",
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
expect(result.attachmentBlocks).toHaveLength(0);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("should create attachment blocks when attachments are found", async () => {
|
|
144
|
+
mockRegistry.parseAllContextIds.mockReturnValue([CONTEXT_ID]);
|
|
145
|
+
mockRegistry.formatContextForAI.mockReturnValue("formatted context");
|
|
146
|
+
mockRegistry.getAttachmentsForContext.mockResolvedValue([
|
|
147
|
+
{
|
|
148
|
+
type: "file",
|
|
149
|
+
url: "http://example.com/file.pdf",
|
|
150
|
+
mediaType: "application/pdf",
|
|
151
|
+
filename: "file.pdf",
|
|
152
|
+
},
|
|
153
|
+
]);
|
|
154
|
+
|
|
155
|
+
const result = await parseContextFromPrompt("prompt with @context1");
|
|
156
|
+
|
|
157
|
+
expect(result.contextBlocks).toHaveLength(1);
|
|
158
|
+
expect(result.attachmentBlocks).toHaveLength(1);
|
|
159
|
+
expect(result.attachmentBlocks[0]).toEqual({
|
|
160
|
+
type: "resource_link",
|
|
161
|
+
uri: "http://example.com/file.pdf",
|
|
162
|
+
mimeType: "application/pdf",
|
|
163
|
+
name: "file.pdf",
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it("should handle empty context string gracefully", async () => {
|
|
168
|
+
mockRegistry.parseAllContextIds.mockReturnValue([CONTEXT_ID]);
|
|
169
|
+
mockRegistry.formatContextForAI.mockReturnValue(" ");
|
|
170
|
+
mockRegistry.getAttachmentsForContext.mockResolvedValue([]);
|
|
171
|
+
|
|
172
|
+
const result = await parseContextFromPrompt("prompt with @context1");
|
|
173
|
+
|
|
174
|
+
expect(result.contextBlocks).toHaveLength(0);
|
|
175
|
+
expect(result.attachmentBlocks).toHaveLength(0);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it("should handle registry errors gracefully", async () => {
|
|
179
|
+
vi.mocked(getAIContextRegistry).mockImplementation(() => {
|
|
180
|
+
throw new Error("Registry error");
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const result = await parseContextFromPrompt("prompt with @context1");
|
|
184
|
+
|
|
185
|
+
expect(result).toEqual({
|
|
186
|
+
contextBlocks: [],
|
|
187
|
+
attachmentBlocks: [],
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it("should handle attachment errors gracefully", async () => {
|
|
192
|
+
mockRegistry.parseAllContextIds.mockReturnValue([CONTEXT_ID]);
|
|
193
|
+
mockRegistry.formatContextForAI.mockReturnValue("formatted context");
|
|
194
|
+
mockRegistry.getAttachmentsForContext.mockRejectedValue(
|
|
195
|
+
new Error("Attachment error"),
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
const result = await parseContextFromPrompt("prompt with @context1");
|
|
199
|
+
|
|
200
|
+
expect(result.contextBlocks).toHaveLength(1);
|
|
201
|
+
expect(result.attachmentBlocks).toHaveLength(0);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it("should use url as name when filename is not provided", async () => {
|
|
205
|
+
mockRegistry.parseAllContextIds.mockReturnValue([CONTEXT_ID]);
|
|
206
|
+
mockRegistry.formatContextForAI.mockReturnValue("formatted context");
|
|
207
|
+
mockRegistry.getAttachmentsForContext.mockResolvedValue([
|
|
208
|
+
{
|
|
209
|
+
type: "file",
|
|
210
|
+
url: "http://example.com/file.pdf",
|
|
211
|
+
mediaType: "application/pdf",
|
|
212
|
+
filename: undefined,
|
|
213
|
+
},
|
|
214
|
+
]);
|
|
215
|
+
|
|
216
|
+
const result = await parseContextFromPrompt("prompt with @context1");
|
|
217
|
+
|
|
218
|
+
expect((result.attachmentBlocks[0] as { name: string }).name).toBe(
|
|
219
|
+
"http://example.com/file.pdf",
|
|
220
|
+
);
|
|
221
|
+
});
|
|
222
|
+
});
|
|
@@ -604,17 +604,13 @@ describe("state utility functions", () => {
|
|
|
604
604
|
describe("getAgentConnectionCommand", () => {
|
|
605
605
|
it("should return correct command for claude", () => {
|
|
606
606
|
expect(getAgentConnectionCommand("claude")).toMatchInlineSnapshot(`
|
|
607
|
-
"npx
|
|
608
|
-
"npx @zed-industries/claude-code-acp" \\
|
|
609
|
-
--outputTransport ws --port 3017 "
|
|
607
|
+
"npx stdio-to-ws "npx @zed-industries/claude-code-acp" --port 3017"
|
|
610
608
|
`);
|
|
611
609
|
});
|
|
612
610
|
|
|
613
611
|
it("should return correct command for gemini", () => {
|
|
614
612
|
expect(getAgentConnectionCommand("gemini")).toMatchInlineSnapshot(`
|
|
615
|
-
"npx
|
|
616
|
-
"npx @google/gemini-cli --experimental-acp" \\
|
|
617
|
-
--outputTransport ws --port 3019 "
|
|
613
|
+
"npx stdio-to-ws "npx @google/gemini-cli --experimental-acp" --port 3019"
|
|
618
614
|
`);
|
|
619
615
|
});
|
|
620
616
|
});
|