@marimo-team/islands 0.23.17-dev5 → 0.23.17-dev51
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-f9vLjQiF.js → ConnectedDataExplorerComponent-BBZRR1-C.js} +16 -16
- package/dist/{ErrorBoundary-DhLqwSWA.js → ErrorBoundary-CptUP1_t.js} +3 -3
- package/dist/{ImageComparisonComponent-C3y7L3eJ.js → ImageComparisonComponent-fdY0LUuf.js} +38 -27
- package/dist/{any-language-editor-DifE-fl7.js → any-language-editor-BnIS9Mvg.js} +21 -21
- package/dist/assets/__vite-browser-external-DUz7vvG0.js +1 -0
- package/dist/assets/{worker-CDEKsBgX.js → worker-Cl1KJG2_.js} +2 -2
- package/dist/{button-BSm8IXRU.js → button-8aS1QcHr.js} +4 -2
- package/dist/{capabilities-C6OQ5YBO.js → capabilities-D23lgbH_.js} +1 -1
- package/dist/chat-ui-DH9M4L-6.js +8620 -0
- package/dist/{check-DP7U0H0G.js → check-D-BaEjEh.js} +1 -1
- package/dist/{common-uMQbwoo-.js → common-Cpr8ff_v.js} +787 -794
- package/dist/{copy-DNq8q3zw.js → copy-BkA3t7rJ.js} +2 -2
- package/dist/{defaultLocale-BpsHxBd7.js → defaultLocale-BsfI21d_.js} +26 -26
- package/dist/{diagram-PSM6KHXK-D4ftnaua.js → diagram-PSM6KHXK-D3GsWNcQ.js} +1 -1
- package/dist/{dist-BpeiYpG2.js → dist-3F1XDSYc.js} +2 -2
- package/dist/{dist-BP39XM8u.js → dist-B-upo54Y.js} +1 -1
- package/dist/{dist-BdqCKQ1O.js → dist-B1W12VzQ.js} +2 -2
- package/dist/{dist-3NCqaWjt.js → dist-B6au1PTB.js} +2 -2
- package/dist/{dist-BCX980mU.js → dist-BDwn6WIk.js} +2 -2
- package/dist/dist-BDzVdDo2.js +8 -0
- package/dist/dist-BH2Tq1S1.js +5 -0
- package/dist/dist-BIhBrzqg.js +5 -0
- package/dist/dist-BLCsvXTG.js +6 -0
- package/dist/dist-BNVtuFdd.js +5 -0
- package/dist/{dist-BOMbzyb3.js → dist-BNeJ8P-4.js} +3 -3
- package/dist/{dist-LpLXa9Lb.js → dist-BT96P4oR.js} +2 -2
- package/dist/{dist-CFSb_Frm.js → dist-BUzxp0Xf.js} +1 -1
- package/dist/{dist-JGehap7K.js → dist-Ba1WFESi.js} +5 -5
- package/dist/{dist-CRmSjkLP.js → dist-BrKNRKC8.js} +2 -2
- package/dist/{dist-BIEt-mlF.js → dist-BvvQUFUK.js} +1 -1
- package/dist/{dist-pqIwD8VD.js → dist-BxR-g1ig.js} +2 -2
- package/dist/{dist-DviUBLUp.js → dist-CBraAJlT.js} +4 -4
- package/dist/{dist-BwhXBwMd.js → dist-CDBV8n0I.js} +1 -1
- package/dist/dist-CEJ6Lowi.js +5 -0
- package/dist/{dist-CSlJrrAw.js → dist-CWYsUX_O.js} +1 -1
- package/dist/dist-CYoa69kj.js +5 -0
- package/dist/{dist-DwqBJ014.js → dist-CdKRSqjo.js} +2 -2
- package/dist/{dist-De-GlSAs.js → dist-Cds3J-EE.js} +15 -9
- package/dist/dist-CnZyXFq3.js +8 -0
- package/dist/dist-D-4HaJKc.js +8 -0
- package/dist/{dist-C18MUWTj.js → dist-D8SX5Qdg.js} +2 -2
- package/dist/{dist-D3bdsa1d.js → dist-DFQ91Mx1.js} +4 -4
- package/dist/{dist-BPF0gTRZ.js → dist-DGQmYLkh.js} +1 -1
- package/dist/dist-DIGyvbg3.js +5 -0
- package/dist/dist-DIhaoCL4.js +5 -0
- package/dist/{dist-C2_4pjuS.js → dist-DK0pFEmc.js} +1 -1
- package/dist/{dist-BTdrwBWP.js → dist-DQj6A7SE.js} +3 -3
- package/dist/{dist-DIlxLFXG.js → dist-DarwE0ZO.js} +11 -11
- package/dist/{dist-DaXJPX8F.js → dist-Ddb0VFS3.js} +2 -2
- package/dist/dist-DgQleHmb.js +8 -0
- package/dist/{dist-DZwp8lro.js → dist-DgkJegw4.js} +3 -3
- package/dist/{dist-BO5HRiTf.js → dist-DjQRRttD.js} +2 -2
- package/dist/dist-JaoLzZmt.js +5 -0
- package/dist/{dist-B2jmQKKx.js → dist-PQDZaPEo.js} +2 -2
- package/dist/{dist-4lVIWUT3.js → dist-Slyx4vXr.js} +1 -1
- package/dist/{dist-DgjbnqTg.js → dist-dJeh9ef4.js} +2 -2
- package/dist/dist-pEX0t8ZV.js +6 -0
- package/dist/{dist-w_gWE5Wf.js → dist-v-kAJfMq.js} +1 -1
- package/dist/{error-banner-B_GQfxxD.js → error-banner-dmkbm21G.js} +2 -2
- package/dist/{esm-DzZVaNH4.js → esm-D8vjxCyn.js} +3 -3
- package/dist/{esm-DKOv6P2M.js → esm-DEQU-LUO.js} +22 -22
- package/dist/{extends-w2_S44bV.js → extends-DDOVg_b8.js} +5 -5
- package/dist/{formats-Dwq1THhr.js → formats-xc-Msa0v.js} +3 -3
- package/dist/{ganttDiagram-JELNMOA3-CXXCN7LN.js → ganttDiagram-JELNMOA3-D9ywbC5g.js} +3 -3
- package/dist/{glide-data-editor-B9K6-MJe.js → glide-data-editor-CyTNN635.js} +9 -9
- package/dist/{html-to-image-CZ1kLKkq.js → html-to-image-DrEMf75G.js} +2219 -2209
- package/dist/{input-C5K4VBvB.js → input-B4ddy8Jy.js} +6 -6
- package/dist/{label-DjoYwqmj.js → label-CzHncDvS.js} +2 -2
- package/dist/{linear-H_vsH9ao.js → linear-BOGUO8Cd.js} +2 -2
- package/dist/{loader-DX-IDS7n.js → loader-C7SfUdOD.js} +10 -10
- package/dist/main.js +729 -733
- package/dist/{mermaid-hv6IckVw.js → mermaid-CpL8y5pw.js} +9 -9
- package/dist/{precisionRound-CnHPY_5v.js → precisionRound-Bq50n-bG.js} +1 -1
- package/dist/{process-output-bVfbcx5_.js → process-output-B9IHQuus.js} +3 -3
- package/dist/{quadrantDiagram-AYHSOK5B-BqDB9tUC.js → quadrantDiagram-AYHSOK5B-DD4QBdSv.js} +2 -2
- package/dist/{react-vega-BazDALhI.js → react-vega-Cg17A6rO.js} +8017 -4777
- package/dist/{reveal-component-CtEDksOP.js → reveal-component-8WsJKKhA.js} +19 -19
- package/dist/{spec-CQKL_D3i.js → spec-uMKNvMRZ.js} +4 -4
- package/dist/{strings-DIy9NAmg.js → strings-BUDhKc0s.js} +299 -299
- package/dist/style.css +1 -1
- package/dist/{swiper-component-CEdAYjb7.js → swiper-component-dl1H9WIW.js} +2 -2
- package/dist/{time-Bb4A7I6a.js → time-CQnohwNM.js} +2 -2
- package/dist/{toDate-CqhBWjvO.js → toDate-BiyuFdnT.js} +3 -3
- package/dist/{tooltip-f-qR0iUx.js → tooltip-CkCWlUOf.js} +3 -3
- package/dist/{types-DicH9Oi0.js → types-uENjXsiC.js} +1 -1
- package/dist/{useAsyncData-CYOQb1EW.js → useAsyncData-DqJLysjC.js} +1 -1
- package/dist/{useDateFormatter-Bt2qr_XS.js → useDateFormatter-BeN3fHMH.js} +2 -2
- package/dist/{useDeepCompareMemoize-CSHQeQEI.js → useDeepCompareMemoize-CvlmHAmy.js} +1 -1
- package/dist/{useIframeCapabilities-kA09ykNq.js → useIframeCapabilities-pLC-ceDP.js} +1 -1
- package/dist/{useLifecycle-CMJGqBaY.js → useLifecycle-D4N02q82.js} +3 -3
- package/dist/{useTheme-DYfby65G.js → useTheme-Dur2p3iC.js} +4 -3
- package/dist/{vega-component-C7nO5zcf.js → vega-component-Cft458Sr.js} +13 -13
- package/dist/{vega-embed-container-DAQAMjiO.js → vega-embed-container-S9gRu4TZ.js} +3 -3
- package/dist/{vega-loader.browser-DmpxCJqx.js → vega-loader.browser-CmLRI3wk.js} +104 -111
- package/dist/{xychartDiagram-PRI3JC2R-BSkNxYNw.js → xychartDiagram-PRI3JC2R-D9DgEtKn.js} +2 -2
- package/dist/{zod-BUcyXSdR.js → zod-yTHHzJiv.js} +37 -38
- package/package.json +7 -7
- package/src/__mocks__/requests.ts +5 -0
- package/src/components/ai/ai-model-dropdown.tsx +2 -2
- package/src/components/app-config/user-config-form.tsx +26 -0
- package/src/components/chat/__tests__/capabilities-popover.test.tsx +38 -0
- package/src/components/chat/__tests__/chat-abort.test.ts +51 -0
- package/src/components/chat/__tests__/use-chat-controller-id.test.tsx +39 -0
- package/src/components/chat/capabilities-popover.tsx +68 -0
- package/src/components/chat/chat-abort.ts +44 -0
- package/src/components/chat/chat-display.tsx +17 -0
- package/src/components/chat/chat-panel.tsx +107 -50
- package/src/components/chat/chat-utils.ts +1 -1
- package/src/components/chat/use-chat-controller-id.ts +24 -0
- package/src/components/data-table/charts/__tests__/schemas.test.ts +35 -0
- package/src/components/data-table/charts/schemas.ts +5 -2
- package/src/components/databases/icons/huggingface.svg +8 -0
- package/src/components/datasets/__tests__/icons.test.ts +21 -0
- package/src/components/datasets/icons.tsx +10 -0
- package/src/components/datasources/__tests__/components.test.tsx +29 -0
- package/src/components/datasources/components.tsx +8 -3
- package/src/components/editor/actions/export-dialog/__tests__/export-dialog.test.tsx +35 -0
- package/src/components/editor/actions/export-dialog/__tests__/state.test.ts +61 -0
- package/src/components/editor/actions/export-dialog/export-command.ts +4 -9
- package/src/components/editor/actions/export-dialog/export-dialog.tsx +27 -23
- package/src/components/editor/actions/export-dialog/format-notice.tsx +28 -0
- package/src/components/editor/actions/export-dialog/state.ts +23 -5
- package/src/components/editor/actions/export-dialog/use-export-dialog.ts +7 -15
- package/src/components/editor/actions/useConfigActions.tsx +24 -0
- package/src/components/editor/ai/add-cell-with-ai.tsx +2 -2
- package/src/components/editor/ai/ai-completion-editor.tsx +2 -2
- package/src/components/editor/chrome/panels/outline/__tests__/useActiveOutline.test.ts +26 -0
- package/src/components/editor/chrome/wrapper/useOpenAiAssistant.ts +2 -2
- package/src/components/editor/code/__tests__/readonly-python-code.test.tsx +114 -2
- package/src/components/editor/connections/storage/__tests__/__snapshots__/as-code.test.ts.snap +14 -0
- package/src/components/editor/connections/storage/__tests__/as-code.test.ts +20 -0
- package/src/components/editor/connections/storage/add-storage-form.tsx +10 -0
- package/src/components/editor/connections/storage/as-code.ts +19 -1
- package/src/components/editor/connections/storage/schemas.ts +19 -0
- package/src/components/storage/__tests__/storage-snippets.test.ts +88 -0
- package/src/components/storage/components.tsx +2 -0
- package/src/components/storage/storage-snippets.ts +58 -0
- package/src/core/__tests__/mode.test.ts +85 -0
- package/src/core/ai/__tests__/config.test.tsx +96 -0
- package/src/core/ai/config.ts +16 -22
- package/src/core/ai/constants.ts +7 -0
- package/src/core/ai/staged-cells.ts +3 -0
- package/src/core/ai/state.ts +10 -0
- package/src/core/codemirror/__tests__/setup.test.ts +1 -1
- package/src/core/codemirror/cm.ts +2 -2
- package/src/core/codemirror/code-lens/__tests__/extension.test.ts +4 -5
- package/src/core/codemirror/code-lens/extension.ts +3 -3
- package/src/core/codemirror/find-replace/search-highlight.ts +8 -3
- package/src/core/codemirror/language/languages/sql/__tests__/renderers.test.tsx +44 -0
- package/src/core/codemirror/language/languages/sql/renderers.tsx +3 -3
- package/src/core/codemirror/theme/__tests__/light.test.ts +59 -123
- package/src/core/codemirror/theme/dark.ts +6 -5
- package/src/core/codemirror/theme/light.ts +6 -4
- package/src/core/config/__tests__/config-schema.test.ts +2 -0
- package/src/core/config/config-schema.ts +1 -0
- package/src/core/config/feature-flag.tsx +0 -2
- package/src/core/dom/outline.ts +25 -2
- package/src/core/hotkeys/hotkeys.ts +2 -0
- package/src/core/mode.ts +11 -9
- package/src/core/network/__tests__/api.test.ts +132 -17
- package/src/core/network/__tests__/export-filename.test.ts +43 -0
- package/src/core/network/__tests__/requests-lazy.test.ts +1 -0
- package/src/core/network/api.ts +47 -12
- package/src/core/network/export-filename.ts +37 -0
- package/src/core/network/markdown-export.ts +19 -0
- package/src/core/network/requests-network.ts +29 -5
- package/src/core/network/types.ts +4 -0
- package/src/core/storage/types.ts +1 -0
- package/src/css/app/codemirror.css +11 -7
- package/src/css/globals.css +8 -0
- package/src/plugins/impl/DataEditorPlugin.tsx +2 -9
- package/src/plugins/impl/__tests__/DataEditorPlugin.test.ts +18 -0
- package/src/plugins/impl/chat/chat-ui.tsx +51 -3
- package/src/plugins/impl/data-frames/__tests__/schema.test.ts +30 -0
- package/src/plugins/impl/data-frames/schema.ts +4 -1
- package/src/plugins/impl/image-comparison/ImageComparisonComponent.tsx +20 -1
- package/src/plugins/impl/image-comparison/__tests__/ImageComparisonComponent.test.tsx +58 -0
- package/src/stories/theme.stories.tsx +71 -1
- package/dist/assets/__vite-browser-external-DltVXDv7.js +0 -1
- package/dist/chat-ui-DsZJj75A.js +0 -8369
- package/dist/dist-5o9uz19x.js +0 -5
- package/dist/dist-BpDH0DEP.js +0 -5
- package/dist/dist-BpXhbQXO.js +0 -8
- package/dist/dist-ByadAk_L.js +0 -5
- package/dist/dist-CK0g7JvP.js +0 -8
- package/dist/dist-CfzCtiq2.js +0 -5
- package/dist/dist-D8JtVFrW.js +0 -8
- package/dist/dist-DK2Oocpu.js +0 -6
- package/dist/dist-DnSu977r.js +0 -5
- package/dist/dist-UmxJm6sW.js +0 -8
- package/dist/dist-aYSBQSyM.js +0 -5
- package/dist/dist-kkZ1Ztlh.js +0 -6
- package/dist/dist-ngPczwNi.js +0 -5
- package/dist/dist-wQRyxyTV.js +0 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@marimo-team/islands",
|
|
3
|
-
"version": "0.23.17-
|
|
3
|
+
"version": "0.23.17-dev51",
|
|
4
4
|
"main": "dist/main.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"./unstable_internal/*": "./src/*"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@ai-sdk/react": "^
|
|
24
|
+
"@ai-sdk/react": "^4.0.40",
|
|
25
25
|
"@anywidget/types": "^0.4.0",
|
|
26
26
|
"@codemirror/autocomplete": "^6.20.1",
|
|
27
27
|
"@codemirror/commands": "^6.10.2",
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"@xterm/addon-web-links": "^0.12.0",
|
|
93
93
|
"@xterm/xterm": "^5.5.0",
|
|
94
94
|
"@zed-industries/agent-client-protocol": "^0.4.5",
|
|
95
|
-
"ai": "^
|
|
95
|
+
"ai": "^7.0.37",
|
|
96
96
|
"ansi_up": "^6.0.6",
|
|
97
97
|
"class-variance-authority": "^0.7.1",
|
|
98
98
|
"clsx": "^2.1.1",
|
|
@@ -195,7 +195,7 @@
|
|
|
195
195
|
"@babel/preset-typescript": "^7.25.9",
|
|
196
196
|
"@codecov/vite-plugin": "^1.9.1",
|
|
197
197
|
"@csstools/postcss-light-dark-function": "^2.0.11",
|
|
198
|
-
"@playwright/test": "^1.
|
|
198
|
+
"@playwright/test": "^1.62.1",
|
|
199
199
|
"@storybook/addon-docs": "^10.3.5",
|
|
200
200
|
"@storybook/addon-links": "^10.3.5",
|
|
201
201
|
"@storybook/react-vite": "^10.3.5",
|
|
@@ -211,7 +211,7 @@
|
|
|
211
211
|
"@types/react-dom": "^19.2.3",
|
|
212
212
|
"@types/timestring": "^6.0.5",
|
|
213
213
|
"@vitejs/plugin-react": "^5.2.0",
|
|
214
|
-
"@vitest/coverage-v8": "3.2.
|
|
214
|
+
"@vitest/coverage-v8": "3.2.7",
|
|
215
215
|
"babel-plugin-react-compiler": "19.1.0-rc.3",
|
|
216
216
|
"blob-polyfill": "^7.0.20220408",
|
|
217
217
|
"cross-env": "^7.0.3",
|
|
@@ -228,11 +228,11 @@
|
|
|
228
228
|
"storybook": "^10.3.5",
|
|
229
229
|
"stylelint": "^16.26.1",
|
|
230
230
|
"stylelint-config-standard": "^36.0.1",
|
|
231
|
-
"tailwindcss": "^4.3.
|
|
231
|
+
"tailwindcss": "^4.3.3",
|
|
232
232
|
"vega-typings": "^2.1.0",
|
|
233
233
|
"vite": "npm:rolldown-vite@7.3.1",
|
|
234
234
|
"vite-plugin-top-level-await": "^1.6.0",
|
|
235
235
|
"vite-plugin-wasm": "^3.6.0",
|
|
236
|
-
"vitest": "^3.2.
|
|
236
|
+
"vitest": "^3.2.7"
|
|
237
237
|
}
|
|
238
238
|
}
|
|
@@ -72,26 +72,31 @@ export const MockRequestClient = {
|
|
|
72
72
|
format: "html",
|
|
73
73
|
dependenciesAvailable: true,
|
|
74
74
|
missingPackages: [],
|
|
75
|
+
missingSetup: [],
|
|
75
76
|
},
|
|
76
77
|
{
|
|
77
78
|
format: "markdown",
|
|
78
79
|
dependenciesAvailable: true,
|
|
79
80
|
missingPackages: [],
|
|
81
|
+
missingSetup: [],
|
|
80
82
|
},
|
|
81
83
|
{
|
|
82
84
|
format: "ipynb",
|
|
83
85
|
dependenciesAvailable: true,
|
|
84
86
|
missingPackages: [],
|
|
87
|
+
missingSetup: [],
|
|
85
88
|
},
|
|
86
89
|
{
|
|
87
90
|
format: "pdf",
|
|
88
91
|
dependenciesAvailable: true,
|
|
89
92
|
missingPackages: [],
|
|
93
|
+
missingSetup: [],
|
|
90
94
|
},
|
|
91
95
|
{
|
|
92
96
|
format: "script",
|
|
93
97
|
dependenciesAvailable: true,
|
|
94
98
|
missingPackages: [],
|
|
99
|
+
missingSetup: [],
|
|
95
100
|
},
|
|
96
101
|
],
|
|
97
102
|
}),
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
CircleHelpIcon,
|
|
10
10
|
} from "lucide-react";
|
|
11
11
|
import React from "react";
|
|
12
|
-
import { type SupportedRole,
|
|
12
|
+
import { type SupportedRole, useAIConfigActions } from "@/core/ai/config";
|
|
13
13
|
import {
|
|
14
14
|
AiModelId,
|
|
15
15
|
isKnownAIProvider,
|
|
@@ -64,7 +64,7 @@ export const AIModelDropdown = ({
|
|
|
64
64
|
|
|
65
65
|
const ai = useAtomValue(aiAtom);
|
|
66
66
|
const completion = useAtomValue(completionAtom);
|
|
67
|
-
const { saveModelChange } =
|
|
67
|
+
const { saveModelChange } = useAIConfigActions();
|
|
68
68
|
const { handleClick } = useOpenSettingsToTab();
|
|
69
69
|
|
|
70
70
|
// Only include autocompleteModel if copilot is set to "custom"
|
|
@@ -985,6 +985,32 @@ export const UserConfigForm: React.FC = () => {
|
|
|
985
985
|
</div>
|
|
986
986
|
)}
|
|
987
987
|
/>
|
|
988
|
+
<OverriddenFormField
|
|
989
|
+
control={form.control}
|
|
990
|
+
name="display.code_lens"
|
|
991
|
+
render={({ field, override }) => (
|
|
992
|
+
<div className="flex flex-col space-y-1">
|
|
993
|
+
<FormItem className={formItemClasses}>
|
|
994
|
+
<FormLabel>Code lens</FormLabel>
|
|
995
|
+
<FormControl>
|
|
996
|
+
<Checkbox
|
|
997
|
+
data-testid="code-lens-checkbox"
|
|
998
|
+
checked={override.value}
|
|
999
|
+
disabled={override.isOverridden}
|
|
1000
|
+
onCheckedChange={field.onChange}
|
|
1001
|
+
/>
|
|
1002
|
+
</FormControl>
|
|
1003
|
+
<FormMessage />
|
|
1004
|
+
<IsOverridden override={override} />
|
|
1005
|
+
</FormItem>
|
|
1006
|
+
|
|
1007
|
+
<FormDescription>
|
|
1008
|
+
Shows inline icons in cell editors for datasources and
|
|
1009
|
+
storage buckets.
|
|
1010
|
+
</FormDescription>
|
|
1011
|
+
</div>
|
|
1012
|
+
)}
|
|
1013
|
+
/>
|
|
988
1014
|
</SettingGroup>
|
|
989
1015
|
<SettingGroup title="Outputs">
|
|
990
1016
|
<OverriddenFormField
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
4
|
+
import { createStore, Provider } from "jotai";
|
|
5
|
+
import { describe, expect, it } from "vitest";
|
|
6
|
+
import { chatOptionsAtom } from "@/core/ai/state";
|
|
7
|
+
import { CapabilitiesPopover } from "../capabilities-popover";
|
|
8
|
+
|
|
9
|
+
describe("CapabilitiesPopover", () => {
|
|
10
|
+
it("updates ephemeral chat options", () => {
|
|
11
|
+
const store = createStore();
|
|
12
|
+
|
|
13
|
+
render(
|
|
14
|
+
<Provider store={store}>
|
|
15
|
+
<CapabilitiesPopover />
|
|
16
|
+
</Provider>,
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const trigger = screen.getByRole("button", { name: "Capabilities" });
|
|
20
|
+
expect(trigger).not.toHaveAttribute("data-active");
|
|
21
|
+
|
|
22
|
+
fireEvent.click(trigger);
|
|
23
|
+
const webSearch = screen.getByRole("switch", { name: "Web search" });
|
|
24
|
+
expect(webSearch).not.toBeChecked();
|
|
25
|
+
|
|
26
|
+
fireEvent.click(webSearch);
|
|
27
|
+
|
|
28
|
+
expect(webSearch).toBeChecked();
|
|
29
|
+
expect(trigger).toHaveAttribute("data-active", "true");
|
|
30
|
+
expect(trigger).toHaveClass("bg-primary/15", "text-primary", "ring-1");
|
|
31
|
+
expect(trigger).toHaveAttribute(
|
|
32
|
+
"title",
|
|
33
|
+
"Capabilities (web search enabled)",
|
|
34
|
+
);
|
|
35
|
+
expect(store.get(chatOptionsAtom)).toEqual({ webSearch: true });
|
|
36
|
+
expect(createStore().get(chatOptionsAtom)).toEqual({ webSearch: false });
|
|
37
|
+
});
|
|
38
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import type { UIMessageChunk } from "ai";
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
import {
|
|
6
|
+
describeChatAbortReason,
|
|
7
|
+
getAbortReasonFromChunk,
|
|
8
|
+
resolveChatAbortReason,
|
|
9
|
+
USER_CANCELLED_ABORT_REASON,
|
|
10
|
+
} from "../chat-abort";
|
|
11
|
+
|
|
12
|
+
describe("chat-abort", () => {
|
|
13
|
+
it("reads reason from abort chunks", () => {
|
|
14
|
+
const chunk: UIMessageChunk = {
|
|
15
|
+
type: "abort",
|
|
16
|
+
reason: USER_CANCELLED_ABORT_REASON,
|
|
17
|
+
};
|
|
18
|
+
expect(getAbortReasonFromChunk(chunk)).toBe(USER_CANCELLED_ABORT_REASON);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("returns undefined for non-abort chunks", () => {
|
|
22
|
+
expect(
|
|
23
|
+
getAbortReasonFromChunk({ type: "text-delta", id: "1", delta: "hi" }),
|
|
24
|
+
).toBeUndefined();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("defaults client-side aborts to user_cancelled", () => {
|
|
28
|
+
expect(resolveChatAbortReason({ isAbort: true, streamReason: null })).toBe(
|
|
29
|
+
USER_CANCELLED_ABORT_REASON,
|
|
30
|
+
);
|
|
31
|
+
expect(resolveChatAbortReason({ isAbort: false })).toBeNull();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("preserves stream abort reasons when present", () => {
|
|
35
|
+
expect(
|
|
36
|
+
resolveChatAbortReason({ isAbort: true, streamReason: "timeout" }),
|
|
37
|
+
).toBe("timeout");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("describes known and unknown abort reasons", () => {
|
|
41
|
+
expect(describeChatAbortReason(USER_CANCELLED_ABORT_REASON)).toBe(
|
|
42
|
+
"Generation stopped",
|
|
43
|
+
);
|
|
44
|
+
expect(describeChatAbortReason("timeout")).toBe(
|
|
45
|
+
"Generation stopped: timed out",
|
|
46
|
+
);
|
|
47
|
+
expect(describeChatAbortReason("provider_reset")).toBe(
|
|
48
|
+
"Generation stopped: provider_reset",
|
|
49
|
+
);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { act, renderHook } from "@testing-library/react";
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
import type { ChatId } from "@/core/ai/state";
|
|
6
|
+
import { useChatControllerId } from "../use-chat-controller-id";
|
|
7
|
+
|
|
8
|
+
describe("useChatControllerId", () => {
|
|
9
|
+
it("creates a new ID when starting another unsaved chat", () => {
|
|
10
|
+
const activeChatId = "existing-chat" as ChatId;
|
|
11
|
+
const initialProps: { activeId: ChatId | undefined } = {
|
|
12
|
+
activeId: undefined,
|
|
13
|
+
};
|
|
14
|
+
const { result, rerender } = renderHook(
|
|
15
|
+
({ activeId }: { activeId: ChatId | undefined }) =>
|
|
16
|
+
useChatControllerId(activeId),
|
|
17
|
+
{ initialProps },
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const initialDraftId = result.current.chatControllerId;
|
|
21
|
+
rerender({ activeId: activeChatId });
|
|
22
|
+
|
|
23
|
+
expect(result.current.chatControllerId).toBe(activeChatId);
|
|
24
|
+
|
|
25
|
+
act(() => result.current.renewDraftChatId());
|
|
26
|
+
rerender({ activeId: undefined });
|
|
27
|
+
|
|
28
|
+
expect(result.current.chatControllerId).not.toBe(initialDraftId);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("renews the ID when the current chat is already unsaved", () => {
|
|
32
|
+
const { result } = renderHook(() => useChatControllerId(undefined));
|
|
33
|
+
const initialDraftId = result.current.chatControllerId;
|
|
34
|
+
|
|
35
|
+
act(() => result.current.renewDraftChatId());
|
|
36
|
+
|
|
37
|
+
expect(result.current.chatControllerId).not.toBe(initialDraftId);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import { useAtom } from "jotai";
|
|
4
|
+
import { SlidersHorizontalIcon } from "lucide-react";
|
|
5
|
+
import type React from "react";
|
|
6
|
+
import { Button } from "@/components/ui/button";
|
|
7
|
+
import {
|
|
8
|
+
Popover,
|
|
9
|
+
PopoverContent,
|
|
10
|
+
PopoverTrigger,
|
|
11
|
+
} from "@/components/ui/popover";
|
|
12
|
+
import { ExternalLink } from "@/components/ui/links";
|
|
13
|
+
import { Switch } from "@/components/ui/switch";
|
|
14
|
+
import { chatOptionsAtom } from "@/core/ai/state";
|
|
15
|
+
import { cn } from "@/utils/cn";
|
|
16
|
+
|
|
17
|
+
export const CapabilitiesPopover: React.FC = () => {
|
|
18
|
+
const [chatOptions, setChatOptions] = useAtom(chatOptionsAtom);
|
|
19
|
+
const webSearchOn = chatOptions.webSearch;
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<Popover>
|
|
23
|
+
<PopoverTrigger asChild={true}>
|
|
24
|
+
<Button
|
|
25
|
+
aria-label="Capabilities"
|
|
26
|
+
title={
|
|
27
|
+
webSearchOn ? "Capabilities (web search enabled)" : "Capabilities"
|
|
28
|
+
}
|
|
29
|
+
data-active={webSearchOn || undefined}
|
|
30
|
+
variant="text"
|
|
31
|
+
size="icon"
|
|
32
|
+
className={cn(
|
|
33
|
+
"h-6 w-6 shrink-0 bg-muted hover:bg-muted/30",
|
|
34
|
+
webSearchOn &&
|
|
35
|
+
"bg-primary/15 text-primary ring-1 ring-primary/30 opacity-100 hover:bg-primary/20",
|
|
36
|
+
)}
|
|
37
|
+
>
|
|
38
|
+
<SlidersHorizontalIcon className="h-3.5 w-3.5" />
|
|
39
|
+
</Button>
|
|
40
|
+
</PopoverTrigger>
|
|
41
|
+
<PopoverContent className="w-76 p-3" align="start" side="top">
|
|
42
|
+
<label
|
|
43
|
+
htmlFor="web-search-toggle"
|
|
44
|
+
className="flex items-start justify-between gap-3 cursor-pointer"
|
|
45
|
+
>
|
|
46
|
+
<div className="flex flex-col gap-0.5">
|
|
47
|
+
<span className="text-sm font-semibold">Web search</span>
|
|
48
|
+
<span className="text-xs text-muted-foreground">
|
|
49
|
+
Search the web when the model supports it.{" "}
|
|
50
|
+
<ExternalLink href="https://docs.marimo.io/guides/editor_features/tools#web-search-and-fetch">
|
|
51
|
+
Learn more
|
|
52
|
+
</ExternalLink>
|
|
53
|
+
</span>
|
|
54
|
+
</div>
|
|
55
|
+
<Switch
|
|
56
|
+
id="web-search-toggle"
|
|
57
|
+
aria-label="Web search"
|
|
58
|
+
size="sm"
|
|
59
|
+
checked={webSearchOn}
|
|
60
|
+
onCheckedChange={(webSearch) =>
|
|
61
|
+
setChatOptions((options) => ({ ...options, webSearch }))
|
|
62
|
+
}
|
|
63
|
+
/>
|
|
64
|
+
</label>
|
|
65
|
+
</PopoverContent>
|
|
66
|
+
</Popover>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import type { UIMessageChunk } from "ai";
|
|
4
|
+
|
|
5
|
+
/** Matches pydantic-ai / marimo AbortChunk.reason for user Stop. */
|
|
6
|
+
export const USER_CANCELLED_ABORT_REASON = "user_cancelled";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Read an abort reason from a UI message stream chunk, if present.
|
|
10
|
+
*/
|
|
11
|
+
export function getAbortReasonFromChunk(
|
|
12
|
+
chunk: UIMessageChunk,
|
|
13
|
+
): string | undefined {
|
|
14
|
+
if (chunk.type !== "abort") {
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
17
|
+
return chunk.reason;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Resolve the abort reason for logging/UI. Client-side Stop often never
|
|
22
|
+
* receives a server AbortChunk (the fetch is aborted first), so default to
|
|
23
|
+
* user_cancelled when the turn ended as an abort.
|
|
24
|
+
*/
|
|
25
|
+
export function resolveChatAbortReason(opts: {
|
|
26
|
+
isAbort: boolean;
|
|
27
|
+
streamReason?: string | null;
|
|
28
|
+
}): string | null {
|
|
29
|
+
if (!opts.isAbort) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return opts.streamReason || USER_CANCELLED_ABORT_REASON;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function describeChatAbortReason(reason: string): string {
|
|
36
|
+
switch (reason) {
|
|
37
|
+
case USER_CANCELLED_ABORT_REASON:
|
|
38
|
+
return "Generation stopped";
|
|
39
|
+
case "timeout":
|
|
40
|
+
return "Generation stopped: timed out";
|
|
41
|
+
default:
|
|
42
|
+
return `Generation stopped: ${reason}`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -92,6 +92,17 @@ export const renderUIMessage = ({
|
|
|
92
92
|
);
|
|
93
93
|
case "file":
|
|
94
94
|
return <AttachmentRenderer attachment={part} key={index} />;
|
|
95
|
+
case "reasoning-file":
|
|
96
|
+
return (
|
|
97
|
+
<AttachmentRenderer
|
|
98
|
+
key={index}
|
|
99
|
+
attachment={{
|
|
100
|
+
type: "file",
|
|
101
|
+
mediaType: part.mediaType,
|
|
102
|
+
url: part.url,
|
|
103
|
+
}}
|
|
104
|
+
/>
|
|
105
|
+
);
|
|
95
106
|
case "dynamic-tool":
|
|
96
107
|
return (
|
|
97
108
|
<ToolCallView
|
|
@@ -128,6 +139,12 @@ export const renderUIMessage = ({
|
|
|
128
139
|
href={part.url}
|
|
129
140
|
/>
|
|
130
141
|
);
|
|
142
|
+
case "custom":
|
|
143
|
+
return (
|
|
144
|
+
<div key={index} className="text-sm text-muted-foreground">
|
|
145
|
+
{part.kind}
|
|
146
|
+
</div>
|
|
147
|
+
);
|
|
131
148
|
case "step-start":
|
|
132
149
|
return null;
|
|
133
150
|
default:
|