@marimo-team/islands 0.16.4 → 0.16.5
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-CCjhPKMy.js → ConnectedDataExplorerComponent-D96i9G-X.js} +3 -3
- package/dist/assets/__vite-browser-external-Dv_SHu1h.js +1 -0
- package/dist/assets/{worker-DnuXpGWN.js → worker-DVOR9oZG.js} +2 -2
- package/dist/{formats-D5C6JAJf.js → formats-ChrNdVdJ.js} +1 -1
- package/dist/{glide-data-editor-CYfKmSNp.js → glide-data-editor-D_kEsT07.js} +68 -68
- package/dist/main.js +84 -213
- package/dist/{mermaid-BlJDcO4M.js → mermaid-MWiyXDcI.js} +2 -2
- package/dist/style.css +1 -1
- package/dist/{types-Dcb1hf55.js → types-1X1uZB4y.js} +323 -179
- package/dist/{useAsyncData-DAtPzJzP.js → useAsyncData-C4IqQK0g.js} +1 -1
- package/dist/{useDateFormatter-CiUlIu7v.js → useDateFormatter-BCsBqetx.js} +1 -1
- package/dist/{useTheme-CmsvrO5o.js → useTheme-C2pgJzDH.js} +1 -0
- package/dist/{vega-component-B3LA6qbm.js → vega-component-Cv4J8CHz.js} +3 -3
- package/package.json +1 -1
- package/src/__tests__/chat-history.test.ts +123 -0
- package/src/components/app-config/ai-config.tsx +23 -0
- package/src/components/app-config/mcp-config.tsx +42 -2
- package/src/components/app-config/user-config-form.tsx +0 -24
- package/src/components/chat/acp/__tests__/context-utils.test.ts +1 -1
- package/src/components/chat/acp/agent-panel.tsx +1 -1
- package/src/components/chat/acp/blocks.tsx +46 -53
- package/src/components/chat/acp/common.tsx +1 -1
- package/src/components/chat/acp/context-utils.ts +1 -1
- package/src/components/chat/acp/session-tabs.tsx +1 -1
- package/src/components/chat/chat-history-popover.tsx +125 -0
- package/src/components/chat/chat-history-utils.ts +69 -0
- package/src/components/chat/chat-panel.tsx +9 -57
- package/src/components/editor/__tests__/data-attributes.test.tsx +1 -1
- package/src/components/editor/actions/useNotebookActions.tsx +2 -4
- package/src/components/editor/ai/__tests__/completion-utils.test.ts +23 -31
- package/src/components/editor/cell/CreateCellButton.tsx +14 -2
- package/src/components/editor/cell/code/cell-editor.tsx +1 -0
- package/src/components/editor/database/schemas.ts +2 -10
- package/src/components/editor/{Cell.tsx → notebook-cell.tsx} +5 -1
- package/src/components/editor/output/MarimoErrorOutput.tsx +4 -34
- package/src/components/editor/renderers/{CellArray.tsx → cell-array.tsx} +1 -1
- package/src/components/forms/__tests__/form-utils.test.ts +2 -2
- package/src/components/mcp/hooks.ts +48 -0
- package/src/components/mcp/mcp-status-indicator.tsx +144 -0
- package/src/components/ui/number-field.tsx +4 -1
- package/src/core/ai/context/providers/__tests__/__snapshots__/tables.test.ts.snap +13 -19
- package/src/core/ai/context/providers/__tests__/cell-output.test.ts +0 -1
- package/src/core/ai/context/providers/__tests__/datasource.test.ts +5 -6
- package/src/core/ai/context/providers/__tests__/error.test.ts +24 -15
- package/src/core/ai/context/providers/cell-output.ts +5 -5
- package/src/core/ai/context/providers/common.ts +13 -4
- package/src/core/ai/context/providers/datasource.ts +31 -20
- package/src/core/ai/context/providers/error.ts +3 -4
- package/src/core/ai/context/providers/file.ts +2 -2
- package/src/core/ai/context/providers/tables.ts +36 -8
- package/src/core/ai/context/providers/variable.ts +2 -3
- package/src/core/cells/__tests__/cells.test.ts +6 -6
- package/src/core/cells/cells.ts +12 -13
- package/src/core/cells/scrollCellIntoView.ts +1 -1
- package/src/core/codemirror/__tests__/setup.test.ts +1 -0
- package/src/core/codemirror/cm.ts +3 -2
- package/src/core/config/__tests__/config-schema.test.ts +2 -0
- package/src/core/config/config-schema.ts +2 -0
- package/src/core/config/feature-flag.tsx +0 -2
- package/src/core/edit-app.tsx +1 -1
- package/src/core/network/CachingRequestRegistry.ts +2 -2
- package/src/stories/cell.stories.tsx +1 -1
- package/src/stories/layout/vertical/one-column.stories.tsx +1 -1
- package/src/utils/numbers.ts +24 -1
- package/dist/assets/__vite-browser-external-BeNtI_tJ.js +0 -1
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import type { Chat } from "@/core/ai/state";
|
|
4
|
+
|
|
5
|
+
const DATE_GROUP_CONFIG = [
|
|
6
|
+
{ label: "Today", days: 0 },
|
|
7
|
+
{ label: "Yesterday", days: 1 },
|
|
8
|
+
{ label: "2d ago", days: 2 },
|
|
9
|
+
{ label: "3d ago", days: 3 },
|
|
10
|
+
{ label: "This week", days: 7 },
|
|
11
|
+
{ label: "This month", days: 30 },
|
|
12
|
+
] as const;
|
|
13
|
+
|
|
14
|
+
interface DateGroup {
|
|
15
|
+
label: string;
|
|
16
|
+
days: number;
|
|
17
|
+
chats: Chat[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Utility function to group chats by date periods
|
|
21
|
+
export const groupChatsByDate = (chats: Chat[]): DateGroup[] => {
|
|
22
|
+
const now = Date.now();
|
|
23
|
+
const oneDayMs = 24 * 60 * 60 * 1000;
|
|
24
|
+
|
|
25
|
+
// Initialize groups with empty chat arrays
|
|
26
|
+
const groups: DateGroup[] = DATE_GROUP_CONFIG.map((config) => ({
|
|
27
|
+
...config,
|
|
28
|
+
chats: [],
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
const olderGroup: DateGroup = {
|
|
32
|
+
label: "Older",
|
|
33
|
+
days: Infinity,
|
|
34
|
+
chats: [],
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Helper function to determine which group a chat belongs to
|
|
38
|
+
const getGroupForChat = (daysDiff: number): DateGroup => {
|
|
39
|
+
// Use switch for exact day matches, then handle ranges
|
|
40
|
+
switch (daysDiff) {
|
|
41
|
+
case 0:
|
|
42
|
+
return groups[0]; // Today
|
|
43
|
+
case 1:
|
|
44
|
+
return groups[1]; // Yesterday
|
|
45
|
+
case 2:
|
|
46
|
+
return groups[2]; // 2d ago
|
|
47
|
+
case 3:
|
|
48
|
+
return groups[3]; // 3d ago
|
|
49
|
+
default:
|
|
50
|
+
// Handle range-based grouping for older chats
|
|
51
|
+
if (daysDiff >= 4 && daysDiff <= 7) {
|
|
52
|
+
return groups[4]; // This week
|
|
53
|
+
} else if (daysDiff >= 8 && daysDiff <= 30) {
|
|
54
|
+
return groups[5]; // This month
|
|
55
|
+
}
|
|
56
|
+
// Everything else goes to Older
|
|
57
|
+
return olderGroup;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
for (const chat of chats) {
|
|
62
|
+
const daysDiff = Math.floor((now - chat.updatedAt) / oneDayMs);
|
|
63
|
+
const targetGroup = getGroupForChat(daysDiff);
|
|
64
|
+
targetGroup.chats.push(chat);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Return only non-empty groups
|
|
68
|
+
return [...groups, olderGroup].filter((group) => group.chats.length > 0);
|
|
69
|
+
};
|
|
@@ -9,7 +9,6 @@ import { useAtom, useAtomValue, useSetAtom } from "jotai";
|
|
|
9
9
|
import {
|
|
10
10
|
AtSignIcon,
|
|
11
11
|
BotMessageSquareIcon,
|
|
12
|
-
ClockIcon,
|
|
13
12
|
Loader2,
|
|
14
13
|
PaperclipIcon,
|
|
15
14
|
PlusIcon,
|
|
@@ -17,16 +16,9 @@ import {
|
|
|
17
16
|
SettingsIcon,
|
|
18
17
|
SquareIcon,
|
|
19
18
|
} from "lucide-react";
|
|
20
|
-
import { memo, useEffect,
|
|
21
|
-
import { useLocale } from "react-aria";
|
|
19
|
+
import { memo, useEffect, useRef, useState } from "react";
|
|
22
20
|
import useEvent from "react-use-event-hook";
|
|
23
21
|
import { Button } from "@/components/ui/button";
|
|
24
|
-
import {
|
|
25
|
-
Popover,
|
|
26
|
-
PopoverContent,
|
|
27
|
-
PopoverTrigger,
|
|
28
|
-
} from "@/components/ui/popover";
|
|
29
|
-
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
30
22
|
import {
|
|
31
23
|
Select,
|
|
32
24
|
SelectContent,
|
|
@@ -52,8 +44,8 @@ import { useRequestClient } from "@/core/network/requests";
|
|
|
52
44
|
import { useRuntimeManager } from "@/core/runtime/config";
|
|
53
45
|
import { ErrorBanner } from "@/plugins/impl/common/error-banner";
|
|
54
46
|
import { cn } from "@/utils/cn";
|
|
55
|
-
import { timeAgo } from "@/utils/dates";
|
|
56
47
|
import { Logger } from "@/utils/Logger";
|
|
48
|
+
|
|
57
49
|
import { AIModelDropdown } from "../ai/ai-model-dropdown";
|
|
58
50
|
import { useOpenSettingsToTab } from "../app-config/state";
|
|
59
51
|
import { PromptInput } from "../editor/ai/add-cell-with-ai";
|
|
@@ -63,10 +55,12 @@ import {
|
|
|
63
55
|
} from "../editor/ai/completion-utils";
|
|
64
56
|
import { PanelEmptyState } from "../editor/chrome/panels/empty-state";
|
|
65
57
|
import { CopyClipboardIcon } from "../icons/copy-icon";
|
|
58
|
+
import { MCPStatusIndicator } from "../mcp/mcp-status-indicator";
|
|
66
59
|
import { Input } from "../ui/input";
|
|
67
60
|
import { Tooltip, TooltipProvider } from "../ui/tooltip";
|
|
68
61
|
import { toast } from "../ui/use-toast";
|
|
69
62
|
import { AttachmentRenderer, FileAttachmentPill } from "./chat-components";
|
|
63
|
+
import { ChatHistoryPopover } from "./chat-history-popover";
|
|
70
64
|
import {
|
|
71
65
|
buildCompletionRequestBody,
|
|
72
66
|
convertToFileUIPart,
|
|
@@ -104,13 +98,6 @@ const ChatHeader: React.FC<ChatHeaderProps> = ({
|
|
|
104
98
|
setActiveChat,
|
|
105
99
|
}) => {
|
|
106
100
|
const { handleClick } = useOpenSettingsToTab();
|
|
107
|
-
const chatState = useAtomValue(chatStateAtom);
|
|
108
|
-
const { locale } = useLocale();
|
|
109
|
-
const chats = useMemo(() => {
|
|
110
|
-
return [...chatState.chats.values()].sort(
|
|
111
|
-
(a, b) => b.updatedAt - a.updatedAt,
|
|
112
|
-
);
|
|
113
|
-
}, [chatState.chats]);
|
|
114
101
|
|
|
115
102
|
return (
|
|
116
103
|
<div className="flex border-b px-2 py-1 justify-between shrink-0 items-center">
|
|
@@ -120,6 +107,7 @@ const ChatHeader: React.FC<ChatHeaderProps> = ({
|
|
|
120
107
|
</Button>
|
|
121
108
|
</Tooltip>
|
|
122
109
|
<div className="flex items-center gap-2">
|
|
110
|
+
<MCPStatusIndicator />
|
|
123
111
|
<Tooltip content="AI Settings">
|
|
124
112
|
<Button
|
|
125
113
|
variant="text"
|
|
@@ -130,46 +118,10 @@ const ChatHeader: React.FC<ChatHeaderProps> = ({
|
|
|
130
118
|
<SettingsIcon className="h-4 w-4" />
|
|
131
119
|
</Button>
|
|
132
120
|
</Tooltip>
|
|
133
|
-
<
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
<ClockIcon className="h-4 w-4" />
|
|
138
|
-
</Button>
|
|
139
|
-
</PopoverTrigger>
|
|
140
|
-
</Tooltip>
|
|
141
|
-
<PopoverContent className="w-[520px] p-0" align="start" side="right">
|
|
142
|
-
<ScrollArea className="h-[500px] p-4">
|
|
143
|
-
<div className="space-y-4">
|
|
144
|
-
{chats.length === 0 && (
|
|
145
|
-
<PanelEmptyState
|
|
146
|
-
title="No chats yet"
|
|
147
|
-
description="Start a new chat to get started"
|
|
148
|
-
icon={<BotMessageSquareIcon />}
|
|
149
|
-
/>
|
|
150
|
-
)}
|
|
151
|
-
{chats.map((chat) => (
|
|
152
|
-
<button
|
|
153
|
-
key={chat.id}
|
|
154
|
-
className={cn(
|
|
155
|
-
"w-full p-3 rounded-md cursor-pointer hover:bg-accent text-left",
|
|
156
|
-
chat.id === activeChatId && "bg-accent",
|
|
157
|
-
)}
|
|
158
|
-
onClick={() => {
|
|
159
|
-
setActiveChat(chat.id);
|
|
160
|
-
}}
|
|
161
|
-
type="button"
|
|
162
|
-
>
|
|
163
|
-
<div className="font-medium">{chat.title}</div>
|
|
164
|
-
<div className="text-sm text-muted-foreground">
|
|
165
|
-
{timeAgo(chat.updatedAt, locale)}
|
|
166
|
-
</div>
|
|
167
|
-
</button>
|
|
168
|
-
))}
|
|
169
|
-
</div>
|
|
170
|
-
</ScrollArea>
|
|
171
|
-
</PopoverContent>
|
|
172
|
-
</Popover>
|
|
121
|
+
<ChatHistoryPopover
|
|
122
|
+
activeChatId={activeChatId}
|
|
123
|
+
setActiveChat={setActiveChat}
|
|
124
|
+
/>
|
|
173
125
|
</div>
|
|
174
126
|
</div>
|
|
175
127
|
);
|
|
@@ -12,7 +12,7 @@ import type { UserConfig } from "@/core/config/config-schema";
|
|
|
12
12
|
import type { OutputMessage } from "@/core/kernel/messages";
|
|
13
13
|
import type { AppMode } from "@/core/mode";
|
|
14
14
|
import { requestClientAtom } from "@/core/network/requests";
|
|
15
|
-
import { Cell } from "../
|
|
15
|
+
import { Cell } from "../notebook-cell";
|
|
16
16
|
import { OutputArea } from "../Output";
|
|
17
17
|
|
|
18
18
|
function createTestWrapper() {
|
|
@@ -102,7 +102,7 @@ export function useNotebookActions() {
|
|
|
102
102
|
updateCellConfig,
|
|
103
103
|
undoDeleteCell,
|
|
104
104
|
clearAllCellOutputs,
|
|
105
|
-
|
|
105
|
+
addSetupCellIfDoesntExist,
|
|
106
106
|
collapseAllCells,
|
|
107
107
|
expandAllCells,
|
|
108
108
|
} = useCellActions();
|
|
@@ -401,9 +401,7 @@ export function useNotebookActions() {
|
|
|
401
401
|
icon: <DiamondPlusIcon size={14} strokeWidth={1.5} />,
|
|
402
402
|
label: "Add setup cell",
|
|
403
403
|
handle: () => {
|
|
404
|
-
|
|
405
|
-
code: "# Initialization code that runs before all other cells",
|
|
406
|
-
});
|
|
404
|
+
addSetupCellIfDoesntExist({});
|
|
407
405
|
},
|
|
408
406
|
},
|
|
409
407
|
{
|
|
@@ -55,15 +55,13 @@ describe("getAICompletionBody", () => {
|
|
|
55
55
|
expect(result).toMatchInlineSnapshot(`
|
|
56
56
|
{
|
|
57
57
|
"context": {
|
|
58
|
-
"plainText": "<data name="dataset1" source="unknown">
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
- col3: boolean
|
|
66
|
-
- col4: date</data>",
|
|
58
|
+
"plainText": "<data name="dataset1" source="unknown">Columns:
|
|
59
|
+
col1 (number)
|
|
60
|
+
col2 (string)</data>
|
|
61
|
+
|
|
62
|
+
<data name="dataset2" source="unknown">Columns:
|
|
63
|
+
col3 (boolean)
|
|
64
|
+
col4 (date)</data>",
|
|
67
65
|
"schema": [],
|
|
68
66
|
"variables": [],
|
|
69
67
|
},
|
|
@@ -108,10 +106,9 @@ describe("getAICompletionBody", () => {
|
|
|
108
106
|
expect(result).toMatchInlineSnapshot(`
|
|
109
107
|
{
|
|
110
108
|
"context": {
|
|
111
|
-
"plainText": "<data name="existingDataset" source="unknown">
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
- col2: string</data>",
|
|
109
|
+
"plainText": "<data name="existingDataset" source="unknown">Columns:
|
|
110
|
+
col1 (number)
|
|
111
|
+
col2 (string)</data>",
|
|
115
112
|
"schema": [],
|
|
116
113
|
"variables": [],
|
|
117
114
|
},
|
|
@@ -144,14 +141,12 @@ describe("getAICompletionBody", () => {
|
|
|
144
141
|
expect(result).toMatchInlineSnapshot(`
|
|
145
142
|
{
|
|
146
143
|
"context": {
|
|
147
|
-
"plainText": "<data name="dataset.with.dots" source="unknown">
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
Columns:
|
|
154
|
-
- col3: boolean</data>",
|
|
144
|
+
"plainText": "<data name="dataset.with.dots" source="unknown">Columns:
|
|
145
|
+
col1 (number)
|
|
146
|
+
col2 (string)</data>
|
|
147
|
+
|
|
148
|
+
<data name="regular_dataset" source="unknown">Columns:
|
|
149
|
+
col3 (boolean)</data>",
|
|
155
150
|
"schema": [],
|
|
156
151
|
"variables": [],
|
|
157
152
|
},
|
|
@@ -198,9 +193,8 @@ describe("getAICompletionBody", () => {
|
|
|
198
193
|
expect(result).toMatchInlineSnapshot(`
|
|
199
194
|
{
|
|
200
195
|
"context": {
|
|
201
|
-
"plainText": "<data name="table1" source="unknown">
|
|
202
|
-
|
|
203
|
-
- col1: number</data>",
|
|
196
|
+
"plainText": "<data name="table1" source="unknown">Columns:
|
|
197
|
+
col1 (number)</data>",
|
|
204
198
|
"schema": [],
|
|
205
199
|
"variables": [],
|
|
206
200
|
},
|
|
@@ -276,10 +270,9 @@ describe("getAICompletionBody", () => {
|
|
|
276
270
|
expect(result).toMatchInlineSnapshot(`
|
|
277
271
|
{
|
|
278
272
|
"context": {
|
|
279
|
-
"plainText": "<data name="dataset1" source="unknown">
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
- col2: string</data>
|
|
273
|
+
"plainText": "<data name="dataset1" source="unknown">Columns:
|
|
274
|
+
col1 (number)
|
|
275
|
+
col2 (string)</data>
|
|
283
276
|
|
|
284
277
|
<variable name="var1" dataType="string">"string value"</variable>",
|
|
285
278
|
"schema": [],
|
|
@@ -346,9 +339,8 @@ describe("getAICompletionBody", () => {
|
|
|
346
339
|
expect(result).toMatchInlineSnapshot(`
|
|
347
340
|
{
|
|
348
341
|
"context": {
|
|
349
|
-
"plainText": "<data name="conflict" source="unknown">
|
|
350
|
-
|
|
351
|
-
- col1: number</data>",
|
|
342
|
+
"plainText": "<data name="conflict" source="unknown">Columns:
|
|
343
|
+
col1 (number)</data>",
|
|
352
344
|
"schema": [],
|
|
353
345
|
"variables": [],
|
|
354
346
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
|
-
import { DatabaseIcon, PlusIcon } from "lucide-react";
|
|
2
|
+
import { DatabaseIcon, DiamondPlusIcon, PlusIcon } from "lucide-react";
|
|
3
3
|
import { Button } from "@/components/editor/inputs/Inputs";
|
|
4
4
|
import {
|
|
5
5
|
ContextMenu,
|
|
@@ -69,7 +69,7 @@ const CreateCellButtonContextMenu = (props: {
|
|
|
69
69
|
children: React.ReactNode;
|
|
70
70
|
}) => {
|
|
71
71
|
const { children, onClick } = props;
|
|
72
|
-
const { createNewCell } = useCellActions();
|
|
72
|
+
const { createNewCell, addSetupCellIfDoesntExist } = useCellActions();
|
|
73
73
|
|
|
74
74
|
if (!onClick) {
|
|
75
75
|
return children;
|
|
@@ -125,6 +125,18 @@ const CreateCellButtonContextMenu = (props: {
|
|
|
125
125
|
</div>
|
|
126
126
|
SQL cell
|
|
127
127
|
</ContextMenuItem>
|
|
128
|
+
<ContextMenuItem
|
|
129
|
+
key="setup"
|
|
130
|
+
onSelect={(evt) => {
|
|
131
|
+
evt.stopPropagation();
|
|
132
|
+
addSetupCellIfDoesntExist({});
|
|
133
|
+
}}
|
|
134
|
+
>
|
|
135
|
+
<div className="mr-3 text-muted-foreground">
|
|
136
|
+
<DiamondPlusIcon size={13} strokeWidth={1.5} />
|
|
137
|
+
</div>
|
|
138
|
+
Setup cell
|
|
139
|
+
</ContextMenuItem>
|
|
128
140
|
</ContextMenuContent>
|
|
129
141
|
</ContextMenu>
|
|
130
142
|
);
|
|
@@ -187,6 +187,7 @@ const CellEditorInternal = ({
|
|
|
187
187
|
hotkeys: new OverridingHotkeyProvider(userConfig.keymap.overrides ?? {}),
|
|
188
188
|
diagnosticsConfig: userConfig.diagnostics,
|
|
189
189
|
displayConfig: userConfig.display,
|
|
190
|
+
inlineAiTooltip: userConfig.ai?.inline_tooltip ?? false,
|
|
190
191
|
});
|
|
191
192
|
|
|
192
193
|
extensions.push(
|
|
@@ -18,11 +18,7 @@ function passwordField() {
|
|
|
18
18
|
|
|
19
19
|
function tokenField(label?: string, required?: boolean) {
|
|
20
20
|
let field: z.ZodString | z.ZodOptional<z.ZodString> = z.string();
|
|
21
|
-
|
|
22
|
-
field = field.nonempty();
|
|
23
|
-
} else {
|
|
24
|
-
field = field.optional();
|
|
25
|
-
}
|
|
21
|
+
field = required ? field.nonempty() : field.optional();
|
|
26
22
|
|
|
27
23
|
field = field.describe(
|
|
28
24
|
FieldOptions.of({
|
|
@@ -50,11 +46,7 @@ function warehouseNameField() {
|
|
|
50
46
|
|
|
51
47
|
function uriField(label?: string, required?: boolean) {
|
|
52
48
|
let field: z.ZodString | z.ZodOptional<z.ZodString> = z.string();
|
|
53
|
-
|
|
54
|
-
field = field.nonempty();
|
|
55
|
-
} else {
|
|
56
|
-
field = field.optional();
|
|
57
|
-
}
|
|
49
|
+
field = required ? field.nonempty() : field.optional();
|
|
58
50
|
|
|
59
51
|
return field.describe(
|
|
60
52
|
FieldOptions.of({ label: label || "URI", optionRegex: ".*uri.*" }),
|
|
@@ -1051,12 +1051,16 @@ const SetupCellComponent = ({
|
|
|
1051
1051
|
data-status={cellRuntime.status}
|
|
1052
1052
|
ref={cellRef}
|
|
1053
1053
|
{...mergeProps(navigationProps, {
|
|
1054
|
-
className
|
|
1054
|
+
className: cn(
|
|
1055
|
+
className,
|
|
1056
|
+
"focus:ring-1 focus:ring-(--blue-7) focus:ring-offset-0",
|
|
1057
|
+
),
|
|
1055
1058
|
onBlur: closeCompletionHandler,
|
|
1056
1059
|
onKeyDown: resumeCompletionHandler,
|
|
1057
1060
|
})}
|
|
1058
1061
|
{...cellDomProps(cellId, cellData.name)}
|
|
1059
1062
|
title={renderCellTitle()}
|
|
1063
|
+
tabIndex={-1}
|
|
1060
1064
|
data-setup-cell={true}
|
|
1061
1065
|
>
|
|
1062
1066
|
<div className={cn("tray")} data-hidden={!isCellCodeShown}>
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
InfoIcon,
|
|
5
|
-
NotebookPenIcon,
|
|
6
|
-
SquareArrowOutUpRightIcon,
|
|
7
|
-
} from "lucide-react";
|
|
3
|
+
import { NotebookPenIcon, SquareArrowOutUpRightIcon } from "lucide-react";
|
|
8
4
|
import { Fragment, type JSX } from "react";
|
|
9
5
|
import {
|
|
10
6
|
Accordion,
|
|
@@ -499,37 +495,11 @@ export const MarimoErrorOutput = ({
|
|
|
499
495
|
messages.push(
|
|
500
496
|
<div key="sql-errors">
|
|
501
497
|
{sqlErrors.map((error, idx) => {
|
|
502
|
-
const line =
|
|
503
|
-
error.sql_line == null ? null : Math.trunc(error?.sql_line) + 1;
|
|
504
|
-
const col =
|
|
505
|
-
error.sql_col == null ? null : Math.trunc(error?.sql_col) + 1;
|
|
506
498
|
return (
|
|
507
499
|
<div key={`sql-error-${idx}`} className="space-y-2 mt-2">
|
|
508
|
-
<p className="text-muted-foreground
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
<InfoIcon
|
|
512
|
-
size={11}
|
|
513
|
-
className="text-muted-foreground mt-1 flex-shrink-0"
|
|
514
|
-
/>
|
|
515
|
-
<p className="whitespace-pre-wrap text-sm text-muted-foreground">
|
|
516
|
-
{error.hint}
|
|
517
|
-
</p>
|
|
518
|
-
</div>
|
|
519
|
-
)}
|
|
520
|
-
{error.sql_statement && (
|
|
521
|
-
<pre
|
|
522
|
-
lang="sql"
|
|
523
|
-
className="text-xs bg-muted/80 rounded whitespace-pre-wrap p-3.5"
|
|
524
|
-
>
|
|
525
|
-
{error.sql_statement.trim()}
|
|
526
|
-
</pre>
|
|
527
|
-
)}
|
|
528
|
-
{line !== null && col !== null && (
|
|
529
|
-
<p className="text-xs text-muted-foreground">
|
|
530
|
-
Error at line {line}, column {col}
|
|
531
|
-
</p>
|
|
532
|
-
)}
|
|
500
|
+
<p className="text-muted-foreground whitespace-pre-wrap">
|
|
501
|
+
{error.msg}
|
|
502
|
+
</p>
|
|
533
503
|
</div>
|
|
534
504
|
);
|
|
535
505
|
})}
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
} from "lucide-react";
|
|
15
15
|
import { useEffect } from "react";
|
|
16
16
|
import { StartupLogsAlert } from "@/components/editor/alerts/startup-logs-alert";
|
|
17
|
-
import { Cell } from "@/components/editor/
|
|
17
|
+
import { Cell } from "@/components/editor/notebook-cell";
|
|
18
18
|
import { PackageAlert } from "@/components/editor/package-alert";
|
|
19
19
|
import { SortableCellsProvider } from "@/components/sort/SortableCellsProvider";
|
|
20
20
|
import { Button } from "@/components/ui/button";
|
|
@@ -191,7 +191,7 @@ describe("getDefaults", () => {
|
|
|
191
191
|
});
|
|
192
192
|
const result = getDefaults(schema) as { map: Map<string, number> };
|
|
193
193
|
expect(result.map instanceof Map).toBe(true);
|
|
194
|
-
expect(
|
|
194
|
+
expect([...result.map.entries()]).toEqual([["a", 1]]);
|
|
195
195
|
});
|
|
196
196
|
|
|
197
197
|
it("should handle ZodSet with default", () => {
|
|
@@ -200,7 +200,7 @@ describe("getDefaults", () => {
|
|
|
200
200
|
});
|
|
201
201
|
const result = getDefaults(schema) as { set: Set<string> };
|
|
202
202
|
expect(result.set instanceof Set).toBe(true);
|
|
203
|
-
expect(
|
|
203
|
+
expect([...result.set]).toEqual(["a", "b"]);
|
|
204
204
|
});
|
|
205
205
|
|
|
206
206
|
it("should handle deeply nested defaults", () => {
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/* Copyright 2024 Marimo. All rights reserved. */
|
|
2
|
+
|
|
3
|
+
import type { components } from "@marimo-team/marimo-api";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { API } from "@/core/network/api";
|
|
6
|
+
import { useAsyncData } from "@/hooks/useAsyncData";
|
|
7
|
+
import { toast } from "../ui/use-toast";
|
|
8
|
+
|
|
9
|
+
export type MCPStatus = components["schemas"]["MCPStatusResponse"];
|
|
10
|
+
export type MCPRefreshResponse = components["schemas"]["MCPRefreshResponse"];
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Hook to fetch MCP server status
|
|
14
|
+
*/
|
|
15
|
+
export function useMCPStatus() {
|
|
16
|
+
return useAsyncData<MCPStatus>(async () => {
|
|
17
|
+
return API.get<MCPStatus>("/ai/mcp/status");
|
|
18
|
+
}, []);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Hook to refresh MCP server configuration
|
|
23
|
+
*/
|
|
24
|
+
export function useMCPRefresh() {
|
|
25
|
+
const [isRefreshing, setIsRefreshing] = useState(false);
|
|
26
|
+
|
|
27
|
+
const refresh = async () => {
|
|
28
|
+
setIsRefreshing(true);
|
|
29
|
+
try {
|
|
30
|
+
await API.post<object, MCPRefreshResponse>("/ai/mcp/refresh", {});
|
|
31
|
+
toast({
|
|
32
|
+
title: "MCP refreshed",
|
|
33
|
+
description: "MCP server configuration has been refreshed successfully",
|
|
34
|
+
});
|
|
35
|
+
} catch (error) {
|
|
36
|
+
toast({
|
|
37
|
+
title: "Refresh failed",
|
|
38
|
+
description:
|
|
39
|
+
error instanceof Error ? error.message : "Failed to refresh MCP",
|
|
40
|
+
variant: "danger",
|
|
41
|
+
});
|
|
42
|
+
} finally {
|
|
43
|
+
setIsRefreshing(false);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
return { refresh, isRefreshing };
|
|
48
|
+
}
|