@squadbase/vantage 0.2.0 → 0.2.2
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.
- package/README.md +5 -1
- package/dist/{chunk-DIABD3KZ.js → chunk-JC6MT5UU.js} +26 -3
- package/dist/chunk-JC6MT5UU.js.map +1 -0
- package/dist/cli.js +89 -24
- package/dist/cli.js.map +1 -1
- package/dist/components/index.d.ts +7 -1
- package/dist/components/index.js +139 -22
- package/dist/components/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/router/index.d.ts +1 -1
- package/dist/router/index.js.map +1 -1
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +1 -1
- package/docs/en/agent-skills.md +21 -4
- package/docs/en/changelog.md +66 -0
- package/docs/en/cli-reference.md +1 -1
- package/docs/en/components.md +2 -2
- package/docs/en/parts/echart.md +20 -11
- package/docs/en/ui/select.md +16 -10
- package/docs/index.json +2 -2
- package/docs/ja/agent-skills.md +21 -4
- package/docs/ja/changelog.md +63 -0
- package/docs/ja/cli-reference.md +1 -1
- package/docs/ja/components.md +2 -2
- package/docs/ja/parts/echart.md +17 -9
- package/docs/ja/ui/select.md +17 -10
- package/package.json +1 -1
- package/skills/vantage-app/SKILL.md +64 -233
- package/skills/vantage-pitfalls/SKILL.md +22 -14
- package/templates/AGENTS.md +49 -22
- package/dist/chunk-DIABD3KZ.js.map +0 -1
package/docs/ja/ui/select.md
CHANGED
|
@@ -40,24 +40,31 @@ import {
|
|
|
40
40
|
> <Select onValueChange={(value) => value !== null && setRegion(value)}>
|
|
41
41
|
> ```
|
|
42
42
|
|
|
43
|
-
> [!
|
|
44
|
-
> `SelectValue`
|
|
45
|
-
>
|
|
46
|
-
> `items` に渡してください。
|
|
43
|
+
> [!NOTE]
|
|
44
|
+
> Base UI の `SelectValue` は**選択中の `value` をそのまま表示する**部品で、`SelectItem` の中身は
|
|
45
|
+
> 見ません。`value="kanto"` ならトリガーには `kanto` と出ます。
|
|
47
46
|
>
|
|
48
|
-
>
|
|
49
|
-
>
|
|
47
|
+
> Vantage の `Select` はこの癖を**吸収します** — children を辿って `SelectItem` の `value` →
|
|
48
|
+
> ラベルの対応を組み、Base UI の `items` に渡すので、下の書き方でトリガーに「関東」と出ます。
|
|
49
|
+
> `SegmentedControl` が `ToggleGroup` の配列 API を吸収しているのと同じ扱いです。
|
|
50
50
|
>
|
|
51
|
-
>
|
|
51
|
+
> ```tsx
|
|
52
|
+
> <Select value={region} onValueChange={…}>
|
|
52
53
|
> <SelectTrigger><SelectValue placeholder="地域を選ぶ" /></SelectTrigger>
|
|
53
|
-
>
|
|
54
|
+
> <SelectContent>
|
|
55
|
+
> <SelectItem value="kanto">関東</SelectItem>
|
|
56
|
+
> </SelectContent>
|
|
54
57
|
> </Select>;
|
|
55
58
|
> ```
|
|
56
59
|
>
|
|
57
|
-
> `
|
|
60
|
+
> 自動で拾えるのは、**その場に書かれた `SelectItem`** だけです(`map` で並べるのは拾えます)。
|
|
61
|
+
> 項目を別のコンポーネントが返す形にしているときは `items` を明示してください。明示した `items`
|
|
62
|
+
> は常に優先されます。
|
|
58
63
|
>
|
|
59
64
|
> ```tsx
|
|
60
|
-
>
|
|
65
|
+
> const regions = { hokkaido: "北海道", kanto: "関東", kansai: "関西" };
|
|
66
|
+
>
|
|
67
|
+
> <Select items={regions} value={region} onValueChange={…}>
|
|
61
68
|
> ```
|
|
62
69
|
|
|
63
70
|
`SelectContent` は既定で選択中の項目をトリガーに重ねて開きます。素直に下へ開かせたいときは
|
package/package.json
CHANGED
|
@@ -1,51 +1,32 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: vantage-app
|
|
3
|
-
description: Vantage(@squadbase/vantage)
|
|
3
|
+
description: Vantage(@squadbase/vantage)ダッシュボードアプリを一から作る / 既存アプリの構成を広げるときの進め方。骨組みの用意、ルートの決め方、データ取得の選択(外部 API か自前の server/api か)、どこで手を止めて検証するかの順序を扱う。規約そのものはアプリルートの AGENTS.md が正本。
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Vantage
|
|
6
|
+
# Vantage アプリの作り方 — 進め方と検証の順序
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
規約・不変条件・import サブパスの一覧・各 API の書式は、アプリルートの **`AGENTS.md`** が正本。
|
|
9
|
+
**先にそれを読む**(無ければ `vantage add agents` で置ける)。このスキルは「どの順で作り、どこで
|
|
10
|
+
手を止めて検証するか」だけを扱い、AGENTS.md の内容は繰り返さない。
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
`vantage-pitfalls` スキルを参照。
|
|
12
|
+
- ページ / API / UI を 1 つ足すだけなら → `vantage-add-feature`
|
|
13
|
+
- 実装したのに静かに壊れたら → `vantage-pitfalls`
|
|
15
14
|
|
|
16
|
-
##
|
|
15
|
+
## 全体の流れ
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
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
|
-
|
|
39
|
-
`
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
+
vantage add skill # 配置済みの skill(このファイルの仲間)と、その場所
|
|
163
61
|
```
|
|
164
62
|
|
|
165
|
-
|
|
63
|
+
## Step 2 — ルートを決める
|
|
166
64
|
|
|
167
|
-
|
|
65
|
+
ファイル名がそのままルートになる(対応表は AGENTS.md「ルーティング規約」)。**ルーターを設定
|
|
66
|
+
する場所は無い**ので、決めるのは「どんなファイル名で置くか」だけ。
|
|
168
67
|
|
|
169
|
-
-
|
|
170
|
-
-
|
|
171
|
-
|
|
68
|
+
- ページを置いたら `vantage routes` で URL を確かめる。意図と違うなら**ファイル名が違う**。
|
|
69
|
+
- ナビゲーションはリンク配列を手で持たず、`useRoutes()` から組む。この形にしておくと以後は
|
|
70
|
+
ページファイルを足すだけでナビが増える(AGENTS.md「ナビはルート一覧から組む」)。
|
|
71
|
+
- 共通の枠(ヘッダ・サイドバー)は `_layout.tsx`、Not Found と例外は `_404.tsx` / `_error.tsx`。
|
|
72
|
+
- 一覧 → 詳細を作るなら、**動的パラメータの「3 つの綴り」を先に決めてから**両方のファイルを
|
|
73
|
+
書く。後から変えると静かにマッチしなくなる。
|
|
172
74
|
|
|
173
|
-
|
|
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
|
-
|
|
255
|
-
|
|
79
|
+
| データ元 | 使うもの | `server/` |
|
|
80
|
+
| --- | --- | --- |
|
|
81
|
+
| ブラウザから直接叩ける公開 API | `useQuery` + `fetch` | 不要 |
|
|
82
|
+
| DB / シークレットが要る / CORS で叩けない | `server/api/*.ts` + `useApiQuery` | 必要 |
|
|
256
83
|
|
|
257
|
-
|
|
84
|
+
**API キーが要る時点で後者しかない** — クライアントに届く env は `PUBLIC_*` だけで、
|
|
85
|
+
シークレットは `ApiContext.env`(= `server/` の中)にしか届かない。
|
|
258
86
|
|
|
259
|
-
|
|
260
|
-
違反は `CLIENT_IMPORTS_SERVER` エラー(静的にもビルド時にも弾かれる)。
|
|
261
|
-
- サーバー専用のデータ/ヘルパは `server/utils.ts` などに置き、`server/api/**` からのみ import する。
|
|
87
|
+
後者の手順:
|
|
262
88
|
|
|
263
|
-
|
|
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
|
-
|
|
266
|
-
|
|
267
|
-
|
|
97
|
+
ダッシュボードの**絞り込みは `useState` ではなく `useSearchParam` / `useSearchState`** に置く。
|
|
98
|
+
リロードで消えず、URL をそのまま共有できる。これも後から差し替えると全ページに波及するので、
|
|
99
|
+
最初のフィルタを作る時点で決める。
|
|
268
100
|
|
|
269
|
-
## Step
|
|
101
|
+
## Step 4 — 仕上げ
|
|
270
102
|
|
|
271
103
|
```bash
|
|
272
|
-
pnpm check #
|
|
273
|
-
pnpm routes #
|
|
104
|
+
pnpm check # エラーが残っていないか(exit 1 なら残っている)
|
|
105
|
+
pnpm routes # 公開される URL の最終確認
|
|
274
106
|
pnpm build # dist/ に client(+ server)+ vantage-manifest.json
|
|
275
|
-
pnpm 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.*`
|
|
284
|
-
|
|
285
|
-
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
- Base UI(≠ Radix)
|
|
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 プロップ)、
|
|
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,34 @@ Vantage の UI キットは shadcn/ui の **Base UI バリアント**。Radix
|
|
|
19
19
|
- **`Checkbox` の `checked` は `boolean` のみ。** 中間状態は `indeterminate` プロップ。
|
|
20
20
|
- **`ToggleGroup` の `value` は配列。**
|
|
21
21
|
|
|
22
|
-
### `SelectValue`
|
|
22
|
+
### `SelectValue` の value/label は Vantage 側が吸収済み(残る 1 ケースだけ注意)
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
Base UI の `SelectValue` は選択中の **value をそのまま描き**、`SelectItem` の children を見ない
|
|
25
|
+
部品。Vantage の `Select` は children から `value` → ラベルを集めて渡すので、普通に書けば
|
|
26
|
+
トリガーに「関東」と出る。**手当てが要るのは 1 ケースだけ** — `SelectItem` を別のコンポーネント
|
|
27
|
+
が返している場合は集められないので、`items` を明示する:
|
|
26
28
|
|
|
27
29
|
```tsx
|
|
28
|
-
|
|
30
|
+
// SelectItem がこの場に無い(<RegionItems /> の中で作られる)ときだけ必要
|
|
31
|
+
<Select items={{ kanto: "関東", kansai: "関西" }} …>
|
|
32
|
+
<SelectContent><RegionItems /></SelectContent>
|
|
33
|
+
</Select>
|
|
29
34
|
```
|
|
30
35
|
|
|
31
|
-
|
|
32
|
-
組み立てている。自前で `Select` を使うときは忘れやすい。
|
|
36
|
+
トリガーに `kanto` や `/sales` と生の値が出たら、まずこれを疑う。
|
|
33
37
|
|
|
34
|
-
## チャート:`EChart`
|
|
38
|
+
## チャート:`EChart` の配色はトークン追従。上書きは `option.color`
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
系列色は `--chart-1..5`、軸・凡例・ツールチップは文字色/境界色のトークンから組まれる
|
|
41
|
+
(init 時にトークンの実値を解決している)。ライト / ダークの切り替えにも、`styles.css` の
|
|
42
|
+
書き換えやテーマプリセットの適用にも追従する。
|
|
38
43
|
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
-
|
|
44
|
+
- **`theme` プロップを渡すと追従は完全に止まる。** 系列の色だけ変えたいなら `option.color` を
|
|
45
|
+
使う ― option はテーマより優先されるので、軸まわりの追従は残る。
|
|
46
|
+
- キャンバス系なので、**コンテナに高さを与えないと何も見えない**(既定は `h-[400px]`。
|
|
47
|
+
`h-full` を使うなら親に確定した高さが要る)。
|
|
48
|
+
- `option` は `EChartsOption` を annotate するか `satisfies` を付ける。付けないと
|
|
49
|
+
`type: "category"` が `string` に広がってビルドが落ちる。
|
|
42
50
|
|
|
43
51
|
## 境界:クライアントから `server/` を import しない
|
|
44
52
|
|
|
@@ -108,7 +116,7 @@ throw new Error("boom") // → ログに出て汎用 500 に丸
|
|
|
108
116
|
- `vite.config.*` — Vite 設定は Vantage が所有
|
|
109
117
|
- `tailwind.config.*` — テーマは `styles.css` のトークンで調整
|
|
110
118
|
- `postcss.config.*` — Vantage が管理
|
|
111
|
-
- `components.json` — UI は `
|
|
119
|
+
- `components.json` — shadcn CLI は使わない。UI は `@squadbase/vantage/ui` から import する
|
|
112
120
|
- `vantage.config.*` — v1 に設定ファイルは無い(規約で表現)
|
|
113
121
|
|
|
114
122
|
## Markdown:`MarkdownRenderer` は `@squadbase/vantage/markdown` から
|
package/templates/AGENTS.md
CHANGED
|
@@ -9,10 +9,9 @@
|
|
|
9
9
|
> `vantage upgrade` を実行するとフレームワーク同梱の正本から再同期されて上書きされます。
|
|
10
10
|
> アプリ固有のメモは別ファイル(例: `README.md`)に書いてください。
|
|
11
11
|
>
|
|
12
|
-
> 手順を伴う踏み込んだワークフローは、同梱の Claude Code Skill
|
|
13
|
-
>
|
|
14
|
-
>
|
|
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
|
-
##
|
|
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/`
|
|
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
|
|
200
|
-
|
|
210
|
+
自分の `server/api` を叩くときは `useQuery` ではなく `useApiQuery`(→「API を持つ(fullstack
|
|
211
|
+
モード)」)。
|
|
201
212
|
|
|
202
|
-
## API
|
|
213
|
+
## API を持つ(fullstack モード)
|
|
203
214
|
|
|
204
|
-
**`server/`
|
|
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` ほか)
|
|
268
|
+
`DataTablePreset`・`EChart` ほか)。
|
|
259
269
|
- **どの名前が import 可能か / props の詳細は `vantage docs` で引く。** ガイドとコンポーネント
|
|
260
270
|
リファレンスはパッケージに同梱されていて、オフラインでも読める(→「ドキュメントを引く」)。
|
|
261
271
|
**名前が分からないとき**はやりたいことで `vantage search` する(→ 同節)。
|
|
262
|
-
- `vantage add ui
|
|
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,24 @@ pnpm preview # 本番ビルドをローカル実行
|
|
|
302
314
|
|
|
303
315
|
## さらに詳しく(同梱 Skill)
|
|
304
316
|
|
|
305
|
-
|
|
317
|
+
- **`vantage-app`** — アプリを一から作る / 構成を広げるときの進め方と検証の順序
|
|
318
|
+
- **`vantage-add-feature`** — 既存アプリに page / api / ui を 1 つ足す定型
|
|
319
|
+
- **`vantage-pitfalls`** — Base UI(≠ Radix)の癖など、静かに壊れる落とし穴のリファレンス
|
|
320
|
+
|
|
321
|
+
手順書の本体は `SKILL.md` というファイルで、置き場所はアプリによって違う(ルート直下・
|
|
322
|
+
`.claude/skills/`・`.squadbase/skills/` など)。**探す必要はない ― CLI が探す:**
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
vantage add skill # 同梱 skill の一覧と、それぞれの配置場所
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
配置済みならその場所が出るので、その `SKILL.md` を読む。`not placed` のものだけ配置する:
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
vantage add skill --all # 配置済みはスキップ(場所を報告するだけ)
|
|
332
|
+
vantage add skill --all --dir .claude/skills # 置き場所を指定(既定はルート直下)
|
|
333
|
+
```
|
|
306
334
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
非追従など、静かに壊れる落とし穴のリファレンス
|
|
335
|
+
`add skill` はプロジェクト内を走査してから動くので、**再実行しても二重に配置されない**。
|
|
336
|
+
配置済みのコピーを新しいバージョンに更新したいときだけ `--force` を付ける(見つかった場所を
|
|
337
|
+
そのまま書き換える)。
|