@marimo-team/islands 0.19.8-dev5 → 0.19.8-dev50
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/{Combination-Bg-xN8JV.js → Combination-BTMrlhzT.js} +11 -10
- package/dist/{ConnectedDataExplorerComponent-DewsKLl2.js → ConnectedDataExplorerComponent-BAeQ8DWw.js} +11 -11
- package/dist/{ImageComparisonComponent-Bijp8beW.js → ImageComparisonComponent-DkEXPki_.js} +2 -2
- package/dist/{any-language-editor-DZc6NCTp.js → any-language-editor-D0UQItkS.js} +6 -6
- package/dist/{architectureDiagram-VXUJARFQ--NkyBn9Y.js → architectureDiagram-VXUJARFQ-DPPYVq8H.js} +4 -4
- package/dist/assets/__vite-browser-external-WSlCcXn_.js +1 -0
- package/dist/assets/worker-DUYMdbtA.js +73 -0
- package/dist/{blockDiagram-VD42YOAC-DEZZaTW0.js → blockDiagram-VD42YOAC-BA5N05Y9.js} +4 -4
- package/dist/{button-BWvsJ2Wr.js → button-Cy0ElmIm.js} +2 -2
- package/dist/{c4Diagram-YG6GDRKO-Bj7hwWCO.js → c4Diagram-YG6GDRKO-DJLzuGJJ.js} +3 -3
- package/dist/{channel-B_QrFrGg.js → channel-Dob5kWXR.js} +1 -1
- package/dist/{check-CM_kewwn.js → check-DkNR52Mm.js} +1 -1
- package/dist/{chunk-5FQGJX7Z-D5VFKHmt.js → chunk-5FQGJX7Z-BEb20Lzt.js} +3 -3
- package/dist/{chunk-ABZYJK2D-SZPYmRzN.js → chunk-ABZYJK2D-BXTC53mt.js} +1 -1
- package/dist/{chunk-ATLVNIR6-BI_WwH1o.js → chunk-ATLVNIR6-BJDjUR_c.js} +1 -1
- package/dist/{chunk-B4BG7PRW-BlI9Gm1l.js → chunk-B4BG7PRW-DzmUUpfH.js} +4 -4
- package/dist/{chunk-DI55MBZ5-BXxemMn5.js → chunk-DI55MBZ5-gTd3J8Tu.js} +4 -4
- package/dist/{chunk-EXTU4WIE-CzWtDV99.js → chunk-EXTU4WIE-DyoOs5QX.js} +1 -1
- package/dist/{chunk-JA3XYJ7Z-DQ-2ARfa.js → chunk-JA3XYJ7Z-BGnAIbOP.js} +2 -2
- package/dist/{chunk-JZLCHNYA-CVfjf2vv.js → chunk-JZLCHNYA-CIRgweVQ.js} +4 -4
- package/dist/{chunk-N4CR4FBY-BCZvQ7Jq.js → chunk-N4CR4FBY-DKSvXAIS.js} +5 -5
- package/dist/{chunk-QN33PNHL-DY_2Q2zl.js → chunk-QN33PNHL-B6zC8BTi.js} +1 -1
- package/dist/{chunk-QXUST7PY-BMCjAVR_.js → chunk-QXUST7PY-C7750n_u.js} +5 -5
- package/dist/{chunk-S3R3BYOJ-Ddu0H4Qa.js → chunk-S3R3BYOJ-CBkH6JZZ.js} +1 -1
- package/dist/{chunk-TZMSLE5B-C2wVlbMl.js → chunk-TZMSLE5B-DObGL7xi.js} +1 -1
- package/dist/{classDiagram-2ON5EDUG-D-g7zbyO.js → classDiagram-2ON5EDUG-B9pkKjjc.js} +9 -9
- package/dist/{classDiagram-v2-WZHVMYZB-C7v5zNRD.js → classDiagram-v2-WZHVMYZB-CRhhA0tV.js} +9 -9
- package/dist/{click-outside-container-BCN5BtVO.js → click-outside-container-DNfggvIW.js} +1 -1
- package/dist/{code-block-37QAKDTI-eUgXqGNG.js → code-block-37QAKDTI-u5kgjqmr.js} +2 -2
- package/dist/{compiler-runtime-DHFVbq0b.js → compiler-runtime-B_OLMU9S.js} +1 -1
- package/dist/{copy-B59Bw3-w.js → copy-DRaXIb_a.js} +3 -3
- package/dist/{dagre-6UL2VRFP-DKIPL74O.js → dagre-6UL2VRFP-C2C2XxsB.js} +6 -6
- package/dist/{data-grid-overlay-editor-COyFwFmE.js → data-grid-overlay-editor-BXqtz1ia.js} +4 -4
- package/dist/{diagram-PSM6KHXK-CVTrAZaP.js → diagram-PSM6KHXK-DHBY-94p.js} +5 -5
- package/dist/{diagram-QEK2KX5R-BqHBzu3x.js → diagram-QEK2KX5R-CgMshOwn.js} +3 -3
- package/dist/{diagram-S2PKOQOG-CJD6owcg.js → diagram-S2PKOQOG-F1KPva3Y.js} +3 -3
- package/dist/{dist-Co5PD8Fb.js → dist-BBYTEAvO.js} +1 -1
- package/dist/{erDiagram-Q2GNP2WA-CqOceSf9.js → erDiagram-Q2GNP2WA-18gGng8V.js} +9 -9
- package/dist/{error-banner-C7KLpECd.js → error-banner-D2zjeN_a.js} +5 -5
- package/dist/{esm-D4WO8J3G.js → esm-CgRNPmz8.js} +6 -6
- package/dist/{flowDiagram-NV44I4VS-K7-DUifo.js → flowDiagram-NV44I4VS-iHFiHYe0.js} +9 -9
- package/dist/{ganttDiagram-JELNMOA3-BwUFY9Nu.js → ganttDiagram-JELNMOA3-D7GixxiF.js} +2 -2
- package/dist/{gitGraphDiagram-NY62KEGX-CjGRtLb1.js → gitGraphDiagram-NY62KEGX-CJFHytRK.js} +2 -2
- package/dist/{glide-data-editor-C3T7HsLi.js → glide-data-editor-BYwb17Bf.js} +13 -13
- package/dist/{infoDiagram-WHAUD3N6-DNhmDn-6.js → infoDiagram-WHAUD3N6-B5Lkh3A9.js} +2 -2
- package/dist/{journeyDiagram-XKPGCS4Q-BOdK47P8.js → journeyDiagram-XKPGCS4Q-CV_9R9iP.js} +2 -2
- package/dist/{kanban-definition-3W4ZIXB7-A0JC9d0g.js → kanban-definition-3W4ZIXB7-Dp21D5Ym.js} +6 -6
- package/dist/{katex-DJyOeQ91.js → katex-CX2BKujk.js} +1 -1
- package/dist/{katex-Dm9nZf6A.js → katex-Db0k5oV_.js} +1 -1
- package/dist/{label-C4PtQcza.js → label-CxU5JNBW.js} +6 -6
- package/dist/main.js +2000 -1887
- package/dist/mermaid-4DMBBIKO-BhDCqnO1.js +6 -0
- package/dist/{mermaid-Bqp2Xw99.js → mermaid-B__BZSXU.js} +39 -39
- package/dist/{mhchem-BqdXeZVX.js → mhchem-w1tkUnWr.js} +1 -1
- package/dist/{mindmap-definition-VGOIOE7T-CS6nKN_L.js → mindmap-definition-VGOIOE7T-B_5mfdYp.js} +8 -8
- package/dist/{number-overlay-editor-Bz_bDJQb.js → number-overlay-editor-D-4WQAGX.js} +2 -2
- package/dist/{pieDiagram-ADFJNKIX-DSa60Grk.js → pieDiagram-ADFJNKIX-B-DGEopK.js} +3 -3
- package/dist/{quadrantDiagram-AYHSOK5B-CFnMbP2J.js → quadrantDiagram-AYHSOK5B-M_yRSIZn.js} +1 -1
- package/dist/{react-DdA8EBol.js → react-Bs6Z0kvn.js} +1 -1
- package/dist/{react-dom-DJW8xUDg.js → react-dom-CqtLRVZP.js} +2 -2
- package/dist/{react-plotly-jVjTu07w.js → react-plotly-BuRa9xtI.js} +1 -1
- package/dist/{react-vega-DgHpnZ04.js → react-vega-3WcLHYC7.js} +2 -2
- package/dist/{react-vega-CjiPWyw0.js → react-vega-DLFvGrpJ.js} +1 -1
- package/dist/{requirementDiagram-UZGBJVZJ-ytLQrFTk.js → requirementDiagram-UZGBJVZJ-9Wt82hOZ.js} +8 -8
- package/dist/{sankeyDiagram-TZEHDZUN-KQqXDoky.js → sankeyDiagram-TZEHDZUN-x_aTXZeN.js} +1 -1
- package/dist/{sequenceDiagram-WL72ISMW-ByLI04T5.js → sequenceDiagram-WL72ISMW-CXXmJqiQ.js} +3 -3
- package/dist/{slides-component-BVjvNo92.js → slides-component-Dp-y50K9.js} +4 -4
- package/dist/{spec-Dmb1KfK3.js → spec-HoYHAQo2.js} +6 -6
- package/dist/{stateDiagram-FKZM4ZOC-Dfz8vBbP.js → stateDiagram-FKZM4ZOC-CiSKS_Mx.js} +9 -9
- package/dist/{stateDiagram-v2-4FDKWEC3-DRYoLdT5.js → stateDiagram-v2-4FDKWEC3-A43Itnjp.js} +9 -9
- package/dist/style.css +1 -1
- package/dist/{timeline-definition-IT6M3QCI-CO48XU1B.js → timeline-definition-IT6M3QCI-DR26eWb4.js} +1 -1
- package/dist/{types-CzEZ3EWT.js → types-Bb-6p8hv.js} +8 -8
- package/dist/{useAsyncData-BjNwqCfS.js → useAsyncData-Dyq3DyOF.js} +3 -3
- package/dist/{useDeepCompareMemoize-CfoxVor3.js → useDeepCompareMemoize-BhZZsis0.js} +12 -8
- package/dist/{useIframeCapabilities-BBO_R0ww.js → useIframeCapabilities-DurI5SJh.js} +2 -2
- package/dist/{useTheme-BYG2SH8J.js → useTheme-SlKl8MlS.js} +5 -6
- package/dist/{vega-component-rDX7xwxH.js → vega-component-DCxUyPnb.js} +10 -10
- package/dist/{xychartDiagram-PRI3JC2R-CUIfjNVD.js → xychartDiagram-PRI3JC2R-BcVxCRox.js} +4 -4
- package/dist/{zod-DITCj31F.js → zod-bjADtMKr.js} +3 -3
- package/package.json +18 -18
- package/src/components/app-config/ai-config.tsx +11 -2
- package/src/components/app-config/optional-features.tsx +1 -1
- package/src/components/app-config/user-config-form.tsx +0 -54
- package/src/components/chat/__tests__/useFileState.test.tsx +93 -0
- package/src/components/chat/acp/__tests__/state.test.ts +69 -0
- package/src/components/chat/acp/agent-panel.tsx +26 -77
- package/src/components/chat/acp/state.ts +6 -6
- package/src/components/chat/chat-components.tsx +114 -1
- package/src/components/chat/chat-panel.tsx +79 -134
- package/src/components/chat/chat-utils.ts +42 -0
- package/src/components/data-table/__tests__/data-table.test.tsx +94 -2
- package/src/components/editor/actions/useCellActionButton.tsx +14 -1
- package/src/components/editor/ai/add-cell-with-ai.tsx +85 -53
- package/src/components/editor/ai/ai-completion-editor.tsx +15 -38
- package/src/components/editor/cell/CreateCellButton.tsx +2 -1
- package/src/components/editor/cell/code/cell-editor.tsx +12 -0
- package/src/components/editor/chrome/panels/packages-panel.tsx +12 -9
- package/src/components/editor/database/__tests__/__snapshots__/as-code.test.ts.snap +15 -0
- package/src/components/editor/database/__tests__/as-code.test.ts +8 -0
- package/src/components/editor/database/as-code.ts +3 -0
- package/src/components/editor/database/schemas.ts +9 -0
- package/src/components/editor/renderers/cell-array.tsx +2 -1
- package/src/components/editor/renderers/vertical-layout/vertical-layout.tsx +12 -0
- package/src/components/pages/gallery-page.tsx +37 -6
- package/src/core/MarimoApp.tsx +12 -8
- package/src/core/ai/context/providers/file.ts +1 -1
- package/src/core/cells/__tests__/cells.test.ts +120 -0
- package/src/core/cells/__tests__/session.test.ts +37 -1
- package/src/core/cells/cells.ts +14 -0
- package/src/core/cells/session.ts +20 -8
- package/src/core/codemirror/language/languages/markdown.ts +7 -0
- package/src/core/config/feature-flag.tsx +0 -4
- package/src/core/dom/uiregistry.ts +4 -1
- package/src/core/islands/__tests__/bridge.test.ts +7 -2
- package/src/core/islands/bridge.ts +1 -1
- package/src/core/islands/main.ts +7 -0
- package/src/core/network/types.ts +2 -2
- package/src/core/run-app.tsx +11 -4
- package/src/core/static/__tests__/files.test.ts +195 -1
- package/src/core/static/files.ts +39 -9
- package/src/core/wasm/bridge.ts +1 -1
- package/src/core/websocket/useMarimoKernelConnection.tsx +5 -15
- package/src/plugins/core/registerReactComponent.tsx +9 -1
- package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +164 -0
- package/src/plugins/impl/anywidget/AnyWidgetPlugin.tsx +93 -168
- package/src/plugins/impl/anywidget/__tests__/AnyWidgetPlugin.test.tsx +37 -123
- package/src/plugins/impl/anywidget/__tests__/model.test.ts +128 -122
- package/src/{utils/__tests__/data-views.test.ts → plugins/impl/anywidget/__tests__/serialization.test.ts} +42 -96
- package/src/plugins/impl/anywidget/model.ts +348 -223
- package/src/plugins/impl/anywidget/schemas.ts +32 -0
- package/src/{utils/data-views.ts → plugins/impl/anywidget/serialization.ts} +13 -36
- package/src/plugins/impl/anywidget/types.ts +27 -0
- package/src/plugins/impl/chat/chat-ui.tsx +22 -20
- package/src/utils/Deferred.ts +21 -0
- package/src/utils/__tests__/blob.test.ts +3 -3
- package/src/utils/__tests__/id-tree.test.ts +22 -7
- package/src/utils/__tests__/mime-types.test.ts +8 -10
- package/src/utils/__tests__/url-parser.test.ts +22 -0
- package/src/utils/blob.ts +14 -27
- package/src/utils/id-tree.tsx +11 -19
- package/src/utils/json/base64.ts +38 -8
- package/src/utils/mime-types.ts +5 -5
- package/src/utils/url-parser.ts +1 -1
- package/dist/assets/__vite-browser-external-DRa9CT_O.js +0 -1
- package/dist/assets/worker-SqntmiwV.js +0 -73
- package/dist/mermaid-4DMBBIKO-o3xNphpD.js +0 -6
|
@@ -21,8 +21,6 @@ import { atomWithStorage } from "jotai/utils";
|
|
|
21
21
|
import {
|
|
22
22
|
ChevronsUpDown,
|
|
23
23
|
DatabaseIcon,
|
|
24
|
-
Loader2Icon,
|
|
25
|
-
SendHorizontal,
|
|
26
24
|
SparklesIcon,
|
|
27
25
|
XIcon,
|
|
28
26
|
} from "lucide-react";
|
|
@@ -30,9 +28,18 @@ import { useMemo, useRef, useState } from "react";
|
|
|
30
28
|
import useEvent from "react-use-event-hook";
|
|
31
29
|
import { z } from "zod";
|
|
32
30
|
import { AIModelDropdown } from "@/components/ai/ai-model-dropdown";
|
|
31
|
+
import {
|
|
32
|
+
AddContextButton,
|
|
33
|
+
AttachFileButton,
|
|
34
|
+
FileAttachmentPill,
|
|
35
|
+
SendButton,
|
|
36
|
+
} from "@/components/chat/chat-components";
|
|
33
37
|
import {
|
|
34
38
|
buildCompletionRequestBody,
|
|
39
|
+
convertToFileUIPart,
|
|
35
40
|
handleToolCall,
|
|
41
|
+
PROVIDERS_THAT_SUPPORT_ATTACHMENTS,
|
|
42
|
+
useFileState,
|
|
36
43
|
} from "@/components/chat/chat-utils";
|
|
37
44
|
import { Button } from "@/components/ui/button";
|
|
38
45
|
import {
|
|
@@ -43,10 +50,13 @@ import {
|
|
|
43
50
|
DropdownMenuTrigger,
|
|
44
51
|
} from "@/components/ui/dropdown-menu";
|
|
45
52
|
import { toast } from "@/components/ui/use-toast";
|
|
53
|
+
import { AiModelId } from "@/core/ai/ids/ids";
|
|
46
54
|
import { stagedAICellsAtom, useStagedCells } from "@/core/ai/staged-cells";
|
|
47
55
|
import type { ToolNotebookContext } from "@/core/ai/tools/base";
|
|
48
56
|
import { useCellActions } from "@/core/cells/cells";
|
|
49
57
|
import { resourceExtension } from "@/core/codemirror/ai/resources";
|
|
58
|
+
import { aiAtom } from "@/core/config/config";
|
|
59
|
+
import { DEFAULT_AI_MODEL } from "@/core/config/config-schema";
|
|
50
60
|
import { useRequestClient } from "@/core/network/requests";
|
|
51
61
|
import type { AiCompletionRequest } from "@/core/network/types";
|
|
52
62
|
import { useRuntimeManager } from "@/core/runtime/config";
|
|
@@ -57,7 +67,11 @@ import { jotaiJsonStorage } from "@/utils/storage/jotai";
|
|
|
57
67
|
import { ZodLocalStorage } from "@/utils/storage/typed";
|
|
58
68
|
import { PythonIcon } from "../cell/code/icons";
|
|
59
69
|
import { createAiCompletionOnKeydown } from "./completion-handlers";
|
|
60
|
-
import {
|
|
70
|
+
import {
|
|
71
|
+
addContextCompletion,
|
|
72
|
+
CONTEXT_TRIGGER,
|
|
73
|
+
mentionsCompletionSource,
|
|
74
|
+
} from "./completion-utils";
|
|
61
75
|
import { StreamingChunkTransport } from "./transport/chat-transport";
|
|
62
76
|
|
|
63
77
|
// Persist across sessions
|
|
@@ -89,6 +103,10 @@ export const AddCellWithAI: React.FC<{
|
|
|
89
103
|
const stagedAICells = useAtomValue(stagedAICellsAtom);
|
|
90
104
|
const inputRef = useRef<ReactCodeMirrorRef>(null);
|
|
91
105
|
|
|
106
|
+
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
107
|
+
const { files, addFiles, removeFile } = useFileState();
|
|
108
|
+
const aiConfig = useAtomValue(aiAtom);
|
|
109
|
+
|
|
92
110
|
const { createNewCell, prepareForRun } = useCellActions();
|
|
93
111
|
const toolContext: ToolNotebookContext = {
|
|
94
112
|
store,
|
|
@@ -149,14 +167,21 @@ export const AddCellWithAI: React.FC<{
|
|
|
149
167
|
const isLoading = status === "streaming" || status === "submitted";
|
|
150
168
|
const hasCompletion = stagedAICells.size > 0;
|
|
151
169
|
|
|
152
|
-
const
|
|
170
|
+
const currentModel = aiConfig?.models?.edit_model || DEFAULT_AI_MODEL;
|
|
171
|
+
const currentProvider = AiModelId.parse(currentModel).providerId;
|
|
172
|
+
const isAttachmentSupported =
|
|
173
|
+
PROVIDERS_THAT_SUPPORT_ATTACHMENTS.has(currentProvider);
|
|
174
|
+
|
|
175
|
+
const submit = async () => {
|
|
153
176
|
if (!isLoading) {
|
|
154
177
|
if (inputRef.current?.view) {
|
|
155
178
|
storePrompt(inputRef.current.view);
|
|
156
179
|
}
|
|
157
180
|
// TODO: When we have conversations, don't delete existing cells
|
|
158
181
|
deleteAllStagedCells();
|
|
159
|
-
|
|
182
|
+
|
|
183
|
+
const fileParts = files ? await convertToFileUIPart(files) : undefined;
|
|
184
|
+
sendMessage({ text: input, files: fileParts });
|
|
160
185
|
}
|
|
161
186
|
};
|
|
162
187
|
|
|
@@ -176,19 +201,17 @@ export const AddCellWithAI: React.FC<{
|
|
|
176
201
|
|
|
177
202
|
const languageDropdown = (
|
|
178
203
|
<DropdownMenu modal={false}>
|
|
179
|
-
<DropdownMenuTrigger
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
>
|
|
186
|
-
{language === "python" ? pythonIcon : sqlIcon}
|
|
187
|
-
<ChevronsUpDown className="ml-1 h-3.5 w-3.5 text-muted-foreground/70" />
|
|
188
|
-
</Button>
|
|
204
|
+
<DropdownMenuTrigger
|
|
205
|
+
className="flex items-center justify-between h-7 text-xs px-2 py-0.5 border rounded-md hover:text-accent-foreground"
|
|
206
|
+
data-testid="language-button"
|
|
207
|
+
>
|
|
208
|
+
{language === "python" ? pythonIcon : sqlIcon}
|
|
209
|
+
<ChevronsUpDown className="ml-1 h-3.5 w-3.5 text-muted-foreground/70" />
|
|
189
210
|
</DropdownMenuTrigger>
|
|
190
211
|
<DropdownMenuContent align="center">
|
|
191
|
-
<div className="px-2 py-1
|
|
212
|
+
<div className="px-2 py-1 text-sm text-muted-foreground">
|
|
213
|
+
Select language
|
|
214
|
+
</div>
|
|
192
215
|
<DropdownMenuSeparator />
|
|
193
216
|
<DropdownMenuItem onClick={() => setLanguage("python")}>
|
|
194
217
|
{pythonIcon}
|
|
@@ -230,54 +253,63 @@ export const AddCellWithAI: React.FC<{
|
|
|
230
253
|
hasCompletion,
|
|
231
254
|
})}
|
|
232
255
|
/>
|
|
233
|
-
{isLoading && (
|
|
234
|
-
<Button
|
|
235
|
-
data-testid="stop-completion-button"
|
|
236
|
-
variant="text"
|
|
237
|
-
size="sm"
|
|
238
|
-
className="mb-0"
|
|
239
|
-
onClick={stop}
|
|
240
|
-
>
|
|
241
|
-
<Loader2Icon className="animate-spin mr-1" size={14} />
|
|
242
|
-
Stop
|
|
243
|
-
</Button>
|
|
244
|
-
)}
|
|
245
|
-
<Button variant="text" size="sm" onClick={submit} title="Submit">
|
|
246
|
-
<SendHorizontal className="size-4" />
|
|
247
|
-
</Button>
|
|
248
256
|
<Button variant="text" size="sm" className="mb-0 px-1" onClick={onClose}>
|
|
249
257
|
<XIcon className="size-4" />
|
|
250
258
|
</Button>
|
|
251
259
|
</div>
|
|
252
260
|
);
|
|
253
261
|
|
|
254
|
-
|
|
255
|
-
<div className=
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
262
|
+
const footerComponent = (
|
|
263
|
+
<div className="px-3 pt-1 flex flex-row items-center justify-between">
|
|
264
|
+
<div className="flex items-center gap-2">
|
|
265
|
+
<AIModelDropdown
|
|
266
|
+
triggerClassName="h-7 text-xs max-w-64"
|
|
267
|
+
iconSize="small"
|
|
268
|
+
forRole="edit"
|
|
269
|
+
showAddCustomModelDocs={true}
|
|
270
|
+
/>
|
|
271
|
+
{languageDropdown}
|
|
272
|
+
</div>
|
|
273
|
+
<div className="flex flex-row items-center">
|
|
274
|
+
{files.length > 0 && (
|
|
275
|
+
<div className="flex flex-row gap-1 flex-wrap pr-1">
|
|
276
|
+
{files?.map((file, index) => (
|
|
277
|
+
<FileAttachmentPill
|
|
278
|
+
file={file}
|
|
279
|
+
key={`${file.name}-${index}`}
|
|
280
|
+
onRemove={() => removeFile(file)}
|
|
281
|
+
/>
|
|
282
|
+
))}
|
|
283
|
+
</div>
|
|
268
284
|
)}
|
|
269
|
-
<
|
|
270
|
-
{
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
285
|
+
<AddContextButton
|
|
286
|
+
handleAddContext={() => addContextCompletion(inputRef)}
|
|
287
|
+
isLoading={isLoading}
|
|
288
|
+
/>
|
|
289
|
+
{isAttachmentSupported && (
|
|
290
|
+
<AttachFileButton
|
|
291
|
+
fileInputRef={fileInputRef}
|
|
292
|
+
isLoading={isLoading}
|
|
293
|
+
onAddFiles={addFiles}
|
|
276
294
|
/>
|
|
277
|
-
|
|
295
|
+
)}
|
|
296
|
+
<SendButton
|
|
297
|
+
isLoading={isLoading}
|
|
298
|
+
onStop={stop}
|
|
299
|
+
onSendClick={submit}
|
|
300
|
+
isEmpty={!input.trim()}
|
|
301
|
+
showStopLabel={true}
|
|
302
|
+
/>
|
|
278
303
|
</div>
|
|
279
304
|
</div>
|
|
280
305
|
);
|
|
306
|
+
|
|
307
|
+
return (
|
|
308
|
+
<div className={cn("flex flex-col w-full py-2")}>
|
|
309
|
+
{inputComponent}
|
|
310
|
+
{footerComponent}
|
|
311
|
+
</div>
|
|
312
|
+
);
|
|
281
313
|
};
|
|
282
314
|
|
|
283
315
|
export interface AdditionalCompletions {
|
|
@@ -3,10 +3,8 @@
|
|
|
3
3
|
import { useCompletion } from "@ai-sdk/react";
|
|
4
4
|
import { EditorView } from "@codemirror/view";
|
|
5
5
|
import {
|
|
6
|
-
AtSignIcon,
|
|
7
6
|
CircleCheckIcon,
|
|
8
7
|
Loader2Icon,
|
|
9
|
-
SendIcon,
|
|
10
8
|
SparklesIcon,
|
|
11
9
|
XIcon,
|
|
12
10
|
} from "lucide-react";
|
|
@@ -20,6 +18,10 @@ import { storePrompt } from "@marimo-team/codemirror-ai";
|
|
|
20
18
|
import type { ReactCodeMirrorRef } from "@uiw/react-codemirror";
|
|
21
19
|
import { useAtom, useAtomValue } from "jotai";
|
|
22
20
|
import { AIModelDropdown } from "@/components/ai/ai-model-dropdown";
|
|
21
|
+
import {
|
|
22
|
+
AddContextButton,
|
|
23
|
+
SendButton,
|
|
24
|
+
} from "@/components/chat/chat-components";
|
|
23
25
|
import { Checkbox } from "@/components/ui/checkbox";
|
|
24
26
|
import { Label } from "@/components/ui/label";
|
|
25
27
|
import { Switch } from "@/components/ui/switch";
|
|
@@ -253,39 +255,6 @@ export const AiCompletionEditor: React.FC<Props> = ({
|
|
|
253
255
|
}
|
|
254
256
|
};
|
|
255
257
|
|
|
256
|
-
const loadingStopButton = (
|
|
257
|
-
<Button
|
|
258
|
-
data-testid="stop-completion-button"
|
|
259
|
-
variant="text"
|
|
260
|
-
size="xs"
|
|
261
|
-
className="mb-0"
|
|
262
|
-
onClick={stop}
|
|
263
|
-
>
|
|
264
|
-
<Loader2Icon className="animate-spin mr-1" size={14} />
|
|
265
|
-
Stop
|
|
266
|
-
</Button>
|
|
267
|
-
);
|
|
268
|
-
|
|
269
|
-
const submitButton = (
|
|
270
|
-
<Tooltip content="Submit">
|
|
271
|
-
<Button variant="text" size="icon" onClick={handleSubmit}>
|
|
272
|
-
<SendIcon className="h-3 w-3" />
|
|
273
|
-
</Button>
|
|
274
|
-
</Tooltip>
|
|
275
|
-
);
|
|
276
|
-
|
|
277
|
-
const contextButton = (
|
|
278
|
-
<Tooltip content="Add context">
|
|
279
|
-
<Button
|
|
280
|
-
variant="text"
|
|
281
|
-
size="icon"
|
|
282
|
-
onClick={() => addContextCompletion(inputRef)}
|
|
283
|
-
>
|
|
284
|
-
<AtSignIcon className="h-3 w-3" />
|
|
285
|
-
</Button>
|
|
286
|
-
</Tooltip>
|
|
287
|
-
);
|
|
288
|
-
|
|
289
258
|
const completionButtons = (
|
|
290
259
|
<>
|
|
291
260
|
<AcceptCompletionButton
|
|
@@ -355,9 +324,17 @@ export const AiCompletionEditor: React.FC<Props> = ({
|
|
|
355
324
|
|
|
356
325
|
<div className="-mr-1.5 py-1.5">
|
|
357
326
|
<div className="flex flex-row items-center justify-end gap-0.5">
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
327
|
+
<SendButton
|
|
328
|
+
isLoading={isLoading}
|
|
329
|
+
onStop={stop}
|
|
330
|
+
onSendClick={handleSubmit}
|
|
331
|
+
isEmpty={!input.trim()}
|
|
332
|
+
showStopLabel={true}
|
|
333
|
+
/>
|
|
334
|
+
<AddContextButton
|
|
335
|
+
handleAddContext={() => addContextCompletion(inputRef)}
|
|
336
|
+
isLoading={isLoading}
|
|
337
|
+
/>
|
|
361
338
|
<AIModelDropdown
|
|
362
339
|
triggerClassName="h-7 text-xs"
|
|
363
340
|
iconSize="small"
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
import { maybeAddMarimoImport } from "@/core/cells/add-missing-import";
|
|
13
13
|
import { useCellActions } from "@/core/cells/cells";
|
|
14
14
|
import { LanguageAdapters } from "@/core/codemirror/language/LanguageAdapters";
|
|
15
|
+
import { MARKDOWN_INITIAL_HIDE_CODE } from "@/core/codemirror/language/languages/markdown";
|
|
15
16
|
import {
|
|
16
17
|
getConnectionTooltip,
|
|
17
18
|
isAppInteractionDisabled,
|
|
@@ -63,7 +64,7 @@ export const CreateCellButton = ({
|
|
|
63
64
|
maybeAddMarimoImport({ autoInstantiate: true, createNewCell });
|
|
64
65
|
onClick?.({
|
|
65
66
|
code: LanguageAdapters.markdown.defaultCode,
|
|
66
|
-
hideCode:
|
|
67
|
+
hideCode: MARKDOWN_INITIAL_HIDE_CODE,
|
|
67
68
|
});
|
|
68
69
|
};
|
|
69
70
|
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
reconfigureLanguageEffect,
|
|
20
20
|
switchLanguage,
|
|
21
21
|
} from "@/core/codemirror/language/extension";
|
|
22
|
+
import { MARKDOWN_INITIAL_HIDE_CODE } from "@/core/codemirror/language/languages/markdown";
|
|
22
23
|
import type { LanguageAdapterType } from "@/core/codemirror/language/types";
|
|
23
24
|
import {
|
|
24
25
|
connectedDocAtom,
|
|
@@ -149,6 +150,17 @@ const CellEditorInternal = ({
|
|
|
149
150
|
autoInstantiate,
|
|
150
151
|
createNewCell: cellActions.createNewCell,
|
|
151
152
|
});
|
|
153
|
+
// Code stays visible until the user blurs the cell
|
|
154
|
+
if (!cellConfig.hide_code && MARKDOWN_INITIAL_HIDE_CODE) {
|
|
155
|
+
void saveCellConfig({
|
|
156
|
+
configs: { [cellId]: { hide_code: MARKDOWN_INITIAL_HIDE_CODE } },
|
|
157
|
+
});
|
|
158
|
+
cellActions.updateCellConfig({
|
|
159
|
+
cellId,
|
|
160
|
+
config: { hide_code: MARKDOWN_INITIAL_HIDE_CODE },
|
|
161
|
+
});
|
|
162
|
+
cellActions.markUntouched({ cellId });
|
|
163
|
+
}
|
|
152
164
|
});
|
|
153
165
|
|
|
154
166
|
const aiEnabled = isAiEnabled(userConfig);
|
|
@@ -349,8 +349,9 @@ const PackagesList: React.FC<{
|
|
|
349
349
|
|
|
350
350
|
const UpgradeButton: React.FC<{
|
|
351
351
|
packageName: string;
|
|
352
|
+
tags?: { kind: string; value: string }[];
|
|
352
353
|
onSuccess: () => void;
|
|
353
|
-
}> = ({ packageName, onSuccess }) => {
|
|
354
|
+
}> = ({ packageName, tags, onSuccess }) => {
|
|
354
355
|
const [loading, setLoading] = React.useState(false);
|
|
355
356
|
const { addPackage } = useRequestClient();
|
|
356
357
|
|
|
@@ -362,9 +363,11 @@ const UpgradeButton: React.FC<{
|
|
|
362
363
|
const handleUpgradePackage = async () => {
|
|
363
364
|
try {
|
|
364
365
|
setLoading(true);
|
|
366
|
+
const group = tags?.find((tag) => tag.kind === "group")?.value;
|
|
365
367
|
const response = await addPackage({
|
|
366
368
|
package: packageName,
|
|
367
369
|
upgrade: true,
|
|
370
|
+
group,
|
|
368
371
|
});
|
|
369
372
|
if (response.success) {
|
|
370
373
|
onSuccess();
|
|
@@ -395,12 +398,10 @@ const RemoveButton: React.FC<{
|
|
|
395
398
|
const handleRemovePackage = async () => {
|
|
396
399
|
try {
|
|
397
400
|
setLoading(true);
|
|
398
|
-
const
|
|
399
|
-
(tag) => tag.kind === "group" && tag.value === "dev",
|
|
400
|
-
);
|
|
401
|
+
const group = tags?.find((tag) => tag.kind === "group")?.value;
|
|
401
402
|
const response = await removePackage({
|
|
402
403
|
package: packageName,
|
|
403
|
-
|
|
404
|
+
group,
|
|
404
405
|
});
|
|
405
406
|
if (response.success) {
|
|
406
407
|
onSuccess();
|
|
@@ -604,12 +605,14 @@ const DependencyTreeNode: React.FC<{
|
|
|
604
605
|
{/* Actions for top-level packages */}
|
|
605
606
|
{isTopLevel && (
|
|
606
607
|
<div className="flex gap-1 invisible group-hover:visible">
|
|
607
|
-
<UpgradeButton
|
|
608
|
+
<UpgradeButton
|
|
609
|
+
packageName={node.name}
|
|
610
|
+
tags={node.tags}
|
|
611
|
+
onSuccess={onSuccess}
|
|
612
|
+
/>
|
|
613
|
+
|
|
608
614
|
<RemoveButton
|
|
609
615
|
packageName={node.name}
|
|
610
|
-
// FIXME: Backend types are wrong/outdated.
|
|
611
|
-
// tags actually have the shape: Array<{ kind: string; value: string }>
|
|
612
|
-
// @ts-expect-error — backend tag types do not match frontend expectations yet
|
|
613
616
|
tags={node.tags}
|
|
614
617
|
onSuccess={onSuccess}
|
|
615
618
|
/>
|
|
@@ -461,6 +461,21 @@ engine = clickhouse_connect.get_client(
|
|
|
461
461
|
)"
|
|
462
462
|
`;
|
|
463
463
|
|
|
464
|
+
exports[`generateDatabaseCode > edge cases > clickhouse with proxy_path 1`] = `
|
|
465
|
+
"import clickhouse_connect
|
|
466
|
+
import os
|
|
467
|
+
|
|
468
|
+
_password = os.environ.get("CLICKHOUSE_PASSWORD", "pass")
|
|
469
|
+
engine = clickhouse_connect.get_client(
|
|
470
|
+
host="localhost",
|
|
471
|
+
user="user",
|
|
472
|
+
secure=False,
|
|
473
|
+
port=8123,
|
|
474
|
+
password=_password,
|
|
475
|
+
proxy_path="/clickhouse",
|
|
476
|
+
)"
|
|
477
|
+
`;
|
|
478
|
+
|
|
464
479
|
exports[`generateDatabaseCode > edge cases > duckdb with relative path 1`] = `
|
|
465
480
|
"import sqlmodel
|
|
466
481
|
|
|
@@ -532,6 +532,14 @@ describe("generateDatabaseCode", () => {
|
|
|
532
532
|
},
|
|
533
533
|
"clickhouse_connect",
|
|
534
534
|
],
|
|
535
|
+
[
|
|
536
|
+
"clickhouse with proxy_path",
|
|
537
|
+
{
|
|
538
|
+
...clickhouseConnection,
|
|
539
|
+
proxy_path: "/clickhouse",
|
|
540
|
+
},
|
|
541
|
+
"clickhouse_connect",
|
|
542
|
+
],
|
|
535
543
|
[
|
|
536
544
|
"timeplus with no port",
|
|
537
545
|
{
|
|
@@ -412,6 +412,9 @@ class ClickHouseGenerator extends CodeGenerator<"clickhouse_connect"> {
|
|
|
412
412
|
? this.secrets.print("port", this.connection.port)
|
|
413
413
|
: undefined,
|
|
414
414
|
password: this.connection.password ? password : undefined,
|
|
415
|
+
proxy_path: this.connection.proxy_path
|
|
416
|
+
? this.secrets.print("proxy_path", this.connection.proxy_path)
|
|
417
|
+
: undefined,
|
|
415
418
|
};
|
|
416
419
|
|
|
417
420
|
return dedent(`
|
|
@@ -267,6 +267,15 @@ export const ClickhouseConnectionSchema = z
|
|
|
267
267
|
.boolean()
|
|
268
268
|
.default(false)
|
|
269
269
|
.describe(FieldOptions.of({ label: "Use HTTPs" })),
|
|
270
|
+
proxy_path: z
|
|
271
|
+
.string()
|
|
272
|
+
.optional()
|
|
273
|
+
.describe(
|
|
274
|
+
FieldOptions.of({
|
|
275
|
+
label: "Proxy Path",
|
|
276
|
+
placeholder: "/clickhouse",
|
|
277
|
+
}),
|
|
278
|
+
),
|
|
270
279
|
})
|
|
271
280
|
.describe(FieldOptions.of({ direction: "two-columns" }));
|
|
272
281
|
|
|
@@ -23,6 +23,7 @@ import { Tooltip } from "@/components/ui/tooltip";
|
|
|
23
23
|
import { maybeAddMarimoImport } from "@/core/cells/add-missing-import";
|
|
24
24
|
import { SETUP_CELL_ID } from "@/core/cells/ids";
|
|
25
25
|
import { LanguageAdapters } from "@/core/codemirror/language/LanguageAdapters";
|
|
26
|
+
import { MARKDOWN_INITIAL_HIDE_CODE } from "@/core/codemirror/language/languages/markdown";
|
|
26
27
|
import { aiEnabledAtom } from "@/core/config/config";
|
|
27
28
|
import { canInteractWithAppAtom } from "@/core/network/connection";
|
|
28
29
|
import { useBoolean } from "@/hooks/useBoolean";
|
|
@@ -295,7 +296,7 @@ const AddCellButtons: React.FC<{
|
|
|
295
296
|
cellId: { type: "__end__", columnId },
|
|
296
297
|
before: false,
|
|
297
298
|
code: LanguageAdapters.markdown.defaultCode,
|
|
298
|
-
hideCode:
|
|
299
|
+
hideCode: MARKDOWN_INITIAL_HIDE_CODE,
|
|
299
300
|
});
|
|
300
301
|
}}
|
|
301
302
|
>
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
CodeIcon,
|
|
8
8
|
FolderDownIcon,
|
|
9
9
|
ImageIcon,
|
|
10
|
+
Loader2Icon,
|
|
10
11
|
MoreHorizontalIcon,
|
|
11
12
|
} from "lucide-react";
|
|
12
13
|
import type React from "react";
|
|
@@ -32,6 +33,7 @@ import { MarkdownLanguageAdapter } from "@/core/codemirror/language/languages/ma
|
|
|
32
33
|
import { useResolvedMarimoConfig } from "@/core/config/config";
|
|
33
34
|
import { CSSClasses, KnownQueryParams } from "@/core/constants";
|
|
34
35
|
import type { OutputMessage } from "@/core/kernel/messages";
|
|
36
|
+
import { kernelStateAtom } from "@/core/kernel/state";
|
|
35
37
|
import { showCodeInRunModeAtom } from "@/core/meta/state";
|
|
36
38
|
import { isErrorMime } from "@/core/mime";
|
|
37
39
|
import { type AppMode, kioskModeAtom } from "@/core/mode";
|
|
@@ -63,6 +65,7 @@ const VerticalLayoutRenderer: React.FC<VerticalLayoutProps> = ({
|
|
|
63
65
|
}) => {
|
|
64
66
|
const { invisible } = useDelayVisibility(cells.length, mode);
|
|
65
67
|
const kioskMode = useAtomValue(kioskModeAtom);
|
|
68
|
+
const kernelState = useAtomValue(kernelStateAtom);
|
|
66
69
|
const [userConfig] = useResolvedMarimoConfig();
|
|
67
70
|
const showCodeInRunModePreference = useAtomValue(showCodeInRunModeAtom);
|
|
68
71
|
|
|
@@ -140,6 +143,15 @@ const VerticalLayoutRenderer: React.FC<VerticalLayoutProps> = ({
|
|
|
140
143
|
}
|
|
141
144
|
|
|
142
145
|
if (cells.length === 0 && !invisible) {
|
|
146
|
+
// If kernel is not yet instantiated, show loading state
|
|
147
|
+
if (!kernelState.isInstantiated) {
|
|
148
|
+
return (
|
|
149
|
+
<div className="flex-1 flex flex-col items-center justify-center py-8">
|
|
150
|
+
<Loader2Icon className="w-8 h-8 animate-spin text-muted-foreground" />
|
|
151
|
+
</div>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
// Kernel is ready but no cells - truly empty notebook
|
|
143
155
|
return (
|
|
144
156
|
<div className="flex-1 flex flex-col items-center justify-center py-8">
|
|
145
157
|
<Alert variant="info">
|
|
@@ -33,6 +33,15 @@ const tabTarget = (path: string): string => {
|
|
|
33
33
|
return `${getSessionId()}-${encodeURIComponent(path)}`;
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
+
const isHttpsUrl = (value: string): boolean => {
|
|
37
|
+
try {
|
|
38
|
+
const url = new URL(value);
|
|
39
|
+
return url.protocol === "https:";
|
|
40
|
+
} catch {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
36
45
|
const SEARCH_THRESHOLD = 10;
|
|
37
46
|
|
|
38
47
|
const GalleryPage: React.FC = () => {
|
|
@@ -43,10 +52,10 @@ const GalleryPage: React.FC = () => {
|
|
|
43
52
|
[],
|
|
44
53
|
);
|
|
45
54
|
const workspace = response.data;
|
|
46
|
-
const files = workspace?.files ?? [];
|
|
47
|
-
const root = workspace?.root ?? "";
|
|
48
55
|
|
|
49
56
|
const formattedFiles = useMemo(() => {
|
|
57
|
+
const files = workspace?.files ?? [];
|
|
58
|
+
const root = workspace?.root ?? "";
|
|
50
59
|
return files
|
|
51
60
|
.filter((file) => !file.isDirectory)
|
|
52
61
|
.map((file) => {
|
|
@@ -54,17 +63,28 @@ const GalleryPage: React.FC = () => {
|
|
|
54
63
|
root && Paths.isAbsolute(file.path) && file.path.startsWith(root)
|
|
55
64
|
? Paths.rest(file.path, root)
|
|
56
65
|
: file.path;
|
|
57
|
-
const title =
|
|
66
|
+
const title =
|
|
67
|
+
file.opengraph?.title ?? titleCase(Paths.basename(relativePath));
|
|
58
68
|
const subtitle = titleCase(Paths.dirname(relativePath));
|
|
69
|
+
const description = file.opengraph?.description ?? "";
|
|
70
|
+
const opengraphImage = file.opengraph?.image;
|
|
71
|
+
const thumbnailUrl =
|
|
72
|
+
opengraphImage && isHttpsUrl(opengraphImage)
|
|
73
|
+
? opengraphImage
|
|
74
|
+
: asURL(
|
|
75
|
+
`/og/thumbnail?file=${encodeURIComponent(relativePath)}`,
|
|
76
|
+
).toString();
|
|
59
77
|
return {
|
|
60
78
|
...file,
|
|
61
79
|
relativePath,
|
|
62
80
|
title,
|
|
63
81
|
subtitle,
|
|
82
|
+
description,
|
|
83
|
+
thumbnailUrl,
|
|
64
84
|
};
|
|
65
85
|
})
|
|
66
86
|
.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
67
|
-
}, [files, root]);
|
|
87
|
+
}, [workspace?.files, workspace?.root]);
|
|
68
88
|
|
|
69
89
|
const filteredFiles = useMemo(() => {
|
|
70
90
|
if (!searchQuery) {
|
|
@@ -130,8 +150,14 @@ const GalleryPage: React.FC = () => {
|
|
|
130
150
|
target={tabTarget(file.path)}
|
|
131
151
|
className="no-underline"
|
|
132
152
|
>
|
|
133
|
-
<Card className="h-full hover:bg-accent/20 transition-colors">
|
|
134
|
-
<
|
|
153
|
+
<Card className="h-full overflow-hidden hover:bg-accent/20 transition-colors">
|
|
154
|
+
<img
|
|
155
|
+
src={file.thumbnailUrl}
|
|
156
|
+
alt={file.title}
|
|
157
|
+
loading="lazy"
|
|
158
|
+
className="w-full aspect-1200/630 object-cover border-b border-border/60"
|
|
159
|
+
/>
|
|
160
|
+
<CardContent className="p-6 pt-4">
|
|
135
161
|
<div className="flex flex-col gap-1">
|
|
136
162
|
{file.subtitle && (
|
|
137
163
|
<div className="text-sm font-semibold text-muted-foreground">
|
|
@@ -141,6 +167,11 @@ const GalleryPage: React.FC = () => {
|
|
|
141
167
|
<div className="text-lg font-medium">
|
|
142
168
|
{file.title}
|
|
143
169
|
</div>
|
|
170
|
+
{file.description && (
|
|
171
|
+
<div className="text-sm text-muted-foreground line-clamp-3 mt-1">
|
|
172
|
+
{file.description}
|
|
173
|
+
</div>
|
|
174
|
+
)}
|
|
144
175
|
</div>
|
|
145
176
|
</CardContent>
|
|
146
177
|
</Card>
|
package/src/core/MarimoApp.tsx
CHANGED
|
@@ -39,14 +39,18 @@ const LazyGalleryPage = reactLazyWithPreload(
|
|
|
39
39
|
);
|
|
40
40
|
|
|
41
41
|
export function preloadPage(mode: string) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
42
|
+
switch (mode) {
|
|
43
|
+
case "home":
|
|
44
|
+
LazyHomePage.preload();
|
|
45
|
+
break;
|
|
46
|
+
case "gallery":
|
|
47
|
+
LazyGalleryPage.preload();
|
|
48
|
+
break;
|
|
49
|
+
case "read":
|
|
50
|
+
LazyRunPage.preload();
|
|
51
|
+
break;
|
|
52
|
+
default:
|
|
53
|
+
LazyEditPage.preload();
|
|
50
54
|
}
|
|
51
55
|
}
|
|
52
56
|
|
|
@@ -237,7 +237,7 @@ export class FileContextProvider extends AIContextProvider<FileContextItem> {
|
|
|
237
237
|
fileDetails.contents as Base64String,
|
|
238
238
|
mimeType,
|
|
239
239
|
);
|
|
240
|
-
blob =
|
|
240
|
+
blob = deserializeBlob(dataURL);
|
|
241
241
|
} catch {
|
|
242
242
|
// Fallback to treating as text
|
|
243
243
|
blob = new Blob([fileDetails.contents], { type: mimeType });
|