@squadbase/vantage 0.0.1 → 0.1.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.
Files changed (187) hide show
  1. package/README.md +24 -4
  2. package/dist/{chunk-ZGDU5YLH.js → chunk-5L2MH2NG.js} +16 -7
  3. package/dist/chunk-5L2MH2NG.js.map +1 -0
  4. package/dist/chunk-7IPAXPPY.js +51 -0
  5. package/dist/chunk-7IPAXPPY.js.map +1 -0
  6. package/dist/chunk-A2UUGASH.js +12 -0
  7. package/dist/chunk-A2UUGASH.js.map +1 -0
  8. package/dist/chunk-DTDVSFRY.js +29 -0
  9. package/dist/chunk-DTDVSFRY.js.map +1 -0
  10. package/dist/chunk-WQZYXXQW.js +2249 -0
  11. package/dist/chunk-WQZYXXQW.js.map +1 -0
  12. package/dist/{chunk-73J5ZD4C.js → chunk-YLAB6UQS.js} +17 -3
  13. package/dist/chunk-YLAB6UQS.js.map +1 -0
  14. package/dist/cli.js +607 -36
  15. package/dist/cli.js.map +1 -1
  16. package/dist/client/index.d.ts +2 -1
  17. package/dist/client/index.js +3 -1
  18. package/dist/client/index.js.map +1 -1
  19. package/dist/components/index.d.ts +575 -0
  20. package/dist/components/index.js +2097 -0
  21. package/dist/components/index.js.map +1 -0
  22. package/dist/{define-page-B6y9TOfZ.d.ts → define-page-BfhrK99G.d.ts} +5 -0
  23. package/dist/index.d.ts +2 -2
  24. package/dist/index.js +1 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/markdown/index.d.ts +9 -0
  27. package/dist/markdown/index.js +26 -0
  28. package/dist/markdown/index.js.map +1 -0
  29. package/dist/query/index.d.ts +95 -2
  30. package/dist/query/index.js +102 -1
  31. package/dist/query/index.js.map +1 -1
  32. package/dist/router/index.d.ts +86 -1
  33. package/dist/router/index.js +71 -7
  34. package/dist/router/index.js.map +1 -1
  35. package/dist/server/node.js +6 -1
  36. package/dist/server/node.js.map +1 -1
  37. package/dist/ui/index.d.ts +316 -200
  38. package/dist/ui/index.js +566 -511
  39. package/dist/ui/index.js.map +1 -1
  40. package/dist/vite/index.js +2 -2
  41. package/docs/en/agent-skills.md +89 -0
  42. package/docs/en/api-and-server.md +84 -0
  43. package/docs/en/build-and-deploy.md +280 -0
  44. package/docs/en/cli-reference.md +311 -0
  45. package/docs/en/components.md +235 -0
  46. package/docs/en/data-fetching.md +142 -0
  47. package/docs/en/environment-variables.md +69 -0
  48. package/docs/en/getting-started.md +95 -0
  49. package/docs/en/index.md +65 -0
  50. package/docs/en/markdown/markdown-renderer.md +44 -0
  51. package/docs/en/pages-and-metadata.md +59 -0
  52. package/docs/en/parts/app-shell.md +54 -0
  53. package/docs/en/parts/dashboard-card.md +64 -0
  54. package/docs/en/parts/data-table.md +86 -0
  55. package/docs/en/parts/date-range-picker.md +41 -0
  56. package/docs/en/parts/echart.md +53 -0
  57. package/docs/en/parts/filter-bar.md +66 -0
  58. package/docs/en/parts/funnel-steps.md +33 -0
  59. package/docs/en/parts/metric-value.md +23 -0
  60. package/docs/en/parts/multi-select.md +27 -0
  61. package/docs/en/parts/page-shell.md +45 -0
  62. package/docs/en/parts/placeholder.md +38 -0
  63. package/docs/en/parts/searchable-select.md +47 -0
  64. package/docs/en/parts/section-header.md +20 -0
  65. package/docs/en/parts/segmented-control.md +25 -0
  66. package/docs/en/parts/sparkline.md +47 -0
  67. package/docs/en/parts/status-badge.md +44 -0
  68. package/docs/en/parts/trend-indicator.md +26 -0
  69. package/docs/en/routing.md +177 -0
  70. package/docs/en/ui/accordion.md +36 -0
  71. package/docs/en/ui/alert.md +31 -0
  72. package/docs/en/ui/badge.md +23 -0
  73. package/docs/en/ui/breadcrumb.md +38 -0
  74. package/docs/en/ui/button.md +39 -0
  75. package/docs/en/ui/calendar.md +28 -0
  76. package/docs/en/ui/card.md +35 -0
  77. package/docs/en/ui/checkbox.md +34 -0
  78. package/docs/en/ui/cn.md +34 -0
  79. package/docs/en/ui/collapsible.md +21 -0
  80. package/docs/en/ui/command.md +37 -0
  81. package/docs/en/ui/dialog.md +52 -0
  82. package/docs/en/ui/dropdown-menu.md +50 -0
  83. package/docs/en/ui/empty.md +22 -0
  84. package/docs/en/ui/error-state.md +50 -0
  85. package/docs/en/ui/input-group.md +30 -0
  86. package/docs/en/ui/input.md +28 -0
  87. package/docs/en/ui/label.md +19 -0
  88. package/docs/en/ui/loading.md +19 -0
  89. package/docs/en/ui/popover.md +34 -0
  90. package/docs/en/ui/progress.md +28 -0
  91. package/docs/en/ui/scroll-area.md +23 -0
  92. package/docs/en/ui/select.md +63 -0
  93. package/docs/en/ui/separator.md +15 -0
  94. package/docs/en/ui/sheet.md +28 -0
  95. package/docs/en/ui/sidebar.md +92 -0
  96. package/docs/en/ui/skeleton.md +16 -0
  97. package/docs/en/ui/slider.md +21 -0
  98. package/docs/en/ui/spinner.md +14 -0
  99. package/docs/en/ui/switch.md +19 -0
  100. package/docs/en/ui/table.md +24 -0
  101. package/docs/en/ui/tabs.md +23 -0
  102. package/docs/en/ui/textarea.md +15 -0
  103. package/docs/en/ui/toggle-group.md +26 -0
  104. package/docs/en/ui/toggle.md +19 -0
  105. package/docs/en/ui/tooltip.md +22 -0
  106. package/docs/en/ui/use-is-mobile.md +18 -0
  107. package/docs/en/ui-and-theming.md +95 -0
  108. package/docs/index.json +1346 -0
  109. package/docs/ja/agent-skills.md +87 -0
  110. package/docs/ja/api-and-server.md +84 -0
  111. package/docs/ja/build-and-deploy.md +279 -0
  112. package/docs/ja/cli-reference.md +305 -0
  113. package/docs/ja/components.md +231 -0
  114. package/docs/ja/data-fetching.md +142 -0
  115. package/docs/ja/environment-variables.md +68 -0
  116. package/docs/ja/getting-started.md +95 -0
  117. package/docs/ja/index.md +65 -0
  118. package/docs/ja/markdown/markdown-renderer.md +43 -0
  119. package/docs/ja/pages-and-metadata.md +59 -0
  120. package/docs/ja/parts/app-shell.md +54 -0
  121. package/docs/ja/parts/dashboard-card.md +64 -0
  122. package/docs/ja/parts/data-table.md +85 -0
  123. package/docs/ja/parts/date-range-picker.md +41 -0
  124. package/docs/ja/parts/echart.md +52 -0
  125. package/docs/ja/parts/filter-bar.md +64 -0
  126. package/docs/ja/parts/funnel-steps.md +33 -0
  127. package/docs/ja/parts/metric-value.md +25 -0
  128. package/docs/ja/parts/multi-select.md +27 -0
  129. package/docs/ja/parts/page-shell.md +45 -0
  130. package/docs/ja/parts/placeholder.md +37 -0
  131. package/docs/ja/parts/searchable-select.md +47 -0
  132. package/docs/ja/parts/section-header.md +20 -0
  133. package/docs/ja/parts/segmented-control.md +26 -0
  134. package/docs/ja/parts/sparkline.md +47 -0
  135. package/docs/ja/parts/status-badge.md +44 -0
  136. package/docs/ja/parts/trend-indicator.md +26 -0
  137. package/docs/ja/routing.md +175 -0
  138. package/docs/ja/ui/accordion.md +37 -0
  139. package/docs/ja/ui/alert.md +31 -0
  140. package/docs/ja/ui/badge.md +23 -0
  141. package/docs/ja/ui/breadcrumb.md +38 -0
  142. package/docs/ja/ui/button.md +39 -0
  143. package/docs/ja/ui/calendar.md +28 -0
  144. package/docs/ja/ui/card.md +35 -0
  145. package/docs/ja/ui/checkbox.md +34 -0
  146. package/docs/ja/ui/cn.md +34 -0
  147. package/docs/ja/ui/collapsible.md +21 -0
  148. package/docs/ja/ui/command.md +36 -0
  149. package/docs/ja/ui/dialog.md +51 -0
  150. package/docs/ja/ui/dropdown-menu.md +50 -0
  151. package/docs/ja/ui/empty.md +22 -0
  152. package/docs/ja/ui/error-state.md +50 -0
  153. package/docs/ja/ui/input-group.md +30 -0
  154. package/docs/ja/ui/input.md +28 -0
  155. package/docs/ja/ui/label.md +19 -0
  156. package/docs/ja/ui/loading.md +19 -0
  157. package/docs/ja/ui/popover.md +34 -0
  158. package/docs/ja/ui/progress.md +28 -0
  159. package/docs/ja/ui/scroll-area.md +24 -0
  160. package/docs/ja/ui/select.md +64 -0
  161. package/docs/ja/ui/separator.md +15 -0
  162. package/docs/ja/ui/sheet.md +28 -0
  163. package/docs/ja/ui/sidebar.md +92 -0
  164. package/docs/ja/ui/skeleton.md +16 -0
  165. package/docs/ja/ui/slider.md +21 -0
  166. package/docs/ja/ui/spinner.md +14 -0
  167. package/docs/ja/ui/switch.md +19 -0
  168. package/docs/ja/ui/table.md +24 -0
  169. package/docs/ja/ui/tabs.md +24 -0
  170. package/docs/ja/ui/textarea.md +15 -0
  171. package/docs/ja/ui/toggle-group.md +27 -0
  172. package/docs/ja/ui/toggle.md +19 -0
  173. package/docs/ja/ui/tooltip.md +22 -0
  174. package/docs/ja/ui/use-is-mobile.md +18 -0
  175. package/docs/ja/ui-and-theming.md +95 -0
  176. package/package.json +29 -4
  177. package/registry/blocks/sales-overview.tsx +43 -19
  178. package/registry/ui/data-table.tsx +702 -102
  179. package/skills/vantage-add-feature/SKILL.md +153 -0
  180. package/skills/vantage-app/SKILL.md +291 -0
  181. package/skills/vantage-pitfalls/SKILL.md +139 -0
  182. package/templates/AGENTS.md +313 -0
  183. package/theme.css +178 -40
  184. package/dist/chunk-73J5ZD4C.js.map +0 -1
  185. package/dist/chunk-ATYZ45XL.js +0 -19
  186. package/dist/chunk-ATYZ45XL.js.map +0 -1
  187. package/dist/chunk-ZGDU5YLH.js.map +0 -1
@@ -0,0 +1,313 @@
1
+ # AGENTS.md — Vantage アプリの地図
2
+
3
+ このファイルは、**Vantage(`@squadbase/vantage`)アプリを作るコーディングエージェント**が
4
+ 最初に読むための地図です。アプリ作者(= `index.tsx` を書く側)が守るべき規約・不変条件・
5
+ 最小レシピを 1 ページに集約しています。ここに書かれた規約だけで、最小のダッシュボードアプリを
6
+ 正しく組めます。
7
+
8
+ > このファイルは **Vantage フレームワークが所有する正本**のコピーです。手で書き換えても、
9
+ > `vantage upgrade` を実行するとフレームワーク同梱の正本から再同期されて上書きされます。
10
+ > アプリ固有のメモは別ファイル(例: `README.md`)に書いてください。
11
+ >
12
+ > 手順を伴う踏み込んだワークフローは、同梱の Claude Code Skill に分かれています
13
+ > (`vantage add skill --all` で `.claude/skills/` に配置)。このファイルは「地図」、Skill は
14
+ > 「手順書」という役割分担です。詰まったら `vantage-app` / `vantage-add-feature` /
15
+ > `vantage-pitfalls` を参照してください。
16
+
17
+ ## Vantage とは
18
+
19
+ 設定ファイル不要(config-free)な React ダッシュボードフレームワーク。アプリ作者が書くのは
20
+ `index.tsx`(と任意の追加ファイル)だけで、Vite・ルーティング・TanStack Query・Tailwind・
21
+ UI キット・開発サーバー・API サーバー・ビルドはすべて Vantage が所有します。設定は
22
+ 「ファイル名の規約」で表現します。
23
+
24
+ ## 不変条件(破ると静かに壊れる)
25
+
26
+ - **設定ファイルを作らない。** `vite.config.*`・`tailwind.config.*`・`postcss.config.*`・
27
+ `components.json`・`vantage.config.*` はすべて禁止。存在すると `vantage check` がエラーにする
28
+ (`FORBIDDEN_FILE`)。テーマ調整は `styles.css` のトークンで行う。
29
+ - **import は必ず `@squadbase/vantage` のサブパス経由。** `@tanstack/*`・`@base-ui/react`・
30
+ `echarts`・`hono`・`vite`・`tailwindcss` を直接 import しない。`lucide-react` のアイコンだけは
31
+ 直接 import してよい。
32
+ - **モジュール間の相対 import はランタイムの `.js` 指定子で書く**(例:
33
+ `./components/revenue-chart.js`)。ソースが `.tsx`/`.ts` でも拡張子は `.js` と書く。
34
+ ESM の Node 解決に必要。`@squadbase/vantage` のサブパスはこの限りではない。
35
+ - **クライアントコードは `server/` を import してはならない。** 違反は `CLIENT_IMPORTS_SERVER`
36
+ エラー(静的にもビルド時にも弾かれる)。共有したいコードは `lib/` に置く。
37
+ - **クライアントに届く env は `PUBLIC_` 接頭辞のものだけ。** `import.meta.env.PUBLIC_FOO`。
38
+ それ以外を `import.meta.env` で読むと `PUBLIC_ENV_MISUSE` 警告。シークレットは
39
+ `ApiContext.env`(server/ 内)にだけ届く。
40
+ - **`.vantage/` と `dist/` は生成物。** 編集しない・読みにいかない(gitignore 済み)。任意の CLI
41
+ コマンド、または `vantage upgrade` で再生成される。
42
+ - **`definePage` の値はリテラルで書く。** title/description/navLabel はビルド時に静的抽出される
43
+ ため、変数・関数呼び出し・テンプレート補間は使わない。`page` エクスポートはランタイムでは
44
+ 読まれない。
45
+
46
+ ## import サブパスの地図
47
+
48
+ | import 元 | 提供するもの |
49
+ | --- | --- |
50
+ | `@squadbase/vantage` | `definePage`(ページ設定) |
51
+ | `@squadbase/vantage/router` | `Link`・`Outlet`・`useParams`・`useSearch`・`useNavigate`・`redirect`・`notFound`・`useRoutes`・`useCurrentRoute`・`useSearchParam`・`useSearchState` |
52
+ | `@squadbase/vantage/query` | `useApiQuery`・`useApiMutation`・`apiJson`・`apiFetch`・`apiUrl`・`ApiError`、ほか `useQuery`/`useMutation` など TanStack Query の再エクスポート |
53
+ | `@squadbase/vantage/ui` | shadcn/ui(Base UI バリアント)プリミティブ(`Button`・`Loading`・`ErrorState`・`Empty` ほか) |
54
+ | `@squadbase/vantage/components` | 複合パーツ(`PageShell`・`DashboardCardPreset`・`DataTablePreset`・`EChart` ほか) |
55
+ | `@squadbase/vantage/markdown` | `MarkdownRenderer`(Shiki を隔離するための専用サブパス) |
56
+ | `@squadbase/vantage/server` | `ApiContext`・`HttpError`(server/ 側でのみ使う) |
57
+
58
+ ## 最小アプリ(SPA モード)
59
+
60
+ `package.json` と `index.tsx` の 2 ファイルだけで動く。
61
+
62
+ `package.json`:
63
+
64
+ ```json
65
+ {
66
+ "name": "my-dashboard",
67
+ "private": true,
68
+ "type": "module",
69
+ "scripts": {
70
+ "dev": "vantage dev",
71
+ "build": "vantage build",
72
+ "preview": "vantage preview",
73
+ "check": "vantage check",
74
+ "routes": "vantage routes"
75
+ },
76
+ "dependencies": {
77
+ "@squadbase/vantage": "^0.1.0",
78
+ "react": "^19.2.7",
79
+ "react-dom": "^19.2.7"
80
+ }
81
+ }
82
+ ```
83
+
84
+ `index.tsx`(ルートのデフォルトエクスポートが `/` ページになる):
85
+
86
+ ```tsx
87
+ export default function Dashboard() {
88
+ return (
89
+ <main className="p-6">
90
+ <h1 className="text-2xl font-semibold">Dashboard</h1>
91
+ </main>
92
+ )
93
+ }
94
+ ```
95
+
96
+ ```bash
97
+ pnpm install
98
+ pnpm dev # → http://localhost:5173(HMR)
99
+ pnpm check # 静的診断(エラーがあれば exit 1)
100
+ pnpm routes # ページ/API の URL マップ
101
+ ```
102
+
103
+ `server/` ディレクトリが無いので、この時点では **SPA モード**。
104
+
105
+ ## ルーティング規約(ファイル名 → ルート)
106
+
107
+ ルートディレクトリ直下の `.tsx`/`.jsx` がそのままページになる。各ページは**デフォルト
108
+ エクスポートの React コンポーネントが必須**(無いと `MISSING_DEFAULT_EXPORT`)。
109
+
110
+ | ファイル | ルート |
111
+ | --- | --- |
112
+ | `index.tsx` | `/` |
113
+ | `monthly-analysis.tsx` | `/monthly-analysis` |
114
+ | `sales/index.tsx` | `/sales` |
115
+ | `sales/[customerId].tsx` | `/sales/:customerId`(動的パラメータ) |
116
+ | `_layout.tsx` | そのディレクトリ配下を包むネストレイアウト(`Outlet` で子を描く) |
117
+ | `_404.tsx` | Not Found ページ |
118
+ | `_error.tsx` | ルートが throw したときのエラーページ |
119
+
120
+ `components/`・`hooks/`・`lib/`・`server/`・`public/` はルート走査の対象外(ページにならない)。
121
+
122
+ ```tsx
123
+ import { definePage } from "@squadbase/vantage"
124
+
125
+ // title は document.title、navLabel は useRoutes() で組むナビの表示名(既定は title)
126
+ export const page = definePage({ title: "Monthly Analysis · Acme", navLabel: "Monthly" })
127
+
128
+ export default function MonthlyAnalysis() {
129
+ return <main className="p-6">…</main>
130
+ }
131
+ ```
132
+
133
+ ### ナビはルート一覧から組む
134
+
135
+ `useRoutes()` がページルート一覧(スキャン順)を返すので、リンク配列を手で持たない。
136
+ `useCurrentRoute()` は現在のルート(404 なら `undefined`)。
137
+
138
+ ```tsx
139
+ import { Link, useCurrentRoute, useRoutes } from "@squadbase/vantage/router"
140
+
141
+ // 動的ルートは URL が定まらないので外す。label は navLabel → title → path の順
142
+ const routes = useRoutes().filter((r) => !r.dynamic)
143
+ const current = useCurrentRoute()
144
+
145
+ routes.map((r) => (
146
+ <Link key={r.path} to={r.to} activeClassName="font-semibold">
147
+ {r.label}
148
+ </Link>
149
+ ))
150
+ ```
151
+
152
+ `RouteInfo`: `path`(`/sales/:id`)・`to`(`/sales/$id`)・`params`・`dynamic`・`index`・`label`・
153
+ `title`・`description`・`navLabel`。
154
+
155
+ ### フィルタ状態は URL に置く
156
+
157
+ リロードで消えず、URL をそのまま共有できる。`useState` と同じ形。
158
+
159
+ ```tsx
160
+ import { useSearchParam, useSearchState } from "@squadbase/vantage/router"
161
+
162
+ const [region, setRegion] = useSearchParam("region", "all") // 常に string
163
+ const [segments, setSegments] = useSearchState<string[]>("segments", []) // JSON になる値
164
+ ```
165
+
166
+ デフォルト値(または `null`)を書くとキーは URL から消える。履歴は既定で `replace`
167
+ (`{ replace: false }` で push)。
168
+
169
+ ### 動的パラメータの「3 つの綴り」を同期させる
170
+
171
+ 崩すと静かにマッチしなくなる不変条件:
172
+
173
+ - ファイル名 `sales/[customerId].tsx`
174
+ - 表示ルート `/sales/:customerId`
175
+ - リンクトークン `to="/sales/$customerId"` + `params={{ customerId }}`
176
+
177
+ ```tsx
178
+ import { Link, useParams } from "@squadbase/vantage/router"
179
+
180
+ <Link to="/sales/$customerId" params={{ customerId: row.id }}>{row.name}</Link>
181
+
182
+ // 詳細ページ側で取り出す
183
+ const { customerId } = useParams()
184
+ ```
185
+
186
+ ## データ取得(server を持たない fetch)
187
+
188
+ 外部 API を叩くだけなら `server/` は不要。`@squadbase/vantage/query` の `useQuery` を使う:
189
+
190
+ ```tsx
191
+ import { useQuery } from "@squadbase/vantage/query"
192
+ import { Loading, ErrorState } from "@squadbase/vantage/ui"
193
+
194
+ const q = useQuery({ queryKey: ["stats"], queryFn: () => fetch("/…").then((r) => r.json()) })
195
+ if (q.isPending) return <Loading />
196
+ if (q.isError) return <ErrorState message={(q.error as Error).message} />
197
+ ```
198
+
199
+ `QueryClient` は Vantage が 1 つだけ管理する(staleTime 30s・retry 1・
200
+ refetchOnWindowFocus false・networkMode "always")。挙動を変えたいときはクエリ側のオプションで
201
+ 上書きする。
202
+ 自分の `server/api` を叩くときは `useQuery` ではなく `useApiQuery`(→「API を足して fullstack に
203
+ する」)。
204
+
205
+ ## API を足して fullstack にする
206
+
207
+ **`server/` ディレクトリを作った瞬間に fullstack モードになる。** `server/api/**` の各ファイルが
208
+ API ルートになる。
209
+
210
+ `server/api/monthly-analysis.ts` → `GET /api/monthly-analysis`:
211
+
212
+ ```ts
213
+ import type { ApiContext } from "@squadbase/vantage/server"
214
+ import { HttpError } from "@squadbase/vantage/server"
215
+
216
+ export async function GET({ params, env }: ApiContext) {
217
+ if (!env.API_KEY) throw new HttpError(500, "API_KEY missing")
218
+ return Response.json({ ok: true })
219
+ }
220
+ ```
221
+
222
+ - API モジュールは**大文字の HTTP メソッド**(`GET`/`POST`/`PUT`/`PATCH`/`DELETE`/`OPTIONS`)を
223
+ エクスポートする。それ以外の名前は `INVALID_API_EXPORT` エラー。
224
+ - 動的 API も `[id].ts` 記法: `server/api/customers/[id].ts` → `GET /api/customers/:id`。
225
+ `params.id` で取り出す(`noUncheckedIndexedAccess` が効くので narrowing が要る)。
226
+ - **クライアントに見せたいエラーは `HttpError(status, msg)` を throw する。** それ以外の throw は
227
+ ログに記録され汎用の 500 に丸められる。
228
+ - **シークレットは `ApiContext.env` にだけ届く**(クライアントには決して届かない)。
229
+
230
+ クライアント側からは `useApiQuery` で `/api/*` を叩く。ベース URL の解決・JSON パース・
231
+ 非 2xx の `ApiError` 化・クエリキー(`["api", url]`)が入っている:
232
+
233
+ ```tsx
234
+ import { useApiQuery, useApiMutation } from "@squadbase/vantage/query"
235
+
236
+ const q = useApiQuery<Customer>(`/api/customers/${customerId}`)
237
+ if (q.isError) return <ErrorState message={q.error.message} /> // HttpError のメッセージ
238
+
239
+ // クエリ文字列は search で。undefined の項目は落ちる
240
+ const rows = useApiQuery<Row[]>("/api/customers", { search: { segment } })
241
+
242
+ // 書き込み。変数がそのまま JSON ボディになる
243
+ const save = useApiMutation<Customer, Payload>("/api/customers", { method: "POST" })
244
+ ```
245
+
246
+ `ApiError` は `message`・`status`・`body`・`requestId` を持つ。hook が使えない場所では
247
+ `apiJson(path, init)`、生の `Response` が要るときは `apiFetch`。
248
+
249
+ ## 環境変数
250
+
251
+ - **クライアントで読めるのは `import.meta.env.PUBLIC_*` と `MODE`/`DEV`/`PROD`/`SSR`/`BASE_URL`
252
+ のみ。** `VITE_` 系の公開 API は無い。
253
+ - サーバー側のシークレットは `ApiContext.env.MY_SECRET` で読む(`server/` 内のみ)。
254
+
255
+ ## 組み込み UI / コンポーネントの入口
256
+
257
+ - **プリミティブ**は `@squadbase/vantage/ui` から import する(`Button`・`Loading`・
258
+ `ErrorState`・`Empty`・`Select`・`Checkbox` ほか)。編集したいコピーが要るなら
259
+ `vantage add ui <name>` で `components/ui/` に取り出す。
260
+ - **複合パーツ**は `@squadbase/vantage/components` から(`PageShell`・`DashboardCardPreset`・
261
+ `DataTablePreset`・`EChart` ほか)。ブロックは `vantage add block <name>` で取り出す。
262
+ - **どの名前が import 可能か / props の詳細は `vantage docs` で引く。** ガイドとコンポーネント
263
+ リファレンスはパッケージに同梱されていて、オフラインでも読める(→「ドキュメントを引く」)。
264
+ **名前が分からないとき**はやりたいことで `vantage search` する(→ 同節)。
265
+ - `vantage add ui`・`vantage add block` でコピーできる名前は、未知名で実行すると候補が一覧表示
266
+ される。
267
+
268
+ ## ドキュメントを引く(`vantage docs` / `vantage search`)
269
+
270
+ ```bash
271
+ vantage docs # 全ページの一覧(ガイド + コンポーネント)
272
+ vantage docs button # 1 ページ表示(短縮名。ui/button に解決される)
273
+ vantage docs parts/data-table # 完全な slug でも引ける
274
+ vantage docs data-table --json # { slug, title, description, section, content }
275
+ vantage docs --all # 全ページを連結(まとめて読み込む用)
276
+ vantage docs routing --lang en # 英語版(既定は ja)
277
+ ```
278
+
279
+ props の表・使用例・落とし穴はコンポーネントのページに載っている。**推測で props を書く前に
280
+ `vantage docs <name>` を引くこと。** 本文中のリンクは slug なので、`[DataTable](parts/data-table)`
281
+ は `vantage docs parts/data-table` で開ける。
282
+
283
+ **名前が分からないときは検索する。** 対象は `vantage docs` と同じ同梱ドキュメント。
284
+
285
+ ```bash
286
+ vantage search 期間を選ぶ UI が欲しい # 自然言語(BM25)。やりたいことで引く
287
+ vantage search "enable[A-Z]\w+" --regex # 正規表現で props を横断(行番号付き)
288
+ vantage search テーブル --limit 5 --json # 機械可読(slug + score + snippet)
289
+ ```
290
+
291
+ 結果の slug はそのまま `vantage docs <slug>` に渡せる。
292
+
293
+ ## 検証・ビルド・プレビュー
294
+
295
+ ```bash
296
+ pnpm check # 静的診断(禁止ファイル・ルート衝突・境界・API export・env 誤用)
297
+ pnpm routes # ページ + API の URL マップ
298
+ pnpm build # dist/ に client(+ server)+ vantage-manifest.json
299
+ pnpm preview # 本番ビルドをローカル実行
300
+ ```
301
+
302
+ `vantage-manifest.json` の `mode`(`spa` / `fullstack`)は `server/` の有無から自動で決まる
303
+ (手で書かない)。`vantage check --json` / `vantage routes --json` はエージェント向けの機械
304
+ 可読出力。`console.*` とランタイムエラーは開発ターミナルに `[browser:…]` として転送される。
305
+
306
+ ## さらに詳しく(同梱 Skill)
307
+
308
+ `vantage add skill --all` で `.claude/skills/` に配置される:
309
+
310
+ - **`vantage-app`** — アプリを一から作る / SPA を fullstack に広げる手順
311
+ - **`vantage-add-feature`** — 既存アプリに page / api / ui / block を 1 つ足す定型
312
+ - **`vantage-pitfalls`** — Base UI(≠ Radix)の癖、`SelectValue` の挙動、EChart のテーマ
313
+ 非追従など、静かに壊れる落とし穴のリファレンス
package/theme.css CHANGED
@@ -1,29 +1,36 @@
1
1
  /**
2
2
  * Vantage design tokens. Imported after `@import "tailwindcss"` by the
3
- * generated runtime.css. Defines CSS variables (light + dark) and maps them to
4
- * Tailwind v4 color/radius utilities via `@theme inline`.
3
+ * generated runtime.css. Defines CSS variables (light + dark), maps them to
4
+ * Tailwind v4 color/radius utilities via `@theme inline`, and declares the
5
+ * `data-*` custom variants the Base UI component kit styles itself with.
5
6
  */
6
7
 
8
+ @import "tw-animate-css";
9
+
10
+ /* --------------------------------------------------------------- tokens */
11
+ /* shadcn/ui's neutral base palette (oklch), plus the chart hues Vantage adds
12
+ on top — charts need distinguishable colours, not a greyscale ramp. */
13
+
7
14
  :root {
8
- --background: hsl(0 0% 100%);
9
- --foreground: hsl(240 10% 3.9%);
10
- --card: hsl(0 0% 100%);
11
- --card-foreground: hsl(240 10% 3.9%);
12
- --popover: hsl(0 0% 100%);
13
- --popover-foreground: hsl(240 10% 3.9%);
14
- --primary: hsl(240 5.9% 10%);
15
- --primary-foreground: hsl(0 0% 98%);
16
- --secondary: hsl(240 4.8% 95.9%);
17
- --secondary-foreground: hsl(240 5.9% 10%);
18
- --muted: hsl(240 4.8% 95.9%);
19
- --muted-foreground: hsl(240 3.8% 46.1%);
20
- --accent: hsl(240 4.8% 95.9%);
21
- --accent-foreground: hsl(240 5.9% 10%);
22
- --destructive: hsl(0 72% 51%);
23
- --destructive-foreground: hsl(0 0% 98%);
24
- --border: hsl(240 5.9% 90%);
25
- --input: hsl(240 5.9% 90%);
26
- --ring: hsl(240 5.9% 10%);
15
+ --background: oklch(1 0 0);
16
+ --foreground: oklch(0.145 0 0);
17
+ --card: oklch(1 0 0);
18
+ --card-foreground: oklch(0.145 0 0);
19
+ --popover: oklch(1 0 0);
20
+ --popover-foreground: oklch(0.145 0 0);
21
+ --primary: oklch(0.205 0 0);
22
+ --primary-foreground: oklch(0.985 0 0);
23
+ --secondary: oklch(0.97 0 0);
24
+ --secondary-foreground: oklch(0.205 0 0);
25
+ --muted: oklch(0.97 0 0);
26
+ --muted-foreground: oklch(0.556 0 0);
27
+ --accent: oklch(0.97 0 0);
28
+ --accent-foreground: oklch(0.205 0 0);
29
+ --destructive: oklch(0.577 0.245 27.325);
30
+ --destructive-foreground: oklch(0.985 0 0);
31
+ --border: oklch(0.922 0 0);
32
+ --input: oklch(0.922 0 0);
33
+ --ring: oklch(0.708 0 0);
27
34
  --radius: 0.625rem;
28
35
 
29
36
  --chart-1: hsl(221 83% 53%);
@@ -31,35 +38,53 @@
31
38
  --chart-3: hsl(31 90% 55%);
32
39
  --chart-4: hsl(280 65% 60%);
33
40
  --chart-5: hsl(340 75% 55%);
41
+
42
+ --sidebar: oklch(0.985 0 0);
43
+ --sidebar-foreground: oklch(0.145 0 0);
44
+ --sidebar-primary: oklch(0.205 0 0);
45
+ --sidebar-primary-foreground: oklch(0.985 0 0);
46
+ --sidebar-accent: oklch(0.97 0 0);
47
+ --sidebar-accent-foreground: oklch(0.205 0 0);
48
+ --sidebar-border: oklch(0.922 0 0);
49
+ --sidebar-ring: oklch(0.708 0 0);
34
50
  }
35
51
 
36
52
  .dark,
37
53
  [data-theme="dark"] {
38
- --background: hsl(240 10% 3.9%);
39
- --foreground: hsl(0 0% 98%);
40
- --card: hsl(240 8% 6%);
41
- --card-foreground: hsl(0 0% 98%);
42
- --popover: hsl(240 8% 6%);
43
- --popover-foreground: hsl(0 0% 98%);
44
- --primary: hsl(0 0% 98%);
45
- --primary-foreground: hsl(240 5.9% 10%);
46
- --secondary: hsl(240 3.7% 15.9%);
47
- --secondary-foreground: hsl(0 0% 98%);
48
- --muted: hsl(240 3.7% 15.9%);
49
- --muted-foreground: hsl(240 5% 64.9%);
50
- --accent: hsl(240 3.7% 15.9%);
51
- --accent-foreground: hsl(0 0% 98%);
52
- --destructive: hsl(0 62% 50%);
53
- --destructive-foreground: hsl(0 0% 98%);
54
- --border: hsl(240 3.7% 15.9%);
55
- --input: hsl(240 3.7% 18%);
56
- --ring: hsl(240 4.9% 83.9%);
54
+ --background: oklch(0.145 0 0);
55
+ --foreground: oklch(0.985 0 0);
56
+ --card: oklch(0.205 0 0);
57
+ --card-foreground: oklch(0.985 0 0);
58
+ --popover: oklch(0.205 0 0);
59
+ --popover-foreground: oklch(0.985 0 0);
60
+ --primary: oklch(0.922 0 0);
61
+ --primary-foreground: oklch(0.205 0 0);
62
+ --secondary: oklch(0.269 0 0);
63
+ --secondary-foreground: oklch(0.985 0 0);
64
+ --muted: oklch(0.269 0 0);
65
+ --muted-foreground: oklch(0.708 0 0);
66
+ --accent: oklch(0.269 0 0);
67
+ --accent-foreground: oklch(0.985 0 0);
68
+ --destructive: oklch(0.704 0.191 22.216);
69
+ --destructive-foreground: oklch(0.985 0 0);
70
+ --border: oklch(1 0 0 / 10%);
71
+ --input: oklch(1 0 0 / 15%);
72
+ --ring: oklch(0.556 0 0);
57
73
 
58
74
  --chart-1: hsl(217 91% 60%);
59
75
  --chart-2: hsl(160 60% 45%);
60
76
  --chart-3: hsl(31 90% 55%);
61
77
  --chart-4: hsl(280 65% 65%);
62
78
  --chart-5: hsl(340 75% 60%);
79
+
80
+ --sidebar: oklch(0.205 0 0);
81
+ --sidebar-foreground: oklch(0.985 0 0);
82
+ --sidebar-primary: oklch(0.488 0.243 264.376);
83
+ --sidebar-primary-foreground: oklch(0.985 0 0);
84
+ --sidebar-accent: oklch(0.269 0 0);
85
+ --sidebar-accent-foreground: oklch(0.985 0 0);
86
+ --sidebar-border: oklch(1 0 0 / 10%);
87
+ --sidebar-ring: oklch(0.556 0 0);
63
88
  }
64
89
 
65
90
  @theme inline {
@@ -87,10 +112,119 @@
87
112
  --color-chart-3: var(--chart-3);
88
113
  --color-chart-4: var(--chart-4);
89
114
  --color-chart-5: var(--chart-5);
115
+ --color-sidebar: var(--sidebar);
116
+ --color-sidebar-foreground: var(--sidebar-foreground);
117
+ --color-sidebar-primary: var(--sidebar-primary);
118
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
119
+ --color-sidebar-accent: var(--sidebar-accent);
120
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
121
+ --color-sidebar-border: var(--sidebar-border);
122
+ --color-sidebar-ring: var(--sidebar-ring);
90
123
  --radius-sm: calc(var(--radius) - 4px);
91
124
  --radius-md: calc(var(--radius) - 2px);
92
125
  --radius-lg: var(--radius);
93
126
  --radius-xl: calc(var(--radius) + 4px);
127
+
128
+ /* Accordion open/close, driven by Base UI's measured panel height. */
129
+ @keyframes accordion-down {
130
+ from {
131
+ height: 0;
132
+ }
133
+ to {
134
+ height: var(--accordion-panel-height, auto);
135
+ }
136
+ }
137
+ @keyframes accordion-up {
138
+ from {
139
+ height: var(--accordion-panel-height, auto);
140
+ }
141
+ to {
142
+ height: 0;
143
+ }
144
+ }
145
+ }
146
+
147
+ /* ------------------------------------------------------- custom variants */
148
+ /* Base UI reports state through `data-open`, `data-checked`, … rather than
149
+ Radix's `data-state="open"`. These variants accept both spellings so the
150
+ component classes read the same either way. */
151
+
152
+ @custom-variant data-open {
153
+ &:where([data-state="open"]),
154
+ &:where([data-open]:not([data-open="false"])) {
155
+ @slot;
156
+ }
157
+ }
158
+
159
+ @custom-variant data-closed {
160
+ &:where([data-state="closed"]),
161
+ &:where([data-closed]:not([data-closed="false"])) {
162
+ @slot;
163
+ }
164
+ }
165
+
166
+ @custom-variant data-checked {
167
+ &:where([data-state="checked"]),
168
+ &:where([data-checked]:not([data-checked="false"])) {
169
+ @slot;
170
+ }
171
+ }
172
+
173
+ @custom-variant data-unchecked {
174
+ &:where([data-state="unchecked"]),
175
+ &:where([data-unchecked]:not([data-unchecked="false"])) {
176
+ @slot;
177
+ }
178
+ }
179
+
180
+ @custom-variant data-selected {
181
+ &:where([data-selected="true"]) {
182
+ @slot;
183
+ }
184
+ }
185
+
186
+ @custom-variant data-disabled {
187
+ &:where([data-disabled="true"]),
188
+ &:where([data-disabled]:not([data-disabled="false"])) {
189
+ @slot;
190
+ }
191
+ }
192
+
193
+ @custom-variant data-active {
194
+ &:where([data-state="active"]),
195
+ &:where([data-active]:not([data-active="false"])) {
196
+ @slot;
197
+ }
198
+ }
199
+
200
+ @custom-variant data-horizontal {
201
+ &:where([data-orientation="horizontal"]) {
202
+ @slot;
203
+ }
204
+ }
205
+
206
+ @custom-variant data-vertical {
207
+ &:where([data-orientation="vertical"]) {
208
+ @slot;
209
+ }
210
+ }
211
+
212
+ /* ------------------------------------------------------------ utilities */
213
+
214
+ @utility no-scrollbar {
215
+ -ms-overflow-style: none;
216
+ scrollbar-width: none;
217
+
218
+ &::-webkit-scrollbar {
219
+ display: none;
220
+ }
221
+ }
222
+
223
+ /* Icons that must point the other way when the document is right-to-left. */
224
+ .cn-rtl-flip {
225
+ &:where([dir="rtl"], [dir="rtl"] *) {
226
+ transform: scaleX(-1);
227
+ }
94
228
  }
95
229
 
96
230
  body {
@@ -99,3 +233,7 @@ body {
99
233
  font-family: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
100
234
  -webkit-font-smoothing: antialiased;
101
235
  }
236
+
237
+ * {
238
+ border-color: var(--border);
239
+ }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/server/dispatch.ts"],"names":[],"mappings":"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","file":"chunk-73J5ZD4C.js","sourcesContent":["import type { Segment } from \"../core/types.js\"\nimport type { ApiContext, ApiHandler, ApiModule } from \"./index.js\"\nimport { isHttpError } from \"./index.js\"\n\n/** A resolvable API route: its segments plus a lazy module loader. */\nexport interface ApiRouteEntry {\n routePath: string\n segments: Segment[]\n hasCatchall: boolean\n staticCount: number\n load: () => Promise<ApiModule>\n}\n\n/** Order routes so the most specific matches first (static > param > catchall). */\nexport function sortApiRoutes<T extends ApiRouteEntry>(routes: T[]): T[] {\n return [...routes].sort((a, b) => {\n if (a.hasCatchall !== b.hasCatchall) return a.hasCatchall ? 1 : -1\n if (a.staticCount !== b.staticCount) return b.staticCount - a.staticCount\n return b.segments.length - a.segments.length\n })\n}\n\nfunction matchSegments(segments: Segment[], parts: string[]): Record<string, string> | null {\n const params: Record<string, string> = {}\n let i = 0\n for (const seg of segments) {\n if (seg.kind === \"catchall\") {\n params[seg.name] = parts.slice(i).map(decode).join(\"/\")\n return params\n }\n if (i >= parts.length) return null\n if (seg.kind === \"static\") {\n if (parts[i] !== seg.value) return null\n } else {\n params[seg.name] = decode(parts[i]!)\n }\n i++\n }\n return i === parts.length ? params : null\n}\n\nfunction decode(s: string): string {\n try {\n return decodeURIComponent(s)\n } catch {\n return s\n }\n}\n\n/** Match a pathname (e.g. `/api/customers/42`) against the API routes. */\nexport function matchApiRoute(\n routes: ApiRouteEntry[],\n pathname: string,\n): { route: ApiRouteEntry; params: Record<string, string> } | null {\n if (pathname !== \"/api\" && !pathname.startsWith(\"/api/\")) return null\n let rest = pathname.slice(4)\n if (rest.startsWith(\"/\")) rest = rest.slice(1)\n // Normalize a single trailing slash so `/api/customers/` doesn't match a\n // `:id` route with an empty-string param.\n if (rest.endsWith(\"/\")) rest = rest.slice(0, -1)\n const parts = rest === \"\" ? [] : rest.split(\"/\")\n for (const route of routes) {\n const params = matchSegments(route.segments, parts)\n if (params) return { route, params }\n }\n return null\n}\n\n/** Unique per-request id used to correlate the terminal log with the response. */\nfunction requestId(): string {\n const g = globalThis as { crypto?: { randomUUID?: () => string } }\n if (g.crypto?.randomUUID) return g.crypto.randomUUID()\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`\n}\n\n/**\n * Invoke a matched API module for a request. Shared by the dev middleware and\n * the production Node server so behavior is identical across environments.\n */\nexport async function dispatchApi(\n mod: ApiModule,\n request: Request,\n params: Record<string, string>,\n env: Record<string, string | undefined>,\n): Promise<Response> {\n const method = request.method.toUpperCase() as keyof ApiModule\n const handler = mod[method] as ApiHandler | undefined\n const rid = requestId()\n\n if (typeof handler !== \"function\") {\n const allowed = ([\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\", \"OPTIONS\"] as const).filter(\n (m) => typeof mod[m] === \"function\",\n )\n return Response.json(\n { error: \"Method Not Allowed\", method: request.method, requestId: rid },\n { status: 405, headers: { Allow: allowed.join(\", \"), \"x-vantage-request-id\": rid } },\n )\n }\n\n const ctx: ApiContext = {\n request,\n params,\n env,\n // Fire-and-forget: runs after the response is sent, on the server's own\n // event loop; errors are swallowed so background work can't crash it.\n waitUntil: (p) => {\n void Promise.resolve(p).catch(() => undefined)\n },\n }\n\n try {\n const res = await handler(ctx)\n // waitUntil work runs after the response is returned, never blocking it.\n const headers = new Headers(res.headers)\n headers.set(\"x-vantage-request-id\", rid)\n return new Response(res.body, { status: res.status, statusText: res.statusText, headers })\n } catch (err) {\n const status = isHttpError(err) ? err.status : 500\n // Only surface intentional HttpError messages; internal errors are logged\n // server-side but returned generically to avoid information disclosure.\n const message = isHttpError(err) ? err.message : \"Internal Server Error\"\n // Logged with request id so agents can correlate terminal + response.\n console.error(`[vantage] API error (${rid}) ${request.method} ${new URL(request.url).pathname}:`, err)\n return Response.json(\n { error: message, requestId: rid },\n { status, headers: { \"x-vantage-request-id\": rid } },\n )\n }\n}\n"]}
@@ -1,19 +0,0 @@
1
- import { QueryClient } from '@tanstack/react-query';
2
-
3
- // @squadbase/vantage — generated build. Do not edit.
4
-
5
- function createQueryClient() {
6
- return new QueryClient({
7
- defaultOptions: {
8
- queries: {
9
- staleTime: 3e4,
10
- retry: 1,
11
- refetchOnWindowFocus: false
12
- }
13
- }
14
- });
15
- }
16
-
17
- export { createQueryClient };
18
- //# sourceMappingURL=chunk-ATYZ45XL.js.map
19
- //# sourceMappingURL=chunk-ATYZ45XL.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/query/client.ts"],"names":[],"mappings":";;;;AAOO,SAAS,iBAAA,GAAiC;AAC/C,EAAA,OAAO,IAAI,WAAA,CAAY;AAAA,IACrB,cAAA,EAAgB;AAAA,MACd,OAAA,EAAS;AAAA,QACP,SAAA,EAAW,GAAA;AAAA,QACX,KAAA,EAAO,CAAA;AAAA,QACP,oBAAA,EAAsB;AAAA;AACxB;AACF,GACD,CAAA;AACH","file":"chunk-ATYZ45XL.js","sourcesContent":["import { QueryClient } from \"@tanstack/react-query\"\n\n/**\n * Create the QueryClient with Vantage defaults. Individual queries may override\n * these, but replacing the QueryClient wholesale is intentionally not exposed —\n * Squadbase relies on a single managed client for auth, logging and telemetry.\n */\nexport function createQueryClient(): QueryClient {\n return new QueryClient({\n defaultOptions: {\n queries: {\n staleTime: 30_000,\n retry: 1,\n refetchOnWindowFocus: false,\n },\n },\n })\n}\n"]}