-
-
setFormData({...formData, maxTokens: e.target.value})}
- className="h-10"
- />
- {defaultConfig?.max_tokens && (
-
- 🔧 Default: {defaultConfig.max_tokens?.toLocaleString()}
-
- )}
-
diff --git a/src/components/model-config-tabs.tsx b/src/components/model-config-tabs.tsx
index 1b957a7d..5e2b7b38 100644
--- a/src/components/model-config-tabs.tsx
+++ b/src/components/model-config-tabs.tsx
@@ -117,11 +117,15 @@ const categorizeAgent = (agentKey: string): string => {
return 'advanced';
};
-// Frontend-specific agent display interface
+// Frontend-specific agent display interface
export interface AgentDisplayConfig {
key: string;
name: string;
description: string;
+ constraint?: {
+ enabled: boolean;
+ allowedModels: string[];
+ };
}
interface ModelConfigTabsProps {
diff --git a/src/components/monaco-editor/monaco-editor.tsx b/src/components/monaco-editor/monaco-editor.tsx
index 5777877e..b4900047 100644
--- a/src/components/monaco-editor/monaco-editor.tsx
+++ b/src/components/monaco-editor/monaco-editor.tsx
@@ -36,7 +36,7 @@ self.MonacoEnvironment = {
};
// From GitHub Dark theme
-monaco.editor.defineTheme('v1-dev-dark', {
+monaco.editor.defineTheme('vibesdk-dark', {
base: 'vs-dark',
inherit: true,
rules: [
@@ -77,7 +77,7 @@ monaco.editor.defineTheme('v1-dev-dark', {
},
});
-monaco.editor.defineTheme('v1-dev', {
+monaco.editor.defineTheme('vibesdk', {
base: 'vs',
inherit: true,
rules: [
@@ -116,43 +116,13 @@ monaco.editor.defineTheme('v1-dev', {
},
});
-monaco.editor.setTheme('v1-dev');
-
-monaco.languages.typescript.typescriptDefaults.setDiagnosticsOptions({
- noSemanticValidation: true,
- noSyntaxValidation: true,
-});
-
-// Configure TypeScript defaults for JSX support
-monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
- jsx: monaco.languages.typescript.JsxEmit.React,
- allowJs: true,
- allowSyntheticDefaultImports: true,
- esModuleInterop: true,
- moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
- module: monaco.languages.typescript.ModuleKind.ESNext,
- target: monaco.languages.typescript.ScriptTarget.ESNext,
- jsxFactory: 'React.createElement',
- jsxFragmentFactory: 'React.Fragment',
-});
-
-// Configure JavaScript defaults for JSX support
-monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
- allowJs: true,
- allowSyntheticDefaultImports: true,
- esModuleInterop: true,
- jsx: monaco.languages.typescript.JsxEmit.React,
- moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
- module: monaco.languages.typescript.ModuleKind.ESNext,
- target: monaco.languages.typescript.ScriptTarget.ESNext,
- jsxFactory: 'React.createElement',
- jsxFragmentFactory: 'React.Fragment',
-});
+monaco.editor.setTheme('vibesdk');
export type MonacoEditorProps = React.ComponentProps<'div'> & {
createOptions?: monaco.editor.IStandaloneEditorConstructionOptions;
find?: string;
replace?: string;
+ enableTypeScriptFeatures?: 'auto' | boolean;
};
/**
@@ -163,6 +133,7 @@ export const MonacoEditor = memo
(function MonacoEditor({
createOptions = {},
find,
replace,
+ enableTypeScriptFeatures = 'auto',
...props
}) {
const containerRef = useRef(null);
@@ -171,6 +142,63 @@ export const MonacoEditor = memo(function MonacoEditor({
const stickyScroll = useRef(true);
const { theme } = useTheme();
+ const shouldEnableTypeScript = React.useMemo(() => {
+ if (enableTypeScriptFeatures === 'auto') {
+ return !createOptions.readOnly;
+ }
+ return enableTypeScriptFeatures;
+ }, [enableTypeScriptFeatures, createOptions.readOnly]);
+
+ // Configure TypeScript diagnostics based on mode
+ useEffect(() => {
+ const tsDefaults = monaco.languages.typescript.typescriptDefaults;
+ const jsDefaults = monaco.languages.typescript.javascriptDefaults;
+
+ if (shouldEnableTypeScript) {
+ // Enable full IntelliSense for editing
+ tsDefaults.setDiagnosticsOptions({
+ noSemanticValidation: false,
+ noSyntaxValidation: false,
+ });
+ tsDefaults.setCompilerOptions({
+ jsx: monaco.languages.typescript.JsxEmit.React,
+ allowJs: true,
+ allowSyntheticDefaultImports: true,
+ esModuleInterop: true,
+ moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
+ module: monaco.languages.typescript.ModuleKind.ESNext,
+ target: monaco.languages.typescript.ScriptTarget.ESNext,
+ jsxFactory: 'React.createElement',
+ jsxFragmentFactory: 'React.Fragment',
+ });
+ jsDefaults.setCompilerOptions({
+ allowJs: true,
+ allowSyntheticDefaultImports: true,
+ esModuleInterop: true,
+ jsx: monaco.languages.typescript.JsxEmit.React,
+ moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
+ module: monaco.languages.typescript.ModuleKind.ESNext,
+ target: monaco.languages.typescript.ScriptTarget.ESNext,
+ jsxFactory: 'React.createElement',
+ jsxFragmentFactory: 'React.Fragment',
+ });
+ } else {
+ // Disable expensive features for viewing
+ tsDefaults.setDiagnosticsOptions({
+ noSemanticValidation: true,
+ noSyntaxValidation: true,
+ });
+ tsDefaults.setCompilerOptions({
+ jsx: monaco.languages.typescript.JsxEmit.React,
+ target: monaco.languages.typescript.ScriptTarget.ESNext,
+ });
+ jsDefaults.setCompilerOptions({
+ jsx: monaco.languages.typescript.JsxEmit.React,
+ target: monaco.languages.typescript.ScriptTarget.ESNext,
+ });
+ }
+ }, [shouldEnableTypeScript]);
+
useEffect(() => {
let configuredTheme = theme;
@@ -180,7 +208,7 @@ export const MonacoEditor = memo(function MonacoEditor({
editor.current = monaco.editor.create(containerRef.current!, {
language: createOptions.language || 'typescript',
minimap: { enabled: false },
- theme: configuredTheme === 'dark' ? 'v1-dev-dark' : 'v1-dev',
+ theme: configuredTheme === 'dark' ? 'vibesdk-dark' : 'vibesdk',
automaticLayout: true,
value: defaultCode,
fontSize: 13,
@@ -207,6 +235,10 @@ export const MonacoEditor = memo(function MonacoEditor({
}
return () => {
+ const model = editor.current?.getModel();
+ if (model) {
+ model.dispose();
+ }
editor.current?.dispose();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -217,10 +249,16 @@ export const MonacoEditor = memo(function MonacoEditor({
const model = editor.current.getModel();
if (!model) return;
- editor.current.setValue(createOptions.value || '');
+ model.pushEditOperations(
+ [],
+ [{
+ range: model.getFullModelRange(),
+ text: createOptions.value || ''
+ }],
+ () => null
+ );
if (stickyScroll.current) {
- // Scroll to bottom
const lineCount = model.getLineCount();
editor.current.revealLine(lineCount);
}
@@ -293,7 +331,7 @@ export const MonacoEditor = memo(function MonacoEditor({
// Update theme when app theme changes
useEffect(() => {
if (editor.current) {
- monaco.editor.setTheme(theme === 'dark' ? 'v1-dev-dark' : 'v1-dev');
+ monaco.editor.setTheme(theme === 'dark' ? 'vibesdk-dark' : 'vibesdk');
}
}, [theme]);
diff --git a/src/components/ui/model-selector.tsx b/src/components/ui/model-selector.tsx
index 0677d92d..7fbc6155 100644
--- a/src/components/ui/model-selector.tsx
+++ b/src/components/ui/model-selector.tsx
@@ -111,7 +111,7 @@ export function ModelSelector({
className="w-full justify-between"
disabled={disabled}
>
- {getSelectedDisplay() || placeholder}
+ {getSelectedDisplay() || placeholder}
@@ -203,7 +203,7 @@ export function ModelSelector({
{/* System default display */}
{systemDefault && (
-
+
🔧 System default: {getModelDisplayName(systemDefault)}
)}
diff --git a/src/lib/api-client.ts b/src/lib/api-client.ts
index a6ea0952..ae87ebe9 100644
--- a/src/lib/api-client.ts
+++ b/src/lib/api-client.ts
@@ -684,11 +684,14 @@ class ApiClient {
/**
* Get BYOK providers and available models
+ * @param agentAction - Optional agent action to filter models by constraints
*/
- async getByokProviders(): Promise> {
- return this.request(
- '/api/model-configs/byok-providers',
- );
+ async getByokProviders(agentAction?: string): Promise> {
+ const endpoint = agentAction
+ ? `/api/model-configs/byok-providers?agentAction=${encodeURIComponent(agentAction)}`
+ : '/api/model-configs/byok-providers';
+
+ return this.request(endpoint);
}
/**
diff --git a/src/routes/app/index.tsx b/src/routes/app/index.tsx
index b576d1f9..03a9cdc1 100644
--- a/src/routes/app/index.tsx
+++ b/src/routes/app/index.tsx
@@ -1000,7 +1000,7 @@ export default function AppView() {
'on',
scrollBeyondLastLine: false,
fontSize: 13,
- theme: 'v1-dev',
+ theme: 'vibesdk',
automaticLayout: true,
}}
/>
diff --git a/src/routes/chat/chat.tsx b/src/routes/chat/chat.tsx
index 71803cea..df1a3c47 100644
--- a/src/routes/chat/chat.tsx
+++ b/src/routes/chat/chat.tsx
@@ -36,7 +36,6 @@ import { useAutoScroll } from '@/hooks/use-auto-scroll';
import { useImageUpload } from '@/hooks/use-image-upload';
import { useDragDrop } from '@/hooks/use-drag-drop';
import { ImageAttachmentPreview } from '@/components/image-attachment-preview';
-import { createAIMessage } from './utils/message-helpers';
import { Button } from '@/components/ui/button';
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu';
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from '@/components/ui/alert-dialog';
@@ -113,14 +112,12 @@ export default function Chat() {
totalFiles,
websocket,
sendUserMessage,
- sendAiMessage,
blueprint,
previewUrl,
clearEdit,
projectStages,
phaseTimeline,
isThinking,
- onCompleteBootstrap,
// Deployment and generation control
isDeploying,
cloudflareDeploymentUrl,
@@ -235,7 +232,7 @@ export default function Chat() {
const imageInputRef = useRef(null);
// Fake stream bootstrap files
- const { streamedFiles: streamedBootstrapFiles, doneStreaming } =
+ const { streamedFiles: streamedBootstrapFiles } =
useFileContentStream(bootstrapFiles, {
tps: 600,
enabled: isBootstrapping,
@@ -413,28 +410,7 @@ export default function Chat() {
setView('editor');
}
}, [isGeneratingBlueprint, view]);
-
- useEffect(() => {
- // Only show bootstrap completion message for NEW chats, not when reloading existing ones
- if (doneStreaming && !isGeneratingBlueprint && !blueprint && urlChatId === 'new') {
- onCompleteBootstrap();
- sendAiMessage(
- createAIMessage(
- 'creating-blueprint',
- 'Bootstrapping complete, now creating a blueprint for you...',
- true,
- ),
- );
- }
- }, [
- doneStreaming,
- isGeneratingBlueprint,
- sendAiMessage,
- blueprint,
- onCompleteBootstrap,
- urlChatId,
- ]);
-
+
const isRunning = useMemo(() => {
return (
isBootstrapping || isGeneratingBlueprint // || codeGenState === 'active'
@@ -1183,7 +1159,7 @@ export default function Chat() {
lineNumbers: 'on',
scrollBeyondLastLine: false,
fontSize: 13,
- theme: 'v1-dev',
+ theme: 'vibesdk',
automaticLayout: true,
}}
find={
diff --git a/src/routes/chat/components/preview-iframe.tsx b/src/routes/chat/components/preview-iframe.tsx
index eae8d601..1b1986af 100644
--- a/src/routes/chat/components/preview-iframe.tsx
+++ b/src/routes/chat/components/preview-iframe.tsx
@@ -336,6 +336,7 @@ export const PreviewIframe = forwardRef(
if (loadState.status === 'loaded' && loadState.loadedSrc) {
return (