Skip to content
Closed
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
4 changes: 4 additions & 0 deletions apps/web/src/components/FinalizeDesignButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ import type { FinalizeStatus } from '../hooks/useFinalizeProject';
export interface FinalizeDesignButtonProps {
designMdState: Pick<DesignMdState, 'exists' | 'isStale'>;
status: FinalizeStatus;
disabled?: boolean;
onFinalize: () => void;
onCancel: () => void;
}

export function FinalizeDesignButton({
designMdState,
status,
disabled = false,
onFinalize,
onCancel,
}: FinalizeDesignButtonProps) {
Expand Down Expand Up @@ -61,6 +63,8 @@ export function FinalizeDesignButton({
type="button"
className={`project-actions-button ${variantClass}`}
onClick={onFinalize}
disabled={disabled}
title={disabled ? 'Configure API key and model in Settings to enable finalization' : undefined}
>
{label}
</button>
Expand Down
3 changes: 3 additions & 0 deletions apps/web/src/components/ProjectActionsToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type { FinalizeStatus } from '../hooks/useFinalizeProject';
export interface ProjectActionsToolbarProps {
designMdState: Pick<DesignMdState, 'exists' | 'isStale' | 'staleReason'>;
finalizeStatus: FinalizeStatus;
canFinalize: boolean;
onFinalize: () => void;
onCancelFinalize: () => void;
onContinueInCli: () => void | Promise<void>;
Expand All @@ -25,6 +26,7 @@ export interface ProjectActionsToolbarProps {
export function ProjectActionsToolbar({
designMdState,
finalizeStatus,
canFinalize,
onFinalize,
onCancelFinalize,
onContinueInCli,
Expand All @@ -40,6 +42,7 @@ export function ProjectActionsToolbar({
<FinalizeDesignButton
designMdState={designMdState}
status={finalizeStatus}
disabled={!canFinalize}
onFinalize={onFinalize}
onCancel={onCancelFinalize}
/>
Expand Down
19 changes: 17 additions & 2 deletions apps/web/src/components/ProjectView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2294,11 +2294,25 @@ export function ProjectView({
// Continue in CLI / Finalize design package handlers + keyboard
// shortcut wiring. Close to the JSX so the data flow is easy to
// trace from the toolbar back to its sources.

// Check if finalize is available: requires apiKey and model (#1348)
const canFinalize = Boolean(config.apiKey?.trim() && config.model?.trim());

const handleFinalize = useCallback(() => {
// Guard against missing config (should be prevented by disabled state,
// but defend in depth in case the button is triggered programmatically)
if (!config.apiKey?.trim() || !config.model?.trim()) {
setProjectActionsToast({
message: 'Cannot finalize: API key and model are required.',
details: 'Configure your provider in Settings before finalizing.',
});
return;
}

void finalize.trigger({
apiKey: config.apiKey,
apiKey: config.apiKey.trim(),
baseUrl: config.baseUrl,
model: config.model,
model: config.model.trim(),
maxTokens: effectiveMaxTokens(config),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use the trimmed model for max-token defaults

When the saved model has leading/trailing whitespace, this path now correctly sends model: config.model.trim(), but effectiveMaxTokens(config) still looks up the untrimmed value. In that scenario, known models like claude-sonnet-4-5 miss their model-specific default and fall back to 8192 tokens, so finalization can be unnecessarily truncated even though the request uses the trimmed model. Compute the token cap from the same trimmed model that is sent to the daemon.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apps/web/src/components/ProjectView.tsx:2316 now trims config.model before calling finalize.trigger, but maxTokens is still computed from the untrimmed config object. When a saved model has leading or trailing whitespace and there is no explicit max-token override, effectiveMaxTokens(config) falls through to 8192 because apps/web/src/state/maxTokens.ts:96 looks up the raw model string, while the request is sent with the trimmed model id. That means a padded value like claude-sonnet-4-5 can still finalize with the fallback token cap and get unnecessarily truncated even though this PR is trying to normalize whitespace-only config issues. Please derive the trimmed values once, pass the trimmed model into effectiveMaxTokens, and add a regression in apps/web/tests/state/maxTokens.test.ts for a whitespace-padded known model id.

🔁 Powered by Looper · runner=reviewer · agent=opencode · An autonomous AI dev team for your GitHub repos.

}).then((result) => {
if (result) void designMdState.refresh();
Expand Down Expand Up @@ -2500,6 +2514,7 @@ export function ProjectView({
<ProjectActionsToolbar
designMdState={designMdState}
finalizeStatus={finalize.status}
canFinalize={canFinalize}
onFinalize={handleFinalize}
onCancelFinalize={handleCancelFinalize}
onContinueInCli={handleContinueInCli}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "open-design",
"version": "0.7.0",
"version": "0.7.1",
"private": true,
"packageManager": "pnpm@10.33.2",
"type": "module",
Expand Down