@marimo-team/islands 0.23.11-dev9 → 0.23.11

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 (82) hide show
  1. package/dist/{ConnectedDataExplorerComponent-Z3RP7Vmm.js → ConnectedDataExplorerComponent-WqG-xX4l.js} +13 -13
  2. package/dist/{ErrorBoundary-DpbaKVv7.js → ErrorBoundary-BNx_OSVo.js} +2 -2
  3. package/dist/{any-language-editor-CAgFD4Kd.js → any-language-editor-rPSlOll9.js} +5 -5
  4. package/dist/assets/__vite-browser-external-DAm_YW43.js +1 -0
  5. package/dist/assets/{worker-CC0Oul9k.js → worker-DEDLIQQV.js} +2 -2
  6. package/dist/{button-C5K9fIPF.js → button-vQhauTmO.js} +20 -0
  7. package/dist/{capabilities-BceAxrAW.js → capabilities-BEHzIS99.js} +1 -1
  8. package/dist/{chat-ui-c1FdlK-C.js → chat-ui-k2kqhCv5.js} +135 -106
  9. package/dist/{check-BCaJeT-J.js → check-nrzHDi45.js} +1 -1
  10. package/dist/{code-visibility-0vPSjYnM.js → code-visibility-CFUW5L3i.js} +81 -68
  11. package/dist/{copy-UqRYxiOg.js → copy-UhDed7D4.js} +2 -2
  12. package/dist/{dist-Dk6PV_d3.js → dist-DYGLrbYQ.js} +2 -2
  13. package/dist/{error-banner-CJXYJ6Sb.js → error-banner-BHAkVFc2.js} +2 -2
  14. package/dist/{esm-BaH2eg5-.js → esm-Bqu9AE2K.js} +1 -1
  15. package/dist/{extends-D_hDsj6R.js → extends-9Yl5BEcg.js} +4 -4
  16. package/dist/{formats-C4wO47tk.js → formats-BV4bOfMI.js} +3 -3
  17. package/dist/{glide-data-editor-BPkCPs7L.js → glide-data-editor-BDTq6YUb.js} +9 -9
  18. package/dist/{html-to-image-D5-EpALB.js → html-to-image-C86pQALH.js} +16 -16
  19. package/dist/{input-OdWHkobi.js → input-AKkGXdyV.js} +6 -6
  20. package/dist/{label-CC4ytI1X.js → label-E3ZJXHu8.js} +2 -2
  21. package/dist/{loader-BWLPpjKK.js → loader-YPuQvn1Y.js} +1 -1
  22. package/dist/main.js +981 -978
  23. package/dist/{mermaid-BotvIKg9.js → mermaid-QFAR9YgY.js} +5 -5
  24. package/dist/{process-output-WDZE0cyS.js → process-output-nNw4OpSj.js} +3 -3
  25. package/dist/{reveal-component-BZpDHGsk.js → reveal-component-0u6v5UUq.js} +10 -10
  26. package/dist/{spec-X7FwLJni.js → spec-B45_YCNI.js} +4 -4
  27. package/dist/{strings-J57tzLr3.js → strings-Cq2s9_EQ.js} +4 -4
  28. package/dist/style.css +1 -1
  29. package/dist/{swiper-component-B2t5sN1q.js → swiper-component-BNa_4kh2.js} +2 -2
  30. package/dist/{toDate-d8RCRrRd.js → toDate-Do1xRzAo.js} +3 -3
  31. package/dist/{tooltip-DpcyNkQ2.js → tooltip-Bz3OAwrU.js} +3 -3
  32. package/dist/{types-ChtMFmZ2.js → types-D8gEGs4R.js} +1 -1
  33. package/dist/{useAsyncData-PonK__yh.js → useAsyncData-CL3o2p4i.js} +1 -1
  34. package/dist/{useDateFormatter-QB-3MpYr.js → useDateFormatter-BC6iSz9g.js} +2 -2
  35. package/dist/{useDeepCompareMemoize-D3NGWke6.js → useDeepCompareMemoize-BPx2MuOK.js} +1 -1
  36. package/dist/{useIframeCapabilities-C4JTXTIh.js → useIframeCapabilities-C6Ta3EyP.js} +1 -1
  37. package/dist/{useLifecycle-00mO3OSS.js → useLifecycle-C3Ec71q0.js} +3 -3
  38. package/dist/{useTheme-DEhDzATN.js → useTheme-ZhT6uIu3.js} +4 -3
  39. package/dist/{vega-component-DGPUhbDs.js → vega-component-C3AWYGAL.js} +10 -10
  40. package/dist/{zod-aLSua2NL.js → zod-DXqkaI_w.js} +1 -1
  41. package/package.json +1 -1
  42. package/src/components/ai/ai-model-dropdown.tsx +6 -0
  43. package/src/components/app-config/ai-config.tsx +73 -55
  44. package/src/components/app-config/data-form.tsx +33 -43
  45. package/src/components/app-config/is-overridden.tsx +125 -58
  46. package/src/components/app-config/user-config-form.tsx +178 -227
  47. package/src/components/chat/chat-display.tsx +5 -0
  48. package/src/components/chat/chat-panel.tsx +21 -8
  49. package/src/components/chat/tool-call/tool-call-view.tsx +4 -0
  50. package/src/components/chat/tool-call/tool-history-row.tsx +26 -5
  51. package/src/components/data-table/TableTopBar.tsx +4 -4
  52. package/src/components/data-table/__tests__/columns.test.tsx +20 -0
  53. package/src/components/data-table/__tests__/data-table.test.tsx +28 -0
  54. package/src/components/data-table/columns.tsx +16 -1
  55. package/src/components/data-table/data-table.tsx +3 -3
  56. package/src/components/data-table/filters/types.ts +1 -0
  57. package/src/components/data-table/renderers.tsx +5 -1
  58. package/src/components/datasources/__tests__/install-package-button.test.tsx +11 -0
  59. package/src/components/datasources/__tests__/utils.test.ts +127 -2
  60. package/src/components/datasources/datasources.tsx +44 -17
  61. package/src/components/datasources/install-package-button.tsx +3 -1
  62. package/src/components/datasources/utils.ts +45 -0
  63. package/src/components/editor/actions/useNotebookActions.tsx +62 -21
  64. package/src/components/editor/actions/useSetCodeVisibility.ts +57 -0
  65. package/src/components/editor/header/__tests__/status.test.tsx +0 -4
  66. package/src/components/editor/header/status.tsx +2 -3
  67. package/src/core/cells/__tests__/cells.test.ts +33 -0
  68. package/src/core/cells/cells.ts +1 -1
  69. package/src/core/config/config-schema.ts +6 -1
  70. package/src/core/edit-app.tsx +14 -0
  71. package/src/core/hotkeys/hotkeys.ts +20 -0
  72. package/src/core/websocket/__tests__/useMarimoKernelConnection.test.ts +0 -13
  73. package/src/core/websocket/types.ts +0 -1
  74. package/src/core/websocket/useMarimoKernelConnection.tsx +0 -14
  75. package/src/css/app/print.css +8 -0
  76. package/src/css/md.css +12 -0
  77. package/src/plugins/impl/DataTablePlugin.tsx +12 -5
  78. package/src/plugins/impl/__tests__/DataTablePlugin.test.tsx +1 -1
  79. package/src/plugins/impl/chat/chat-ui.tsx +1 -0
  80. package/src/plugins/impl/data-frames/DataFramePlugin.tsx +1 -1
  81. package/dist/assets/__vite-browser-external-eshhtsgZ.js +0 -1
  82. package/src/components/editor/actions/useHideAllMarkdownCode.ts +0 -53
@@ -5,7 +5,6 @@ import type { FieldPath, UseFormReturn } from "react-hook-form";
5
5
  import {
6
6
  FormControl,
7
7
  FormDescription,
8
- FormField,
9
8
  FormItem,
10
9
  FormLabel,
11
10
  FormMessage,
@@ -19,25 +18,23 @@ import {
19
18
  SettingGroup,
20
19
  SQL_OUTPUT_SELECT_OPTIONS,
21
20
  } from "./common";
22
- import { IsOverridden } from "./is-overridden";
21
+ import { IsOverridden, OverriddenFormField } from "./is-overridden";
23
22
 
24
23
  const DISCOVERY_OPTIONS = ["auto", "true", "false"];
25
24
 
26
25
  export const DataForm = ({
27
26
  form,
28
- config,
29
27
  onSubmit,
30
28
  }: {
31
29
  form: UseFormReturn<UserConfig>;
32
- config: UserConfig;
33
30
  onSubmit: (values: UserConfig) => void;
34
31
  }) => {
35
32
  const renderDiscoveryForm = (name: FieldPath<UserConfig>, label: string) => {
36
33
  return (
37
- <FormField
34
+ <OverriddenFormField
38
35
  control={form.control}
39
36
  name={name}
40
- render={({ field }) => {
37
+ render={({ field, override }) => {
41
38
  const onChange = (e: ChangeEvent<HTMLSelectElement>) => {
42
39
  const value = e.target.value;
43
40
  field.onChange(
@@ -54,9 +51,11 @@ export const DataForm = ({
54
51
  data-testid="auto-discover-schemas-select"
55
52
  onChange={onChange}
56
53
  value={
57
- field.value === undefined ? "auto" : field.value.toString()
54
+ override.value === undefined
55
+ ? "auto"
56
+ : override.value.toString()
58
57
  }
59
- disabled={field.disabled}
58
+ disabled={field.disabled || override.isOverridden}
60
59
  className="w-[100px]"
61
60
  >
62
61
  {DISCOVERY_OPTIONS.map((option) => (
@@ -66,7 +65,7 @@ export const DataForm = ({
66
65
  ))}
67
66
  </NativeSelect>
68
67
  </FormControl>
69
- <IsOverridden userConfig={config} name={name} />
68
+ <IsOverridden override={override} />
70
69
  </FormItem>
71
70
  );
72
71
  }}
@@ -76,10 +75,10 @@ export const DataForm = ({
76
75
 
77
76
  return (
78
77
  <>
79
- <FormField
78
+ <OverriddenFormField
80
79
  control={form.control}
81
80
  name="display.dataframes"
82
- render={({ field }) => (
81
+ render={({ field, override }) => (
83
82
  <div className="flex flex-col space-y-1">
84
83
  <FormItem className={formItemClasses}>
85
84
  <FormLabel>Dataframe viewer</FormLabel>
@@ -87,8 +86,8 @@ export const DataForm = ({
87
86
  <NativeSelect
88
87
  data-testid="display-dataframes-select"
89
88
  onChange={(e) => field.onChange(e.target.value)}
90
- value={field.value}
91
- disabled={field.disabled}
89
+ value={override.value}
90
+ disabled={field.disabled || override.isOverridden}
92
91
  className="inline-flex mr-2"
93
92
  >
94
93
  {["rich", "plain"].map((option) => (
@@ -99,7 +98,7 @@ export const DataForm = ({
99
98
  </NativeSelect>
100
99
  </FormControl>
101
100
  <FormMessage />
102
- <IsOverridden userConfig={config} name="display.dataframes" />
101
+ <IsOverridden override={override} />
103
102
  </FormItem>
104
103
 
105
104
  <FormDescription>
@@ -109,10 +108,10 @@ export const DataForm = ({
109
108
  </div>
110
109
  )}
111
110
  />
112
- <FormField
111
+ <OverriddenFormField
113
112
  control={form.control}
114
113
  name="display.default_table_page_size"
115
- render={({ field }) => (
114
+ render={({ field, override }) => (
116
115
  <div className="flex flex-col space-y-1">
117
116
  <FormItem className={formItemClasses}>
118
117
  <FormLabel>Default table page size</FormLabel>
@@ -122,7 +121,8 @@ export const DataForm = ({
122
121
  data-testid="default-table-page-size-input"
123
122
  className="m-0 w-24"
124
123
  {...field}
125
- value={field.value}
124
+ value={override.value}
125
+ isDisabled={override.isOverridden}
126
126
  minValue={1}
127
127
  step={1}
128
128
  onChange={(value) => {
@@ -134,10 +134,7 @@ export const DataForm = ({
134
134
  />
135
135
  </FormControl>
136
136
  <FormMessage />
137
- <IsOverridden
138
- userConfig={config}
139
- name="display.default_table_page_size"
140
- />
137
+ <IsOverridden override={override} />
141
138
  </FormItem>
142
139
  <FormDescription>
143
140
  The default number of rows displayed in dataframes and SQL
@@ -146,10 +143,10 @@ export const DataForm = ({
146
143
  </div>
147
144
  )}
148
145
  />
149
- <FormField
146
+ <OverriddenFormField
150
147
  control={form.control}
151
148
  name="display.default_table_max_columns"
152
- render={({ field }) => (
149
+ render={({ field, override }) => (
153
150
  <div className="flex flex-col space-y-1">
154
151
  <FormItem className={formItemClasses}>
155
152
  <FormLabel>Default table max columns</FormLabel>
@@ -159,7 +156,8 @@ export const DataForm = ({
159
156
  data-testid="default-table-max-columns-input"
160
157
  className="m-0 w-24"
161
158
  {...field}
162
- value={field.value}
159
+ value={override.value}
160
+ isDisabled={override.isOverridden}
163
161
  minValue={1}
164
162
  step={1}
165
163
  onChange={(value) => {
@@ -171,10 +169,7 @@ export const DataForm = ({
171
169
  />
172
170
  </FormControl>
173
171
  <FormMessage />
174
- <IsOverridden
175
- userConfig={config}
176
- name="display.default_table_max_columns"
177
- />
172
+ <IsOverridden override={override} />
178
173
  </FormItem>
179
174
  <FormDescription>
180
175
  The default maximum number of columns displayed in dataframes and
@@ -213,25 +208,23 @@ export const DataForm = ({
213
208
  {renderDiscoveryForm("datasources.auto_discover_columns", "Columns")}
214
209
  </div>
215
210
 
216
- <FormField
211
+ <OverriddenFormField
217
212
  control={form.control}
218
213
  name="diagnostics.sql_linter"
219
- render={({ field }) => (
214
+ render={({ field, override }) => (
220
215
  <div className="flex flex-col space-y-1">
221
216
  <FormItem className={formItemClasses}>
222
217
  <FormLabel>SQL Linter</FormLabel>
223
218
  <FormControl>
224
219
  <Checkbox
225
220
  data-testid="sql-linter-checkbox"
226
- checked={field.value}
221
+ checked={override.value}
222
+ disabled={override.isOverridden}
227
223
  onCheckedChange={field.onChange}
228
224
  />
229
225
  </FormControl>
230
226
  <FormMessage />
231
- <IsOverridden
232
- userConfig={config}
233
- name="diagnostics.sql_linter"
234
- />
227
+ <IsOverridden override={override} />
235
228
  </FormItem>
236
229
  <FormDescription>
237
230
  Better linting and autocompletions for SQL cells.
@@ -240,10 +233,10 @@ export const DataForm = ({
240
233
  )}
241
234
  />
242
235
 
243
- <FormField
236
+ <OverriddenFormField
244
237
  control={form.control}
245
238
  name="runtime.default_sql_output"
246
- render={({ field }) => (
239
+ render={({ field, override }) => (
247
240
  <div className="flex flex-col space-y-1">
248
241
  <FormItem className={formItemClasses}>
249
242
  <FormLabel>Default SQL output</FormLabel>
@@ -251,8 +244,8 @@ export const DataForm = ({
251
244
  <NativeSelect
252
245
  data-testid="user-config-sql-output-select"
253
246
  onChange={(e) => field.onChange(e.target.value)}
254
- value={field.value}
255
- disabled={field.disabled}
247
+ value={override.value}
248
+ disabled={field.disabled || override.isOverridden}
256
249
  className="inline-flex mr-2"
257
250
  >
258
251
  {SQL_OUTPUT_SELECT_OPTIONS.map((option) => (
@@ -263,10 +256,7 @@ export const DataForm = ({
263
256
  </NativeSelect>
264
257
  </FormControl>
265
258
  <FormMessage />
266
- <IsOverridden
267
- userConfig={config}
268
- name="runtime.default_sql_output"
269
- />
259
+ <IsOverridden override={override} />
270
260
  </FormItem>
271
261
 
272
262
  <FormDescription>
@@ -1,36 +1,130 @@
1
1
  /* Copyright 2026 Marimo. All rights reserved. */
2
2
 
3
3
  import { useAtomValue } from "jotai";
4
- import { get } from "lodash-es";
4
+ import { get, has } from "lodash-es";
5
5
  import { FolderCog2 } from "lucide-react";
6
- import type { FieldPath } from "react-hook-form";
6
+ import { useCallback } from "react";
7
+ import type {
8
+ Control,
9
+ ControllerRenderProps,
10
+ FieldPath,
11
+ FieldPathValue,
12
+ } from "react-hook-form";
13
+ import { FormField } from "@/components/ui/form";
7
14
  import { Tooltip } from "@/components/ui/tooltip";
8
15
  import { configOverridesAtom, useUserConfig } from "@/core/config/config";
9
16
  import type { UserConfig } from "@/core/config/config-schema";
10
17
  import { Kbd } from "../ui/kbd";
11
18
 
19
+ export interface ConfigOverride<T> {
20
+ isOverridden: boolean;
21
+ /**
22
+ * The effective value: the project config value when overridden, otherwise
23
+ * the user's own value.
24
+ */
25
+ value: T;
26
+ userValue: unknown;
27
+ projectValue: unknown;
28
+ }
29
+
30
+ function resolveOverride<T>({
31
+ userConfig,
32
+ overrides,
33
+ name,
34
+ value,
35
+ }: {
36
+ userConfig: UserConfig;
37
+ overrides: unknown;
38
+ name: FieldPath<UserConfig>;
39
+ value: T;
40
+ }): ConfigOverride<T> {
41
+ const userValue = get(userConfig, name);
42
+ const projectValue = get(overrides as UserConfig, name);
43
+ // A setting is overridden when the project config specifies the path at all
44
+ // (so it stays locked even if the value equals the user's, or is `null`).
45
+ const isOverridden = has(overrides, name);
46
+ return {
47
+ isOverridden,
48
+ userValue,
49
+ projectValue,
50
+ value: isOverridden ? (projectValue as T) : value,
51
+ };
52
+ }
53
+
12
54
  /**
13
- * Hook to determine if a user config value is overridden by project config.
14
- * Returns { isOverridden, currentValue, overriddenValue }
55
+ * Returns a function that resolves a form field against the project config
56
+ * overrides (e.g. `pyproject.toml`).
57
+ *
58
+ * The returned function is a plain callback (not a hook), so it's safe to call
59
+ * inside `FormField` render callbacks and loops.
15
60
  */
16
- function useIsConfigOverridden(
17
- userConfig: UserConfig,
61
+ export function useConfigOverride(): <T>(
18
62
  name: FieldPath<UserConfig>,
19
- ): {
20
- isOverridden: boolean;
21
- currentValue: unknown;
22
- overriddenValue: unknown;
23
- } {
24
- const currentValue = get(userConfig, name);
63
+ value: T,
64
+ ) => ConfigOverride<T> {
65
+ const [userConfig] = useUserConfig();
25
66
  const overrides = useAtomValue(configOverridesAtom);
26
- const overriddenValue = get(overrides as UserConfig, name);
27
-
28
- const isOverridden =
29
- overriddenValue != null && currentValue !== overriddenValue;
67
+ return useCallback(
68
+ <T,>(name: FieldPath<UserConfig>, value: T): ConfigOverride<T> =>
69
+ resolveOverride({ userConfig, overrides, name, value }),
70
+ [userConfig, overrides],
71
+ );
72
+ }
30
73
 
31
- return { isOverridden, currentValue, overriddenValue };
74
+ /**
75
+ * A `FormField` that resolves the field against the project config overrides.
76
+ */
77
+ export function OverriddenFormField<TName extends FieldPath<UserConfig>>({
78
+ control,
79
+ name,
80
+ disabled,
81
+ render,
82
+ }: {
83
+ control: Control<UserConfig>;
84
+ name: TName;
85
+ disabled?: boolean;
86
+ render: (args: {
87
+ field: ControllerRenderProps<UserConfig, TName>;
88
+ override: ConfigOverride<FieldPathValue<UserConfig, TName>>;
89
+ }) => React.ReactElement;
90
+ }) {
91
+ const getOverride = useConfigOverride();
92
+ return (
93
+ <FormField
94
+ control={control}
95
+ name={name}
96
+ disabled={disabled}
97
+ render={({ field }) =>
98
+ render({
99
+ field,
100
+ // `field.value` is `FieldPathValue<UserConfig, TName>`, but the deep
101
+ // conditional type doesn't reduce for the compiler, so we narrow it.
102
+ override: getOverride(name, field.value) as ConfigOverride<
103
+ FieldPathValue<UserConfig, TName>
104
+ >,
105
+ })
106
+ }
107
+ />
108
+ );
32
109
  }
33
110
 
111
+ /**
112
+ * Shared explanation shown in override tooltips, so the wording stays
113
+ * consistent across the badge and the disabled-wrapper.
114
+ */
115
+ const OverriddenExplanation = () => (
116
+ <>
117
+ <p>
118
+ This setting is overridden by the{" "}
119
+ <Kbd className="inline mx-px">pyproject.toml</Kbd> config.
120
+ </p>
121
+ <p>
122
+ To change it, edit the project config{" "}
123
+ <Kbd className="inline mx-px">pyproject.toml</Kbd> directly.
124
+ </p>
125
+ </>
126
+ );
127
+
34
128
  /**
35
129
  * Wraps a component and shows a tooltip if the user config is overridden by the
36
130
  * project config.
@@ -42,22 +136,13 @@ export const DisableIfOverridden = ({
42
136
  name: FieldPath<UserConfig>;
43
137
  children: React.ReactNode;
44
138
  }) => {
45
- const [userConfig] = useUserConfig();
46
- const { isOverridden } = useIsConfigOverridden(userConfig, name);
139
+ const { isOverridden } = useConfigOverride()(name, undefined);
47
140
  return isOverridden ? (
48
141
  <Tooltip
49
142
  delayDuration={200}
50
143
  content={
51
144
  <div className="flex flex-col gap-2">
52
- <p>
53
- This setting is overridden by the{" "}
54
- <Kbd className="inline mx-px">pyproject.toml</Kbd> config.
55
- </p>
56
- <p>
57
- To change it, edit the project config{" "}
58
- <Kbd className="inline mx-px">pyproject.toml</Kbd>
59
- directly.
60
- </p>
145
+ <OverriddenExplanation />
61
146
  </div>
62
147
  }
63
148
  >
@@ -71,48 +156,30 @@ export const DisableIfOverridden = ({
71
156
  };
72
157
 
73
158
  export const IsOverridden = ({
74
- userConfig,
75
- name,
159
+ override,
76
160
  }: {
77
- userConfig: UserConfig;
78
- name: FieldPath<UserConfig>;
161
+ override: ConfigOverride<unknown>;
79
162
  }) => {
80
- const { isOverridden, currentValue, overriddenValue } = useIsConfigOverridden(
81
- userConfig,
82
- name,
83
- );
84
-
85
- if (!isOverridden) {
163
+ if (!override.isOverridden) {
86
164
  return null;
87
165
  }
88
166
 
89
167
  return (
90
168
  <Tooltip
91
169
  content={
92
- <>
93
- <span>
94
- This setting is overridden by{" "}
95
- <Kbd className="inline">pyproject.toml</Kbd>.
96
- </span>
97
- <br />
98
- <span>
99
- Edit the <Kbd className="inline">pyproject.toml</Kbd> file directly
100
- to change this setting.
101
- </span>
102
- <br />
103
- <span>
104
- User value: <strong>{String(currentValue)}</strong>
105
- </span>
106
- <br />
107
- <span>
108
- Project value: <strong>{String(overriddenValue)}</strong>
109
- </span>
110
- </>
170
+ <div className="flex flex-col gap-2">
171
+ <OverriddenExplanation />
172
+ <p>
173
+ User value: <strong>{String(override.userValue)}</strong>
174
+ <br />
175
+ Project value: <strong>{String(override.projectValue)}</strong>
176
+ </p>
177
+ </div>
111
178
  }
112
179
  >
113
180
  <span className="text-(--amber-12) text-xs flex items-center gap-1 border rounded px-2 py-1 bg-(--amber-2) border-(--amber-6) ml-1">
114
181
  <FolderCog2 className="w-3 h-3" />
115
- Overridden by pyproject.toml [{String(overriddenValue)}]
182
+ Overridden by pyproject.toml
116
183
  </span>
117
184
  </Tooltip>
118
185
  );