fix(web): scope generation-preview override to the empty design-files tab (#3684)

Address review: dropping the DESIGN_FILES_TAB guard outright let the
generation card win for every in-flight run on the design-files tab,
hiding the file browser of a populated project until a previewable
artifact existed. Scope the override to the truly empty tab, mirroring
DesignFilesPanel's own empty-state gate (no files, no live artifacts, no
folders), so a populated project keeps browsing while generating.

Adds a regression test that a populated design-files tab still renders
file rows while a run is active.
This commit is contained in:
lefarcen
2026-06-04 15:16:29 +00:00
committed by GitHub
parent 668d556097
commit 9fd65efec0
2 changed files with 40 additions and 9 deletions
+16 -9
View File
@@ -1712,17 +1712,24 @@ export function FileWorkspace({
const isActiveSketch = activeFile?.kind === 'sketch' && isSketchName(activeFile.name);
const activeSketch = activeFile && isActiveSketch ? sketches[activeFile.name] : null;
// The generation progress card takes priority over the design-files tab's
// empty "Creations will appear here" list: while a run is in flight and no
// previewable artifact exists yet, the design-files tab (the default landing
// tab) must still show generation progress rather than an idle empty state.
// `buildGenerationPreviewState` already returns null once a preview surface
// exists, so this never hides a finished artifact. (Pre-#3516 the preview
// branch rendered before the design-files branch with no tab guard; the
// composer rewrite added an `activeTab !== DESIGN_FILES_TAB` clause here that
// accidentally hid the progress card on the default tab.)
// The design-files tab is the default landing tab, so while a run is in
// flight and no previewable artifact exists yet the progress card must take
// over its empty "Creations will appear here" state rather than leave an idle
// empty list. (Pre-#3516 the preview branch rendered before the design-files
// branch with no tab guard; the composer rewrite added an `activeTab !==
// DESIGN_FILES_TAB` clause here that hid the progress card on the default
// tab.) But the override is scoped to the *empty* design-files tab: a
// populated project keeps its file browser while generating. The condition
// mirrors DesignFilesPanel's own empty-state gate exactly (no files, no live
// artifacts, no folders), so the card only wins where the panel would have
// shown its empty placeholder.
const designFilesTabIsEmpty =
visibleFiles.length === 0
&& liveArtifactEntries.length === 0
&& projectFolders.length === 0;
const showGenerationPreview = Boolean(generationPreview)
&& activeTab !== DESIGN_SYSTEM_TAB
&& (activeTab !== DESIGN_FILES_TAB || designFilesTabIsEmpty)
&& !isBrowserTabId(activeTab)
&& !isSideChatTabId(activeTab)
&& !isTerminalTabId(activeTab)
@@ -1430,6 +1430,30 @@ describe('FileWorkspace generation failure recovery', () => {
expect(screen.getByTestId('generation-preview-stage')).toBeTruthy();
});
// The override above is scoped to the *empty* design-files tab. A populated
// project must keep its file browser while a run is in flight instead of
// having the generation card hijack the tab — otherwise the fresh-project fix
// would regress browsing for everyone with existing files.
it('keeps the file browser on a populated design-files tab while a run is active', async () => {
render(
<FileWorkspace
projectId="project-1"
projectKind="prototype"
files={[workspaceFile('index.html')]}
liveArtifacts={[]}
onRefreshFiles={vi.fn()}
isDeck={false}
streaming
tabsState={{ tabs: [], active: DESIGN_FILES_TAB }}
onTabsStateChange={vi.fn()}
messages={[generatingAssistantMessage()]}
/>,
);
expect(await screen.findByText('index.html')).toBeTruthy();
expect(screen.queryByTestId('generation-preview-stage')).toBeNull();
});
});
describe('DesignFilesPanel plugin folders', () => {