Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 6 additions & 10 deletions apps/ui/src/components/views/board-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -636,10 +636,8 @@ export function BoardView() {
const result = await api.features.bulkUpdate(currentProject.path, featureIds, finalUpdates);

if (result.success) {
// Update local state
featureIds.forEach((featureId) => {
updateFeature(featureId, finalUpdates);
});
// Invalidate React Query cache to refetch features with server-updated values
loadFeatures();
toast.success(`Updated ${result.updatedCount} features`);
exitSelectionMode();
} else {
Expand All @@ -655,7 +653,7 @@ export function BoardView() {
[
currentProject,
selectedFeatureIds,
updateFeature,
loadFeatures,
exitSelectionMode,
getPrimaryWorktreeBranch,
addAndSelectWorktree,
Expand Down Expand Up @@ -783,10 +781,8 @@ export function BoardView() {
const result = await api.features.bulkUpdate(currentProject.path, featureIds, updates);

if (result.success) {
// Update local state for all features
featureIds.forEach((featureId) => {
updateFeature(featureId, updates);
});
// Invalidate React Query cache to refetch features with server-updated values
loadFeatures();
toast.success(`Verified ${result.updatedCount} features`);
exitSelectionMode();
} else {
Expand All @@ -798,7 +794,7 @@ export function BoardView() {
logger.error('Bulk verify failed:', error);
toast.error('Failed to verify features');
}
}, [currentProject, selectedFeatureIds, updateFeature, exitSelectionMode]);
}, [currentProject, selectedFeatureIds, loadFeatures, exitSelectionMode]);

// Handler for addressing PR comments - creates a feature and starts it automatically
const handleAddressPRComments = useCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ export function MassEditDialog({
// Field values
const [model, setModel] = useState<ModelAlias>('claude-sonnet');
const [thinkingLevel, setThinkingLevel] = useState<ThinkingLevel>('none');
const [providerId, setProviderId] = useState<string | undefined>(undefined);
const [planningMode, setPlanningMode] = useState<PlanningMode>('skip');
const [requirePlanApproval, setRequirePlanApproval] = useState(false);
const [priority, setPriority] = useState(2);
Expand Down Expand Up @@ -162,6 +163,7 @@ export function MassEditDialog({
});
setModel(getInitialValue(selectedFeatures, 'model', 'claude-sonnet') as ModelAlias);
setThinkingLevel(getInitialValue(selectedFeatures, 'thinkingLevel', 'none') as ThinkingLevel);
setProviderId(undefined); // Features don't store providerId, but we track it after selection
setPlanningMode(getInitialValue(selectedFeatures, 'planningMode', 'skip') as PlanningMode);
setRequirePlanApproval(getInitialValue(selectedFeatures, 'requirePlanApproval', false));
setPriority(getInitialValue(selectedFeatures, 'priority', 2));
Expand Down Expand Up @@ -226,10 +228,11 @@ export function MassEditDialog({
Select a specific model configuration
</p>
<PhaseModelSelector
value={{ model, thinkingLevel }}
value={{ model, thinkingLevel, providerId }}
onChange={(entry: PhaseModelEntry) => {
setModel(entry.model as ModelAlias);
setThinkingLevel(entry.thinkingLevel || 'none');
setProviderId(entry.providerId);
// Auto-enable model and thinking level for apply state
setApplyState((prev) => ({
...prev,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,44 @@ export function PhaseModelSelector({
}
}

// Fallback: Check ClaudeCompatibleProvider models by model ID only (when providerId is not set)
// This handles cases where features store model ID but not providerId
for (const provider of enabledProviders) {
const providerModel = provider.models?.find((m) => m.id === selectedModel);
if (providerModel) {
// Count providers of same type to determine if we need provider name suffix
const sameTypeCount = enabledProviders.filter(
(p) => p.providerType === provider.providerType
).length;
const suffix = sameTypeCount > 1 ? ` (${provider.name})` : '';
// Add thinking level to label if not 'none'
const thinkingLabel =
selectedThinkingLevel !== 'none'
? ` (${THINKING_LEVEL_LABELS[selectedThinkingLevel]} Thinking)`
: '';
// Get icon based on provider type
const getIconForProviderType = () => {
switch (provider.providerType) {
case 'glm':
return GlmIcon;
case 'minimax':
return MiniMaxIcon;
case 'openrouter':
return OpenRouterIcon;
default:
return getProviderIconForModel(providerModel.id) || OpenRouterIcon;
}
};
return {
id: selectedModel,
label: `${providerModel.displayName}${suffix}${thinkingLabel}`,
description: provider.name,
provider: 'claude-compatible' as const,
icon: getIconForProviderType(),
};
}
}

return null;
}, [
selectedModel,
Expand Down