@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.
- package/dist/{ErrorBoundary-Cg9q4kUW.js → ErrorBoundary-Ba6GHuhB.js} +1 -1
- package/dist/{code-visibility-Dp-DkL1e.js → code-visibility-BXNYYFb5.js} +1 -1
- package/dist/{common-DHr2MXY3.js → common-BHSOMSDG.js} +1 -1
- package/dist/{data-grid-overlay-editor-B-OJHxuO.js → data-grid-overlay-editor-CGLPw73V.js} +1 -1
- package/dist/{feedback-button-DGAcA9xH.js → feedback-button-BUf6rqKQ.js} +2 -2
- package/dist/{glide-data-editor-09FJOvJw.js → glide-data-editor-Dm3DXKHl.js} +3 -3
- package/dist/{globals-2eTRp9qC.js → globals-DjSaXJeh.js} +1 -1
- package/dist/main.js +4179 -4062
- package/dist/{number-overlay-editor-XJ1jA8Jv.js → number-overlay-editor-BVAnAmYf.js} +1 -1
- package/dist/{reveal-component-DxdLWQiq.js → reveal-component-Cfoa8xAb.js} +2 -2
- package/dist/style.css +1 -1
- package/package.json +1 -1
- package/src/components/app-config/ai-config.tsx +148 -147
- package/src/components/app-config/app-config-button.tsx +6 -5
- package/src/components/app-config/common.tsx +7 -9
- package/src/components/app-config/data-form.tsx +112 -113
- package/src/components/app-config/mcp-config.tsx +30 -53
- package/src/components/app-config/user-config-form.css +91 -0
- package/src/components/app-config/user-config-form.tsx +236 -298
- package/src/components/editor/actions/__tests__/pair-with-agent-modal.test.tsx +9 -5
- package/src/components/editor/actions/__tests__/pair-with-agent.test.tsx +3 -3
- package/src/components/editor/actions/pair-with-agent-modal.tsx +5 -5
- package/src/components/editor/actions/pair-with-agent.tsx +10 -7
- package/src/core/notebook/replica/__tests__/executions.test.ts +201 -0
- package/src/core/notebook/replica/__tests__/replica.test.ts +6 -0
- package/src/core/notebook/replica/__tests__/results.test.ts +193 -0
- package/src/core/notebook/replica/index.ts +7 -0
- package/src/core/notebook/replica/replica.ts +34 -2
- package/src/core/notebook/replica/resources/executions.ts +157 -0
- package/src/core/notebook/replica/resources/results.ts +48 -0
package/package.json
CHANGED
|
@@ -119,7 +119,7 @@ export const AiProviderTitle: React.FC<AiProviderTitleProps> = ({
|
|
|
119
119
|
children,
|
|
120
120
|
}) => {
|
|
121
121
|
return (
|
|
122
|
-
<div className="flex items-center text-
|
|
122
|
+
<div className="flex items-center text-sm font-medium">
|
|
123
123
|
{provider && <AiProviderIcon provider={provider} className="mr-2" />}
|
|
124
124
|
{children}
|
|
125
125
|
</div>
|
|
@@ -149,9 +149,10 @@ export const ApiKey: React.FC<ApiKeyProps> = ({
|
|
|
149
149
|
control={form.control}
|
|
150
150
|
name={name}
|
|
151
151
|
render={({ field, override }) => (
|
|
152
|
-
<div className="flex flex-col space-y-1">
|
|
152
|
+
<div className="settings-field flex flex-col space-y-1">
|
|
153
153
|
<FormItem className={formItemClasses}>
|
|
154
|
-
<FormLabel>API
|
|
154
|
+
<FormLabel>API key</FormLabel>
|
|
155
|
+
{description && <FormDescription>{description}</FormDescription>}
|
|
155
156
|
<FormControl>
|
|
156
157
|
<Input
|
|
157
158
|
data-testid={testId}
|
|
@@ -174,7 +175,6 @@ export const ApiKey: React.FC<ApiKeyProps> = ({
|
|
|
174
175
|
<FormMessage />
|
|
175
176
|
<IsOverridden override={override} />
|
|
176
177
|
</FormItem>
|
|
177
|
-
{description && <FormDescription>{description}</FormDescription>}
|
|
178
178
|
</div>
|
|
179
179
|
)}
|
|
180
180
|
/>
|
|
@@ -218,9 +218,10 @@ export const BaseUrl: React.FC<BaseUrlProps> = ({
|
|
|
218
218
|
control={form.control}
|
|
219
219
|
name={name}
|
|
220
220
|
render={({ field, override }) => (
|
|
221
|
-
<div className="flex flex-col space-y-1">
|
|
221
|
+
<div className="settings-field flex flex-col space-y-1">
|
|
222
222
|
<FormItem className={formItemClasses}>
|
|
223
223
|
<FormLabel>Base URL</FormLabel>
|
|
224
|
+
{description && <FormDescription>{description}</FormDescription>}
|
|
224
225
|
<FormControl>
|
|
225
226
|
<Input
|
|
226
227
|
data-testid={testId}
|
|
@@ -239,7 +240,6 @@ export const BaseUrl: React.FC<BaseUrlProps> = ({
|
|
|
239
240
|
<FormMessage />
|
|
240
241
|
<IsOverridden override={override} />
|
|
241
242
|
</FormItem>
|
|
242
|
-
{description && <FormDescription>{description}</FormDescription>}
|
|
243
243
|
</div>
|
|
244
244
|
)}
|
|
245
245
|
/>
|
|
@@ -283,6 +283,7 @@ export const ModelSelector: React.FC<ModelSelectorProps> = ({
|
|
|
283
283
|
const renderFormItem = () => (
|
|
284
284
|
<FormItem className={formItemClasses}>
|
|
285
285
|
<FormLabel>{label}</FormLabel>
|
|
286
|
+
{description && <FormDescription>{description}</FormDescription>}
|
|
286
287
|
<FormControl>
|
|
287
288
|
<AIModelDropdown
|
|
288
289
|
value={value}
|
|
@@ -325,11 +326,10 @@ export const ModelSelector: React.FC<ModelSelectorProps> = ({
|
|
|
325
326
|
);
|
|
326
327
|
|
|
327
328
|
return (
|
|
328
|
-
<div className="flex flex-col space-y-1">
|
|
329
|
+
<div className="settings-field flex flex-col space-y-1">
|
|
329
330
|
{renderFormItem()}
|
|
330
331
|
<IsOverridden override={override} />
|
|
331
332
|
<IncorrectModelId value={value} />
|
|
332
|
-
{description && <FormDescription>{description}</FormDescription>}
|
|
333
333
|
</div>
|
|
334
334
|
);
|
|
335
335
|
}}
|
|
@@ -344,6 +344,7 @@ interface ProviderSelectProps {
|
|
|
344
344
|
options: string[];
|
|
345
345
|
testId: string;
|
|
346
346
|
disabled?: boolean;
|
|
347
|
+
description?: React.ReactNode;
|
|
347
348
|
}
|
|
348
349
|
|
|
349
350
|
export const ProviderSelect: React.FC<ProviderSelectProps> = ({
|
|
@@ -352,15 +353,17 @@ export const ProviderSelect: React.FC<ProviderSelectProps> = ({
|
|
|
352
353
|
options,
|
|
353
354
|
testId,
|
|
354
355
|
disabled = false,
|
|
356
|
+
description,
|
|
355
357
|
}) => {
|
|
356
358
|
return (
|
|
357
359
|
<OverriddenFormField
|
|
358
360
|
control={form.control}
|
|
359
361
|
name={name}
|
|
360
362
|
render={({ field, override }) => (
|
|
361
|
-
<div className="flex flex-col space-y-1">
|
|
363
|
+
<div className="settings-field flex flex-col space-y-1">
|
|
362
364
|
<FormItem className={formItemClasses}>
|
|
363
365
|
<FormLabel>Provider</FormLabel>
|
|
366
|
+
{description && <FormDescription>{description}</FormDescription>}
|
|
364
367
|
<FormControl>
|
|
365
368
|
<NativeSelect
|
|
366
369
|
data-testid={testId}
|
|
@@ -440,7 +443,7 @@ const renderCopilotProvider = ({
|
|
|
440
443
|
if (copilot === "custom") {
|
|
441
444
|
return (
|
|
442
445
|
<ModelSelector
|
|
443
|
-
label="Autocomplete
|
|
446
|
+
label="Autocomplete model"
|
|
444
447
|
form={form}
|
|
445
448
|
config={config}
|
|
446
449
|
name="ai.models.autocomplete_model"
|
|
@@ -461,7 +464,9 @@ const SettingGroup = ({
|
|
|
461
464
|
className?: string;
|
|
462
465
|
}) => {
|
|
463
466
|
return (
|
|
464
|
-
<div className={cn("flex flex-col gap-4
|
|
467
|
+
<div className={cn("settings-group flex flex-col gap-4", className)}>
|
|
468
|
+
{children}
|
|
469
|
+
</div>
|
|
465
470
|
);
|
|
466
471
|
};
|
|
467
472
|
|
|
@@ -563,18 +568,14 @@ export const AiCodeCompletionConfig: React.FC<AiConfigProps> = ({
|
|
|
563
568
|
const { value: copilot } = getOverride("completion.copilot", watchedCopilot);
|
|
564
569
|
return (
|
|
565
570
|
<SettingGroup>
|
|
566
|
-
<SettingSubtitle>
|
|
567
|
-
<p className="text-sm text-muted-secondary">
|
|
568
|
-
Choose GitHub Copilot, Codeium, or a custom provider (such as Ollama) to
|
|
569
|
-
enable AI-powered code completion.
|
|
570
|
-
</p>
|
|
571
|
-
|
|
571
|
+
<SettingSubtitle>Copilot</SettingSubtitle>
|
|
572
572
|
<ProviderSelect
|
|
573
573
|
form={form}
|
|
574
574
|
config={config}
|
|
575
575
|
name="completion.copilot"
|
|
576
576
|
options={["none", "github", "codeium", "custom"]}
|
|
577
577
|
testId="copilot-select"
|
|
578
|
+
description="Choose GitHub Copilot, Codeium, or a custom provider (such as Ollama) to enable AI-powered code completion."
|
|
578
579
|
/>
|
|
579
580
|
|
|
580
581
|
{renderCopilotProvider({ form, config, onSubmit, copilot })}
|
|
@@ -696,7 +697,7 @@ export const CustomProvidersConfig: React.FC<AiConfigProps> = ({
|
|
|
696
697
|
const providerForm = (
|
|
697
698
|
<div className="flex flex-col gap-3 p-4 border border-border rounded-md bg-muted/20">
|
|
698
699
|
<div className="flex flex-col gap-1.5">
|
|
699
|
-
<Label htmlFor={providerNameInputId}>Provider
|
|
700
|
+
<Label htmlFor={providerNameInputId}>Provider name</Label>
|
|
700
701
|
<Input
|
|
701
702
|
id={providerNameInputId}
|
|
702
703
|
placeholder="e.g., together, groq, mistral"
|
|
@@ -734,7 +735,7 @@ export const CustomProvidersConfig: React.FC<AiConfigProps> = ({
|
|
|
734
735
|
</div>
|
|
735
736
|
|
|
736
737
|
<div className="flex flex-col gap-1.5">
|
|
737
|
-
<Label htmlFor={apiKeyInputId}>API
|
|
738
|
+
<Label htmlFor={apiKeyInputId}>API key (optional)</Label>
|
|
738
739
|
<Input
|
|
739
740
|
id={apiKeyInputId}
|
|
740
741
|
placeholder="sk-..."
|
|
@@ -750,7 +751,7 @@ export const CustomProvidersConfig: React.FC<AiConfigProps> = ({
|
|
|
750
751
|
disabled={!hasValidValues}
|
|
751
752
|
size="xs"
|
|
752
753
|
>
|
|
753
|
-
Add
|
|
754
|
+
Add provider
|
|
754
755
|
</Button>
|
|
755
756
|
<Button variant="outline" onClick={resetForm} size="xs">
|
|
756
757
|
Cancel
|
|
@@ -839,7 +840,7 @@ export const CustomProvidersConfig: React.FC<AiConfigProps> = ({
|
|
|
839
840
|
className="w-fit self-end"
|
|
840
841
|
>
|
|
841
842
|
<Trash2Icon className="h-4 w-4 mr-2" />
|
|
842
|
-
Remove
|
|
843
|
+
Remove provider
|
|
843
844
|
</Button>
|
|
844
845
|
</AccordionFormItem>
|
|
845
846
|
);
|
|
@@ -847,10 +848,10 @@ export const CustomProvidersConfig: React.FC<AiConfigProps> = ({
|
|
|
847
848
|
|
|
848
849
|
return (
|
|
849
850
|
<SettingGroup>
|
|
850
|
-
<SettingSubtitle>Custom
|
|
851
|
+
<SettingSubtitle>Custom providers</SettingSubtitle>
|
|
851
852
|
<p className="text-sm text-muted-secondary">
|
|
852
853
|
Add your own OpenAI-compatible provider. Once added, you can
|
|
853
|
-
configure models in the AI
|
|
854
|
+
configure models in the AI models tab.
|
|
854
855
|
</p>
|
|
855
856
|
|
|
856
857
|
{customProviderEntries.length > 0 && (
|
|
@@ -872,7 +873,7 @@ export const CustomProvidersConfig: React.FC<AiConfigProps> = ({
|
|
|
872
873
|
className="self-start"
|
|
873
874
|
isFormOpen={isAddingProvider}
|
|
874
875
|
setIsFormOpen={setIsAddingProvider}
|
|
875
|
-
label="Add
|
|
876
|
+
label="Add provider"
|
|
876
877
|
/>
|
|
877
878
|
)}
|
|
878
879
|
</SettingGroup>
|
|
@@ -1208,9 +1209,12 @@ export const AiProvidersConfig: React.FC<AiConfigProps> = ({
|
|
|
1208
1209
|
disabled={isWasmRuntime}
|
|
1209
1210
|
name="ai.bedrock.region_name"
|
|
1210
1211
|
render={({ field, override }) => (
|
|
1211
|
-
<div className="flex flex-col space-y-1">
|
|
1212
|
+
<div className="settings-field flex flex-col space-y-1">
|
|
1212
1213
|
<FormItem className={formItemClasses}>
|
|
1213
|
-
<FormLabel>AWS
|
|
1214
|
+
<FormLabel>AWS region</FormLabel>
|
|
1215
|
+
<FormDescription>
|
|
1216
|
+
The AWS region where Bedrock service is available.
|
|
1217
|
+
</FormDescription>
|
|
1214
1218
|
<FormControl>
|
|
1215
1219
|
<NativeSelect
|
|
1216
1220
|
data-testid="bedrock-region-select"
|
|
@@ -1233,9 +1237,6 @@ export const AiProvidersConfig: React.FC<AiConfigProps> = ({
|
|
|
1233
1237
|
<FormMessage />
|
|
1234
1238
|
<IsOverridden override={override} />
|
|
1235
1239
|
</FormItem>
|
|
1236
|
-
<FormDescription>
|
|
1237
|
-
The AWS region where Bedrock service is available.
|
|
1238
|
-
</FormDescription>
|
|
1239
1240
|
</div>
|
|
1240
1241
|
)}
|
|
1241
1242
|
/>
|
|
@@ -1245,9 +1246,13 @@ export const AiProvidersConfig: React.FC<AiConfigProps> = ({
|
|
|
1245
1246
|
disabled={isWasmRuntime}
|
|
1246
1247
|
name="ai.bedrock.profile_name"
|
|
1247
1248
|
render={({ field, override }) => (
|
|
1248
|
-
<div className="flex flex-col space-y-1">
|
|
1249
|
+
<div className="settings-field flex flex-col space-y-1">
|
|
1249
1250
|
<FormItem className={formItemClasses}>
|
|
1250
|
-
<FormLabel>AWS
|
|
1251
|
+
<FormLabel>AWS profile name (optional)</FormLabel>
|
|
1252
|
+
<FormDescription>
|
|
1253
|
+
The AWS profile name from your ~/.aws/credentials file.
|
|
1254
|
+
Leave blank to use your default AWS credentials.
|
|
1255
|
+
</FormDescription>
|
|
1251
1256
|
<FormControl>
|
|
1252
1257
|
<Input
|
|
1253
1258
|
data-testid="bedrock-profile-input"
|
|
@@ -1262,17 +1267,13 @@ export const AiProvidersConfig: React.FC<AiConfigProps> = ({
|
|
|
1262
1267
|
<FormMessage />
|
|
1263
1268
|
<IsOverridden override={override} />
|
|
1264
1269
|
</FormItem>
|
|
1265
|
-
<FormDescription>
|
|
1266
|
-
The AWS profile name from your ~/.aws/credentials file. Leave
|
|
1267
|
-
blank to use your default AWS credentials.
|
|
1268
|
-
</FormDescription>
|
|
1269
1270
|
</div>
|
|
1270
1271
|
)}
|
|
1271
1272
|
/>
|
|
1272
1273
|
</AccordionFormItem>
|
|
1273
1274
|
|
|
1274
1275
|
<AccordionFormItem
|
|
1275
|
-
title="OpenAI-
|
|
1276
|
+
title="OpenAI-compatible (legacy)"
|
|
1276
1277
|
provider="openai-compatible"
|
|
1277
1278
|
isConfigured={
|
|
1278
1279
|
hasValue("ai.open_ai_compatible.api_key") &&
|
|
@@ -1280,7 +1281,7 @@ export const AiProvidersConfig: React.FC<AiConfigProps> = ({
|
|
|
1280
1281
|
}
|
|
1281
1282
|
>
|
|
1282
1283
|
<p className="text-sm text-amber-600 dark:text-amber-400 mb-2">
|
|
1283
|
-
Consider using Custom
|
|
1284
|
+
Consider using Custom providers instead, which allows you to add
|
|
1284
1285
|
multiple providers with distinct names.
|
|
1285
1286
|
</p>
|
|
1286
1287
|
<ApiKey
|
|
@@ -1326,98 +1327,11 @@ export const AiAssistConfig: React.FC<AiConfigProps> = ({
|
|
|
1326
1327
|
|
|
1327
1328
|
return (
|
|
1328
1329
|
<SettingGroup>
|
|
1329
|
-
<SettingSubtitle>AI
|
|
1330
|
-
|
|
1331
|
-
<FormField
|
|
1332
|
-
control={form.control}
|
|
1333
|
-
name="ai.inline_tooltip"
|
|
1334
|
-
render={({ field }) => (
|
|
1335
|
-
<div className="flex flex-col gap-y-1">
|
|
1336
|
-
<FormItem className={formItemClasses}>
|
|
1337
|
-
<FormLabel className="font-normal">AI Edit Tooltip</FormLabel>
|
|
1338
|
-
<FormControl>
|
|
1339
|
-
<Checkbox
|
|
1340
|
-
data-testid="inline-ai-checkbox"
|
|
1341
|
-
checked={field.value === true}
|
|
1342
|
-
onCheckedChange={field.onChange}
|
|
1343
|
-
/>
|
|
1344
|
-
</FormControl>
|
|
1345
|
-
</FormItem>
|
|
1346
|
-
<FormDescription>
|
|
1347
|
-
Enable "Edit with AI" tooltip when selecting code.
|
|
1348
|
-
</FormDescription>
|
|
1349
|
-
</div>
|
|
1350
|
-
)}
|
|
1351
|
-
/>
|
|
1352
|
-
|
|
1353
|
-
<FormField
|
|
1354
|
-
control={form.control}
|
|
1355
|
-
name="ai.max_tokens"
|
|
1356
|
-
render={({ field }) => {
|
|
1357
|
-
return (
|
|
1358
|
-
<div className="flex flex-col gap-y-1">
|
|
1359
|
-
<div className="flex items-center gap-x-2">
|
|
1360
|
-
<FormItem className={formItemClasses}>
|
|
1361
|
-
<FormLabel className="font-normal">
|
|
1362
|
-
Max output tokens
|
|
1363
|
-
</FormLabel>
|
|
1364
|
-
<FormControl>
|
|
1365
|
-
<Input
|
|
1366
|
-
data-testid="ai-max-tokens-input"
|
|
1367
|
-
type="number"
|
|
1368
|
-
min={1}
|
|
1369
|
-
disabled={!maxTokensEnabled}
|
|
1370
|
-
className="w-28 h-6"
|
|
1371
|
-
value={field.value ?? (maxTokensEnabled ? "" : 32768)}
|
|
1372
|
-
onChange={(e) => {
|
|
1373
|
-
const n = Number.parseInt(e.target.value, 10);
|
|
1374
|
-
field.onChange(Number.isFinite(n) && n > 0 ? n : null);
|
|
1375
|
-
}}
|
|
1376
|
-
/>
|
|
1377
|
-
</FormControl>
|
|
1378
|
-
</FormItem>
|
|
1379
|
-
<FormItem className={formItemClasses}>
|
|
1380
|
-
<Checkbox
|
|
1381
|
-
data-testid="ai-max-tokens-checkbox"
|
|
1382
|
-
checked={maxTokensEnabled}
|
|
1383
|
-
onCheckedChange={(checked) => {
|
|
1384
|
-
const isChecked = checked === true;
|
|
1385
|
-
setMaxTokensEnabled(isChecked);
|
|
1386
|
-
// null signals delete to the server; cast because
|
|
1387
|
-
// UserConfig (OpenAPI-derived) types max_tokens as
|
|
1388
|
-
// `number | undefined`, but zod accepts `null`.
|
|
1389
|
-
const next = (
|
|
1390
|
-
isChecked ? (field.value ?? 32768) : null
|
|
1391
|
-
) as number | undefined;
|
|
1392
|
-
// shouldDirty: true forces RHF to keep this in
|
|
1393
|
-
// dirtyFields even when `next` happens to equal the
|
|
1394
|
-
// form's defaultValue (e.g. untick → tick when disk
|
|
1395
|
-
// started with 32768). Otherwise getDirtyValues
|
|
1396
|
-
// would skip it and the save body would be empty.
|
|
1397
|
-
form.setValue("ai.max_tokens", next, {
|
|
1398
|
-
shouldDirty: true,
|
|
1399
|
-
shouldTouch: true,
|
|
1400
|
-
});
|
|
1401
|
-
onSubmit(form.getValues());
|
|
1402
|
-
}}
|
|
1403
|
-
/>
|
|
1404
|
-
<FormLabel className="font-normal">Override</FormLabel>
|
|
1405
|
-
</FormItem>
|
|
1406
|
-
</div>
|
|
1407
|
-
|
|
1408
|
-
<FormDescription>
|
|
1409
|
-
Each provider sets its own max output tokens (Anthropic uses a
|
|
1410
|
-
recommended default). Adjust to control costs or enable more
|
|
1411
|
-
output.
|
|
1412
|
-
</FormDescription>
|
|
1413
|
-
</div>
|
|
1414
|
-
);
|
|
1415
|
-
}}
|
|
1416
|
-
/>
|
|
1330
|
+
<SettingSubtitle>AI assistant</SettingSubtitle>
|
|
1417
1331
|
|
|
1418
1332
|
<FormErrorsBanner />
|
|
1419
1333
|
<ModelSelector
|
|
1420
|
-
label="Chat
|
|
1334
|
+
label="Chat model"
|
|
1421
1335
|
form={form}
|
|
1422
1336
|
config={config}
|
|
1423
1337
|
name="ai.models.chat_model"
|
|
@@ -1429,7 +1343,7 @@ export const AiAssistConfig: React.FC<AiConfigProps> = ({
|
|
|
1429
1343
|
onSubmit={onSubmit}
|
|
1430
1344
|
/>
|
|
1431
1345
|
<ModelSelector
|
|
1432
|
-
label="Edit
|
|
1346
|
+
label="Edit model"
|
|
1433
1347
|
form={form}
|
|
1434
1348
|
config={config}
|
|
1435
1349
|
name="ai.models.edit_model"
|
|
@@ -1444,13 +1358,80 @@ export const AiAssistConfig: React.FC<AiConfigProps> = ({
|
|
|
1444
1358
|
onSubmit={onSubmit}
|
|
1445
1359
|
/>
|
|
1446
1360
|
|
|
1361
|
+
<FormField
|
|
1362
|
+
control={form.control}
|
|
1363
|
+
name="ai.max_tokens"
|
|
1364
|
+
render={({ field }) => {
|
|
1365
|
+
return (
|
|
1366
|
+
<div className="settings-field flex flex-col gap-y-1">
|
|
1367
|
+
<FormItem className={formItemClasses}>
|
|
1368
|
+
<FormLabel className="font-normal">Max output tokens</FormLabel>
|
|
1369
|
+
<FormDescription>
|
|
1370
|
+
Limit output tokens for both chat and code edits. When not
|
|
1371
|
+
overridden, each provider uses its default (Anthropic uses a
|
|
1372
|
+
recommended default).
|
|
1373
|
+
</FormDescription>
|
|
1374
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
1375
|
+
<FormControl>
|
|
1376
|
+
<Input
|
|
1377
|
+
data-testid="ai-max-tokens-input"
|
|
1378
|
+
type="number"
|
|
1379
|
+
min={1}
|
|
1380
|
+
disabled={!maxTokensEnabled}
|
|
1381
|
+
className="w-20 h-7"
|
|
1382
|
+
value={field.value ?? (maxTokensEnabled ? "" : 32768)}
|
|
1383
|
+
onChange={(e) => {
|
|
1384
|
+
const n = Number.parseInt(e.target.value, 10);
|
|
1385
|
+
field.onChange(Number.isFinite(n) && n > 0 ? n : null);
|
|
1386
|
+
}}
|
|
1387
|
+
/>
|
|
1388
|
+
</FormControl>
|
|
1389
|
+
<FormItem className="flex-row items-center gap-1.5">
|
|
1390
|
+
<FormControl>
|
|
1391
|
+
<Checkbox
|
|
1392
|
+
data-testid="ai-max-tokens-checkbox"
|
|
1393
|
+
checked={maxTokensEnabled}
|
|
1394
|
+
onCheckedChange={(checked) => {
|
|
1395
|
+
const isChecked = checked === true;
|
|
1396
|
+
setMaxTokensEnabled(isChecked);
|
|
1397
|
+
// null signals delete to the server; cast because
|
|
1398
|
+
// UserConfig (OpenAPI-derived) types max_tokens as
|
|
1399
|
+
// `number | undefined`, but zod accepts `null`.
|
|
1400
|
+
const next = (
|
|
1401
|
+
isChecked ? (field.value ?? 32768) : null
|
|
1402
|
+
) as number | undefined;
|
|
1403
|
+
// shouldDirty: true forces RHF to keep this in
|
|
1404
|
+
// dirtyFields even when `next` happens to equal the
|
|
1405
|
+
// form's defaultValue (e.g. untick → tick when disk
|
|
1406
|
+
// started with 32768). Otherwise getDirtyValues
|
|
1407
|
+
// would skip it and the save body would be empty.
|
|
1408
|
+
form.setValue("ai.max_tokens", next, {
|
|
1409
|
+
shouldDirty: true,
|
|
1410
|
+
shouldTouch: true,
|
|
1411
|
+
});
|
|
1412
|
+
onSubmit(form.getValues());
|
|
1413
|
+
}}
|
|
1414
|
+
/>
|
|
1415
|
+
</FormControl>
|
|
1416
|
+
<FormLabel className="font-normal">Override</FormLabel>
|
|
1417
|
+
</FormItem>
|
|
1418
|
+
</div>
|
|
1419
|
+
</FormItem>
|
|
1420
|
+
</div>
|
|
1421
|
+
);
|
|
1422
|
+
}}
|
|
1423
|
+
/>
|
|
1424
|
+
|
|
1447
1425
|
<OverriddenFormField
|
|
1448
1426
|
control={form.control}
|
|
1449
1427
|
name="ai.rules"
|
|
1450
1428
|
render={({ field, override }) => (
|
|
1451
|
-
<div className="flex flex-col">
|
|
1429
|
+
<div className="settings-field flex flex-col">
|
|
1452
1430
|
<FormItem>
|
|
1453
|
-
<FormLabel>Custom
|
|
1431
|
+
<FormLabel>Custom rules</FormLabel>
|
|
1432
|
+
<FormDescription>
|
|
1433
|
+
Custom rules to include in all AI completion prompts.
|
|
1434
|
+
</FormDescription>
|
|
1454
1435
|
<FormControl>
|
|
1455
1436
|
<Textarea
|
|
1456
1437
|
data-testid="ai-rules-input"
|
|
@@ -1464,8 +1445,28 @@ export const AiAssistConfig: React.FC<AiConfigProps> = ({
|
|
|
1464
1445
|
<FormMessage />
|
|
1465
1446
|
<IsOverridden override={override} />
|
|
1466
1447
|
</FormItem>
|
|
1448
|
+
</div>
|
|
1449
|
+
)}
|
|
1450
|
+
/>
|
|
1451
|
+
<FormField
|
|
1452
|
+
control={form.control}
|
|
1453
|
+
name="ai.inline_tooltip"
|
|
1454
|
+
render={({ field }) => (
|
|
1455
|
+
<div className="settings-field flex flex-col gap-y-1">
|
|
1456
|
+
<FormItem className={formItemClasses}>
|
|
1457
|
+
<FormLabel className="font-normal">
|
|
1458
|
+
Show “Edit with AI” on text selection
|
|
1459
|
+
</FormLabel>
|
|
1460
|
+
<FormControl>
|
|
1461
|
+
<Checkbox
|
|
1462
|
+
data-testid="inline-ai-checkbox"
|
|
1463
|
+
checked={field.value === true}
|
|
1464
|
+
onCheckedChange={field.onChange}
|
|
1465
|
+
/>
|
|
1466
|
+
</FormControl>
|
|
1467
|
+
</FormItem>
|
|
1467
1468
|
<FormDescription>
|
|
1468
|
-
|
|
1469
|
+
Enable "Edit with AI" tooltip when selecting code.
|
|
1469
1470
|
</FormDescription>
|
|
1470
1471
|
</div>
|
|
1471
1472
|
)}
|
|
@@ -1658,7 +1659,7 @@ export const AiModelDisplayConfig: React.FC<AiConfigProps> = ({
|
|
|
1658
1659
|
|
|
1659
1660
|
<div className="bg-background">
|
|
1660
1661
|
<Tree
|
|
1661
|
-
aria-label="AI
|
|
1662
|
+
aria-label="AI models by provider"
|
|
1662
1663
|
className="flex-1 overflow-auto outline-hidden focus-visible:outline-hidden"
|
|
1663
1664
|
selectionMode="none"
|
|
1664
1665
|
>
|
|
@@ -1770,7 +1771,7 @@ export const AddModelForm: React.FC<{
|
|
|
1770
1771
|
{customProviderNames.length > 0 && (
|
|
1771
1772
|
<>
|
|
1772
1773
|
<p className="px-2 py-1 text-xs text-muted-secondary font-medium">
|
|
1773
|
-
Custom
|
|
1774
|
+
Custom providers
|
|
1774
1775
|
</p>
|
|
1775
1776
|
{customProviderNames.map((p) => (
|
|
1776
1777
|
<SelectItem key={p} value={p}>
|
|
@@ -1782,7 +1783,7 @@ export const AddModelForm: React.FC<{
|
|
|
1782
1783
|
))}
|
|
1783
1784
|
{knownProviders.length > 0 && (
|
|
1784
1785
|
<p className="px-2 py-1 text-xs text-muted-secondary font-medium mt-1">
|
|
1785
|
-
Built-in
|
|
1786
|
+
Built-in providers
|
|
1786
1787
|
</p>
|
|
1787
1788
|
)}
|
|
1788
1789
|
</>
|
|
@@ -1881,7 +1882,7 @@ export const AddModelForm: React.FC<{
|
|
|
1881
1882
|
<AddButton
|
|
1882
1883
|
isFormOpen={isFormOpen}
|
|
1883
1884
|
setIsFormOpen={setIsFormOpen}
|
|
1884
|
-
label="Add
|
|
1885
|
+
label="Add model"
|
|
1885
1886
|
className="pl-2"
|
|
1886
1887
|
/>
|
|
1887
1888
|
{modelAdded && (
|
|
@@ -1935,9 +1936,9 @@ const AiEnabledConfig: React.FC<AiConfigProps> = ({ form }) => {
|
|
|
1935
1936
|
control={form.control}
|
|
1936
1937
|
name="ai.enabled"
|
|
1937
1938
|
render={({ field, override }) => (
|
|
1938
|
-
<div className="flex flex-col gap-y-1">
|
|
1939
|
+
<div className="settings-field flex flex-col gap-y-1">
|
|
1939
1940
|
<FormItem className={formItemClasses}>
|
|
1940
|
-
<FormLabel className="font-normal">
|
|
1941
|
+
<FormLabel className="font-normal">Show AI features</FormLabel>
|
|
1941
1942
|
<FormControl>
|
|
1942
1943
|
<Checkbox
|
|
1943
1944
|
data-testid="ai-enabled-checkbox"
|
|
@@ -1951,8 +1952,8 @@ const AiEnabledConfig: React.FC<AiConfigProps> = ({ form }) => {
|
|
|
1951
1952
|
<IsOverridden override={override} />
|
|
1952
1953
|
</FormItem>
|
|
1953
1954
|
<FormDescription>
|
|
1954
|
-
When disabled, AI
|
|
1955
|
-
UI.
|
|
1955
|
+
When disabled, various AI-related features are hidden from the
|
|
1956
|
+
marimo UI.
|
|
1956
1957
|
</FormDescription>
|
|
1957
1958
|
</div>
|
|
1958
1959
|
)}
|
|
@@ -1986,32 +1987,32 @@ export const AiConfig: React.FC<AiConfigProps> = ({
|
|
|
1986
1987
|
onValueChange={(value) => setActiveTab(value as AiSettingsSubTab)}
|
|
1987
1988
|
className="flex-1"
|
|
1988
1989
|
>
|
|
1989
|
-
<TabsList className="mb-2">
|
|
1990
|
-
<TabsTrigger value="ai-features">AI
|
|
1990
|
+
<TabsList className="mb-2 max-w-full justify-start overflow-x-auto">
|
|
1991
|
+
<TabsTrigger value="ai-features">AI features</TabsTrigger>
|
|
1991
1992
|
{aiEnabled !== false && (
|
|
1992
1993
|
<>
|
|
1993
|
-
<TabsTrigger value="ai-providers">AI
|
|
1994
|
-
<TabsTrigger value="ai-models">AI
|
|
1994
|
+
<TabsTrigger value="ai-providers">AI providers</TabsTrigger>
|
|
1995
|
+
<TabsTrigger value="ai-models">AI models</TabsTrigger>
|
|
1995
1996
|
{!wasm && <TabsTrigger value="mcp">MCP</TabsTrigger>}
|
|
1996
1997
|
</>
|
|
1997
1998
|
)}
|
|
1998
1999
|
</TabsList>
|
|
2000
|
+
{aiEnabled !== false && <PairWithAgentBanner className="mt-2 mb-6" />}
|
|
1999
2001
|
|
|
2000
|
-
<TabsContent value="ai-features">
|
|
2002
|
+
<TabsContent value="ai-features" className="space-y-6">
|
|
2003
|
+
{aiEnabled !== false && (
|
|
2004
|
+
<AiAssistConfig form={form} config={config} onSubmit={onSubmit} />
|
|
2005
|
+
)}
|
|
2001
2006
|
<AiCodeCompletionConfig
|
|
2002
2007
|
form={form}
|
|
2003
2008
|
config={config}
|
|
2004
2009
|
onSubmit={onSubmit}
|
|
2005
2010
|
/>
|
|
2006
2011
|
<AiEnabledConfig form={form} config={config} onSubmit={onSubmit} />
|
|
2007
|
-
{aiEnabled !== false && (
|
|
2008
|
-
<AiAssistConfig form={form} config={config} onSubmit={onSubmit} />
|
|
2009
|
-
)}
|
|
2010
2012
|
</TabsContent>
|
|
2011
2013
|
{aiEnabled !== false && (
|
|
2012
2014
|
<>
|
|
2013
2015
|
<TabsContent value="ai-providers">
|
|
2014
|
-
<PairWithAgentBanner className="mb-4" />
|
|
2015
2016
|
<AiProvidersConfig
|
|
2016
2017
|
form={form}
|
|
2017
2018
|
config={config}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
import { useAtom } from "jotai";
|
|
4
4
|
import { SettingsIcon } from "lucide-react";
|
|
5
|
-
import { VisuallyHidden } from "react-aria";
|
|
6
5
|
import { AppConfigForm } from "@/components/app-config/app-config-form";
|
|
7
6
|
import {
|
|
8
7
|
Popover,
|
|
@@ -51,10 +50,12 @@ export const ConfigButton: React.FC<Props> = ({
|
|
|
51
50
|
);
|
|
52
51
|
|
|
53
52
|
const userSettingsDialog = (
|
|
54
|
-
<DialogContent className="w-[
|
|
55
|
-
<
|
|
56
|
-
<DialogTitle
|
|
57
|
-
|
|
53
|
+
<DialogContent className="flex flex-col gap-0 w-[94vw] h-[90vh] overflow-hidden sm:max-w-5xl top-[5vh] p-0 font-prose">
|
|
54
|
+
<div className="border-b px-6 py-4 pr-12">
|
|
55
|
+
<DialogTitle className="text-base font-semibold">
|
|
56
|
+
User settings
|
|
57
|
+
</DialogTitle>
|
|
58
|
+
</div>
|
|
58
59
|
<UserConfigForm />
|
|
59
60
|
</DialogContent>
|
|
60
61
|
);
|
|
@@ -4,7 +4,8 @@ import type { HTMLProps, PropsWithChildren } from "react";
|
|
|
4
4
|
import type { SqlOutputType } from "@/core/config/config-schema";
|
|
5
5
|
import { cn } from "@/utils/cn";
|
|
6
6
|
|
|
7
|
-
export const formItemClasses =
|
|
7
|
+
export const formItemClasses =
|
|
8
|
+
"settings-row flex flex-row items-center space-x-1 space-y-0";
|
|
8
9
|
|
|
9
10
|
export const SettingTitle: React.FC<PropsWithChildren> = ({ children }) => {
|
|
10
11
|
return (
|
|
@@ -14,21 +15,18 @@ export const SettingTitle: React.FC<PropsWithChildren> = ({ children }) => {
|
|
|
14
15
|
);
|
|
15
16
|
};
|
|
16
17
|
|
|
17
|
-
export const SettingSubtitle: React.FC<HTMLProps<
|
|
18
|
+
export const SettingSubtitle: React.FC<HTMLProps<HTMLHeadingElement>> = ({
|
|
18
19
|
children,
|
|
19
20
|
className,
|
|
20
21
|
...props
|
|
21
22
|
}) => {
|
|
22
23
|
return (
|
|
23
|
-
<
|
|
24
|
+
<h3
|
|
24
25
|
{...props}
|
|
25
|
-
className={cn(
|
|
26
|
-
"text-base font-semibold underline-offset-2 text-accent-foreground uppercase tracking-wide",
|
|
27
|
-
className,
|
|
28
|
-
)}
|
|
26
|
+
className={cn("text-base font-semibold text-foreground", className)}
|
|
29
27
|
>
|
|
30
28
|
{children}
|
|
31
|
-
</
|
|
29
|
+
</h3>
|
|
32
30
|
);
|
|
33
31
|
};
|
|
34
32
|
|
|
@@ -43,7 +41,7 @@ export const SettingGroup: React.FC<{
|
|
|
43
41
|
children: React.ReactNode;
|
|
44
42
|
}> = ({ title, children }) => {
|
|
45
43
|
return (
|
|
46
|
-
<div className="flex flex-col gap-4
|
|
44
|
+
<div className="settings-group flex flex-col gap-4">
|
|
47
45
|
<SettingSubtitle>{title}</SettingSubtitle>
|
|
48
46
|
{children}
|
|
49
47
|
</div>
|