@squadbase/vantage 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,51 +1,32 @@
1
1
  ---
2
2
  name: vantage-app
3
- description: Vantage(@squadbase/vantage)ダッシュボードアプリの新規作成と、index.tsx 1ファイルの SPA から server/api・ネストレイアウト・動的ルート・404/error を備えた fullstack への拡張手順。Vantage アプリを一から作る/構成を広げるとき、ルーティング規約や definePage の書き方に迷ったときに使う。
3
+ description: Vantage(@squadbase/vantage)ダッシュボードアプリを一から作る / 既存アプリの構成を広げるときの進め方。骨組みの用意、ルートの決め方、データ取得の選択(外部 API か自前の server/api か)、どこで手を止めて検証するかの順序を扱う。規約そのものはアプリルートの AGENTS.md が正本。
4
4
  ---
5
5
 
6
- # Vantage アプリの作成と fullstack 拡張
6
+ # Vantage アプリの作り方 進め方と検証の順序
7
7
 
8
- Vantage は設定ファイル不要(config-free)な React ダッシュボードフレームワーク。アプリ作者が
9
- 書くのは `index.tsx`(と任意の追加ファイル)だけで、Vite・ルーティング・TanStack Query・
10
- Tailwind・UI キット・開発サーバー・API サーバー・ビルドはすべて Vantage が所有する。
8
+ 規約・不変条件・import サブパスの一覧・各 API の書式は、アプリルートの **`AGENTS.md`** が正本。
9
+ **先にそれを読む**(無ければ `vantage add agents` で置ける)。このスキルは「どの順で作り、どこで
10
+ 手を止めて検証するか」だけを扱い、AGENTS.md の内容は繰り返さない。
11
11
 
12
- このスキルは「アプリを一から作る」「最小の SPAfullstack に広げる」ワークフローを扱う。
13
- 個別のページ/API/UI の追加は `vantage-add-feature` スキル、実装中に踏みやすい落とし穴は
14
- `vantage-pitfalls` スキルを参照。
12
+ - ページ / API / UI 1 つ足すだけなら → `vantage-add-feature`
13
+ - 実装したのに静かに壊れたら `vantage-pitfalls`
15
14
 
16
- ## 大原則(先に頭に入れる)
15
+ ## 全体の流れ
17
16
 
18
- - **設定ファイルを作らない。** `vite.config.*`・`tailwind.config.*`・`postcss.config.*`・
19
- `components.json`・`vantage.config.*` はすべて禁止。存在すると `vantage check` がエラーにする。
20
- 設定は「ファイル名の規約」で表現する。
21
- - **import は必ず `@squadbase/vantage` のサブパス経由。** `@tanstack/*`・`@base-ui/react`・
22
- `echarts`・`hono`・`vite`・`tailwindcss` を直接 import しない。`lucide-react` のアイコンだけは
23
- 直接 import してよい。
24
- - **`.vantage/` と `dist/` は生成物。** 編集しない・読みにいかない(gitignore 済み)。
17
+ 1. **骨組み** `pnpm dev` が上がるところまで
18
+ 2. **ルートを決める** — ページファイルを置き、`vantage routes` で URL を確認
19
+ 3. **データを繋ぐ** — 外部 API か、自前の `server/api` か
20
+ 4. **仕上げ** `check` `build` → `preview`
25
21
 
26
- ## サブパスの地図
22
+ **各段階の終わりに `vantage check` を通す。** 静的診断(禁止ファイル・ルート衝突・境界違反・
23
+ API export・env 誤用)はユーザーコードを実行しないので速く、エラーがあれば exit 1 になる。
24
+ まとめて最後に回すと、原因の切り分けが難しくなる。
27
25
 
28
- | import | 提供するもの |
29
- | --- | --- |
30
- | `@squadbase/vantage` | `definePage`(ページ設定) |
31
- | `@squadbase/vantage/router` | `Link`・`Outlet`・`useParams`・`useSearch`・`useNavigate`・`redirect`・`notFound`・`useRoutes`・`useCurrentRoute`・`useSearchParam`・`useSearchState` |
32
- | `@squadbase/vantage/query` | `useApiQuery`・`useApiMutation`・`apiJson`・`apiFetch`・`apiUrl`・`ApiError`、ほか `useQuery`/`useMutation` など TanStack Query の再エクスポート |
33
- | `@squadbase/vantage/ui` | shadcn/ui 系プリミティブ(`Button`・`Loading`・`ErrorState`・`Empty` ほか) |
34
- | `@squadbase/vantage/components` | 複合パーツ(`PageShell`・`DashboardCardPreset`・`DataTablePreset`・`EChart` ほか) |
35
- | `@squadbase/vantage/markdown` | `MarkdownRenderer`(Shiki を隔離するため専用サブパス) |
36
- | `@squadbase/vantage/server` | `ApiContext`・`HttpError`(server/ 側でのみ使う) |
26
+ ## Step 1 骨組み
37
27
 
38
- どのコンポーネントがあるか、props が何かは **`vantage docs` で引く**(パッケージ同梱。
39
- `vantage docs` で一覧、`vantage docs button` / `vantage docs parts/data-table` で個別ページ、
40
- `--json` で機械可読)。**props を推測で書かない。** 名前が分からないときは
41
- **`vantage search <やりたいこと>`**(例: `vantage search 期間を選ぶ UI が欲しい`)で探し、
42
- 出てきた slug を `vantage docs` に渡す。
43
-
44
- ## Step 1 — 最小アプリ(SPA モード)
45
-
46
- `package.json` と `index.tsx` の2ファイルだけで動く。
47
-
48
- `package.json`:
28
+ 新規なら `package.json` `index.tsx` の 2 ファイルだけ。設定ファイルは**作らない**
29
+ (`vite.config.*` 等は `vantage check` がエラーにする)。
49
30
 
50
31
  ```json
51
32
  {
@@ -67,223 +48,73 @@ Tailwind・UI キット・開発サーバー・API サーバー・ビルドは
67
48
  }
68
49
  ```
69
50
 
70
- `index.tsx`(ルートのデフォルトエクスポートが `/` ページになる):
71
-
72
- ```tsx
73
- export default function Dashboard() {
74
- return (
75
- <main className="p-6">
76
- <h1 className="text-2xl font-semibold">Dashboard</h1>
77
- </main>
78
- )
79
- }
80
- ```
51
+ `index.tsx` はデフォルトエクスポートの React コンポーネント 1 つ(これが `/` になる)。あとは
52
+ `pnpm install && pnpm dev` で http://localhost:5173 が上がる。
81
53
 
82
- 起動と検証:
54
+ **既存アプリに合流したときは、作る前に現状を読む:**
83
55
 
84
56
  ```bash
85
- pnpm install
86
- pnpm dev # → http://localhost:5173(HMR)
87
- pnpm check # 静的診断。エラーがあれば exit 1
88
- pnpm routes # ページ/API の URL マップ
89
- ```
90
-
91
- `server/` ディレクトリが無いので、この時点では **SPA モード**(`vantage-manifest.json` の
92
- `mode: "spa"`)。
93
-
94
- ## Step 2 — ページを足してファイルベースルーティングにする
95
-
96
- ルートディレクトリ直下の `.tsx`/`.jsx` がそのままページになる。規約:
97
-
98
- | ファイル | ルート |
99
- | --- | --- |
100
- | `index.tsx` | `/` |
101
- | `monthly-analysis.tsx` | `/monthly-analysis` |
102
- | `sales/index.tsx` | `/sales` |
103
- | `sales/[customerId].tsx` | `/sales/:customerId`(動的パラメータ) |
104
- | `_layout.tsx` | そのディレクトリ配下を包むネストレイアウト |
105
- | `_404.tsx` | Not Found ページ |
106
- | `_error.tsx` | ルートが throw したときのエラーページ |
107
-
108
- `components/`・`hooks/`・`lib/`・`server/`・`public/` はルート走査の対象外(ページにならない)。
109
-
110
- 各ページは **デフォルトエクスポートの React コンポーネントが必須**。タイトル等は `definePage`:
111
-
112
- ```tsx
113
- import { definePage } from "@squadbase/vantage"
114
-
115
- // navLabel は useRoutes() で組むナビの表示名(省略時は title)
116
- export const page = definePage({ title: "Monthly Analysis · Acme", navLabel: "Monthly" })
117
-
118
- export default function MonthlyAnalysis() {
119
- return <main className="p-6">…</main>
120
- }
121
- ```
122
-
123
- > `page` エクスポートはランタイムでは読まれない。title/description/navLabel はビルド時に
124
- > **静的抽出**されるので、値はリテラルで書く(変数や関数呼び出しにしない)。
125
-
126
- ### ネストレイアウトと特殊ページ
127
-
128
- `_layout.tsx` は `Outlet` で子ルートを描く。ナビは `useRoutes()` から組むと、ページファイルを
129
- 足すだけでリンクが増える(手で持つリンク配列を作らない):
130
-
131
- ```tsx
132
- import { Link, Outlet, useCurrentRoute, useRoutes } from "@squadbase/vantage/router"
133
-
134
- export default function RootLayout() {
135
- // 動的ルート(/sales/:id)は URL が定まらないので外す。label は navLabel → title → path
136
- const routes = useRoutes().filter((r) => !r.dynamic)
137
- const current = useCurrentRoute()
138
-
139
- return (
140
- <div className="min-h-screen">
141
- <header>
142
- {routes.map((r) => (
143
- <Link key={r.path} to={r.to} activeClassName="font-semibold">
144
- {r.label}
145
- </Link>
146
- ))}
147
- </header>
148
- <Outlet />
149
- </div>
150
- )
151
- }
152
- ```
153
-
154
- `_404.tsx` / `_error.tsx` は `ui/` の状態コンポーネントを使うと早い:
155
-
156
- ```tsx
157
- // _error.tsx
158
- import { ErrorState } from "@squadbase/vantage/ui"
159
- export default function RouteError({ error }: { error: unknown }) {
160
- const message = error instanceof Error ? error.message : String(error)
161
- return <ErrorState title="This page failed to render" message={message} />
162
- }
57
+ vantage routes # 既にあるページと API の URL マップ
58
+ vantage check # いま壊れていないか(これから出すエラーと切り分ける)
59
+ ls server/ # あれば fullstack モード。無ければ SPA
60
+ ls .claude/skills # 配置済みの skill(このファイルの仲間)
163
61
  ```
164
62
 
165
- ### 動的パラメータの「3つの綴り」を必ず同期させる
63
+ ## Step 2 — ルートを決める
166
64
 
167
- これは崩すと壊れる不変条件:
65
+ ファイル名がそのままルートになる(対応表は AGENTS.md「ルーティング規約」)。**ルーターを設定
66
+ する場所は無い**ので、決めるのは「どんなファイル名で置くか」だけ。
168
67
 
169
- - ファイル名 `sales/[customerId].tsx`
170
- - 表示ルート `/sales/:customerId`
171
- - リンク: `to="/sales/$customerId"` + `params={{ customerId }}`
68
+ - ページを置いたら `vantage routes` で URL を確かめる。意図と違うなら**ファイル名が違う**。
69
+ - ナビゲーションはリンク配列を手で持たず、`useRoutes()` から組む。この形にしておくと以後は
70
+ ページファイルを足すだけでナビが増える(AGENTS.md「ナビはルート一覧から組む」)。
71
+ - 共通の枠(ヘッダ・サイドバー)は `_layout.tsx`、Not Found と例外は `_404.tsx` / `_error.tsx`。
72
+ - 一覧 → 詳細を作るなら、**動的パラメータの「3 つの綴り」を先に決めてから**両方のファイルを
73
+ 書く。後から変えると静かにマッチしなくなる。
172
74
 
173
- ```tsx
174
- import { Link, useParams } from "@squadbase/vantage/router"
75
+ ## Step 3 — データを繋ぐ
175
76
 
176
- // 一覧側のリンク
177
- <Link to="/sales/$customerId" params={{ customerId: row.id }}>{row.name}</Link>
178
-
179
- // 詳細ページ側で取り出す
180
- const { customerId } = useParams()
181
- ```
182
-
183
- ## Step 3 — サーバー状態(API を持たない fetch)
184
-
185
- 外部 API を叩くだけなら `server/` は不要。`@squadbase/vantage/query` の `useQuery` を使う:
186
-
187
- ```tsx
188
- import { useQuery } from "@squadbase/vantage/query"
189
-
190
- const q = useQuery({ queryKey: ["stats"], queryFn: () => fetch("/…").then((r) => r.json()) })
191
- if (q.isPending) return <Loading />
192
- if (q.isError) return <ErrorState message={(q.error as Error).message} />
193
- ```
194
-
195
- `QueryClient` は Vantage が1つだけ管理する(staleTime 30s・retry 1・refetchOnWindowFocus false・
196
- networkMode "always" = ブラウザのオフライン判定に従わず必ず投げる)。挙動を変えたいときは
197
- クエリ側のオプションで上書きする(クライアントごと差し替える口は無い)。
198
- 自分の `server/api` を叩くときは `useQuery` ではなく `useApiQuery`(→ Step 4)。
199
-
200
- ### フィルタ状態は URL に置く
201
-
202
- ダッシュボードの絞り込みは `useState` ではなく `useSearchParam` にする。リロードで消えず、
203
- URL をそのまま共有できる:
204
-
205
- ```tsx
206
- import { useSearchParam, useSearchState } from "@squadbase/vantage/router"
207
-
208
- const [region, setRegion] = useSearchParam("region", "all") // 値は常に string
209
- const [segments, setSegments] = useSearchState<string[]>("segments", []) // 配列・オブジェクト
210
- ```
211
-
212
- デフォルト値(または `null`)を書き込むとキーは URL から消える。履歴は既定で `replace`
213
- (`{ replace: false }` で push)。動的ルートの上でもパスパラメータは保たれる。
214
-
215
- ## Step 4 — fullstack へ拡張(server/api を足す)
216
-
217
- **`server/` ディレクトリを作った瞬間に fullstack モードになる。** `server/api/**` の各ファイルが
218
- API ルートになり、ビルドは client + server バンドル + `mode: "fullstack"` の manifest を出す。
219
-
220
- `server/api/monthly-analysis.ts` → `GET /api/monthly-analysis`:
221
-
222
- ```ts
223
- import type { ApiContext } from "@squadbase/vantage/server"
224
-
225
- export async function GET(_ctx: ApiContext) {
226
- return Response.json({ ok: true })
227
- }
228
- ```
229
-
230
- - API モジュールは大文字の HTTP メソッド(`GET`/`POST`/`PUT`/`PATCH`/`DELETE`/`OPTIONS`)を
231
- エクスポートする。それ以外の名前は `INVALID_API_EXPORT` エラー。
232
- - 動的 API も `[id].ts` 記法: `server/api/customers/[id].ts` → `GET /api/customers/:id`。
233
- `params.id` で取り出す(`noUncheckedIndexedAccess` が効くので `params.id!` 等で narrowing)。
234
- - クライアントに見せたいエラーは `HttpError(status, msg)` を throw する。それ以外の throw は
235
- ログに記録され汎用の 500 になる。
236
- - **シークレットは `ApiContext.env` にだけ届く**(クライアントには決して届かない)。
237
-
238
- クライアント側からは `useApiQuery` で `/api/*` を叩く(ベース URL 解決・JSON パース・非 2xx の
239
- `ApiError` 化・クエリキー `["api", url]` が入っている):
240
-
241
- ```tsx
242
- import { useApiQuery, useApiMutation } from "@squadbase/vantage/query"
243
-
244
- const q = useApiQuery<Customer>(`/api/customers/${customerId}`)
245
- if (q.isError) return <ErrorState message={q.error.message} /> // HttpError のメッセージが入る
246
-
247
- // クエリ文字列は search で渡す(undefined の項目は落ちる → URL にもキーにも出ない)
248
- const rows = useApiQuery<Row[]>("/api/customers", { search: { segment } })
249
-
250
- // 書き込み。変数がそのまま JSON ボディになる
251
- const save = useApiMutation<Customer, Payload>("/api/customers", { method: "POST" })
252
- ```
77
+ **どちらの経路かを先に決める。** ここを間違えると後で全部書き直しになる:
253
78
 
254
- `ApiError` `message`・`status`・`body`・`requestId`(dev ターミナルのログと突き合わせられる)を
255
- 持つ。hook が使えない場所では `apiJson(path, init)`、生の `Response` が要るときは `apiFetch`。
79
+ | データ元 | 使うもの | `server/` |
80
+ | --- | --- | --- |
81
+ | ブラウザから直接叩ける公開 API | `useQuery` + `fetch` | 不要 |
82
+ | DB / シークレットが要る / CORS で叩けない | `server/api/*.ts` + `useApiQuery` | 必要 |
256
83
 
257
- ### server/ とクライアントの境界(絶対に守る)
84
+ **API キーが要る時点で後者しかない** — クライアントに届く env は `PUBLIC_*` だけで、
85
+ シークレットは `ApiContext.env`(= `server/` の中)にしか届かない。
258
86
 
259
- - **クライアントコードは `server/` を import してはならない。** 共有したいコードは `lib/` に置く。
260
- 違反は `CLIENT_IMPORTS_SERVER` エラー(静的にもビルド時にも弾かれる)。
261
- - サーバー専用のデータ/ヘルパは `server/utils.ts` などに置き、`server/api/**` からのみ import する。
87
+ 後者の手順:
262
88
 
263
- ## Step 5 環境変数
89
+ 1. `server/api/<name>.ts` `GET` を書く。**このディレクトリを作った時点で fullstack モード**
90
+ になる(設定変更は不要)。
91
+ 2. `vantage routes` に `/api/<name>` が出ることを確認する。
92
+ 3. dev サーバーか `curl` で叩き、**返す JSON の形を確定させてから** UI を書く。エラー応答は
93
+ `HttpError(status, msg)` を throw して作る。
94
+ 4. クライアントから `useApiQuery<T>("/api/<name>")` で受ける。`isPending` / `isError` の分岐を
95
+ 最初から書く(`Loading` / `ErrorState` が `ui/` にある)。
264
96
 
265
- - **クライアントに届くのは `PUBLIC_` 接頭辞の付いた env のみ**。`import.meta.env.PUBLIC_FOO`。
266
- `VITE_` 系の API は無い。それ以外を `import.meta.env` で読むと `PUBLIC_ENV_MISUSE` 警告。
267
- - サーバー側のシークレットは `ApiContext.env.MY_SECRET` で読む(`server/` 内のみ)。
97
+ ダッシュボードの**絞り込みは `useState` ではなく `useSearchParam` / `useSearchState`** に置く。
98
+ リロードで消えず、URL をそのまま共有できる。これも後から差し替えると全ページに波及するので、
99
+ 最初のフィルタを作る時点で決める。
268
100
 
269
- ## Step 6検証・ビルド・プレビュー
101
+ ## Step 4仕上げ
270
102
 
271
103
  ```bash
272
- pnpm check # 静的診断(禁止ファイル・ルート衝突・境界・API export・env 誤用)
273
- pnpm routes # ページ + API の URL マップを確認
104
+ pnpm check # エラーが残っていないか(exit 1 なら残っている)
105
+ pnpm routes # 公開される URL の最終確認
274
106
  pnpm build # dist/ に client(+ server)+ vantage-manifest.json
275
- pnpm preview # 本番ビルドをローカル実行(fullstack:4173 / spa は Vite preview)
107
+ pnpm preview # 本番ビルドをローカルで動かす
276
108
  ```
277
109
 
278
110
  `vantage-manifest.json` の `mode` は `server/` の有無から自動で決まる(手で書かない)。
279
- デプロイまでの詳細は別途デプロイ手順を参照。
111
+ `preview` まで通れば、そのまま同じ成果物がデプロイされる。
280
112
 
281
113
  ## 詰まったら
282
114
 
283
- - `console.*` とランタイムエラーは開発ターミナルに `[browser:…]` として転送される。
284
- - `vantage check --json` / `vantage routes --json` はエージェント向けの機械可読出力。
285
- - 規約や props を確かめたいときは `vantage docs <name>`(ガイドは `vantage docs routing` など、
286
- 一覧は `vantage docs`)。名前が思い出せないときは `vantage search <やりたいこと>`、
287
- 綴りを横断で確かめたいときは `vantage search "<regex>" --regex`。
288
- - Base UI(≠ Radix)固有の罠、`SelectValue` の挙動、EChart のテーマ非追従などは
289
- `vantage-pitfalls` スキルにまとまっている。
115
+ - ブラウザの `console.*` とランタイムエラーは、**開発ターミナルに `[browser:…]` として転送
116
+ される**。ブラウザの devtools を開かなくても読める。
117
+ - `vantage check --json` / `vantage routes --json` は機械可読出力。
118
+ - props や規約を確かめたいときは `vantage docs <name>`、名前が思い出せないときは
119
+ `vantage search <やりたいこと>`。**推測で props を書かない。**
120
+ - Base UI(≠ Radix)の癖など、静かに壊れる系は `vantage-pitfalls` にまとまっている。
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: vantage-pitfalls
3
- description: Vantage(@squadbase/vantage)アプリを書くときに静かに壊れる落とし穴のリファレンス。Base UI は Radix ではない(asChild 無し・render プロップ)、SelectValue value を描く、クライアントから server/ を import しない、EChart はテーマ非追従、動的パラメータの3綴り同期、PUBLIC_ env、HttpError など。Vantage の UI コンポーネントやルーティングが思った通りに動かないとき、ビルドやスタイルが静かに欠落するときに参照する。
3
+ description: Vantage(@squadbase/vantage)アプリを書くときに静かに壊れる落とし穴のリファレンス。Base UI は Radix ではない(asChild 無し・render プロップ)、Select value/label、クライアントから server/ を import しない、EChart の配色と高さ、動的パラメータの3綴り同期、PUBLIC_ env、HttpError など。Vantage の UI コンポーネントやルーティングが思った通りに動かないとき、ビルドやスタイルが静かに欠落するときに参照する。
4
4
  ---
5
5
 
6
6
  # Vantage アプリの落とし穴リファレンス
@@ -19,26 +19,33 @@ Vantage の UI キットは shadcn/ui の **Base UI バリアント**。Radix
19
19
  - **`Checkbox` の `checked` は `boolean` のみ。** 中間状態は `indeterminate` プロップ。
20
20
  - **`ToggleGroup` の `value` は配列。**
21
21
 
22
- ### `SelectValue` は「選択中の value」を描く。`SelectItem` children は見ない
22
+ ### `SelectValue` value/label Vantage 側が吸収済み(残る 1 ケースだけ注意)
23
23
 
24
- 値とラベルが違うと、トリガーに `kanto` `/sales` といった**生の値**が出てしまう。ラベルを
25
- 出したいときは `items`(value→label のマップ)を渡す:
24
+ Base UI の `SelectValue` は選択中の **value をそのまま描き**、`SelectItem` の children を見ない
25
+ 部品。Vantage `Select` は children から `value` ラベルを集めて渡すので、普通に書けば
26
+ トリガーに「関東」と出る。**手当てが要るのは 1 ケースだけ** — `SelectItem` を別のコンポーネント
27
+ が返している場合は集められないので、`items` を明示する:
26
28
 
27
29
  ```tsx
28
- <Select items={{ kanto: "関東", kansai: "関西" }} … />
30
+ // SelectItem がこの場に無い(<RegionItems /> の中で作られる)ときだけ必要
31
+ <Select items={{ kanto: "関東", kansai: "関西" }} …>
32
+ <SelectContent><RegionItems /></SelectContent>
33
+ </Select>
29
34
  ```
30
35
 
31
- `FilterBarSelect` / `AppShell` header variant / `DataTablePagination` は内部でこの `items` を
32
- 組み立てている。自前で `Select` を使うときは忘れやすい。
36
+ トリガーに `kanto` `/sales` と生の値が出たら、まずこれを疑う。
33
37
 
34
- ## チャート:`EChart` はテーマに自動追従しない
38
+ ## チャート:`EChart` の配色はトークン追従。上書きは `option.color`
35
39
 
36
- `EChart` は薄いラッパー(init/resize/dispose・loading・`onEvents`・PNG コピー/ダウンロードだけ)。
37
- 配色は**各アプリが `option.color`** で決める(または `theme` に echarts テーマを渡す)。
40
+ 系列色は `--chart-1..5`、軸・凡例・ツールチップは文字色/境界色のトークンから組まれ、
41
+ ライト / ダークの切り替えにも追従する(init 時にトークンの実値を解決している)。
38
42
 
39
- - `--chart-1..5` を読んで明暗テーマを自動で組む機構は**意図的に無い**(ECharts はキャンバス
40
- 描画で CSS 変数を読めない)。ダークモード追従が要るなら自前で色を切り替える。
41
- - キャンバス系なので、コンテナに高さを与えないと何も見えない。
43
+ - **`theme` プロップを渡すと追従は完全に止まる。** 系列の色だけ変えたいなら `option.color` を
44
+ 使う option はテーマより優先されるので、軸まわりの追従は残る。
45
+ - キャンバス系なので、**コンテナに高さを与えないと何も見えない**(既定は `h-[400px]`。
46
+ `h-full` を使うなら親に確定した高さが要る)。
47
+ - `option` は `EChartsOption` を annotate するか `satisfies` を付ける。付けないと
48
+ `type: "category"` が `string` に広がってビルドが落ちる。
42
49
 
43
50
  ## 境界:クライアントから `server/` を import しない
44
51
 
@@ -108,7 +115,7 @@ throw new Error("boom") // → ログに出て汎用 500 に丸
108
115
  - `vite.config.*` — Vite 設定は Vantage が所有
109
116
  - `tailwind.config.*` — テーマは `styles.css` のトークンで調整
110
117
  - `postcss.config.*` — Vantage が管理
111
- - `components.json` — UI は `vantage add ui <name>` でコピー
118
+ - `components.json` — shadcn CLI は使わない。UI は `@squadbase/vantage/ui` から import する
112
119
  - `vantage.config.*` — v1 に設定ファイルは無い(規約で表現)
113
120
 
114
121
  ## Markdown:`MarkdownRenderer` は `@squadbase/vantage/markdown` から
@@ -9,10 +9,9 @@
9
9
  > `vantage upgrade` を実行するとフレームワーク同梱の正本から再同期されて上書きされます。
10
10
  > アプリ固有のメモは別ファイル(例: `README.md`)に書いてください。
11
11
  >
12
- > 手順を伴う踏み込んだワークフローは、同梱の Claude Code Skill に分かれています
13
- > (`vantage add skill --all --dir .claude/skills` で配置)。このファイルは「地図」、Skill
14
- > 「手順書」という役割分担です。詰まったら `vantage-app` / `vantage-add-feature` /
15
- > `vantage-pitfalls` を参照してください。
12
+ > 手順を伴う踏み込んだワークフローは、同梱の Claude Code Skill に分かれています。このファイルは
13
+ > 「地図」、Skill は「手順書」という役割分担です。**配置済みの Skill を先に探す**手順は
14
+ > 末尾の「さらに詳しく(同梱 Skill)」にあります。
16
15
 
17
16
  ## Vantage とは
18
17
 
@@ -50,9 +49,21 @@ UI キット・開発サーバー・API サーバー・ビルドはすべて Van
50
49
  | `@squadbase/vantage/ui` | shadcn/ui(Base UI バリアント)プリミティブ(`Button`・`Loading`・`ErrorState`・`Empty` ほか) |
51
50
  | `@squadbase/vantage/components` | 複合パーツ(`PageShell`・`DashboardCardPreset`・`DataTablePreset`・`EChart` ほか) |
52
51
  | `@squadbase/vantage/markdown` | `MarkdownRenderer`(Shiki を隔離するための専用サブパス) |
53
- | `@squadbase/vantage/server` | `ApiContext`・`HttpError`(server/ 側でのみ使う) |
52
+ | `@squadbase/vantage/server` | `ApiContext`・`ApiHandler`・`HttpError`・`isHttpError`・`json`(server/ 側でのみ使う) |
54
53
 
55
- ## 最小アプリ(SPA モード)
54
+ ## モードは 2 つ、切り替えるのは `server/` の有無だけ
55
+
56
+ `server/` ディレクトリがあれば **fullstack**、無ければ **SPA**。`vantage-manifest.json` の
57
+ `mode` はここから自動で決まり、手で書く設定は無い。**SPA から始めて fullstack へ「昇格」する
58
+ 必要はない** ― テンプレートから始めた場合は最初から `server/` があり、その時点で fullstack。
59
+ 下の 2 節は段階ではなく、いま自分がどちらにいるかを確かめるための地図として読む。
60
+
61
+ - `server/` が**ある** → 「API を持つ(fullstack モード)」を見る。データは自前の `/api/*` から
62
+ `useApiQuery` で取る。
63
+ - `server/` が**無い** → 「最小アプリ(2 ファイル)」の構成。外部 API を直接叩くなら `useQuery`。
64
+ 自前の API が要るようになったら `server/api/*.ts` を足すだけでよい(設定変更は不要)。
65
+
66
+ ## 最小アプリ(2 ファイル)
56
67
 
57
68
  `package.json` と `index.tsx` の 2 ファイルだけで動く。
58
69
 
@@ -97,7 +108,7 @@ pnpm check # 静的診断(エラーがあれば exit 1)
97
108
  pnpm routes # ページ/API の URL マップ
98
109
  ```
99
110
 
100
- `server/` ディレクトリが無いので、この時点では **SPA モード**。
111
+ `server/` ディレクトリが無いので、この構成は **SPA モード**。
101
112
 
102
113
  ## ルーティング規約(ファイル名 → ルート)
103
114
 
@@ -196,13 +207,13 @@ if (q.isError) return <ErrorState message={(q.error as Error).message} />
196
207
  `QueryClient` は Vantage が 1 つだけ管理する(staleTime 30s・retry 1・
197
208
  refetchOnWindowFocus false・networkMode "always")。挙動を変えたいときはクエリ側のオプションで
198
209
  上書きする。
199
- 自分の `server/api` を叩くときは `useQuery` ではなく `useApiQuery`(→「API を足して fullstack
200
- する」)。
210
+ 自分の `server/api` を叩くときは `useQuery` ではなく `useApiQuery`(→「API を持つ(fullstack
211
+ モード)」)
201
212
 
202
- ## API を足して fullstack にする
213
+ ## API を持つ(fullstack モード)
203
214
 
204
- **`server/` ディレクトリを作った瞬間に fullstack モードになる。** `server/api/**` の各ファイルが
205
- API ルートになる。
215
+ **`server/` ディレクトリがあれば fullstack モード**(無いアプリに足せば、その時点で切り替わる)。
216
+ `server/api/**` の各ファイルが API ルートになる。
206
217
 
207
218
  `server/api/monthly-analysis.ts` → `GET /api/monthly-analysis`:
208
219
 
@@ -252,15 +263,16 @@ const save = useApiMutation<Customer, Payload>("/api/customers", { method: "POST
252
263
  ## 組み込み UI / コンポーネントの入口
253
264
 
254
265
  - **プリミティブ**は `@squadbase/vantage/ui` から import する(`Button`・`Loading`・
255
- `ErrorState`・`Empty`・`Select`・`Checkbox` ほか)。編集したいコピーが要るなら
256
- `vantage add ui <name>` で `components/ui/` に取り出す。
266
+ `ErrorState`・`Empty`・`Select`・`Checkbox` ほか)
257
267
  - **複合パーツ**は `@squadbase/vantage/components` から(`PageShell`・`DashboardCardPreset`・
258
- `DataTablePreset`・`EChart` ほか)。ブロックは `vantage add block <name>` で取り出す。
268
+ `DataTablePreset`・`EChart` ほか)
259
269
  - **どの名前が import 可能か / props の詳細は `vantage docs` で引く。** ガイドとコンポーネント
260
270
  リファレンスはパッケージに同梱されていて、オフラインでも読める(→「ドキュメントを引く」)。
261
271
  **名前が分からないとき**はやりたいことで `vantage search` する(→ 同節)。
262
- - `vantage add ui`・`vantage add block` でコピーできる名前は、未知名で実行すると候補が一覧表示
263
- される。
272
+ - **UI は import して使うのが既定。取り出す(eject)必要はない。** `vantage add ui|block`
273
+ ソースを手元にコピーして**改造したいとき専用**のコマンドで、取り出せるのは
274
+ `ui: data-table` と `block: sales-overview` の 2 つだけ。それ以外の名前は import 専用で
275
+ `add` の対象ではない(未知名で実行すると候補が一覧表示される)。
264
276
 
265
277
  ## ドキュメントを引く(`vantage docs` / `vantage search`)
266
278
 
@@ -302,9 +314,22 @@ pnpm preview # 本番ビルドをローカル実行
302
314
 
303
315
  ## さらに詳しく(同梱 Skill)
304
316
 
305
- `vantage add skill --all --dir .claude/skills` で配置される(`--dir` を省くとルート直下):
317
+ - **`vantage-app`** アプリを一から作る / 構成を広げるときの進め方と検証の順序
318
+ - **`vantage-add-feature`** — 既存アプリに page / api / ui を 1 つ足す定型
319
+ - **`vantage-pitfalls`** — Base UI(≠ Radix)の癖など、静かに壊れる落とし穴のリファレンス
320
+
321
+ **まず、このアプリに配置済みかを探す。** 手順書の本体は SKILL.md というファイルで、置き場所は
322
+ アプリによって違う:
323
+
324
+ ```bash
325
+ ls .claude/skills # よくある置き場所
326
+ ls -d vantage-* # ルート直下に置くのが既定
327
+ ```
328
+
329
+ 見つかったらその `SKILL.md` を読む(`vantage add skill` を再実行する必要はない)。**無いときだけ**
330
+ 配置する ― 一覧は名前を省いて実行すると出る:
306
331
 
307
- - **`vantage-app`** — アプリを一から作る / SPA を fullstack に広げる手順
308
- - **`vantage-add-feature`** 既存アプリに page / api / ui / block を 1 つ足す定型
309
- - **`vantage-pitfalls`** Base UI( Radix)の癖、`SelectValue` の挙動、EChart のテーマ
310
- 非追従など、静かに壊れる落とし穴のリファレンス
332
+ ```bash
333
+ vantage add skill # 同梱されている skill 名の一覧
334
+ vantage add skill --all --dir .claude/skills # 配置(--dir を省くとルート直下)
335
+ ```