@marimo-team/islands 0.23.11-dev12 → 0.23.11-dev18

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 (34) hide show
  1. package/dist/{ConnectedDataExplorerComponent-Z3RP7Vmm.js → ConnectedDataExplorerComponent-BnPKA-xR.js} +4 -4
  2. package/dist/assets/__vite-browser-external-DAm_YW43.js +1 -0
  3. package/dist/assets/{worker-CC0Oul9k.js → worker-DEDLIQQV.js} +2 -2
  4. package/dist/{chat-ui-ZCkzXjpY.js → chat-ui-Bf2NICxi.js} +6 -6
  5. package/dist/{code-visibility-w2IqZ0FR.js → code-visibility-BCzDeiYd.js} +8 -8
  6. package/dist/{formats-C4wO47tk.js → formats-BOYEeoL_.js} +1 -1
  7. package/dist/{glide-data-editor-BPkCPs7L.js → glide-data-editor-A_l7_PQZ.js} +2 -2
  8. package/dist/{html-to-image-AwZNbzIe.js → html-to-image-BJbLjZyG.js} +5 -5
  9. package/dist/{input-OdWHkobi.js → input-Cn-SZdXY.js} +1 -1
  10. package/dist/main.js +18 -18
  11. package/dist/{mermaid-BotvIKg9.js → mermaid-Br973nIL.js} +2 -2
  12. package/dist/{process-output-BnIUIlAz.js → process-output-Dw_ngzH2.js} +1 -1
  13. package/dist/{reveal-component-CJnarjEW.js → reveal-component-DchNN1ES.js} +5 -5
  14. package/dist/{spec-X7FwLJni.js → spec-f8-xgaui.js} +1 -1
  15. package/dist/style.css +1 -1
  16. package/dist/{toDate-d8RCRrRd.js → toDate-B4-pUFYh.js} +1 -1
  17. package/dist/{useAsyncData-PonK__yh.js → useAsyncData-B3PfqrwD.js} +1 -1
  18. package/dist/{useDeepCompareMemoize-D3NGWke6.js → useDeepCompareMemoize-YInAvJP3.js} +1 -1
  19. package/dist/{useLifecycle-00mO3OSS.js → useLifecycle-B3NOEiY5.js} +1 -1
  20. package/dist/{useTheme-DEhDzATN.js → useTheme-BPeFKs1Q.js} +2 -1
  21. package/dist/{vega-component-DGPUhbDs.js → vega-component-CMicMpWX.js} +5 -5
  22. package/package.json +1 -1
  23. package/src/components/ai/ai-model-dropdown.tsx +6 -0
  24. package/src/components/app-config/ai-config.tsx +67 -54
  25. package/src/components/app-config/data-form.tsx +33 -43
  26. package/src/components/app-config/is-overridden.tsx +125 -58
  27. package/src/components/app-config/user-config-form.tsx +178 -227
  28. package/src/components/chat/chat-panel.tsx +17 -8
  29. package/src/components/datasources/__tests__/utils.test.ts +127 -2
  30. package/src/components/datasources/datasources.tsx +44 -17
  31. package/src/components/datasources/utils.ts +45 -0
  32. package/src/core/config/config-schema.ts +6 -1
  33. package/src/css/md.css +12 -0
  34. package/dist/assets/__vite-browser-external-eshhtsgZ.js +0 -1
@@ -23,6 +23,7 @@ import {
23
23
  PlusIcon,
24
24
  SparklesIcon,
25
25
  SettingsIcon,
26
+ CodeIcon,
26
27
  } from "lucide-react";
27
28
  import { memo, useEffect, useRef, useState } from "react";
28
29
  import useEvent from "react-use-event-hook";
@@ -268,24 +269,32 @@ const ChatInputFooter: React.FC<ChatInputFooterProps> = memo(
268
269
  {
269
270
  value: "ask",
270
271
  label: "Ask",
271
- subtitle:
272
- "Use AI with access to read-only tools like documentation search",
272
+ subtitle: "AI with access to read-only tools like documentation search",
273
273
  Icon: NotebookText,
274
274
  },
275
275
  {
276
276
  value: "agent",
277
- label: "Agent (beta)",
278
- subtitle: "Use AI with access to read and write tools",
277
+ label: "Agent",
278
+ subtitle: "AI with access to read and write tools",
279
279
  Icon: HatGlasses,
280
280
  },
281
281
  ];
282
282
 
283
+ if (import.meta.env.DEV) {
284
+ modeOptions.push({
285
+ value: "code_mode",
286
+ label: "Code Mode (experimental)",
287
+ subtitle: "AI with access to the notebook's kernel. Use with caution.",
288
+ Icon: CodeIcon,
289
+ });
290
+ }
291
+
283
292
  const isAttachmentSupported =
284
293
  PROVIDERS_THAT_SUPPORT_ATTACHMENTS.has(currentProvider);
285
294
 
286
- const CurrentModeIcon = modeOptions.find(
287
- (o) => o.value === currentMode,
288
- )?.Icon;
295
+ const currentModeOption = modeOptions.find((o) => o.value === currentMode);
296
+ const CurrentModeIcon = currentModeOption?.Icon;
297
+ const CurrentModeLabel = currentModeOption?.label;
289
298
 
290
299
  return (
291
300
  <TooltipProvider>
@@ -294,7 +303,7 @@ const ChatInputFooter: React.FC<ChatInputFooterProps> = memo(
294
303
  <Select value={currentMode} onValueChange={saveModeChange}>
295
304
  <SelectTrigger className="h-6 text-xs border-border shadow-none! ring-0! bg-muted hover:bg-muted/30 py-0 px-2 gap-1.5">
296
305
  {CurrentModeIcon && <CurrentModeIcon className="h-3 w-3" />}
297
- <span className="capitalize">{currentMode}</span>
306
+ <span>{CurrentModeLabel}</span>
298
307
  </SelectTrigger>
299
308
  <SelectContent>
300
309
  <SelectGroup>
@@ -3,8 +3,18 @@
3
3
  import { describe, expect, it } from "vitest";
4
4
  import type { SQLTableContext } from "@/core/datasets/data-source-connections";
5
5
  import { DUCKDB_ENGINE } from "@/core/datasets/engines";
6
- import type { DataTable, DataTableColumn } from "@/core/kernel/messages";
7
- import { sqlCode, tableUniqueId } from "../utils";
6
+ import type {
7
+ Database,
8
+ DatabaseSchema,
9
+ DataTable,
10
+ DataTableColumn,
11
+ } from "@/core/kernel/messages";
12
+ import {
13
+ schemaSubtreeMatchesSearch,
14
+ shouldExpandDatabaseForSearch,
15
+ sqlCode,
16
+ tableUniqueId,
17
+ } from "../utils";
8
18
 
9
19
  describe("sqlCode", () => {
10
20
  const mockTable: DataTable = {
@@ -519,3 +529,118 @@ describe("tableUniqueId", () => {
519
529
  );
520
530
  });
521
531
  });
532
+
533
+ function makeTable(name: string): DataTable {
534
+ return {
535
+ name,
536
+ columns: [],
537
+ source: "memory",
538
+ source_type: "local",
539
+ type: "table",
540
+ engine: null,
541
+ indexes: null,
542
+ num_columns: null,
543
+ num_rows: null,
544
+ variable_name: null,
545
+ primary_keys: null,
546
+ };
547
+ }
548
+
549
+ function makeSchema(opts: {
550
+ name: string;
551
+ tables: DataTable[];
552
+ tables_resolved?: boolean;
553
+ child_schemas?: DatabaseSchema[];
554
+ child_schemas_resolved?: boolean;
555
+ }): DatabaseSchema {
556
+ return {
557
+ name: opts.name,
558
+ tables: opts.tables,
559
+ tables_resolved: opts.tables_resolved ?? true,
560
+ child_schemas: opts.child_schemas ?? [],
561
+ child_schemas_resolved: opts.child_schemas_resolved ?? true,
562
+ };
563
+ }
564
+
565
+ function makeDatabase(
566
+ name: string,
567
+ schemas: DatabaseSchema[],
568
+ schemas_resolved = true,
569
+ ): Database {
570
+ return { name, dialect: "duckdb", schemas, schemas_resolved, engine: null };
571
+ }
572
+
573
+ describe("schemaSubtreeMatchesSearch", () => {
574
+ it("returns false for an empty query", () => {
575
+ const schema = makeSchema({ name: "main", tables: [makeTable("users")] });
576
+ expect(schemaSubtreeMatchesSearch(schema, "")).toBe(false);
577
+ expect(schemaSubtreeMatchesSearch(schema, " ")).toBe(false);
578
+ expect(schemaSubtreeMatchesSearch(schema, undefined)).toBe(false);
579
+ });
580
+
581
+ it("matches a table name case-insensitively", () => {
582
+ const schema = makeSchema({ name: "main", tables: [makeTable("Users")] });
583
+ expect(schemaSubtreeMatchesSearch(schema, "user")).toBe(true);
584
+ expect(schemaSubtreeMatchesSearch(schema, "orders")).toBe(false);
585
+ });
586
+
587
+ it("matches a table in a resolved child schema", () => {
588
+ const schema = makeSchema({
589
+ name: "parent",
590
+ tables: [],
591
+ child_schemas: [
592
+ makeSchema({ name: "child", tables: [makeTable("orders")] }),
593
+ ],
594
+ });
595
+ expect(schemaSubtreeMatchesSearch(schema, "orders")).toBe(true);
596
+ });
597
+
598
+ it("ignores deferred tables so search never triggers a fetch", () => {
599
+ const schema = makeSchema({
600
+ name: "main",
601
+ tables: [makeTable("users")],
602
+ tables_resolved: false,
603
+ });
604
+ expect(schemaSubtreeMatchesSearch(schema, "users")).toBe(false);
605
+ });
606
+
607
+ it("ignores deferred child schemas", () => {
608
+ const schema = makeSchema({
609
+ name: "parent",
610
+ tables: [],
611
+ child_schemas: [
612
+ makeSchema({ name: "child", tables: [makeTable("orders")] }),
613
+ ],
614
+ child_schemas_resolved: false,
615
+ });
616
+ expect(schemaSubtreeMatchesSearch(schema, "orders")).toBe(false);
617
+ });
618
+ });
619
+
620
+ describe("shouldExpandDatabaseForSearch", () => {
621
+ it("returns false for an empty query", () => {
622
+ const db = makeDatabase("memory", [
623
+ makeSchema({ name: "main", tables: [makeTable("users")] }),
624
+ ]);
625
+ expect(shouldExpandDatabaseForSearch(db, "")).toBe(false);
626
+ expect(shouldExpandDatabaseForSearch(db, undefined)).toBe(false);
627
+ });
628
+
629
+ it("expands when a loaded schema contains a matching table", () => {
630
+ const db = makeDatabase("memory", [
631
+ makeSchema({ name: "main", tables: [makeTable("users")] }),
632
+ makeSchema({ name: "other", tables: [makeTable("orders")] }),
633
+ ]);
634
+ expect(shouldExpandDatabaseForSearch(db, "user")).toBe(true);
635
+ expect(shouldExpandDatabaseForSearch(db, "nomatch")).toBe(false);
636
+ });
637
+
638
+ it("does not expand when the schema list itself is deferred", () => {
639
+ const db = makeDatabase(
640
+ "memory",
641
+ [makeSchema({ name: "main", tables: [makeTable("users")] })],
642
+ /* schemas_resolved */ false,
643
+ );
644
+ expect(shouldExpandDatabaseForSearch(db, "users")).toBe(false);
645
+ });
646
+ });
@@ -83,6 +83,8 @@ import {
83
83
  areSchemasResolved,
84
84
  areTablesResolved,
85
85
  isSchemaless,
86
+ schemaSubtreeMatchesSearch,
87
+ shouldExpandDatabaseForSearch,
86
88
  sqlCode,
87
89
  tableUniqueId,
88
90
  } from "./utils";
@@ -360,7 +362,6 @@ export const DataSources: React.FC = () => {
360
362
  key={database.name}
361
363
  connection={connection}
362
364
  database={database}
363
- hasSearch={hasSearch}
364
365
  searchValue={searchValue}
365
366
  />
366
367
  ))}
@@ -435,7 +436,6 @@ interface DataSourceTree {
435
436
  dialect: string;
436
437
  engineName: string;
437
438
  databaseName: string;
438
- hasSearch: boolean;
439
439
  searchValue?: string;
440
440
  }
441
441
 
@@ -470,9 +470,8 @@ function buildSqlTableContext(
470
470
  const DatabaseTree: React.FC<{
471
471
  connection: DataSourceConnection;
472
472
  database: Database;
473
- hasSearch: boolean;
474
473
  searchValue?: string;
475
- }> = ({ connection, database, hasSearch, searchValue }) => {
474
+ }> = ({ connection, database, searchValue }) => {
476
475
  const tree = React.useMemo<DataSourceTree>(
477
476
  () => ({
478
477
  engineName: connection.name,
@@ -480,7 +479,6 @@ const DatabaseTree: React.FC<{
480
479
  dialect: connection.dialect,
481
480
  defaultSchema: connection.default_schema,
482
481
  defaultDatabase: connection.default_database,
483
- hasSearch,
484
482
  searchValue,
485
483
  }),
486
484
  [
@@ -489,7 +487,6 @@ const DatabaseTree: React.FC<{
489
487
  connection.default_schema,
490
488
  connection.default_database,
491
489
  database.name,
492
- hasSearch,
493
490
  searchValue,
494
491
  ],
495
492
  );
@@ -498,7 +495,7 @@ const DatabaseTree: React.FC<{
498
495
  <DatabaseItem
499
496
  engineName={connection.name}
500
497
  database={database}
501
- hasSearch={hasSearch}
498
+ searchValue={searchValue}
502
499
  >
503
500
  <DataSourceTreeContext.Provider value={tree}>
504
501
  <SchemaList
@@ -513,18 +510,30 @@ const DatabaseTree: React.FC<{
513
510
  };
514
511
 
515
512
  const DatabaseItem: React.FC<{
516
- hasSearch: boolean;
513
+ searchValue?: string;
517
514
  engineName: string;
518
515
  database: Database;
519
516
  children: React.ReactNode;
520
- }> = ({ hasSearch, engineName, database, children }) => {
521
- const [isExpanded, setIsExpanded] = React.useState(false);
517
+ }> = ({ searchValue, engineName, database, children }) => {
518
+ // Auto-expand during search only when a loaded schema under this database
519
+ // contains a matching table.
520
+ const expandForSearch = shouldExpandDatabaseForSearch(database, searchValue);
521
+ const [isExpanded, setIsExpanded] = React.useState(expandForSearch);
522
522
  const [isSelected, setIsSelected] = React.useState(false);
523
- const [prevHasSearch, setPrevHasSearch] = React.useState(hasSearch);
524
-
525
- if (prevHasSearch !== hasSearch) {
526
- setPrevHasSearch(hasSearch);
527
- setIsExpanded(hasSearch);
523
+ const [prevSearchValue, setPrevSearchValue] = React.useState(searchValue);
524
+ const [prevExpandForSearch, setPrevExpandForSearch] =
525
+ React.useState(expandForSearch);
526
+
527
+ // Re-sync when the query changes or when newly resolved data turns this
528
+ // branch into a match under the same query (deferred subtrees resolve
529
+ // asynchronously, so the decision can flip without the query changing).
530
+ if (
531
+ prevSearchValue !== searchValue ||
532
+ prevExpandForSearch !== expandForSearch
533
+ ) {
534
+ setPrevSearchValue(searchValue);
535
+ setPrevExpandForSearch(expandForSearch);
536
+ setIsExpanded(expandForSearch);
528
537
  }
529
538
 
530
539
  return (
@@ -657,12 +666,30 @@ interface SchemaNodeProps {
657
666
  const SchemaNode: React.FC<SchemaNodeProps> = (props) => {
658
667
  const { schema, schemaPath, depth } = props;
659
668
  const tree = useDataSourceTree();
660
- const { databaseName, hasSearch, searchValue } = tree;
661
- const [isExpanded, setIsExpanded] = React.useState(hasSearch);
669
+ const { databaseName, searchValue } = tree;
670
+ // Auto-expand during search only when this schema's loaded subtree contains a
671
+ // matching table.
672
+ const expandForSearch = schemaSubtreeMatchesSearch(schema, searchValue);
673
+ const [isExpanded, setIsExpanded] = React.useState(expandForSearch);
662
674
  const [isSelected, setIsSelected] = React.useState(false);
675
+ const [prevSearchValue, setPrevSearchValue] = React.useState(searchValue);
676
+ const [prevExpandForSearch, setPrevExpandForSearch] =
677
+ React.useState(expandForSearch);
663
678
  const uniqueValue = `${databaseName}:${schemaPath.join(".")}`;
664
679
  const childSchemas = schema.child_schemas ?? [];
665
680
 
681
+ // Re-sync when the query changes or when newly resolved data turns this
682
+ // subtree into a match under the same query (deferred tables/child schemas
683
+ // resolve asynchronously, so the decision can flip without the query changing).
684
+ if (
685
+ prevSearchValue !== searchValue ||
686
+ prevExpandForSearch !== expandForSearch
687
+ ) {
688
+ setPrevSearchValue(searchValue);
689
+ setPrevExpandForSearch(expandForSearch);
690
+ setIsExpanded(expandForSearch);
691
+ }
692
+
666
693
  return (
667
694
  <>
668
695
  <CommandItem
@@ -52,6 +52,51 @@ export function areChildSchemasResolved(schema: DatabaseSchema): boolean {
52
52
  return schema.child_schemas_resolved !== false;
53
53
  }
54
54
 
55
+ /**
56
+ * Whether a loaded schema subtree contains a table whose name matches
57
+ * `searchValue`. Deferred (not-yet-fetched) tables and child schemas are
58
+ * treated as non-matching, so searching never triggers a lazy catalog fetch on
59
+ * large/remote connections.
60
+ */
61
+ export function schemaSubtreeMatchesSearch(
62
+ schema: DatabaseSchema,
63
+ searchValue: string | undefined,
64
+ ): boolean {
65
+ const query = searchValue?.trim().toLowerCase();
66
+ if (!query) {
67
+ return false;
68
+ }
69
+ if (
70
+ areTablesResolved(schema) &&
71
+ schema.tables.some((table) => table.name.toLowerCase().includes(query))
72
+ ) {
73
+ return true;
74
+ }
75
+ if (areChildSchemasResolved(schema)) {
76
+ return (schema.child_schemas ?? []).some((child) =>
77
+ schemaSubtreeMatchesSearch(child, query),
78
+ );
79
+ }
80
+ return false;
81
+ }
82
+
83
+ /**
84
+ * Auto-expand a database row during search only when one of its loaded schemas
85
+ * contains a matching table. Returns false when the schema list itself is
86
+ * deferred.
87
+ */
88
+ export function shouldExpandDatabaseForSearch(
89
+ database: Database,
90
+ searchValue: string | undefined,
91
+ ): boolean {
92
+ if (!searchValue?.trim() || !areSchemasResolved(database)) {
93
+ return false;
94
+ }
95
+ return database.schemas.some((schema) =>
96
+ schemaSubtreeMatchesSearch(schema, searchValue),
97
+ );
98
+ }
99
+
55
100
  interface SqlCodeFormatter {
56
101
  /**
57
102
  * Format the table path based on dialect-specific rules
@@ -45,7 +45,12 @@ export const DEFAULT_AI_MODEL = "openai/gpt-4o";
45
45
  const AUTO_DOWNLOAD_FORMATS = ["html", "markdown", "ipynb"] as const;
46
46
 
47
47
  export type CopilotMode = NonNullable<schemas["AiConfig"]["mode"]>;
48
- export const COPILOT_MODES: CopilotMode[] = ["manual", "ask", "agent"];
48
+ export const COPILOT_MODES: CopilotMode[] = [
49
+ "manual",
50
+ "ask",
51
+ "agent",
52
+ "code_mode",
53
+ ];
49
54
 
50
55
  const AiConfigSchema = z
51
56
  .object({
package/src/css/md.css CHANGED
@@ -49,6 +49,18 @@
49
49
  .markdown h6 {
50
50
  font-weight: inherit;
51
51
  font-family: var(--heading-font);
52
+
53
+ /*
54
+ Anchor heading spacing to GitHub's markdown rhythm (Primer): a uniform
55
+ 24px top / 16px bottom margin and 1.25 line-height across every level,
56
+ letting font-size — not margin — carry the hierarchy. Tailwind Typography's
57
+ prose defaults scale heading margins per-level up to ~2em (≈48px on h2),
58
+ which is tuned for long-form articles and reads as too airy in a notebook.
59
+ Ref: https://github.com/sindresorhus/github-markdown-css
60
+ */
61
+ margin-top: 1.5rem;
62
+ margin-bottom: 1rem;
63
+ line-height: 1.25;
52
64
  }
53
65
 
54
66
  .markdown hr {
@@ -1 +0,0 @@
1
- import{t as e}from"./worker-CC0Oul9k.js";var t=e(((e,t)=>{t.exports={}}));export default t();