From 876edc35eb1cc0de5ab40a6bdc58bc7b1b5ef720 Mon Sep 17 00:00:00 2001 From: Sean Teramae Date: Tue, 30 Jun 2026 16:49:17 -0700 Subject: [PATCH 1/3] feat(studio): add DataDesignerJobBuildRoute with CreateFilesetStart Signed-off-by: Sean Teramae --- .../CreateFilesetStart/StartOptionCard.tsx | 64 ++++++++++++++ .../CreateFilesetStart/StartOptionDetail.tsx | 87 +++++++++++++++++++ .../CreateFilesetStart/constants.ts | 45 ++++++++++ .../CreateFilesetStart/index.test.tsx | 49 +++++++++++ .../components/CreateFilesetStart/index.tsx | 78 +++++++++++++++++ .../components/CreateFilesetStart/types.ts | 32 +++++++ web/packages/studio/src/constants/routes.ts | 1 + .../DataDesignerJobBuildRoute/index.tsx | 45 ++++++++++ .../routes/NewDataDesignerJobRoute/index.tsx | 26 +++--- .../src/routes/groups/dataDesignerRoutes.tsx | 12 +++ web/packages/studio/src/routes/utils.ts | 4 + 11 files changed, 431 insertions(+), 12 deletions(-) create mode 100644 web/packages/studio/src/components/CreateFilesetStart/StartOptionCard.tsx create mode 100644 web/packages/studio/src/components/CreateFilesetStart/StartOptionDetail.tsx create mode 100644 web/packages/studio/src/components/CreateFilesetStart/constants.ts create mode 100644 web/packages/studio/src/components/CreateFilesetStart/index.test.tsx create mode 100644 web/packages/studio/src/components/CreateFilesetStart/index.tsx create mode 100644 web/packages/studio/src/components/CreateFilesetStart/types.ts create mode 100644 web/packages/studio/src/routes/DataDesignerJobBuildRoute/index.tsx diff --git a/web/packages/studio/src/components/CreateFilesetStart/StartOptionCard.tsx b/web/packages/studio/src/components/CreateFilesetStart/StartOptionCard.tsx new file mode 100644 index 0000000000..dd22034091 --- /dev/null +++ b/web/packages/studio/src/components/CreateFilesetStart/StartOptionCard.tsx @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { Badge, Flex, Text } from '@nvidia/foundations-react-core'; +import type { StartOption } from '@studio/components/CreateFilesetStart/types'; +import type { FC } from 'react'; + +export interface StartOptionCardProps { + option: StartOption; + /** Whether this tile reads as selected (draws the brand-green border). */ + selected: boolean; + /** Fired on click / keyboard activation. Only invoked for enabled options. */ + onSelect: () => void; +} + +/** + * A single "How do you want to start?" tile: a leading icon badge above a title, + * description, and a metadata badge, in a bordered card rendered as a ` + ); +}; diff --git a/web/packages/studio/src/components/CreateFilesetStart/StartOptionDetail.tsx b/web/packages/studio/src/components/CreateFilesetStart/StartOptionDetail.tsx new file mode 100644 index 0000000000..abaa0e01b2 --- /dev/null +++ b/web/packages/studio/src/components/CreateFilesetStart/StartOptionDetail.tsx @@ -0,0 +1,87 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { Divider, Flex, Stack, Text } from '@nvidia/foundations-react-core'; +import type { StartOption } from '@studio/components/CreateFilesetStart/types'; +import { Layers, Sparkles, Wand2 } from 'lucide-react'; +import type { FC, ReactNode } from 'react'; + +interface DetailPoint { + icon: typeof Layers; + title: string; + description: string; +} + +const SCRATCH_POINTS: DetailPoint[] = [ + { + icon: Layers, + title: 'Add columns block by block', + description: + 'Drop in samplers, LLM generations, transforms and validators in any order on an empty canvas.', + }, + { + icon: Wand2, + title: 'Wire columns together', + description: 'Reference earlier columns in prompts and expressions to build up each record.', + }, + { + icon: Sparkles, + title: 'Preview and run', + description: 'Generate a sample at any time, tweak, and run the full job when it looks right.', + }, +]; + +/** Per-option content for the section that appears below the tiles once a tile is selected. */ +const DETAIL_CONTENT: Partial> = { + scratch: ( + + {SCRATCH_POINTS.map(({ icon: Icon, title, description }) => ( + + + + + + {title} + + + {description} + + + ))} + + ), +}; + +export interface StartOptionDetailProps { + option: StartOption; +} + +/** + * The lower half of the new-fileset view. Its content changes based on the selected + * tile. Today only "Build from scratch" is wired up; selecting it shows what the empty + * canvas offers. + */ +export const StartOptionDetail: FC = ({ option }) => { + const content = DETAIL_CONTENT[option.id]; + if (!content) { + return null; + } + + return ( + + + + {option.title} + + {content} + + ); +}; diff --git a/web/packages/studio/src/components/CreateFilesetStart/constants.ts b/web/packages/studio/src/components/CreateFilesetStart/constants.ts new file mode 100644 index 0000000000..87bfd415ab --- /dev/null +++ b/web/packages/studio/src/components/CreateFilesetStart/constants.ts @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import type { StartOption } from '@studio/components/CreateFilesetStart/types'; +import { Copy, LayoutGrid, Plus, Sparkles } from 'lucide-react'; + +/** + * The "How do you want to start?" tiles, in display order. Only "Build from scratch" + * is enabled today; the others are placeholders for upcoming entry points. + */ +export const START_OPTIONS: StartOption[] = [ + { + id: 'ai', + title: 'Describe with AI', + description: + 'Tell us what you need in plain language. AI drafts the columns and prompts — then you refine everything visually.', + icon: Sparkles, + tag: { label: 'Recommended', color: 'green', kind: 'solid' }, + enabled: false, + }, + { + id: 'template', + title: 'Start from a template', + description: 'Pick a ready-made recipe for SFT, classification, RAG eval, tool-use and more.', + icon: LayoutGrid, + tag: { label: '8 recipes', color: 'blue', kind: 'outline' }, + enabled: false, + }, + { + id: 'clone', + title: 'Clone a fileset', + description: 'Reuse the recipe from a fileset you already built. Tweak the columns and re-run.', + icon: Copy, + tag: { label: 'From your library', color: 'purple', kind: 'outline' }, + enabled: false, + }, + { + id: 'scratch', + title: 'Build from scratch', + description: 'Open an empty canvas and add columns block by block, your way.', + icon: Plus, + tag: { label: 'Blank', color: 'gray', kind: 'outline' }, + enabled: true, + }, +]; diff --git a/web/packages/studio/src/components/CreateFilesetStart/index.test.tsx b/web/packages/studio/src/components/CreateFilesetStart/index.test.tsx new file mode 100644 index 0000000000..552d3c1e17 --- /dev/null +++ b/web/packages/studio/src/components/CreateFilesetStart/index.test.tsx @@ -0,0 +1,49 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { CreateFilesetStart } from '@studio/components/CreateFilesetStart'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +describe('CreateFilesetStart', () => { + it('renders all four start options', () => { + render(); + + expect(screen.getByText('Describe with AI')).toBeInTheDocument(); + expect(screen.getByText('Start from a template')).toBeInTheDocument(); + expect(screen.getByText('Clone a fileset')).toBeInTheDocument(); + expect(screen.getByText('Build from scratch')).toBeInTheDocument(); + }); + + it('shows no Continue footer until a selectable option is chosen', () => { + render(); + + expect(screen.queryByRole('button', { name: /continue/i })).not.toBeInTheDocument(); + }); + + it('does not select disabled options (they are no-ops)', async () => { + const user = userEvent.setup(); + const onContinue = vi.fn(); + render(); + + await user.click(screen.getByText('Describe with AI')); + + expect(screen.queryByRole('button', { name: /continue/i })).not.toBeInTheDocument(); + expect(onContinue).not.toHaveBeenCalled(); + }); + + it('selecting Build from scratch reveals Continue and invokes onContinue with "scratch"', async () => { + const user = userEvent.setup(); + const onContinue = vi.fn(); + render(); + + await user.click(screen.getByText('Build from scratch')); + + const continueButton = screen.getByRole('button', { name: /continue/i }); + expect(continueButton).toBeInTheDocument(); + + await user.click(continueButton); + expect(onContinue).toHaveBeenCalledTimes(1); + expect(onContinue).toHaveBeenCalledWith('scratch'); + }); +}); diff --git a/web/packages/studio/src/components/CreateFilesetStart/index.tsx b/web/packages/studio/src/components/CreateFilesetStart/index.tsx new file mode 100644 index 0000000000..8a9dd41190 --- /dev/null +++ b/web/packages/studio/src/components/CreateFilesetStart/index.tsx @@ -0,0 +1,78 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { + Button, + Flex, + Grid, + GridItem, + PageHeader, + Stack, + Text, +} from '@nvidia/foundations-react-core'; +import { START_OPTIONS } from '@studio/components/CreateFilesetStart/constants'; +import { StartOptionCard } from '@studio/components/CreateFilesetStart/StartOptionCard'; +import { StartOptionDetail } from '@studio/components/CreateFilesetStart/StartOptionDetail'; +import type { StartOptionId } from '@studio/components/CreateFilesetStart/types'; +import { ArrowRight } from 'lucide-react'; +import { useState, type FC } from 'react'; + +export interface CreateFilesetStartProps { + /** Fired when the user confirms a selected start option via the Continue footer. */ + onContinue: (optionId: StartOptionId) => void; +} + +/** + * The Data Designer "Create a fileset" landing view: a row of start-option tiles whose + * lower half changes with the selection, plus a bottom-anchored footer with a Continue + * action that appears once a (selectable) tile is chosen. + */ +export const CreateFilesetStart: FC = ({ onContinue }) => { + const [selectedId, setSelectedId] = useState(null); + const selectedOption = START_OPTIONS.find((option) => option.id === selectedId) ?? null; + + return ( +
+
+ + + + + + How do you want to start? + + + {START_OPTIONS.map((option) => ( + + setSelectedId(option.id)} + /> + + ))} + + + + {selectedOption ? : null} + +
+ + {selectedOption ? ( + + + + ) : null} +
+ ); +}; diff --git a/web/packages/studio/src/components/CreateFilesetStart/types.ts b/web/packages/studio/src/components/CreateFilesetStart/types.ts new file mode 100644 index 0000000000..b8f5cd7027 --- /dev/null +++ b/web/packages/studio/src/components/CreateFilesetStart/types.ts @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import type { BadgeProps } from '@nvidia/foundations-react-core'; +import type { LucideIcon } from 'lucide-react'; + +/** The four ways to start a Data Designer fileset shown as tiles on the new-fileset view. */ +export type StartOptionId = 'ai' | 'template' | 'clone' | 'scratch'; + +export interface StartOptionTag { + label: string; + color: NonNullable; + kind: NonNullable; +} + +export interface StartOption { + id: StartOptionId; + /** Tile title. */ + title: string; + /** One-line tile description. */ + description: string; + /** Leading Lucide icon. */ + icon: LucideIcon; + /** Small badge rendered at the bottom of the tile. */ + tag: StartOptionTag; + /** + * Whether this option is wired up. Disabled options still render (so the full set + * of future entry points is visible) but are no-ops — they cannot be selected and + * never reveal a detail panel or the Continue footer. + */ + enabled: boolean; +} diff --git a/web/packages/studio/src/constants/routes.ts b/web/packages/studio/src/constants/routes.ts index 09f11f4e7d..c1220d9f2d 100644 --- a/web/packages/studio/src/constants/routes.ts +++ b/web/packages/studio/src/constants/routes.ts @@ -103,6 +103,7 @@ export const ROUTES = { dataDesignerJobList: `/workspaces/:${P.workspace}/data-designer`, dataDesignerJobDetails: `/workspaces/:${P.workspace}/data-designer/:${P.dataDesignerJobName}`, dataDesignerJobNew: `/workspaces/:${P.workspace}/data-designer/new`, + dataDesignerJobBuild: `/workspaces/:${P.workspace}/data-designer/new/build`, secrets: `/workspaces/:${P.workspace}/secrets`, guardrails: `/workspaces/:${P.workspace}/guardrails`, guardrailDetail: `/workspaces/:${P.workspace}/guardrails/:${P.guardrailConfigName}`, diff --git a/web/packages/studio/src/routes/DataDesignerJobBuildRoute/index.tsx b/web/packages/studio/src/routes/DataDesignerJobBuildRoute/index.tsx new file mode 100644 index 0000000000..0f93ffd39f --- /dev/null +++ b/web/packages/studio/src/routes/DataDesignerJobBuildRoute/index.tsx @@ -0,0 +1,45 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { Flex, PageHeader, Stack, Text } from '@nvidia/foundations-react-core'; +import { AccessibleTitle } from '@studio/components/AccessibleTitle'; +import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; +import { useBreadcrumbs } from '@studio/providers/breadcrumbs/useBreadcrumbs'; +import { getDataDesignerJobListRoute, getNewDataDesignerJobRoute } from '@studio/routes/utils'; +import type { FC } from 'react'; + +/** + * Placeholder for the "Build from scratch" empty-canvas column builder. Reached from the + * Create-a-fileset view's Continue action; the canvas itself is not wired up yet. + */ +export const DataDesignerJobBuildRoute: FC = () => { + const workspace = useWorkspaceFromPath(); + + useBreadcrumbs({ + items: [ + { href: getDataDesignerJobListRoute(workspace), slotLabel: 'Data Designer' }, + { href: getNewDataDesignerJobRoute(workspace), slotLabel: 'New fileset' }, + { slotLabel: 'Build from scratch' }, + ], + }); + + return ( + + + + + + Empty canvas — the column builder is coming soon. + + + + + ); +}; diff --git a/web/packages/studio/src/routes/NewDataDesignerJobRoute/index.tsx b/web/packages/studio/src/routes/NewDataDesignerJobRoute/index.tsx index 0d8cd01b4b..18763c5865 100644 --- a/web/packages/studio/src/routes/NewDataDesignerJobRoute/index.tsx +++ b/web/packages/studio/src/routes/NewDataDesignerJobRoute/index.tsx @@ -1,33 +1,35 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { PageHeader, Stack } from '@nvidia/foundations-react-core'; import { AccessibleTitle } from '@studio/components/AccessibleTitle'; -import { NewDataDesignerJobForm } from '@studio/components/NewDataDesignerJobForm'; +import { CreateFilesetStart } from '@studio/components/CreateFilesetStart'; +import type { StartOptionId } from '@studio/components/CreateFilesetStart/types'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { useBreadcrumbs } from '@studio/providers/breadcrumbs/useBreadcrumbs'; -import { getDataDesignerJobListRoute } from '@studio/routes/utils'; +import { getDataDesignerJobBuildRoute, getDataDesignerJobListRoute } from '@studio/routes/utils'; import type { FC } from 'react'; +import { useNavigate } from 'react-router-dom'; export const NewDataDesignerJobRoute: FC = () => { const workspace = useWorkspaceFromPath(); + const navigate = useNavigate(); useBreadcrumbs({ items: [ { href: getDataDesignerJobListRoute(workspace), slotLabel: 'Data Designer' }, - { slotLabel: 'New Job' }, + { slotLabel: 'New fileset' }, ], }); + const handleContinue = (optionId: StartOptionId) => { + if (optionId === 'scratch') { + navigate(getDataDesignerJobBuildRoute(workspace)); + } + }; + return ( - - - - - + + ); }; diff --git a/web/packages/studio/src/routes/groups/dataDesignerRoutes.tsx b/web/packages/studio/src/routes/groups/dataDesignerRoutes.tsx index ccfab8aecf..6f6d28eb02 100644 --- a/web/packages/studio/src/routes/groups/dataDesignerRoutes.tsx +++ b/web/packages/studio/src/routes/groups/dataDesignerRoutes.tsx @@ -29,6 +29,13 @@ const NewDataDesignerJobRoute = default: m.NewDataDesignerJobRoute, })) ); +const DataDesignerJobBuildRoute = + DATA_DESIGNER_ENABLED && + lazy(() => + import('@studio/routes/DataDesignerJobBuildRoute').then((m) => ({ + default: m.DataDesignerJobBuildRoute, + })) + ); export const dataDesignerRoutes: RouteObject[] = gateDataDesignerRoutes([ { @@ -46,4 +53,9 @@ export const dataDesignerRoutes: RouteObject[] = gateDataDesignerRoutes([ element: NewDataDesignerJobRoute ? : null, errorElement: , }, + { + path: ROUTES.workspace.dataDesignerJobBuild, + element: DataDesignerJobBuildRoute ? : null, + errorElement: , + }, ]); diff --git a/web/packages/studio/src/routes/utils.ts b/web/packages/studio/src/routes/utils.ts index b6caa70c3e..1da9b33a94 100644 --- a/web/packages/studio/src/routes/utils.ts +++ b/web/packages/studio/src/routes/utils.ts @@ -480,6 +480,10 @@ export const getNewDataDesignerJobRoute = (workspace: string) => { return generatePath(ROUTES.workspace.dataDesignerJobNew, { workspace }); }; +export const getDataDesignerJobBuildRoute = (workspace: string) => { + return generatePath(ROUTES.workspace.dataDesignerJobBuild, { workspace }); +}; + export const getModelChatRoute = (model: NamedEntityRef) => { const { modelNamespace, modelName } = getModelRouteParamsFromEntityRef(model); return generatePath(ROUTES.models.modelChat, { modelNamespace, modelName }); From c4fa6947416b65b04a0ca3cc71256a2b582bc101 Mon Sep 17 00:00:00 2001 From: Sean Teramae Date: Wed, 1 Jul 2026 11:34:00 -0700 Subject: [PATCH 2/3] feat(studio): keep old data designer job form as a legacy route Preserve the pre-DAG NewDataDesignerJobForm at /data-designer/new/legacy so it stays reachable by direct URL, since the new fileset entry point now defaults to the DAG canvas builder. Not linked from any UI. Signed-off-by: Sean Teramae --- web/packages/studio/src/constants/routes.ts | 2 + .../LegacyNewDataDesignerJobRoute/index.tsx | 37 +++++++++++++++++++ .../src/routes/groups/dataDesignerRoutes.tsx | 12 ++++++ web/packages/studio/src/routes/utils.ts | 5 +++ 4 files changed, 56 insertions(+) create mode 100644 web/packages/studio/src/routes/LegacyNewDataDesignerJobRoute/index.tsx diff --git a/web/packages/studio/src/constants/routes.ts b/web/packages/studio/src/constants/routes.ts index c1220d9f2d..8f0ab7d645 100644 --- a/web/packages/studio/src/constants/routes.ts +++ b/web/packages/studio/src/constants/routes.ts @@ -104,6 +104,8 @@ export const ROUTES = { dataDesignerJobDetails: `/workspaces/:${P.workspace}/data-designer/:${P.dataDesignerJobName}`, dataDesignerJobNew: `/workspaces/:${P.workspace}/data-designer/new`, dataDesignerJobBuild: `/workspaces/:${P.workspace}/data-designer/new/build`, + /** Legacy job-creation form, not linked from any UI — reachable only by typing the URL. */ + dataDesignerJobNewLegacy: `/workspaces/:${P.workspace}/data-designer/new/legacy`, secrets: `/workspaces/:${P.workspace}/secrets`, guardrails: `/workspaces/:${P.workspace}/guardrails`, guardrailDetail: `/workspaces/:${P.workspace}/guardrails/:${P.guardrailConfigName}`, diff --git a/web/packages/studio/src/routes/LegacyNewDataDesignerJobRoute/index.tsx b/web/packages/studio/src/routes/LegacyNewDataDesignerJobRoute/index.tsx new file mode 100644 index 0000000000..fa993f6d3b --- /dev/null +++ b/web/packages/studio/src/routes/LegacyNewDataDesignerJobRoute/index.tsx @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { PageHeader, Stack } from '@nvidia/foundations-react-core'; +import { AccessibleTitle } from '@studio/components/AccessibleTitle'; +import { NewDataDesignerJobForm } from '@studio/components/NewDataDesignerJobForm'; +import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; +import { useBreadcrumbs } from '@studio/providers/breadcrumbs/useBreadcrumbs'; +import { getDataDesignerJobListRoute } from '@studio/routes/utils'; +import type { FC } from 'react'; + +/** + * Legacy job-creation form, superseded by {@link CreateFilesetStart} and the DAG canvas + * builder. Kept for now but not linked from any UI — reachable only by typing the URL. + */ +export const LegacyNewDataDesignerJobRoute: FC = () => { + const workspace = useWorkspaceFromPath(); + + useBreadcrumbs({ + items: [ + { href: getDataDesignerJobListRoute(workspace), slotLabel: 'Data Designer' }, + { slotLabel: 'New Job' }, + ], + }); + + return ( + + + + + + + ); +}; diff --git a/web/packages/studio/src/routes/groups/dataDesignerRoutes.tsx b/web/packages/studio/src/routes/groups/dataDesignerRoutes.tsx index 6f6d28eb02..4000146558 100644 --- a/web/packages/studio/src/routes/groups/dataDesignerRoutes.tsx +++ b/web/packages/studio/src/routes/groups/dataDesignerRoutes.tsx @@ -36,6 +36,13 @@ const DataDesignerJobBuildRoute = default: m.DataDesignerJobBuildRoute, })) ); +const LegacyNewDataDesignerJobRoute = + DATA_DESIGNER_ENABLED && + lazy(() => + import('@studio/routes/LegacyNewDataDesignerJobRoute').then((m) => ({ + default: m.LegacyNewDataDesignerJobRoute, + })) + ); export const dataDesignerRoutes: RouteObject[] = gateDataDesignerRoutes([ { @@ -58,4 +65,9 @@ export const dataDesignerRoutes: RouteObject[] = gateDataDesignerRoutes([ element: DataDesignerJobBuildRoute ? : null, errorElement: , }, + { + path: ROUTES.workspace.dataDesignerJobNewLegacy, + element: LegacyNewDataDesignerJobRoute ? : null, + errorElement: , + }, ]); diff --git a/web/packages/studio/src/routes/utils.ts b/web/packages/studio/src/routes/utils.ts index 1da9b33a94..c2877a3c60 100644 --- a/web/packages/studio/src/routes/utils.ts +++ b/web/packages/studio/src/routes/utils.ts @@ -484,6 +484,11 @@ export const getDataDesignerJobBuildRoute = (workspace: string) => { return generatePath(ROUTES.workspace.dataDesignerJobBuild, { workspace }); }; +/** Not linked from any UI — reachable only by typing the URL. */ +export const getLegacyNewDataDesignerJobRoute = (workspace: string) => { + return generatePath(ROUTES.workspace.dataDesignerJobNewLegacy, { workspace }); +}; + export const getModelChatRoute = (model: NamedEntityRef) => { const { modelNamespace, modelName } = getModelRouteParamsFromEntityRef(model); return generatePath(ROUTES.models.modelChat, { modelNamespace, modelName }); From 9c551421407bcbf0ac755a22b3621e3912094bee Mon Sep 17 00:00:00 2001 From: Sean Teramae Date: Thu, 9 Jul 2026 13:43:07 -0700 Subject: [PATCH 3/3] small fixes for review Signed-off-by: Sean Teramae --- .../CreateFilesetStart/StartOptionCard.tsx | 8 +++++--- .../src/components/CreateFilesetStart/constants.ts | 12 +----------- .../src/components/CreateFilesetStart/index.test.tsx | 1 - .../src/components/CreateFilesetStart/index.tsx | 11 ++++++----- .../src/components/CreateFilesetStart/types.ts | 2 +- 5 files changed, 13 insertions(+), 21 deletions(-) diff --git a/web/packages/studio/src/components/CreateFilesetStart/StartOptionCard.tsx b/web/packages/studio/src/components/CreateFilesetStart/StartOptionCard.tsx index dd22034091..344a8a08c9 100644 --- a/web/packages/studio/src/components/CreateFilesetStart/StartOptionCard.tsx +++ b/web/packages/studio/src/components/CreateFilesetStart/StartOptionCard.tsx @@ -56,9 +56,11 @@ export const StartOptionCard: FC = ({ option, selected, on
- - {option.tag.label} - + {option.tag ? ( + + {option.tag.label} + + ) : null} ); }; diff --git a/web/packages/studio/src/components/CreateFilesetStart/constants.ts b/web/packages/studio/src/components/CreateFilesetStart/constants.ts index 87bfd415ab..9f20047dbe 100644 --- a/web/packages/studio/src/components/CreateFilesetStart/constants.ts +++ b/web/packages/studio/src/components/CreateFilesetStart/constants.ts @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { StartOption } from '@studio/components/CreateFilesetStart/types'; -import { Copy, LayoutGrid, Plus, Sparkles } from 'lucide-react'; +import { LayoutGrid, Plus, Sparkles } from 'lucide-react'; /** * The "How do you want to start?" tiles, in display order. Only "Build from scratch" @@ -15,7 +15,6 @@ export const START_OPTIONS: StartOption[] = [ description: 'Tell us what you need in plain language. AI drafts the columns and prompts — then you refine everything visually.', icon: Sparkles, - tag: { label: 'Recommended', color: 'green', kind: 'solid' }, enabled: false, }, { @@ -26,20 +25,11 @@ export const START_OPTIONS: StartOption[] = [ tag: { label: '8 recipes', color: 'blue', kind: 'outline' }, enabled: false, }, - { - id: 'clone', - title: 'Clone a fileset', - description: 'Reuse the recipe from a fileset you already built. Tweak the columns and re-run.', - icon: Copy, - tag: { label: 'From your library', color: 'purple', kind: 'outline' }, - enabled: false, - }, { id: 'scratch', title: 'Build from scratch', description: 'Open an empty canvas and add columns block by block, your way.', icon: Plus, - tag: { label: 'Blank', color: 'gray', kind: 'outline' }, enabled: true, }, ]; diff --git a/web/packages/studio/src/components/CreateFilesetStart/index.test.tsx b/web/packages/studio/src/components/CreateFilesetStart/index.test.tsx index 552d3c1e17..1e9ba8b66e 100644 --- a/web/packages/studio/src/components/CreateFilesetStart/index.test.tsx +++ b/web/packages/studio/src/components/CreateFilesetStart/index.test.tsx @@ -11,7 +11,6 @@ describe('CreateFilesetStart', () => { expect(screen.getByText('Describe with AI')).toBeInTheDocument(); expect(screen.getByText('Start from a template')).toBeInTheDocument(); - expect(screen.getByText('Clone a fileset')).toBeInTheDocument(); expect(screen.getByText('Build from scratch')).toBeInTheDocument(); }); diff --git a/web/packages/studio/src/components/CreateFilesetStart/index.tsx b/web/packages/studio/src/components/CreateFilesetStart/index.tsx index 8a9dd41190..a379ff5721 100644 --- a/web/packages/studio/src/components/CreateFilesetStart/index.tsx +++ b/web/packages/studio/src/components/CreateFilesetStart/index.tsx @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { + Block, Button, Flex, Grid, @@ -32,8 +33,8 @@ export const CreateFilesetStart: FC = ({ onContinue }) const selectedOption = START_OPTIONS.find((option) => option.id === selectedId) ?? null; return ( -
-
+ + = ({ onContinue }) How do you want to start? - + {START_OPTIONS.map((option) => ( = ({ onContinue }) {selectedOption ? : null} -
+ {selectedOption ? ( = ({ onContinue }) ) : null} -
+ ); }; diff --git a/web/packages/studio/src/components/CreateFilesetStart/types.ts b/web/packages/studio/src/components/CreateFilesetStart/types.ts index b8f5cd7027..1690df4956 100644 --- a/web/packages/studio/src/components/CreateFilesetStart/types.ts +++ b/web/packages/studio/src/components/CreateFilesetStart/types.ts @@ -22,7 +22,7 @@ export interface StartOption { /** Leading Lucide icon. */ icon: LucideIcon; /** Small badge rendered at the bottom of the tile. */ - tag: StartOptionTag; + tag?: StartOptionTag; /** * Whether this option is wired up. Disabled options still render (so the full set * of future entry points is visible) but are no-ops — they cannot be selected and