@marimo-team/islands 0.25.3-dev222 → 0.25.3-dev224

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 (30) hide show
  1. package/dist/{ErrorBoundary-Cg9q4kUW.js → ErrorBoundary-Ba6GHuhB.js} +1 -1
  2. package/dist/{code-visibility-Dp-DkL1e.js → code-visibility-BXNYYFb5.js} +1 -1
  3. package/dist/{common-DHr2MXY3.js → common-BHSOMSDG.js} +1 -1
  4. package/dist/{data-grid-overlay-editor-B-OJHxuO.js → data-grid-overlay-editor-CGLPw73V.js} +1 -1
  5. package/dist/{feedback-button-DGAcA9xH.js → feedback-button-BUf6rqKQ.js} +2 -2
  6. package/dist/{glide-data-editor-09FJOvJw.js → glide-data-editor-Dm3DXKHl.js} +3 -3
  7. package/dist/{globals-2eTRp9qC.js → globals-DjSaXJeh.js} +1 -1
  8. package/dist/main.js +4179 -4062
  9. package/dist/{number-overlay-editor-XJ1jA8Jv.js → number-overlay-editor-BVAnAmYf.js} +1 -1
  10. package/dist/{reveal-component-DxdLWQiq.js → reveal-component-Cfoa8xAb.js} +2 -2
  11. package/dist/style.css +1 -1
  12. package/package.json +1 -1
  13. package/src/components/app-config/ai-config.tsx +148 -147
  14. package/src/components/app-config/app-config-button.tsx +6 -5
  15. package/src/components/app-config/common.tsx +7 -9
  16. package/src/components/app-config/data-form.tsx +112 -113
  17. package/src/components/app-config/mcp-config.tsx +30 -53
  18. package/src/components/app-config/user-config-form.css +91 -0
  19. package/src/components/app-config/user-config-form.tsx +236 -298
  20. package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +9 -5
  21. package/src/components/editor/actions/__tests__/pair-with-agent.test.tsx +3 -3
  22. package/src/components/editor/actions/pair-with-agent-modal.tsx +5 -5
  23. package/src/components/editor/actions/pair-with-agent.tsx +10 -7
  24. package/src/core/notebook/replica/__tests__/executions.test.ts +201 -0
  25. package/src/core/notebook/replica/__tests__/replica.test.ts +6 -0
  26. package/src/core/notebook/replica/__tests__/results.test.ts +193 -0
  27. package/src/core/notebook/replica/index.ts +7 -0
  28. package/src/core/notebook/replica/replica.ts +34 -2
  29. package/src/core/notebook/replica/resources/executions.ts +157 -0
  30. package/src/core/notebook/replica/resources/results.ts +48 -0
@@ -1,21 +1,14 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
3
  import { zodResolver } from "@hookform/resolvers/zod";
4
+ import { useMediaQuery } from "@uidotdev/usehooks";
4
5
  import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
5
- import {
6
- AlertTriangleIcon,
7
- BrainIcon,
8
- CpuIcon,
9
- EditIcon,
10
- FlaskConicalIcon,
11
- FolderCog2,
12
- LayersIcon,
13
- MonitorIcon,
14
- } from "lucide-react";
6
+ import { AlertTriangleIcon, ArrowUpRightIcon } from "lucide-react";
15
7
  import React, { useId, useRef } from "react";
16
8
  import { useLocale } from "react-aria";
17
9
  import { useForm } from "react-hook-form";
18
10
  import type z from "zod";
11
+ import { MinimalShortcut } from "@/components/shortcuts/renderShortcut";
19
12
  import { Button } from "@/components/ui/button";
20
13
  import { Checkbox } from "@/components/ui/checkbox";
21
14
  import { acceptCompletionOnEnterAtom } from "@/core/codemirror/completion/accept-on-enter-atom";
@@ -30,7 +23,7 @@ import {
30
23
  import { Kbd } from "@/components/ui/kbd";
31
24
  import { NativeSelect } from "@/components/ui/native-select";
32
25
  import { NumberField } from "@/components/ui/number-field";
33
- import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
26
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
34
27
  import { KEYMAP_PRESETS } from "@/core/codemirror/keymaps/keymaps";
35
28
  import { capabilitiesAtom } from "@/core/config/capabilities";
36
29
  import { mergeConfig, useUserConfig } from "@/core/config/config";
@@ -49,7 +42,6 @@ import { useDebouncedCallback } from "@/hooks/useDebounce";
49
42
  import { Banner } from "@/plugins/impl/common/error-banner";
50
43
  import { THEMES } from "@/theme/useTheme";
51
44
  import { arrayToggle } from "@/utils/arrays";
52
- import { cn } from "@/utils/cn";
53
45
  import { autoPopulateModels } from "../ai/ai-utils";
54
46
  import { keyboardShortcutsAtom } from "../editor/controls/keyboard-shortcuts";
55
47
  import { Badge } from "../ui/badge";
@@ -65,49 +57,36 @@ import {
65
57
  useConfigOverride,
66
58
  } from "./is-overridden";
67
59
  import { OptionalFeatures } from "./optional-features";
60
+ import "./user-config-form.css";
68
61
 
69
62
  const categories = [
70
63
  {
71
64
  id: "editor",
72
65
  label: "Editor",
73
- Icon: EditIcon,
74
- className: "bg-(--blue-4)",
75
66
  },
76
67
  {
77
68
  id: "display",
78
69
  label: "Display",
79
- Icon: MonitorIcon,
80
- className: "bg-(--grass-4)",
81
70
  },
82
71
  {
83
72
  id: "packageManagementAndData",
84
73
  label: "Packages & Data",
85
- Icon: LayersIcon,
86
- className: "bg-(--red-4)",
87
74
  },
88
75
  {
89
76
  id: "runtime",
90
77
  label: "Runtime",
91
- Icon: CpuIcon,
92
- className: "bg-(--amber-4)",
93
78
  },
94
79
  {
95
80
  id: "ai",
96
81
  label: "AI",
97
- Icon: BrainIcon,
98
- className: "bg-[linear-gradient(45deg,var(--purple-5),var(--cyan-5))]",
99
82
  },
100
83
  {
101
84
  id: "optionalDeps",
102
85
  label: "Optional Dependencies",
103
- Icon: FolderCog2,
104
- className: "bg-(--orange-4)",
105
86
  },
106
87
  {
107
88
  id: "labs",
108
89
  label: "Labs",
109
- Icon: FlaskConicalIcon,
110
- className: "bg-(--slate-4)",
111
90
  },
112
91
  ] as const;
113
92
 
@@ -122,6 +101,7 @@ const LOCALE_SYSTEM_VALUE = "__system__";
122
101
 
123
102
  export const UserConfigForm: React.FC = () => {
124
103
  const [config, setConfig] = useUserConfig();
104
+ const isWideLayout = useMediaQuery("(min-width: 640px)");
125
105
  const [acceptOnEnter, setAcceptOnEnter] = useAtom(
126
106
  acceptCompletionOnEnterAtom,
127
107
  );
@@ -222,9 +202,7 @@ export const UserConfigForm: React.FC = () => {
222
202
  name="save.autosave"
223
203
  render={({ field, override }) => (
224
204
  <FormItem className={formItemClasses}>
225
- <FormLabel className="font-normal">
226
- Autosave enabled
227
- </FormLabel>
205
+ <FormLabel>Autosave</FormLabel>
228
206
  <FormControl>
229
207
  <Checkbox
230
208
  data-testid="autosave-checkbox"
@@ -235,6 +213,9 @@ export const UserConfigForm: React.FC = () => {
235
213
  }}
236
214
  />
237
215
  </FormControl>
216
+ <FormDescription>
217
+ Automatically save changes to the notebook.
218
+ </FormDescription>
238
219
  <FormMessage />
239
220
  <IsOverridden override={override} />
240
221
  </FormItem>
@@ -251,6 +232,9 @@ export const UserConfigForm: React.FC = () => {
251
232
  return (
252
233
  <FormItem className={formItemClasses}>
253
234
  <FormLabel>Autosave delay (seconds)</FormLabel>
235
+ <FormDescription>
236
+ Time to wait after the last change before saving.
237
+ </FormDescription>
254
238
  <FormControl>
255
239
  <NumberField
256
240
  aria-label="Autosave delay"
@@ -283,9 +267,15 @@ export const UserConfigForm: React.FC = () => {
283
267
  control={form.control}
284
268
  name="runtime.default_auto_download"
285
269
  render={({ field, override }) => (
286
- <div className="flex flex-col gap-y-1">
270
+ <div className="settings-field flex flex-col gap-y-1">
287
271
  <FormItem className={formItemClasses}>
288
272
  <FormLabel>Save cell outputs as</FormLabel>
273
+ <FormDescription>
274
+ When enabled, marimo will periodically save notebooks in
275
+ your selected formats (HTML, IPYNB) to a folder named{" "}
276
+ <Kbd className="inline">__marimo__</Kbd> next to your
277
+ notebook file.
278
+ </FormDescription>
289
279
  <FormControl>
290
280
  <div className="flex gap-4">
291
281
  <div className="flex items-center space-x-2">
@@ -333,12 +323,6 @@ export const UserConfigForm: React.FC = () => {
333
323
  <FormMessage />
334
324
  <IsOverridden override={override} />
335
325
  </FormItem>
336
- <FormDescription>
337
- When enabled, marimo will periodically save notebooks in
338
- your selected formats (HTML, IPYNB) to a folder named{" "}
339
- <Kbd className="inline">__marimo__</Kbd> next to your
340
- notebook file.
341
- </FormDescription>
342
326
  </div>
343
327
  )}
344
328
  />
@@ -349,9 +333,7 @@ export const UserConfigForm: React.FC = () => {
349
333
  name="save.format_on_save"
350
334
  render={({ field, override }) => (
351
335
  <FormItem className={formItemClasses}>
352
- <FormLabel className="font-normal">
353
- Format on save
354
- </FormLabel>
336
+ <FormLabel>Format on save</FormLabel>
355
337
  <FormControl>
356
338
  <Checkbox
357
339
  data-testid="format-on-save-checkbox"
@@ -362,6 +344,9 @@ export const UserConfigForm: React.FC = () => {
362
344
  }}
363
345
  />
364
346
  </FormControl>
347
+ <FormDescription>
348
+ Automatically format code when saving the notebook.
349
+ </FormDescription>
365
350
  <FormMessage />
366
351
  <IsOverridden override={override} />
367
352
  </FormItem>
@@ -371,9 +356,12 @@ export const UserConfigForm: React.FC = () => {
371
356
  control={form.control}
372
357
  name="formatting.line_length"
373
358
  render={({ field, override }) => (
374
- <div className="flex flex-col space-y-1">
359
+ <div className="settings-field flex flex-col space-y-1">
375
360
  <FormItem className={formItemClasses}>
376
361
  <FormLabel>Line length</FormLabel>
362
+ <FormDescription>
363
+ Maximum line length when formatting code.
364
+ </FormDescription>
377
365
  <FormControl>
378
366
  <NumberField
379
367
  aria-label="Line length"
@@ -396,10 +384,6 @@ export const UserConfigForm: React.FC = () => {
396
384
  <FormMessage />
397
385
  <IsOverridden override={override} />
398
386
  </FormItem>
399
-
400
- <FormDescription>
401
- Maximum line length when formatting code.
402
- </FormDescription>
403
387
  </div>
404
388
  )}
405
389
  />
@@ -408,9 +392,13 @@ export const UserConfigForm: React.FC = () => {
408
392
  control={form.control}
409
393
  name="formatting.infer_types"
410
394
  render={({ field, override }) => (
411
- <div className="flex flex-col space-y-1">
395
+ <div className="settings-field flex flex-col space-y-1">
412
396
  <FormItem className={formItemClasses}>
413
397
  <FormLabel>Infer types on save</FormLabel>
398
+ <FormDescription>
399
+ Add inferred type annotations to cell signatures when
400
+ saving.
401
+ </FormDescription>
414
402
  <FormControl>
415
403
  <NativeSelect
416
404
  data-testid="infer-types-select"
@@ -429,10 +417,6 @@ export const UserConfigForm: React.FC = () => {
429
417
  <FormMessage />
430
418
  <IsOverridden override={override} />
431
419
  </FormItem>
432
- <FormDescription>
433
- Add inferred type annotations to cell signatures when
434
- saving.
435
- </FormDescription>
436
420
  </div>
437
421
  )}
438
422
  />
@@ -443,11 +427,9 @@ export const UserConfigForm: React.FC = () => {
443
427
  control={form.control}
444
428
  name="completion.activate_on_typing"
445
429
  render={({ field, override }) => (
446
- <div className="flex flex-col space-y-1">
430
+ <div className="settings-field flex flex-col space-y-1">
447
431
  <FormItem className={formItemClasses}>
448
- <FormLabel className="font-normal">
449
- Autocomplete
450
- </FormLabel>
432
+ <FormLabel>Autocomplete</FormLabel>
451
433
  <FormControl>
452
434
  <Checkbox
453
435
  data-testid="autocomplete-checkbox"
@@ -462,32 +444,21 @@ export const UserConfigForm: React.FC = () => {
462
444
  <IsOverridden override={override} />
463
445
  </FormItem>
464
446
  <FormDescription>
465
- When unchecked, code completion is still available through
466
- a hotkey.
447
+ When unchecked, code completion is still available with{" "}
448
+ <Kbd className="inline-flex">
449
+ <MinimalShortcut
450
+ shortcut="cell.complete"
451
+ className="inline-flex ml-0 tracking-normal opacity-100"
452
+ />
453
+ </Kbd>
454
+ .
467
455
  </FormDescription>
468
-
469
- <div>
470
- <Button
471
- variant="link"
472
- className="mb-0 px-0"
473
- type="button"
474
- onClick={(evt) => {
475
- evt.preventDefault();
476
- evt.stopPropagation();
477
- setActiveCategory("ai");
478
- }}
479
- >
480
- Edit AI autocomplete
481
- </Button>
482
- </div>
483
456
  </div>
484
457
  )}
485
458
  />
486
- <div className="flex flex-col space-y-1">
459
+ <div className="settings-field flex flex-col space-y-1">
487
460
  <FormItem className={formItemClasses}>
488
- <FormLabel className="font-normal">
489
- Accept suggestion on Enter
490
- </FormLabel>
461
+ <FormLabel>Accept suggestion on Enter</FormLabel>
491
462
  <FormControl>
492
463
  <Checkbox
493
464
  data-testid="accept-completion-on-enter-checkbox"
@@ -499,8 +470,9 @@ export const UserConfigForm: React.FC = () => {
499
470
  </FormControl>
500
471
  </FormItem>
501
472
  <FormDescription>
502
- When unchecked, pressing Enter inserts a new line instead of
503
- accepting an autocomplete suggestion. Use Tab to accept
473
+ When unchecked, pressing <Kbd className="inline">Enter</Kbd>{" "}
474
+ inserts a new line instead of accepting an autocomplete
475
+ suggestion. Use <Kbd className="inline">Tab</Kbd> to accept
504
476
  suggestions.
505
477
  </FormDescription>
506
478
  </div>
@@ -508,11 +480,9 @@ export const UserConfigForm: React.FC = () => {
508
480
  control={form.control}
509
481
  name="completion.signature_hint_on_typing"
510
482
  render={({ field, override }) => (
511
- <div className="flex flex-col space-y-1">
483
+ <div className="settings-field flex flex-col space-y-1">
512
484
  <FormItem className={formItemClasses}>
513
- <FormLabel className="font-normal">
514
- Signature hints
515
- </FormLabel>
485
+ <FormLabel>Signature hints</FormLabel>
516
486
  <FormControl>
517
487
  <Checkbox
518
488
  data-testid="signature-hint-on-type-checkbox"
@@ -537,11 +507,9 @@ export const UserConfigForm: React.FC = () => {
537
507
  control={form.control}
538
508
  name="completion.auto_close_pairs"
539
509
  render={({ field, override }) => (
540
- <div className="flex flex-col space-y-1">
510
+ <div className="settings-field flex flex-col space-y-1">
541
511
  <FormItem className={formItemClasses}>
542
- <FormLabel className="font-normal">
543
- Auto-close pairs
544
- </FormLabel>
512
+ <FormLabel>Auto-close pairs</FormLabel>
545
513
  <FormControl>
546
514
  <Checkbox
547
515
  data-testid="auto-close-pairs-checkbox"
@@ -567,15 +535,89 @@ export const UserConfigForm: React.FC = () => {
567
535
  )}
568
536
  />
569
537
  </SettingGroup>
570
- <SettingGroup title="Language Servers">
538
+ <SettingGroup title="Keymap">
539
+ <Button
540
+ variant="link"
541
+ className="h-auto self-start gap-1 p-0"
542
+ type="button"
543
+ onClick={() => setKeyboardShortcutsOpen(true)}
544
+ >
545
+ Edit keyboard shortcuts
546
+ <ArrowUpRightIcon className="h-3.5 w-3.5" aria-hidden={true} />
547
+ </Button>
548
+ <OverriddenFormField
549
+ control={form.control}
550
+ name="keymap.preset"
551
+ render={({ field, override }) => (
552
+ <div className="settings-field flex flex-col space-y-1">
553
+ <FormItem className={formItemClasses}>
554
+ <FormLabel>Keymap</FormLabel>
555
+ <FormControl>
556
+ <NativeSelect
557
+ data-testid="keymap-select"
558
+ onChange={(e) => field.onChange(e.target.value)}
559
+ value={override.value}
560
+ disabled={field.disabled || override.isOverridden}
561
+ className="inline-flex mr-2"
562
+ >
563
+ {KEYMAP_PRESETS.map((option) => (
564
+ <option value={option} key={option}>
565
+ {option}
566
+ </option>
567
+ ))}
568
+ </NativeSelect>
569
+ </FormControl>
570
+ <FormMessage />
571
+ <IsOverridden override={override} />
572
+ </FormItem>
573
+ </div>
574
+ )}
575
+ />
576
+ <OverriddenFormField
577
+ control={form.control}
578
+ name="keymap.destructive_delete"
579
+ render={({ field, override }) => (
580
+ <div className="settings-field flex flex-col space-y-1">
581
+ <FormItem className={formItemClasses}>
582
+ <FormLabel>Destructive delete</FormLabel>
583
+ <FormControl>
584
+ <Checkbox
585
+ data-testid="destructive-delete-checkbox"
586
+ checked={override.value}
587
+ disabled={field.disabled || override.isOverridden}
588
+ onCheckedChange={(checked) => {
589
+ field.onChange(Boolean(checked));
590
+ }}
591
+ />
592
+ </FormControl>
593
+ <FormMessage />
594
+ <IsOverridden override={override} />
595
+ </FormItem>
596
+ <FormDescription className="flex items-center gap-1">
597
+ Allow deleting non-empty cells
598
+ <Tooltip
599
+ content={
600
+ <div className="max-w-xs">
601
+ <strong>Use with caution:</strong> Deleting cells
602
+ with code can lose work and computed results since
603
+ variables are removed from memory.
604
+ </div>
605
+ }
606
+ >
607
+ <AlertTriangleIcon className="w-3 h-3 text-(--amber-11)" />
608
+ </Tooltip>
609
+ </FormDescription>
610
+ </div>
611
+ )}
612
+ />
613
+ </SettingGroup>
614
+ <SettingGroup title="Language servers">
571
615
  <FormDescription>
572
616
  See the{" "}
573
617
  <ExternalLink href="https://docs.marimo.io/guides/editor_features/language_server/">
574
618
  docs
575
619
  </ExternalLink>{" "}
576
- for more information about language server support.
577
- </FormDescription>
578
- <FormDescription>
620
+ for more information about language server support.{" "}
579
621
  <strong>Note:</strong> When using multiple language servers,
580
622
  different features may conflict.
581
623
  </FormDescription>
@@ -584,7 +626,7 @@ export const UserConfigForm: React.FC = () => {
584
626
  control={form.control}
585
627
  name="language_servers.pylsp.enabled"
586
628
  render={({ field, override }) => (
587
- <div className="flex flex-col gap-1">
629
+ <div className="settings-field flex flex-col gap-1">
588
630
  <FormItem className={formItemClasses}>
589
631
  <FormLabel>
590
632
  <Badge variant="defaultOutline" className="mr-2">
@@ -624,7 +666,7 @@ export const UserConfigForm: React.FC = () => {
624
666
  control={form.control}
625
667
  name="language_servers.basedpyright.enabled"
626
668
  render={({ field, override }) => (
627
- <div className="flex flex-col gap-1">
669
+ <div className="settings-field flex flex-col gap-1">
628
670
  <FormItem className={formItemClasses}>
629
671
  <FormLabel>
630
672
  <Badge variant="defaultOutline" className="mr-2">
@@ -664,7 +706,7 @@ export const UserConfigForm: React.FC = () => {
664
706
  control={form.control}
665
707
  name="language_servers.pyrefly.enabled"
666
708
  render={({ field, override }) => (
667
- <div className="flex flex-col gap-1">
709
+ <div className="settings-field flex flex-col gap-1">
668
710
  <FormItem className={formItemClasses}>
669
711
  <FormLabel>
670
712
  <Badge variant="defaultOutline" className="mr-2">
@@ -703,7 +745,7 @@ export const UserConfigForm: React.FC = () => {
703
745
  control={form.control}
704
746
  name="language_servers.ty.enabled"
705
747
  render={({ field, override }) => (
706
- <div className="flex flex-col gap-1">
748
+ <div className="settings-field flex flex-col gap-1">
707
749
  <FormItem className={formItemClasses}>
708
750
  <FormLabel>
709
751
  <Badge variant="defaultOutline" className="mr-2">
@@ -765,91 +807,6 @@ export const UserConfigForm: React.FC = () => {
765
807
  )}
766
808
  />
767
809
  </SettingGroup>
768
-
769
- <SettingGroup title="Keymap">
770
- <OverriddenFormField
771
- control={form.control}
772
- name="keymap.preset"
773
- render={({ field, override }) => (
774
- <div className="flex flex-col space-y-1">
775
- <FormItem className={formItemClasses}>
776
- <FormLabel>Keymap</FormLabel>
777
- <FormControl>
778
- <NativeSelect
779
- data-testid="keymap-select"
780
- onChange={(e) => field.onChange(e.target.value)}
781
- value={override.value}
782
- disabled={field.disabled || override.isOverridden}
783
- className="inline-flex mr-2"
784
- >
785
- {KEYMAP_PRESETS.map((option) => (
786
- <option value={option} key={option}>
787
- {option}
788
- </option>
789
- ))}
790
- </NativeSelect>
791
- </FormControl>
792
- <FormMessage />
793
- <IsOverridden override={override} />
794
- </FormItem>
795
- </div>
796
- )}
797
- />
798
- <OverriddenFormField
799
- control={form.control}
800
- name="keymap.destructive_delete"
801
- render={({ field, override }) => (
802
- <div className="flex flex-col space-y-1">
803
- <FormItem className={formItemClasses}>
804
- <FormLabel className="font-normal">
805
- Destructive delete
806
- </FormLabel>
807
- <FormControl>
808
- <Checkbox
809
- data-testid="destructive-delete-checkbox"
810
- checked={override.value}
811
- disabled={field.disabled || override.isOverridden}
812
- onCheckedChange={(checked) => {
813
- field.onChange(Boolean(checked));
814
- }}
815
- />
816
- </FormControl>
817
- <FormMessage />
818
- <IsOverridden override={override} />
819
- </FormItem>
820
- <FormDescription className="flex items-center gap-1">
821
- Allow deleting non-empty cells
822
- <Tooltip
823
- content={
824
- <div className="max-w-xs">
825
- <strong>Use with caution:</strong> Deleting cells
826
- with code can lose work and computed results since
827
- variables are removed from memory.
828
- </div>
829
- }
830
- >
831
- <AlertTriangleIcon className="w-3 h-3 text-(--amber-11)" />
832
- </Tooltip>
833
- </FormDescription>
834
-
835
- <div>
836
- <Button
837
- variant="link"
838
- className="mb-0 px-0"
839
- type="button"
840
- onClick={(evt) => {
841
- evt.preventDefault();
842
- evt.stopPropagation();
843
- setKeyboardShortcutsOpen(true);
844
- }}
845
- >
846
- Edit Keyboard Shortcuts
847
- </Button>
848
- </div>
849
- </div>
850
- )}
851
- />
852
- </SettingGroup>
853
810
  </>
854
811
  );
855
812
  case "display":
@@ -860,9 +817,13 @@ export const UserConfigForm: React.FC = () => {
860
817
  control={form.control}
861
818
  name="display.default_width"
862
819
  render={({ field, override }) => (
863
- <div className="flex flex-col space-y-1">
820
+ <div className="settings-field flex flex-col space-y-1">
864
821
  <FormItem className={formItemClasses}>
865
822
  <FormLabel>Default width</FormLabel>
823
+ <FormDescription>
824
+ The default app width for new notebooks; overridden by
825
+ "width" in the application config.
826
+ </FormDescription>
866
827
  <FormControl>
867
828
  <NativeSelect
868
829
  data-testid="user-config-width-select"
@@ -881,11 +842,6 @@ export const UserConfigForm: React.FC = () => {
881
842
  <FormMessage />
882
843
  <IsOverridden override={override} />
883
844
  </FormItem>
884
-
885
- <FormDescription>
886
- The default app width for new notebooks; overridden by
887
- "width" in the application config.
888
- </FormDescription>
889
845
  </div>
890
846
  )}
891
847
  />
@@ -893,9 +849,13 @@ export const UserConfigForm: React.FC = () => {
893
849
  control={form.control}
894
850
  name="display.theme"
895
851
  render={({ field, override }) => (
896
- <div className="flex flex-col space-y-1">
852
+ <div className="settings-field flex flex-col space-y-1">
897
853
  <FormItem className={formItemClasses}>
898
854
  <FormLabel>Theme</FormLabel>
855
+ <FormDescription>
856
+ This theme will be applied to the user's configuration;
857
+ it does not affect theme when sharing the notebook.
858
+ </FormDescription>
899
859
  <FormControl>
900
860
  <NativeSelect
901
861
  data-testid="theme-select"
@@ -914,11 +874,6 @@ export const UserConfigForm: React.FC = () => {
914
874
  <FormMessage />
915
875
  <IsOverridden override={override} />
916
876
  </FormItem>
917
-
918
- <FormDescription>
919
- This theme will be applied to the user's configuration; it
920
- does not affect theme when sharing the notebook.
921
- </FormDescription>
922
877
  </div>
923
878
  )}
924
879
  />
@@ -955,9 +910,14 @@ export const UserConfigForm: React.FC = () => {
955
910
  control={form.control}
956
911
  name="display.locale"
957
912
  render={({ field, override }) => (
958
- <div className="flex flex-col space-y-1">
913
+ <div className="settings-field flex flex-col space-y-1">
959
914
  <FormItem className={formItemClasses}>
960
915
  <FormLabel>Locale</FormLabel>
916
+ <FormDescription>
917
+ The locale to use for the notebook. If your desired
918
+ locale is not listed, you can change it manually via{" "}
919
+ <Kbd className="inline">marimo config show</Kbd>.
920
+ </FormDescription>
961
921
  <FormControl>
962
922
  <NativeSelect
963
923
  data-testid="locale-select"
@@ -983,12 +943,6 @@ export const UserConfigForm: React.FC = () => {
983
943
  <FormMessage />
984
944
  <IsOverridden override={override} />
985
945
  </FormItem>
986
-
987
- <FormDescription>
988
- The locale to use for the notebook. If your desired locale
989
- is not listed, you can change it manually via{" "}
990
- <Kbd className="inline">marimo config show</Kbd>.
991
- </FormDescription>
992
946
  </div>
993
947
  )}
994
948
  />
@@ -996,7 +950,7 @@ export const UserConfigForm: React.FC = () => {
996
950
  control={form.control}
997
951
  name="display.reference_highlighting"
998
952
  render={({ field, override }) => (
999
- <div className="flex flex-col space-y-1">
953
+ <div className="settings-field flex flex-col space-y-1">
1000
954
  <FormItem className={formItemClasses}>
1001
955
  <FormLabel>Reference highlighting</FormLabel>
1002
956
  <FormControl>
@@ -1022,7 +976,7 @@ export const UserConfigForm: React.FC = () => {
1022
976
  control={form.control}
1023
977
  name="display.code_lens"
1024
978
  render={({ field, override }) => (
1025
- <div className="flex flex-col space-y-1">
979
+ <div className="settings-field flex flex-col space-y-1">
1026
980
  <FormItem className={formItemClasses}>
1027
981
  <FormLabel>Code lens</FormLabel>
1028
982
  <FormControl>
@@ -1050,9 +1004,12 @@ export const UserConfigForm: React.FC = () => {
1050
1004
  control={form.control}
1051
1005
  name="display.cell_output"
1052
1006
  render={({ field, override }) => (
1053
- <div className="flex flex-col space-y-1">
1007
+ <div className="settings-field flex flex-col space-y-1">
1054
1008
  <FormItem className={formItemClasses}>
1055
1009
  <FormLabel>Cell output area</FormLabel>
1010
+ <FormDescription>
1011
+ Where to display cell's output.
1012
+ </FormDescription>
1056
1013
  <FormControl>
1057
1014
  <NativeSelect
1058
1015
  data-testid="cell-output-select"
@@ -1071,10 +1028,6 @@ export const UserConfigForm: React.FC = () => {
1071
1028
  <FormMessage />
1072
1029
  <IsOverridden override={override} />
1073
1030
  </FormItem>
1074
-
1075
- <FormDescription>
1076
- Where to display cell's output.
1077
- </FormDescription>
1078
1031
  </div>
1079
1032
  )}
1080
1033
  />
@@ -1084,15 +1037,31 @@ export const UserConfigForm: React.FC = () => {
1084
1037
  case "packageManagementAndData":
1085
1038
  return (
1086
1039
  <>
1087
- <SettingGroup title="Package Management">
1040
+ <SettingGroup title="Package management">
1088
1041
  <OverriddenFormField
1089
1042
  control={form.control}
1090
1043
  disabled={isWasmRuntime}
1091
1044
  name="package_management.manager"
1092
1045
  render={({ field, override }) => (
1093
- <div className="flex flex-col space-y-1">
1046
+ <div className="settings-field flex flex-col space-y-1">
1094
1047
  <FormItem className={formItemClasses}>
1095
1048
  <FormLabel>Manager</FormLabel>
1049
+ <FormDescription>
1050
+ When marimo comes across a module that is not installed,
1051
+ you will be prompted to install it using your preferred
1052
+ package manager. Learn more in the{" "}
1053
+ <ExternalLink href="https://docs.marimo.io/guides/editor_features/package_management.html">
1054
+ docs
1055
+ </ExternalLink>
1056
+ .
1057
+ <br />
1058
+ <br />
1059
+ Running marimo in a{" "}
1060
+ <ExternalLink href="https://docs.marimo.io/guides/package_management/inlining_dependencies.html">
1061
+ sandboxed environment
1062
+ </ExternalLink>{" "}
1063
+ is only supported by <Kbd className="inline">uv</Kbd>
1064
+ </FormDescription>
1096
1065
  <FormControl>
1097
1066
  <NativeSelect
1098
1067
  data-testid="package-manager-select"
@@ -1111,30 +1080,11 @@ export const UserConfigForm: React.FC = () => {
1111
1080
  <FormMessage />
1112
1081
  <IsOverridden override={override} />
1113
1082
  </FormItem>
1114
-
1115
- <FormDescription>
1116
- When marimo comes across a module that is not installed,
1117
- you will be prompted to install it using your preferred
1118
- package manager. Learn more in the{" "}
1119
- <ExternalLink href="https://docs.marimo.io/guides/editor_features/package_management.html">
1120
- docs
1121
- </ExternalLink>
1122
- .
1123
- <br />
1124
- <br />
1125
- Running marimo in a{" "}
1126
- <ExternalLink href="https://docs.marimo.io/guides/package_management/inlining_dependencies.html">
1127
- sandboxed environment
1128
- </ExternalLink>{" "}
1129
- is only supported by <Kbd className="inline">uv</Kbd>
1130
- </FormDescription>
1131
1083
  </div>
1132
1084
  )}
1133
1085
  />
1134
1086
  </SettingGroup>
1135
- <SettingGroup title="Data">
1136
- <DataForm form={form} onSubmit={onSubmit} />
1137
- </SettingGroup>
1087
+ <DataForm form={form} onSubmit={onSubmit} />
1138
1088
  </>
1139
1089
  );
1140
1090
  case "runtime":
@@ -1144,11 +1094,9 @@ export const UserConfigForm: React.FC = () => {
1144
1094
  control={form.control}
1145
1095
  name="runtime.auto_instantiate"
1146
1096
  render={({ field, override }) => (
1147
- <div className="flex flex-col gap-y-1">
1097
+ <div className="settings-field flex flex-col gap-y-1">
1148
1098
  <FormItem className={formItemClasses}>
1149
- <FormLabel className="font-normal">
1150
- Autorun on startup
1151
- </FormLabel>
1099
+ <FormLabel>Autorun on startup</FormLabel>
1152
1100
  <FormControl>
1153
1101
  <Checkbox
1154
1102
  data-testid="auto-instantiate-checkbox"
@@ -1171,11 +1119,16 @@ export const UserConfigForm: React.FC = () => {
1171
1119
  control={form.control}
1172
1120
  name="runtime.on_cell_change"
1173
1121
  render={({ field, override }) => (
1174
- <div className="flex flex-col gap-y-1">
1122
+ <div className="settings-field flex flex-col gap-y-1">
1175
1123
  <FormItem className={formItemClasses}>
1176
- <FormLabel className="font-normal">
1177
- On cell change
1178
- </FormLabel>
1124
+ <FormLabel>On cell change</FormLabel>
1125
+ <FormDescription>
1126
+ Whether marimo should automatically run cells or just mark
1127
+ them as stale. If "autorun", marimo will automatically run
1128
+ affected cells when a cell is run or a UI element is
1129
+ interacted with; if "lazy", marimo will mark affected
1130
+ cells as stale but won't re-run them.
1131
+ </FormDescription>
1179
1132
  <FormControl>
1180
1133
  <NativeSelect
1181
1134
  data-testid="on-cell-change-select"
@@ -1194,13 +1147,6 @@ export const UserConfigForm: React.FC = () => {
1194
1147
  <FormMessage />
1195
1148
  <IsOverridden override={override} />
1196
1149
  </FormItem>
1197
- <FormDescription>
1198
- Whether marimo should automatically run cells or just mark
1199
- them as stale. If "autorun", marimo will automatically run
1200
- affected cells when a cell is run or a UI element is
1201
- interacted with; if "lazy", marimo will mark affected cells
1202
- as stale but won't re-run them.
1203
- </FormDescription>
1204
1150
  </div>
1205
1151
  )}
1206
1152
  />
@@ -1208,11 +1154,15 @@ export const UserConfigForm: React.FC = () => {
1208
1154
  control={form.control}
1209
1155
  name="runtime.auto_reload"
1210
1156
  render={({ field, override }) => (
1211
- <div className="flex flex-col gap-y-1">
1157
+ <div className="settings-field flex flex-col gap-y-1">
1212
1158
  <FormItem className={formItemClasses}>
1213
- <FormLabel className="font-normal">
1214
- On module change
1215
- </FormLabel>
1159
+ <FormLabel>On module change</FormLabel>
1160
+ <FormDescription>
1161
+ Whether marimo should automatically reload modules before
1162
+ executing cells. If "lazy", marimo will mark cells
1163
+ affected by module modifications as stale; if "autorun",
1164
+ affected cells will be automatically re-run.
1165
+ </FormDescription>
1216
1166
  <FormControl>
1217
1167
  <NativeSelect
1218
1168
  data-testid="auto-reload-select"
@@ -1231,12 +1181,6 @@ export const UserConfigForm: React.FC = () => {
1231
1181
  <FormMessage />
1232
1182
  <IsOverridden override={override} />
1233
1183
  </FormItem>
1234
- <FormDescription>
1235
- Whether marimo should automatically reload modules before
1236
- executing cells. If "lazy", marimo will mark cells affected
1237
- by module modifications as stale; if "autorun", affected
1238
- cells will be automatically re-run.
1239
- </FormDescription>
1240
1184
  </div>
1241
1185
  )}
1242
1186
  />
@@ -1245,11 +1189,9 @@ export const UserConfigForm: React.FC = () => {
1245
1189
  control={form.control}
1246
1190
  name="runtime.reactive_tests"
1247
1191
  render={({ field, override }) => (
1248
- <div className="flex flex-col gap-y-1">
1192
+ <div className="settings-field flex flex-col gap-y-1">
1249
1193
  <FormItem className={formItemClasses}>
1250
- <FormLabel className="font-normal">
1251
- Autorun Unit Tests
1252
- </FormLabel>
1194
+ <FormLabel>Autorun Unit Tests</FormLabel>
1253
1195
  <FormControl>
1254
1196
  <Checkbox
1255
1197
  data-testid="reactive-test-checkbox"
@@ -1272,7 +1214,7 @@ export const UserConfigForm: React.FC = () => {
1272
1214
  />
1273
1215
 
1274
1216
  <FormDescription>
1275
- Learn more in the{" "}
1217
+ Learn more about how marimo runs cells in the{" "}
1276
1218
  <ExternalLink href="https://docs.marimo.io/guides/reactivity/#configuring-how-marimo-runs-cells">
1277
1219
  docs
1278
1220
  </ExternalLink>
@@ -1286,8 +1228,8 @@ export const UserConfigForm: React.FC = () => {
1286
1228
  return <OptionalFeatures />;
1287
1229
  case "labs":
1288
1230
  return (
1289
- <SettingGroup title="Experimental Features">
1290
- <p className="text-sm text-muted-secondary mb-4">
1231
+ <SettingGroup title="Experimental features">
1232
+ <p className="text-sm text-muted-foreground mb-4">
1291
1233
  ⚠️ These features are experimental and may require restarting your
1292
1234
  notebook to take effect.
1293
1235
  </p>
@@ -1296,9 +1238,9 @@ export const UserConfigForm: React.FC = () => {
1296
1238
  control={form.control}
1297
1239
  name="experimental.debugger"
1298
1240
  render={({ field, override }) => (
1299
- <div className="flex flex-col gap-y-1">
1241
+ <div className="settings-field flex flex-col gap-y-1">
1300
1242
  <FormItem className={formItemClasses}>
1301
- <FormLabel className="font-normal">Debugger</FormLabel>
1243
+ <FormLabel>Debugger</FormLabel>
1302
1244
  <FormControl>
1303
1245
  <Checkbox
1304
1246
  data-testid="debugger-checkbox"
@@ -1322,9 +1264,9 @@ export const UserConfigForm: React.FC = () => {
1322
1264
  control={form.control}
1323
1265
  name="experimental.line_timing"
1324
1266
  render={({ field, override }) => (
1325
- <div className="flex flex-col gap-y-1">
1267
+ <div className="settings-field flex flex-col gap-y-1">
1326
1268
  <FormItem className={formItemClasses}>
1327
- <FormLabel className="font-normal">Line Timing</FormLabel>
1269
+ <FormLabel>Line Timing</FormLabel>
1328
1270
  <FormControl>
1329
1271
  <Checkbox
1330
1272
  data-testid="line-timing-checkbox"
@@ -1350,9 +1292,7 @@ export const UserConfigForm: React.FC = () => {
1350
1292
  };
1351
1293
 
1352
1294
  const configMessage = (
1353
- <p className="text-muted-secondary">
1354
- User configuration is stored in <Kbd className="inline">marimo.toml</Kbd>
1355
- <br />
1295
+ <p className="text-muted-foreground">
1356
1296
  Run <Kbd className="inline">marimo config show</Kbd> in your terminal to
1357
1297
  show your current configuration and file location.
1358
1298
  </p>
@@ -1363,49 +1303,47 @@ export const UserConfigForm: React.FC = () => {
1363
1303
  <form
1364
1304
  ref={formElement}
1365
1305
  onChange={form.handleSubmit((values) => onSubmit(values))}
1366
- className="flex text-pretty overflow-hidden"
1306
+ className="user-config-form flex min-h-0 flex-1 text-sm overflow-hidden"
1367
1307
  >
1368
1308
  <Tabs
1369
1309
  value={activeCategory}
1370
1310
  onValueChange={(value) =>
1371
1311
  setActiveCategory(value as SettingCategoryId)
1372
1312
  }
1373
- orientation="vertical"
1374
- className="w-1/3 border-r h-full overflow-auto p-3"
1313
+ orientation={isWideLayout ? "vertical" : "horizontal"}
1314
+ className="flex min-h-0 min-w-0 flex-1 flex-col sm:flex-row"
1375
1315
  >
1376
- <TabsList className="self-start max-h-none flex flex-col gap-2 shrink-0 bg-background flex-1 min-h-full">
1377
- {categories.map((category) => (
1378
- <TabsTrigger
1379
- key={category.id}
1380
- value={category.id}
1381
- className="w-full text-left p-2 data-[state=active]:bg-primary data-[state=active]:text-primary-foreground justify-start"
1382
- >
1383
- <div className="flex gap-4 items-center text-lg overflow-hidden">
1384
- <span
1385
- className={cn(
1386
- category.className,
1387
- "w-8 h-8 rounded flex items-center justify-center text-muted-foreground shrink-0",
1388
- )}
1389
- >
1390
- <category.Icon className="w-4 h-4" />
1391
- </span>
1392
- <span className="truncate">{category.label}</span>
1393
- </div>
1394
- </TabsTrigger>
1395
- ))}
1396
-
1397
- <div className="p-2 text-xs text-muted-foreground self-start flex flex-col gap-1">
1398
- <span>Version: {marimoVersion}</span>
1399
- <span>Locale: {locale}</span>
1316
+ <aside className="flex shrink-0 flex-col border-b bg-muted/20 p-3 sm:w-52 sm:overflow-y-auto sm:border-b-0 sm:border-r">
1317
+ <TabsList
1318
+ aria-label="Settings categories"
1319
+ className="max-h-none justify-start gap-1 overflow-x-auto rounded-none bg-transparent p-0 sm:flex-col sm:items-stretch"
1320
+ >
1321
+ {categories.map((category) => (
1322
+ <TabsTrigger
1323
+ key={category.id}
1324
+ value={category.id}
1325
+ className="justify-start px-3 py-2 text-left font-normal hover:bg-muted/60 hover:text-foreground data-[state=active]:bg-muted data-[state=active]:font-medium data-[state=active]:shadow-none"
1326
+ >
1327
+ {category.label}
1328
+ </TabsTrigger>
1329
+ ))}
1330
+ </TabsList>
1331
+ <div className="mt-auto hidden pt-6 px-3 text-xs leading-relaxed text-muted-foreground sm:flex sm:flex-col sm:gap-3">
1332
+ {!isWasm() && configMessage}
1333
+ <div>
1334
+ <div>Version: {marimoVersion}</div>
1335
+ <div>Locale: {locale}</div>
1336
+ </div>
1400
1337
  </div>
1401
-
1402
- <div className="flex-1" />
1403
- {!isWasm() && configMessage}
1404
- </TabsList>
1338
+ </aside>
1339
+ <TabsContent
1340
+ key={activeCategory}
1341
+ value={activeCategory}
1342
+ className="settings-content m-0 min-h-0 min-w-0 flex-1 overflow-auto p-5 sm:p-6"
1343
+ >
1344
+ <div className="flex flex-col gap-6">{renderBody()}</div>
1345
+ </TabsContent>
1405
1346
  </Tabs>
1406
- <div className="w-2/3 pl-6 gap-2 flex flex-col overflow-auto p-6">
1407
- {renderBody()}
1408
- </div>
1409
1347
  </form>
1410
1348
  </Form>
1411
1349
  );