Add model selector to plan messages proceed button
This commit is contained in:
57
IMPLEMENTATION_SUMMARY.txt
Normal file
57
IMPLEMENTATION_SUMMARY.txt
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# Model Selector for Plan Messages - Implementation Complete
|
||||||
|
|
||||||
|
## What Was Implemented
|
||||||
|
|
||||||
|
When the "Proceed with Build" button appears in plan messages, a model selector dropdown is now displayed above it, allowing users to choose which AI model to use for the build process.
|
||||||
|
|
||||||
|
## Changes Made
|
||||||
|
|
||||||
|
### 1. New Function: `createInlineModelSelector()`
|
||||||
|
Location: chat/public/builder.js (line 1900-1975)
|
||||||
|
|
||||||
|
This helper function:
|
||||||
|
- Creates a container div with the class 'inline-model-selector'
|
||||||
|
- Adds a label "Select Model for Build:" above the dropdown
|
||||||
|
- Creates a styled select dropdown element
|
||||||
|
- Populates it with all available models from state.models
|
||||||
|
- Shows model labels, names, and multipliers (e.g., "2x")
|
||||||
|
- Pre-selects the currently chosen model
|
||||||
|
- Handles user selection changes and syncs with the main model selector
|
||||||
|
- Updates state.selectedModelId and calls markUserModelChange()
|
||||||
|
|
||||||
|
### 2. Modified "Proceed with Build" Button Section
|
||||||
|
Location: chat/public/builder.js (line 2174-2193)
|
||||||
|
|
||||||
|
The proceed button section now:
|
||||||
|
- Creates a container div (buildActionsContainer) using flexbox layout
|
||||||
|
- Adds the inline model selector above the button
|
||||||
|
- Adds the "Proceed with Build" button below the selector
|
||||||
|
- Appends the entire container to the message body
|
||||||
|
|
||||||
|
## Key Features
|
||||||
|
|
||||||
|
Model selector appears inline in plan messages
|
||||||
|
User can change model before proceeding with build
|
||||||
|
Selection syncs with main model selector in composer
|
||||||
|
State is properly managed and protected from server overwrites
|
||||||
|
Handles edge cases (no models available)
|
||||||
|
Matches existing UI styling and design system
|
||||||
|
JavaScript syntax validation passed
|
||||||
|
No breaking changes to existing functionality
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
1. User receives a plan message from AI
|
||||||
|
2. The renderMessages function detects it's a plan message ready for building
|
||||||
|
3. A buildActionsContainer is created with the inline model selector and proceed button
|
||||||
|
4. User can select a different model from the dropdown if desired
|
||||||
|
5. Selection changes update state and sync with main selector
|
||||||
|
6. When user clicks "Proceed with Build", the selected model is used for the build
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
- Uses flexbox for layout (gap: 12px, margin-top: 16px)
|
||||||
|
- Select element styled with custom SVG dropdown arrow
|
||||||
|
- Full-width design for easy selection on all devices
|
||||||
|
- Integrates with existing state management functions
|
||||||
|
- No backend changes required
|
||||||
72
MODEL_SELECTOR_FOR_PLAN_MESSAGES.md
Normal file
72
MODEL_SELECTOR_FOR_PLAN_MESSAGES.md
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
# Model Selector for Plan Messages Implementation
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
When the "Proceed with Build" button appears in plan messages, a model selector dropdown is now displayed above it, allowing users to choose the AI model they want to use for the build process.
|
||||||
|
|
||||||
|
## Changes Made
|
||||||
|
|
||||||
|
### 1. New Helper Function: `createInlineModelSelector()`
|
||||||
|
**Location:** `chat/public/builder.js` (lines ~1899-1975)
|
||||||
|
|
||||||
|
Created a new helper function that generates an inline model selector specifically for plan messages:
|
||||||
|
- Creates a labeled select dropdown with the title "Select Model for Build:"
|
||||||
|
- Populates the dropdown with all available models from `state.models`
|
||||||
|
- Shows model labels, names, and multiplier indicators (e.g., "2x")
|
||||||
|
- Pre-selects the currently selected model from `state.selectedModelId`
|
||||||
|
- Handles model selection changes and updates:
|
||||||
|
- The inline selector's value
|
||||||
|
- The main model selector in the composer area
|
||||||
|
- The state's selectedModelId
|
||||||
|
- Marks the change as user-initiated to prevent server overwrites
|
||||||
|
|
||||||
|
### 2. Modified "Proceed with Build" Button Section
|
||||||
|
**Location:** `chat/public/builder.js` (lines ~2174-2193)
|
||||||
|
|
||||||
|
Updated the code where the "Proceed with Build" button is created:
|
||||||
|
- Creates a container div (`buildActionsContainer`) to hold both the model selector and the button
|
||||||
|
- Adds the inline model selector above the button
|
||||||
|
- Maintains the existing "Proceed with Build" button functionality
|
||||||
|
- Uses flexbox layout with proper spacing (gap: 12px, margin-top: 16px)
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
1. **Plan Message Displayed**: When an AI plan message is received and ready for building
|
||||||
|
2. **Model Selector Created**: The `createInlineModelSelector()` function is called to generate the dropdown
|
||||||
|
3. **User Selection**: User can select a different model for the build if desired
|
||||||
|
4. **State Sync**: Selection changes are synchronized with the main model selector
|
||||||
|
5. **Build Process**: When "Proceed with Build" is clicked, the selected model is used for the build
|
||||||
|
|
||||||
|
## Benefits
|
||||||
|
|
||||||
|
- **Better UX**: Users can now choose their model right where they need it (in the plan message)
|
||||||
|
- **Flexibility**: No need to go back to the composer area to change models before proceeding
|
||||||
|
- **Clear Context**: Model selector is clearly labeled as "Select Model for Build:"
|
||||||
|
- **State Consistency**: Model selection is synchronized across all UI elements
|
||||||
|
- **Prevents Conflicts**: User model selections are protected from server overwrites
|
||||||
|
|
||||||
|
## Technical Details
|
||||||
|
|
||||||
|
### Styling
|
||||||
|
The inline model selector uses:
|
||||||
|
- Consistent styling with the rest of the UI
|
||||||
|
- Custom SVG dropdown arrow
|
||||||
|
- Proper padding and borders matching the design system
|
||||||
|
- Full-width layout for easy selection on all devices
|
||||||
|
|
||||||
|
### State Management
|
||||||
|
- Updates `state.selectedModelId` when changed
|
||||||
|
- Calls `markUserModelChange()` to prevent server overwrites
|
||||||
|
- Syncs with the main model selector in the composer
|
||||||
|
- Preserves existing state management patterns
|
||||||
|
|
||||||
|
### Integration
|
||||||
|
- Works with the existing `updateModelSelectDisplay()` function
|
||||||
|
- Compatible with `markUserModelChange()` for state protection
|
||||||
|
- Integrates seamlessly with the current build flow
|
||||||
|
- No changes to backend or API required
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
- JavaScript syntax check: ✓ Passed
|
||||||
|
- Function definition: ✓ Present at line 1900
|
||||||
|
- Function call: ✓ Present at line 2181
|
||||||
|
- No breaking changes to existing functionality
|
||||||
@@ -1895,6 +1895,85 @@ function hideLoadingIndicator() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Expose for builder.html
|
// Expose for builder.html
|
||||||
|
|
||||||
|
// Helper function to create inline model selector for plan messages
|
||||||
|
function createInlineModelSelector() {
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.className = 'inline-model-selector';
|
||||||
|
container.style.cssText = 'display: flex; flex-direction: column; gap: 8px;';
|
||||||
|
|
||||||
|
// Label
|
||||||
|
const label = document.createElement('label');
|
||||||
|
label.textContent = 'Select Model for Build:';
|
||||||
|
label.style.cssText = 'font-weight: 600; font-size: 13px; color: var(--ink);';
|
||||||
|
|
||||||
|
// Create a select element (simple and reliable)
|
||||||
|
const select = document.createElement('select');
|
||||||
|
select.className = 'inline-model-select';
|
||||||
|
select.style.cssText = `
|
||||||
|
padding: 10px 12px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #fff;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--ink);
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 100%;
|
||||||
|
appearance: none;
|
||||||
|
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right 12px center;
|
||||||
|
background-size: 14px;
|
||||||
|
padding-right: 40px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Populate with available models
|
||||||
|
if (state.models && state.models.length > 0) {
|
||||||
|
state.models.forEach(model => {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = model.id || model.name || model;
|
||||||
|
option.textContent = model.label || model.name || model.id || model;
|
||||||
|
if (model.multiplier && model.multiplier !== 1) {
|
||||||
|
option.textContent += ` (${model.multiplier}x)`;
|
||||||
|
}
|
||||||
|
select.appendChild(option);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set current selection
|
||||||
|
const currentSelection = state.selectedModelId || el.modelSelect?.value;
|
||||||
|
if (currentSelection) {
|
||||||
|
select.value = currentSelection;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const option = document.createElement('option');
|
||||||
|
option.value = '';
|
||||||
|
option.textContent = 'No models available';
|
||||||
|
option.disabled = true;
|
||||||
|
select.appendChild(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle selection change
|
||||||
|
select.addEventListener('change', (e) => {
|
||||||
|
const selectedModel = e.target.value;
|
||||||
|
console.log('[Inline Model Selector] Model selected:', selectedModel);
|
||||||
|
// Update state
|
||||||
|
state.selectedModelId = selectedModel;
|
||||||
|
// Also update the main model selector if it exists
|
||||||
|
if (el.modelSelect) {
|
||||||
|
el.modelSelect.value = selectedModel;
|
||||||
|
updateModelSelectDisplay(selectedModel);
|
||||||
|
}
|
||||||
|
// Mark that user changed model (prevent server from overwriting)
|
||||||
|
markUserModelChange();
|
||||||
|
});
|
||||||
|
|
||||||
|
container.appendChild(label);
|
||||||
|
container.appendChild(select);
|
||||||
|
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
window.hideLoadingIndicator = hideLoadingIndicator;
|
window.hideLoadingIndicator = hideLoadingIndicator;
|
||||||
|
|
||||||
function renderMessages(session) {
|
function renderMessages(session) {
|
||||||
@@ -2093,13 +2172,24 @@ function renderMessages(session) {
|
|||||||
const needsClarification = clarificationPatterns.some(pattern => pattern.test(replyContent));
|
const needsClarification = clarificationPatterns.some(pattern => pattern.test(replyContent));
|
||||||
|
|
||||||
if (isOpenRouterMessage && hasReply && (isPlanPhase || isBuildPhase) && !needsClarification) {
|
if (isOpenRouterMessage && hasReply && (isPlanPhase || isBuildPhase) && !needsClarification) {
|
||||||
|
// Create a container for model selector and proceed button
|
||||||
|
const buildActionsContainer = document.createElement('div');
|
||||||
|
buildActionsContainer.className = 'build-actions-container';
|
||||||
|
buildActionsContainer.style.cssText = 'display: flex; flex-direction: column; gap: 12px; margin-top: 16px;';
|
||||||
|
|
||||||
|
// Create inline model selector for plan messages
|
||||||
|
const inlineModelSelector = createInlineModelSelector();
|
||||||
|
buildActionsContainer.appendChild(inlineModelSelector);
|
||||||
|
|
||||||
|
// Create proceed button
|
||||||
const proceedBtn = document.createElement('button');
|
const proceedBtn = document.createElement('button');
|
||||||
proceedBtn.className = 'primary';
|
proceedBtn.className = 'primary';
|
||||||
proceedBtn.style.marginTop = '12px';
|
|
||||||
proceedBtn.style.width = '100%';
|
proceedBtn.style.width = '100%';
|
||||||
proceedBtn.textContent = 'Proceed with Build';
|
proceedBtn.textContent = 'Proceed with Build';
|
||||||
proceedBtn.onclick = () => proceedWithBuild(replyContent);
|
proceedBtn.onclick = () => proceedWithBuild(replyContent);
|
||||||
assistantBody.appendChild(proceedBtn);
|
buildActionsContainer.appendChild(proceedBtn);
|
||||||
|
|
||||||
|
assistantBody.appendChild(buildActionsContainer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add Download ZIP button for OpenCode messages that are finished and are the latest message
|
// Add Download ZIP button for OpenCode messages that are finished and are the latest message
|
||||||
|
|||||||
Reference in New Issue
Block a user