@marimo-team/islands 0.20.3-dev92 → 0.20.3-dev96

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.
Files changed (29) hide show
  1. package/dist/main.js +8 -1
  2. package/dist/style.css +1 -1
  3. package/package.json +1 -1
  4. package/src/components/databases/icons/google-drive.svg +8 -0
  5. package/src/components/datasources/datasources.tsx +5 -5
  6. package/src/components/editor/actions/useNotebookActions.tsx +15 -2
  7. package/src/components/editor/connections/add-connection-dialog.tsx +83 -0
  8. package/src/components/editor/connections/components.tsx +177 -0
  9. package/src/components/editor/{database → connections/database}/__tests__/as-code.test.ts +1 -1
  10. package/src/components/editor/connections/database/add-database-form.tsx +303 -0
  11. package/src/components/editor/{database → connections/database}/as-code.ts +1 -1
  12. package/src/components/editor/connections/storage/__tests__/__snapshots__/as-code.test.ts.snap +100 -0
  13. package/src/components/editor/connections/storage/__tests__/as-code.test.ts +166 -0
  14. package/src/components/editor/connections/storage/add-storage-form.tsx +135 -0
  15. package/src/components/editor/connections/storage/as-code.ts +188 -0
  16. package/src/components/editor/connections/storage/schemas.ts +141 -0
  17. package/src/components/storage/components.tsx +9 -3
  18. package/src/components/storage/storage-inspector.tsx +20 -1
  19. package/src/core/cells/__tests__/session.test.ts +1 -1
  20. package/src/core/codemirror/__tests__/format.test.ts +9 -1
  21. package/src/core/storage/types.ts +1 -0
  22. package/src/plugins/core/__test__/sanitize.test.ts +47 -2
  23. package/src/plugins/core/sanitize.ts +4 -0
  24. package/src/components/editor/database/add-database-form.tsx +0 -420
  25. /package/src/components/editor/{database → connections}/__tests__/secrets.test.ts +0 -0
  26. /package/src/components/editor/{database → connections/database}/__tests__/__snapshots__/as-code.test.ts.snap +0 -0
  27. /package/src/components/editor/{database → connections/database}/schemas.ts +0 -0
  28. /package/src/components/editor/{database → connections}/form-renderers.tsx +0 -0
  29. /package/src/components/editor/{database → connections}/secrets.ts +0 -0
@@ -1,420 +0,0 @@
1
- /* Copyright 2026 Marimo. All rights reserved. */
2
-
3
- import { zodResolver } from "@hookform/resolvers/zod";
4
- import { useState } from "react";
5
- import { useForm } from "react-hook-form";
6
- import type { z } from "zod";
7
- import { DatabaseLogo, type DBLogoName } from "@/components/databases/icon";
8
- import { type FormRenderer, ZodForm } from "@/components/forms/form";
9
- import { getDefaults } from "@/components/forms/form-utils";
10
- import { Button } from "@/components/ui/button";
11
- import {
12
- Dialog,
13
- DialogContent,
14
- DialogDescription,
15
- DialogHeader,
16
- DialogTitle,
17
- DialogTrigger,
18
- } from "@/components/ui/dialog";
19
- import { FormErrorsBanner } from "@/components/ui/form";
20
- import { ExternalLink } from "@/components/ui/links";
21
- import {
22
- Select,
23
- SelectContent,
24
- SelectGroup,
25
- SelectItem,
26
- SelectTrigger,
27
- SelectValue,
28
- } from "@/components/ui/select";
29
- import { useCellActions } from "@/core/cells/cells";
30
- import { useLastFocusedCellId } from "@/core/cells/focus";
31
- import {
32
- ConnectionDisplayNames,
33
- type ConnectionLibrary,
34
- generateDatabaseCode,
35
- } from "./as-code";
36
- import { ENV_RENDERER, SecretsProvider } from "./form-renderers";
37
- import {
38
- BigQueryConnectionSchema,
39
- ChdbConnectionSchema,
40
- ClickhouseConnectionSchema,
41
- type DatabaseConnection,
42
- DatabricksConnectionSchema,
43
- DataFusionConnectionSchema,
44
- DuckDBConnectionSchema,
45
- IcebergConnectionSchema,
46
- MotherDuckConnectionSchema,
47
- MySQLConnectionSchema,
48
- PostgresConnectionSchema,
49
- PySparkConnectionSchema,
50
- RedshiftConnectionSchema,
51
- SnowflakeConnectionSchema,
52
- SQLiteConnectionSchema,
53
- SupabaseConnectionSchema,
54
- TimeplusConnectionSchema,
55
- TrinoConnectionSchema,
56
- } from "./schemas";
57
-
58
- interface Props {
59
- onSubmit: () => void;
60
- }
61
-
62
- interface ConnectionSchema {
63
- name: string;
64
- schema: z.ZodType<DatabaseConnection>;
65
- color: string;
66
- logo: DBLogoName;
67
- connectionLibraries: {
68
- libraries: ConnectionLibrary[];
69
- preferred: ConnectionLibrary;
70
- };
71
- }
72
-
73
- // default to sqlalchemy because it has fewer dependencies
74
- const DATABASES = [
75
- {
76
- name: "PostgreSQL",
77
- schema: PostgresConnectionSchema,
78
- color: "#336791",
79
- logo: "postgres",
80
- connectionLibraries: {
81
- libraries: ["sqlalchemy", "sqlmodel"],
82
- preferred: "sqlalchemy",
83
- },
84
- },
85
- {
86
- name: "MySQL",
87
- schema: MySQLConnectionSchema,
88
- color: "#00758F",
89
- logo: "mysql",
90
- connectionLibraries: {
91
- libraries: ["sqlalchemy", "sqlmodel"],
92
- preferred: "sqlalchemy",
93
- },
94
- },
95
- {
96
- name: "SQLite",
97
- schema: SQLiteConnectionSchema,
98
- color: "#003B57",
99
- logo: "sqlite",
100
- connectionLibraries: {
101
- libraries: ["sqlalchemy", "sqlmodel"],
102
- preferred: "sqlalchemy",
103
- },
104
- },
105
- {
106
- name: "DuckDB",
107
- schema: DuckDBConnectionSchema,
108
- color: "#FFD700",
109
- logo: "duckdb",
110
- connectionLibraries: {
111
- libraries: ["duckdb"],
112
- preferred: "duckdb",
113
- },
114
- },
115
- {
116
- name: "MotherDuck",
117
- schema: MotherDuckConnectionSchema,
118
- color: "#ff9538",
119
- logo: "motherduck",
120
- connectionLibraries: {
121
- libraries: ["duckdb"],
122
- preferred: "duckdb",
123
- },
124
- },
125
- {
126
- name: "Snowflake",
127
- schema: SnowflakeConnectionSchema,
128
- color: "#29B5E8",
129
- logo: "snowflake",
130
- connectionLibraries: {
131
- libraries: ["sqlalchemy", "sqlmodel"],
132
- preferred: "sqlalchemy",
133
- },
134
- },
135
- {
136
- name: "ClickHouse",
137
- schema: ClickhouseConnectionSchema,
138
- color: "#2C2C1D",
139
- logo: "clickhouse",
140
- connectionLibraries: {
141
- libraries: ["clickhouse_connect"],
142
- preferred: "clickhouse_connect",
143
- },
144
- },
145
- {
146
- name: "Timeplus",
147
- schema: TimeplusConnectionSchema,
148
- color: "#B83280",
149
- logo: "timeplus",
150
- connectionLibraries: {
151
- libraries: ["sqlalchemy", "sqlmodel"],
152
- preferred: "sqlalchemy",
153
- },
154
- },
155
- {
156
- name: "BigQuery",
157
- schema: BigQueryConnectionSchema,
158
- color: "#4285F4",
159
- logo: "bigquery",
160
- connectionLibraries: {
161
- libraries: ["sqlalchemy", "sqlmodel"],
162
- preferred: "sqlalchemy",
163
- },
164
- },
165
- {
166
- name: "ClickHouse Embedded",
167
- schema: ChdbConnectionSchema,
168
- color: "#f2b611",
169
- logo: "clickhouse",
170
- connectionLibraries: {
171
- libraries: ["chdb"],
172
- preferred: "chdb",
173
- },
174
- },
175
- {
176
- name: "Trino",
177
- schema: TrinoConnectionSchema,
178
- color: "#d466b6",
179
- logo: "trino",
180
- connectionLibraries: {
181
- libraries: ["sqlalchemy", "sqlmodel"],
182
- preferred: "sqlalchemy",
183
- },
184
- },
185
- {
186
- name: "DataFusion",
187
- schema: DataFusionConnectionSchema,
188
- color: "#202A37",
189
- logo: "datafusion",
190
- connectionLibraries: {
191
- libraries: ["ibis"],
192
- preferred: "ibis",
193
- },
194
- },
195
- {
196
- name: "PySpark",
197
- schema: PySparkConnectionSchema,
198
- color: "#1C5162",
199
- logo: "pyspark",
200
- connectionLibraries: {
201
- libraries: ["ibis"],
202
- preferred: "ibis",
203
- },
204
- },
205
- {
206
- name: "Redshift",
207
- schema: RedshiftConnectionSchema,
208
- color: "#522BAE",
209
- logo: "redshift",
210
- connectionLibraries: {
211
- libraries: ["redshift"],
212
- preferred: "redshift",
213
- },
214
- },
215
- {
216
- name: "Databricks",
217
- schema: DatabricksConnectionSchema,
218
- color: "#c41e0c",
219
- logo: "databricks",
220
- connectionLibraries: {
221
- libraries: ["sqlalchemy", "sqlmodel", "ibis"],
222
- preferred: "sqlalchemy",
223
- },
224
- },
225
- {
226
- name: "Supabase",
227
- schema: SupabaseConnectionSchema,
228
- color: "#238F5F",
229
- logo: "supabase",
230
- connectionLibraries: {
231
- libraries: ["sqlalchemy", "sqlmodel"],
232
- preferred: "sqlalchemy",
233
- },
234
- },
235
- ] satisfies ConnectionSchema[];
236
-
237
- const DATA_CATALOGS = [
238
- {
239
- name: "Iceberg",
240
- schema: IcebergConnectionSchema,
241
- color: "#000000",
242
- logo: "iceberg",
243
- connectionLibraries: {
244
- libraries: ["pyiceberg"],
245
- preferred: "pyiceberg",
246
- },
247
- },
248
- ] satisfies ConnectionSchema[];
249
-
250
- const DatabaseSchemaSelector: React.FC<{
251
- onSelect: (schema: z.ZodType<DatabaseConnection>) => void;
252
- }> = ({ onSelect }) => {
253
- const renderItem = ({ name, schema, color, logo }: ConnectionSchema) => {
254
- return (
255
- <button
256
- type="button"
257
- key={name}
258
- className="py-3 flex flex-col items-center justify-center gap-1 transition-all hover:scale-105 hover:brightness-110 rounded shadow-sm-solid hover:shadow-md-solid"
259
- style={{ backgroundColor: color }}
260
- onClick={() => onSelect(schema)}
261
- >
262
- <DatabaseLogo
263
- name={logo}
264
- className="w-8 h-8 text-white brightness-0 invert dark:invert"
265
- />
266
- <span className="text-white font-medium text-lg">{name}</span>
267
- </button>
268
- );
269
- };
270
-
271
- return (
272
- <>
273
- <div className="grid grid-cols-2 md:grid-cols-3 gap-4">
274
- {DATABASES.map(renderItem)}
275
- </div>
276
- <h4 className="font-semibold text-muted-foreground text-lg flex items-center gap-4">
277
- Data Catalogs
278
- <hr className="flex-1" />
279
- </h4>
280
- <div className="grid grid-cols-2 md:grid-cols-3 gap-4">
281
- {DATA_CATALOGS.map(renderItem)}
282
- </div>
283
- </>
284
- );
285
- };
286
-
287
- const RENDERERS: FormRenderer[] = [ENV_RENDERER];
288
-
289
- const DatabaseForm: React.FC<{
290
- schema: z.ZodType<DatabaseConnection>;
291
- onSubmit: () => void;
292
- onBack: () => void;
293
- }> = ({ schema, onSubmit, onBack }) => {
294
- const form = useForm<DatabaseConnection>({
295
- defaultValues: getDefaults(schema),
296
- resolver: zodResolver(
297
- schema as unknown as z.ZodType<unknown, DatabaseConnection>,
298
- ),
299
- reValidateMode: "onChange",
300
- });
301
-
302
- const connectionLibraries = [...DATABASES, ...DATA_CATALOGS].find(
303
- (s) => s.schema === schema,
304
- )?.connectionLibraries;
305
- const [preferredConnection, setPreferredConnection] =
306
- useState<ConnectionLibrary>(connectionLibraries?.preferred ?? "sqlalchemy");
307
-
308
- const { createNewCell } = useCellActions();
309
- const lastFocusedCellId = useLastFocusedCellId();
310
-
311
- const handleInsertCode = (code: string) => {
312
- createNewCell({
313
- code,
314
- before: false,
315
- cellId: lastFocusedCellId ?? "__end__",
316
- skipIfCodeExists: true,
317
- });
318
- };
319
-
320
- const handleSubmit = (values: DatabaseConnection) => {
321
- handleInsertCode(generateDatabaseCode(values, preferredConnection));
322
- onSubmit();
323
- };
324
-
325
- return (
326
- <form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-4">
327
- <SecretsProvider>
328
- <ZodForm schema={schema} form={form} renderers={RENDERERS}>
329
- <FormErrorsBanner />
330
- </ZodForm>
331
- </SecretsProvider>
332
- <div className="flex gap-2 justify-between">
333
- <div className="flex gap-2">
334
- <Button type="button" variant="outline" onClick={onBack}>
335
- Back
336
- </Button>
337
- <Button type="submit" disabled={!form.formState.isValid}>
338
- Add
339
- </Button>
340
- </div>
341
- <div>
342
- <Select
343
- value={preferredConnection}
344
- onValueChange={(value) =>
345
- setPreferredConnection(value as ConnectionLibrary)
346
- }
347
- >
348
- <div className="flex flex-col gap-1 items-end">
349
- <SelectTrigger>
350
- <SelectValue placeholder="Select a library" />
351
- </SelectTrigger>
352
- <span className="text-xs text-muted-foreground">
353
- Preferred connection library
354
- </span>
355
- </div>
356
- <SelectContent>
357
- <SelectGroup>
358
- {connectionLibraries?.libraries.map((library) => (
359
- <SelectItem key={library} value={library}>
360
- {ConnectionDisplayNames[library]}
361
- </SelectItem>
362
- ))}
363
- </SelectGroup>
364
- </SelectContent>
365
- </Select>
366
- </div>
367
- </div>
368
- </form>
369
- );
370
- };
371
-
372
- const AddDatabaseForm: React.FC<Props> = ({ onSubmit }) => {
373
- const [selectedSchema, setSelectedSchema] =
374
- useState<z.ZodType<DatabaseConnection> | null>(null);
375
-
376
- if (!selectedSchema) {
377
- return <DatabaseSchemaSelector onSelect={setSelectedSchema} />;
378
- }
379
-
380
- return (
381
- <DatabaseForm
382
- schema={selectedSchema}
383
- onSubmit={onSubmit}
384
- onBack={() => setSelectedSchema(null)}
385
- />
386
- );
387
- };
388
-
389
- export const AddDatabaseDialog: React.FC<{
390
- children: React.ReactNode;
391
- }> = ({ children }) => {
392
- const [open, setOpen] = useState(false);
393
-
394
- return (
395
- <Dialog open={open} onOpenChange={setOpen}>
396
- <DialogTrigger asChild={true}>{children}</DialogTrigger>
397
- <AddDatabaseDialogContent onClose={() => setOpen(false)} />
398
- </Dialog>
399
- );
400
- };
401
-
402
- export const AddDatabaseDialogContent: React.FC<{
403
- onClose: () => void;
404
- }> = ({ onClose }) => {
405
- return (
406
- <DialogContent className="max-h-[75vh] overflow-y-auto">
407
- <DialogHeader className="mb-4">
408
- <DialogTitle>Add Connection</DialogTitle>
409
- <DialogDescription>
410
- Connect to your database or data catalog to query data directly from
411
- your notebook. Learn more about how to connect to your database in our{" "}
412
- <ExternalLink href="https://docs.marimo.io/guides/working_with_data/sql/#connecting-to-a-custom-database">
413
- docs.
414
- </ExternalLink>
415
- </DialogDescription>
416
- </DialogHeader>
417
- <AddDatabaseForm onSubmit={() => onClose()} />
418
- </DialogContent>
419
- );
420
- };