@marimo-team/islands 0.22.6-dev8 → 0.22.6-dev9

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/main.js CHANGED
@@ -65645,7 +65645,7 @@ ${c}
65645
65645
  return Logger.warn("Failed to get version from mount config"), null;
65646
65646
  }
65647
65647
  }
65648
- const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.22.6-dev8"), showCodeInRunModeAtom = atom(true);
65648
+ const marimoVersionAtom = atom(getVersionFromMountConfig() || "0.22.6-dev9"), showCodeInRunModeAtom = atom(true);
65649
65649
  atom(null);
65650
65650
  var VIRTUAL_FILE_REGEX = /\/@file\/([^\s"&'/]+)\.([\dA-Za-z]+)/g, VirtualFileTracker = class e {
65651
65651
  constructor() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marimo-team/islands",
3
- "version": "0.22.6-dev8",
3
+ "version": "0.22.6-dev9",
4
4
  "main": "dist/main.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -19,6 +19,7 @@ import { useForm } from "react-hook-form";
19
19
  import type z from "zod";
20
20
  import { Button } from "@/components/ui/button";
21
21
  import { Checkbox } from "@/components/ui/checkbox";
22
+ import { acceptCompletionOnEnterAtom } from "@/core/codemirror/completion/accept-on-enter-atom";
22
23
  import {
23
24
  Form,
24
25
  FormControl,
@@ -118,6 +119,9 @@ const LOCALE_SYSTEM_VALUE = "__system__";
118
119
 
119
120
  export const UserConfigForm: React.FC = () => {
120
121
  const [config, setConfig] = useUserConfig();
122
+ const [acceptOnEnter, setAcceptOnEnter] = useAtom(
123
+ acceptCompletionOnEnterAtom,
124
+ );
121
125
  const formElement = useRef<HTMLFormElement>(null);
122
126
  const setKeyboardShortcutsOpen = useSetAtom(keyboardShortcutsAtom);
123
127
  const [activeCategory, setActiveCategory] = useAtom(
@@ -446,6 +450,27 @@ export const UserConfigForm: React.FC = () => {
446
450
  </div>
447
451
  )}
448
452
  />
453
+ <div className="flex flex-col space-y-1">
454
+ <FormItem className={formItemClasses}>
455
+ <FormLabel className="font-normal">
456
+ Accept suggestion on Enter
457
+ </FormLabel>
458
+ <FormControl>
459
+ <Checkbox
460
+ data-testid="accept-completion-on-enter-checkbox"
461
+ checked={acceptOnEnter}
462
+ onCheckedChange={(checked) =>
463
+ setAcceptOnEnter(Boolean(checked))
464
+ }
465
+ />
466
+ </FormControl>
467
+ </FormItem>
468
+ <FormDescription>
469
+ When unchecked, pressing Enter inserts a new line instead of
470
+ accepting an autocomplete suggestion. Use Tab to accept
471
+ suggestions.
472
+ </FormDescription>
473
+ </div>
449
474
  <FormField
450
475
  control={form.control}
451
476
  name="completion.signature_hint_on_typing"
@@ -13,6 +13,7 @@ import { useCellActions } from "@/core/cells/cells";
13
13
  import { usePendingDeleteService } from "@/core/cells/pending-delete-service";
14
14
  import type { CellData, CellRuntimeState } from "@/core/cells/types";
15
15
  import { setupCodeMirror } from "@/core/codemirror/cm";
16
+ import { acceptCompletionOnEnterAtom } from "@/core/codemirror/completion/accept-on-enter-atom";
16
17
  import {
17
18
  getInitialLanguageAdapter,
18
19
  languageAdapterState,
@@ -146,6 +147,7 @@ const CellEditorInternal = ({
146
147
  });
147
148
 
148
149
  const autoInstantiate = useAtomValue(autoInstantiateAtom);
150
+ const acceptCompletionOnEnter = useAtomValue(acceptCompletionOnEnterAtom);
149
151
  const afterToggleMarkdown = useEvent(() => {
150
152
  maybeAddMarimoImport({
151
153
  autoInstantiate,
@@ -212,6 +214,7 @@ const CellEditorInternal = ({
212
214
  },
213
215
  },
214
216
  completionConfig: userConfig.completion,
217
+ acceptCompletionOnEnter,
215
218
  keymapConfig: userConfig.keymap,
216
219
  lspConfig: userConfig.language_servers,
217
220
  theme,
@@ -261,6 +264,7 @@ const CellEditorInternal = ({
261
264
  return extensions;
262
265
  }, [
263
266
  cellId,
267
+ acceptCompletionOnEnter,
264
268
  userConfig.keymap,
265
269
  userConfig.completion,
266
270
  userConfig.language_servers,
@@ -73,6 +73,7 @@ export interface CodeMirrorSetupOpts {
73
73
  cellId: CellId;
74
74
  showPlaceholder: boolean;
75
75
  enableAI: boolean;
76
+ acceptCompletionOnEnter?: boolean;
76
77
  cellActions: CodemirrorCellActions;
77
78
  completionConfig: CompletionConfig;
78
79
  keymapConfig: KeymapConfig;
@@ -175,6 +176,7 @@ export const basicBundle = (opts: CodeMirrorSetupOpts): Extension[] => {
175
176
  theme,
176
177
  hotkeys,
177
178
  completionConfig,
179
+ acceptCompletionOnEnter,
178
180
  cellId,
179
181
  lspConfig,
180
182
  diagnosticsConfig,
@@ -207,7 +209,7 @@ export const basicBundle = (opts: CodeMirrorSetupOpts): Extension[] => {
207
209
  foldGutter(),
208
210
  stringsAutoCloseBraces(),
209
211
  closeBrackets(),
210
- completionKeymap(),
212
+ completionKeymap(acceptCompletionOnEnter),
211
213
  // to avoid clash with charDeleteBackward keymap
212
214
  Prec.high(keymap.of(closeBracketsKeymap)),
213
215
  bracketMatching(),
@@ -1,8 +1,21 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
3
  import { completionKeymap as defaultCompletionKeymap } from "@codemirror/autocomplete";
4
+ import { EditorState } from "@codemirror/state";
5
+ import { keymap } from "@codemirror/view";
4
6
  import { describe, expect, it } from "vitest";
5
- import { filterCompletionBindings } from "../keymap";
7
+ import { completionKeymap, filterCompletionBindings } from "../keymap";
8
+
9
+ function hasEnterBinding(acceptOnEnter: boolean): boolean {
10
+ const state = EditorState.create({
11
+ extensions: [completionKeymap(acceptOnEnter)],
12
+ });
13
+
14
+ return state
15
+ .facet(keymap)
16
+ .flat()
17
+ .some((binding) => binding.key === "Enter");
18
+ }
6
19
 
7
20
  describe("completionKeymap", () => {
8
21
  it("upstream includes the macOS-only completion bindings we care about", () => {
@@ -21,4 +34,30 @@ describe("completionKeymap", () => {
21
34
  expect(filtered.some((binding) => binding.key === "Escape")).toBe(false);
22
35
  expect(filtered.some((binding) => binding.mac === "Alt-i")).toBe(true);
23
36
  });
37
+
38
+ it("includes Enter by default", () => {
39
+ const filtered = filterCompletionBindings(defaultCompletionKeymap);
40
+ expect(filtered.some((binding) => binding.key === "Enter")).toBe(true);
41
+ });
42
+
43
+ it("removes Enter when passed a keysToRemove set containing Enter", () => {
44
+ const keysToRemove = new Set<string | undefined>([
45
+ "Escape",
46
+ "Alt-`",
47
+ "Enter",
48
+ ]);
49
+ const filtered = filterCompletionBindings(
50
+ defaultCompletionKeymap,
51
+ keysToRemove,
52
+ );
53
+ expect(filtered.some((binding) => binding.key === "Enter")).toBe(false);
54
+ });
55
+
56
+ it("completionKeymap includes Enter when acceptOnEnter is true", () => {
57
+ expect(hasEnterBinding(true)).toBe(true);
58
+ });
59
+
60
+ it("completionKeymap removes Enter when acceptOnEnter is false", () => {
61
+ expect(hasEnterBinding(false)).toBe(false);
62
+ });
24
63
  });
@@ -0,0 +1,10 @@
1
+ /* Copyright 2026 Marimo. All rights reserved. */
2
+ import { atomWithStorage } from "jotai/utils";
3
+ import { jotaiJsonStorage } from "@/utils/storage/jotai";
4
+ // Default: true (Enter accepts suggestion, matching VS Code default)
5
+ export const acceptCompletionOnEnterAtom = atomWithStorage<boolean>(
6
+ "marimo:accept-completion-on-enter",
7
+ true,
8
+ jotaiJsonStorage,
9
+ { getOnInit: true },
10
+ );
@@ -22,20 +22,27 @@ const KEYS_TO_REMOVE = new Set<string | undefined>([
22
22
  "Alt-`",
23
23
  ]);
24
24
 
25
- function hasRemovedKeybinding(binding: KeyBinding): boolean {
26
- return [binding.key, binding.mac, binding.linux, binding.win].some((key) =>
27
- KEYS_TO_REMOVE.has(key),
28
- );
29
- }
30
-
31
25
  export function filterCompletionBindings(
32
26
  bindings: readonly KeyBinding[],
27
+ keysToRemove: Set<string | undefined> = KEYS_TO_REMOVE,
33
28
  ): readonly KeyBinding[] {
34
- return bindings.filter((binding) => !hasRemovedKeybinding(binding));
29
+ return bindings.filter(
30
+ (binding) =>
31
+ ![binding.key, binding.mac, binding.linux, binding.win].some((key) =>
32
+ keysToRemove.has(key),
33
+ ),
34
+ );
35
35
  }
36
36
 
37
- export function completionKeymap(): Extension {
38
- const withoutKeysToRemove = filterCompletionBindings(defaultCompletionKeymap);
37
+ export function completionKeymap(acceptOnEnter = true): Extension {
38
+ const keysToRemove = new Set(KEYS_TO_REMOVE);
39
+ if (!acceptOnEnter) {
40
+ keysToRemove.add("Enter");
41
+ }
42
+ const withoutKeysToRemove = filterCompletionBindings(
43
+ defaultCompletionKeymap,
44
+ keysToRemove,
45
+ );
39
46
 
40
47
  return Prec.highest(
41
48
  keymap.of([