Recipes
opt-shell는 화면을 무한 자유 조합으로 두지 않고, 반복되는 제품 작업 흐름을 canonical recipe로 고정합니다.
reopt design업데이트
1. 왜 recipe가 필요한가
page slot만 제공하면 결국 화면마다 다른 해석이 생깁니다. recipe는 slot을 더 상위 수준의 작업 흐름으로 묶어서, 어떤 화면이 list인지 editor인지 detail인지, 혹은 landing인지 먼저 결정하게 만듭니다. 이 단계가 있어야 density, aside behavior, state UX를 화면 타입별로 일관되게 유지할 수 있습니다.
runtime에서는 `ShellPage recipe="list"`처럼 recipe를 명시합니다. 이 값이 page width 기본값과 audit 기준의 출발점이 됩니다.
표에 보이는 `ListWorkspace`, `DetailWorkspace`, `EditorWorkspace`, `DashboardWorkspace`, `LandingWorkspace`는 현재 recipe label이자 primary API 이름입니다. explicit recipe는 runtime metadata로 유지되고, covered screen에서는 workspace-first abstraction이 기본 진입점입니다.
2. canonical recipe
| Recipe | 기본 슬롯 | 대표 화면 | 핵심 작업 |
|---|---|---|---|
| ListWorkspace | header, toolbar, filters, content, aside, footer | data sources, audit log, operations table | scan, filter, bulk action, preview |
| DetailWorkspace | header, summary, sections, optional aside | project overview, entity detail, release summary | 핵심 상태와 세부 정보 계층 |
| EditorWorkspace | header, toolbar, status, editor, inspector, footer | project brief, spec authoring, structured notes | editing flow, save state, review loop |
| DashboardWorkspace | header, status strip, cards, activity, optional aside | builder landing, ops overview, health dashboard | triage, scan, prioritization |
| LandingWorkspace | header, content bands, footer | public examples, onboarding, campaign pages | hero, narrative sections, CTA |
3. 선택 규칙
- 표나 목록을 스캔하고 필터링하며 bulk action을 하는 화면은 `ListWorkspace`로 시작합니다.
- 단일 리소스의 상태와 세부 정보를 위계적으로 읽는 화면은 `DetailWorkspace`를 사용합니다.
- 문서를 생성, 편집, 검수, 저장하는 흐름은 `EditorWorkspace`로 제한합니다.
- 여러 지표를 스캔하고 triage하는 시작 화면은 `DashboardWorkspace`로 취급합니다.
- 히어로, stacked section, CTA가 중심인 공개/프로모션 화면은 `LandingWorkspace` 또는 더 풍부한 hero chrome을 가진 `ShellLandingPage`로 시작합니다.
4. workspace-first 방향
| 단계 | 주요 진입점 | 의미 |
|---|---|---|
| Primary API | `ListWorkspace`, `DetailWorkspace`, `EditorWorkspace`, `DashboardWorkspace`, `LandingWorkspace` | covered screen에서 recipe 자체가 slot rhythm과 구조를 더 강하게 소유합니다. |
| Low-level fallback | `ShellPage recipe="..."` | adapter prototype이나 예외 화면에서만 쓰는 escape hatch로 남깁니다. |
관리 UI에서도 같은 원칙을 씁니다. Builder Harness Manager는 raw JSON editor를 먼저 노출하지 않고, policy와 recipe를 시각적 control로 다루게 한 뒤, preview와 review gate를 통해 workspace 정책으로 승격시키는 방향을 택합니다.
5. slot contract와 enforcement
recipe는 label만 있는 것이 아니라 최소 slot contract를 가집니다. 현재 primary workspace API는 모두 `header`와 `children | content`를 필수로 요구합니다. recipe contract 기준으로는 landing만 `content`가 필수이고 `header / footer`는 optional입니다. covered Builder 화면에서는 audit가 `toolbar / filters / aside / footer` 같은 기대 slot과 `ShellSection / ShellStateBoundary` helper 존재를 함께 검사합니다.
| 계층 | 무엇을 고정하는가 |
|---|---|
| Type contract | workspace component가 최소 `header`와 body를 강제합니다. |
| Rollout audit | covered screen에서 expected workspace, expected slot, shared helper를 검사합니다. |
| UI proof | smoke와 visual proof가 recipe 화면 구조가 실제로 유지되는지 확인합니다. |
6. 실제 적용 예
Builder의 `data-sources` 화면은 `ListWorkspace`에 가깝습니다. grid engine은 유지하되, toolbar와 status, footer, preview frame은 harness가 소유합니다.
import {
ShellDataGridAdapter,
ListWorkspace,
ShellSection,
ShellStateBoundary,
} from "@reopt-ai/opt-shell";
export function DataSourcesScreen() {
return (
<ListWorkspace
header={<div>Data Sources</div>}
toolbar={<div>Toolbar</div>}
filters={<div>Filters</div>}
aside={<div>Preview</div>}
footer={<div>Last sync: 2m ago</div>}
>
<ShellStateBoundary loading={false}>
<ShellDataGridAdapter
title="Sources"
description="Grid engine은 유지하고 page chrome은 harness가 소유합니다."
toolbar={<div>Bulk actions</div>}
status={<div>42 rows</div>}
footer={<div>Selection summary</div>}
rows={rows}
columns={columns}
getRowId={(row) => row.id}
/>
</ShellStateBoundary>
</ListWorkspace>
);
}7. scaffold로 시작하기
새 화면은 raw `div` 조합보다 scaffold로 시작하는 편이 좋습니다. workspace와 fullscreen-tool 모두 CLI가 기본 골격을 생성하고, 그 위에 실제 domain content를 올리도록 유도합니다.
현재 scaffold CLI의 workspace 생성 대상은 `list`, `detail`, `editor`, `dashboard`입니다. `landing`은 runtime recipe와 문서 컴포넌트로는 지원하지만 scaffold 옵션에는 아직 포함하지 않았습니다.
bun run --filter @reopt-ai/opt-shell scaffold -- \
--kind workspace \
--workspace list \
--target apps/web/builder-app/projects/[projectId]/orders/page.tsx \
--title "Orders" \
--toolbar --filters --aside --footer8. recipe intent 메타데이터
각 recipe는 intent, antiPatterns, selectionHeuristics메타데이터를 포함합니다. AI agent가 recipe를 선택할 때 "왜 이 recipe인지"를 판단하는 근거로 사용됩니다.
| Field | Purpose | Example (list) |
|---|---|---|
| intent | recipe가 존재하는 이유 | Collection browsing is the primary user action. |
| antiPatterns | 하면 안 되는 것 | Do not use list for single-resource detail views. |
| selectionHeuristics | 선택 시그널 | The screen shows rows of records or resources. |
이 메타데이터는 SHELL_RECIPE_DEFINITIONS / getShellRecipeDefinition()에 그대로 담겨 있어, recipe 선택 로직이나 AI agent의 system prompt에 직접 주입해 활용할 수 있습니다.