@squadbase/vantage-template 0.1.1-dev1.da9fa09 → 0.1.1-dev1.e87430f

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.
@@ -77,19 +77,27 @@ Vantage アプリの見た目は 2 つのファイルで決まる。
77
77
 
78
78
  ```css
79
79
  body {
80
- font-family: "<Font>", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Hiragino Sans", "Hiragino Kaku Gothic ProN", "Noto Sans JP", "Yu Gothic UI", Meiryo, sans-serif;
80
+ font-family: "<Font>", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Hiragino Sans", "Hiragino Kaku Gothic ProN", "Noto Sans JP", Meiryo, ui-sans-serif, system-ui, sans-serif;
81
81
  }
82
82
  :root {
83
- --slide-font-display: "<Font>", system-ui, -apple-system, "Hiragino Kaku Gothic ProN", "Hiragino Sans", "Yu Gothic", Meiryo, sans-serif;
83
+ --slide-font-display: "<Font>", -apple-system, BlinkMacSystemFont, "Segoe UI", "Hiragino Kaku Gothic ProN", "Hiragino Sans", "Yu Gothic", Meiryo, system-ui, sans-serif;
84
84
  --slide-font-body: var(--slide-font-display);
85
85
  }
86
86
  ```
87
87
 
88
88
  欧文の Web フォントだけを先頭に足すときも、日本語の system face(`"Hiragino Sans"` 〜 `Meiryo`)は
89
89
  必ず残す。消すとカナ・漢字がブラウザ任せのフォールバックになり、細く表示される。
90
+ `ui-sans-serif` / `system-ui` は日本語の face より**後ろ**に置く。日本語の Windows ではどちらも
91
+ Yu Gothic UI(UI のリサイズを避けるために字幅を詰めた書体)に解決され、カナがここで決まって
92
+ 半角風に見える。macOS の Chrome は `-apple-system` を解釈しないので `BlinkMacSystemFont` も並べる。
93
+ 既存アプリの `styles.css` に `ui-sans-serif, system-ui, …` が先頭にある、または `"Yu Gothic UI"` が
94
+ 残っているときは、`body` の `font-family` を上の並びに直す(ほかの行は書き換えない)。
90
95
  日本語の Web フォントは face が 100 を超えるので原則使わない(pptx 書き出しも壊れる。
91
96
  `vantage-slide` の `references/tokens.md` 参照)。
92
97
 
98
+ 長い表(ダッシュボード): カードに `max-h-*` + `overflow-y-auto` の縦スクロール領域を作らず、
99
+ ページング付きの `DataTablePreset`(`enablePagination`)にする(ホイールや End キーがページに届かなくなるため)。
100
+
93
101
  `DESIGN.md`: `references/format.md` の書式で書く。**色コードを書かない**(トークン名だけ)。
94
102
  150 行を超えない(超えると `vantage check` が `DESIGN_MD_TOO_LONG` を出す)。ロゴなどの素材は
95
103
  `public/brand/logo.svg` のように `public/` 配下のパスで「ブランド素材」行に書く。
@@ -93,13 +93,25 @@ root/
93
93
  | `@squadbase/vantage/router` | `Link`・`Outlet`・`useParams`・`useSearch`・`useNavigate`・`redirect`・`notFound`・`useRoutes`・`useCurrentRoute`・`useSearchParam`・`useSearchState` |
94
94
  | `@squadbase/vantage/query` | `useApiQuery`・`useApiMutation`・`apiJson`・`apiFetch`・`apiUrl`・`ApiError`、ほか `useQuery`/`useMutation` など TanStack Query の再エクスポート |
95
95
  | `@squadbase/vantage/ui` | shadcn/ui(Base UI バリアント)プリミティブ(`Button`・`Loading`・`ErrorState`・`Empty` ほか) |
96
- | `@squadbase/vantage/components` | 複合パーツ(`PageShell`・`DashboardCardPreset`・`DataTablePreset`、型付きチャート `LineChart`・`BarChart`・`AreaChart`・`DonutChart`・`BarList` ほか、`EChart`) |
96
+ | `@squadbase/vantage/components` | 複合パーツ(`PageShell`・`DashboardCardPreset`・`DataTablePreset`、KPI の `KpiCard`・`Delta`、型付きチャート `LineChart`・`BarChart`・`AreaChart`・`DonutChart`・`BarList` ほか、`EChart`、書式の `formatValue`) |
97
97
  | `@squadbase/vantage/table` | `FilePageView`・`TableViewer`(データファイルページの表ビューア) |
98
98
  | `@squadbase/vantage/slide` | `SlideDeck`・`Slide`・`Steps`・`Step`・`MorphElement`・`useSlideNumber`・`useIsActiveSlide`(スライド専用。1920×1080 の固定キャンバス) |
99
99
  | `@squadbase/vantage/doc` | `Document`(ドキュメント専用。中の素の HTML 要素に見出し・表などのスタイルが当たる) |
100
100
  | `@squadbase/vantage/markdown` | `MarkdownRenderer`(Shiki を隔離するための専用サブパス) |
101
101
  | `@squadbase/vantage/server` | `ApiContext`・`ApiHandler`・`HttpError`・`isHttpError`・`json`(server/ 側でのみ使う) |
102
102
 
103
+ **KPI は `KpiCard`、増減は `Delta`(`comparison` と `polarity` を渡す)。** 向き・率・色は値から計算
104
+ されるので手で渡さない。`TrendIndicator` は非推奨(`direction` を手で渡すので値と食い違いうる)。props は
105
+ `vantage docs kpi-card` / `vantage docs delta` を引く。
106
+
107
+ ```tsx
108
+ import { KpiCard, Delta } from "@squadbase/vantage/components";
109
+
110
+ <KpiCard label="売上" value={48_200_000} format="currency"
111
+ comparison={{ previous: 42_900_000, vs: "prior-period" }} />
112
+ <Delta value={0.021} comparison={{ previous: 0.026, vs: "prior-period" }} polarity="lower-is-better" />
113
+ ```
114
+
103
115
  ## モードは 2 つ、切り替えるのは `server/` の有無だけ
104
116
 
105
117
  `server/` ディレクトリがあれば **fullstack**、無ければ **SPA**。`vantage-manifest.json` の
@@ -233,7 +245,7 @@ vantage add skill --all --dir .claude/skills # 置き場所を指定(既定は
233
245
  > `vantage upgrade` はこのファイルをフレームワーク同梱の正本で丸ごと上書きするため、
234
246
  > このセクションは消えます。消えたら squadbase-monorepo の
235
247
  > `packages/vantage-template/agents/vantage/` から貼り直してください
236
- > (リポジトリ側では `npm run agents` が正本と結合して再生成します)。
248
+ > (リポジトリ側では `pnpm run agents` が正本と結合して再生成します)。
237
249
 
238
250
  ## Squadbase の Connection / Query からデータを読む
239
251
 
@@ -351,6 +363,10 @@ return Response.json((report.rows ?? []).map(toSessionsDaily))
351
363
  `components/build-recovery.tsx` の `BuildRecovery` が、その画面を DOM から検知して
352
364
  `Working on it…` 表示を被せ、dev では 1.2 秒待ってから自動でリロードして復帰させる
353
365
  (上限 3 回。使い切ったら「再読み込み」の案内に降格し、無事に描けたら残数が戻る)。
366
+ **ただし `window.__VANTAGE_DRAFT__?.active` が `true` の間(AI の書きかけのファイルを下書きとして
367
+ プレビューに出している間)は、覆わず・リロードもしない。** 下書き中の描画エラーはフレームワークが
368
+ 直前に通った版へ戻すので、ここで覆うとその版の表示まで隠れ、リロードすると下書きの表示ごと消える。
369
+ 下書きが終わった時点でエラー画面が残っていれば、通常どおり覆う。
354
370
  降格した画面にはエラー本文をそのまま出し、`Copy log` でコピーできるようにしてある —
355
371
  自力で直らないときに本文をそのまま貼り戻してもらうため。
356
372
  `_error.tsx` も同じ `RecoveringState` を描くので、ルートレイアウトが throw したときの
@@ -386,6 +402,9 @@ lockfile は `bun.lock`)。Squadbase のサンドボックスは bun と依存
386
402
  `bun install` を一度走らせて `bun.lock` を追従させ、両方を同じコミットに入れる。
387
403
  - スクリプトは `bun run <script>`(`bun run dev` / `bun run build` / `bun run check` /
388
404
  `bun run routes`)。`npm run <script>` でも動くが、install に npm を使わないこと。
405
+ - **公開から 48 時間未満の版は入らない。** `bunfig.toml` の `minimumReleaseAge`(サプライチェーン対策)で
406
+ 弾かれる。`bun add` / `bun install` が「バージョンが見つからない」で落ちたら、直近の版を避けて
407
+ 少し前の版を指定する。`bunfig.toml` の設定を緩めたり `@squadbase/*` 以外の除外を足したりしない。
389
408
 
390
409
  ## このアプリで作れるもの
391
410
 
@@ -454,3 +473,39 @@ Squadbase のチャットは、回答の根拠になったグラフ・表・KPI
454
473
  - **ページ内で一意にする。**
455
474
  - **新しく作った・直した画面では付ける。更新では既存の値を変えない** — 領域の意味が変わったときだけ変える
456
475
  (値を変えると、それを指していた過去のリンクが開けなくなる)。触っていない画面へ遡って付けることはしない。
476
+
477
+ ## ファイル内の書く順序(値 → 画面本体 → 子の部品)
478
+
479
+ ページ・部品のファイルは、上から次の順に書く。
480
+
481
+ 1. `import`(と `definePage(...)`)
482
+ 2. **値** — データ・定数・列定義・小さな関数。短く
483
+ 3. **画面本体** — `export default` のコンポーネント
484
+ 4. **子の部品** — 大文字始まりの部品。`function` 宣言で書く
485
+
486
+ 理由: ファイルは上から順に書かれ、書きかけでもプレビューに反映される。**本体が書かれた時点で画面が出る。**
487
+ 値を本体より後ろに書くと、本体はまだ無い値を読むので、その値が書かれるまで画面が出ない。
488
+ 子の部品は後ろでよい — 未定義の部品はプレビューが仮の枠で埋める。
489
+
490
+ ```tsx
491
+ import { definePage } from "@squadbase/vantage"
492
+
493
+ export const page = definePage({ title: "売上" })
494
+
495
+ const KPI_ITEMS: Kpi[] = [/* … */] // 値は本体より上
496
+ type Kpi = { label: string; value: number }
497
+
498
+ export default function SalesPage() { // 本体
499
+ return <KpiRow items={KPI_ITEMS} />
500
+ }
501
+
502
+ function KpiRow({ items }: { items: Kpi[] }) { /* … */ } // 子の部品は下
503
+ ```
504
+
505
+ ### 追加の不変条件
506
+
507
+ - **子の部品は `function` 宣言で書く。** 宣言は巻き上げられるので、本体より後ろでも安全。
508
+ `const Foo = () => …` のアロー関数は後ろに置くと、モジュール評価時に参照された瞬間に TDZ(初期化前参照)で落ちる。
509
+ - **値(定数・データ・列定義・小さな関数)は本体より上。** 小文字の関数・hook も値として上に置く。
510
+ - 型は消えるのでどこでもよい。値のそばに置く。
511
+ - 順序だけのために、既に並んでいるファイルを書き換えない。
@@ -177,6 +177,16 @@ refetchOnWindowFocus false・networkMode "always")。挙動を変えたいとき
177
177
  `/components` の複合パーツも全部取り出せる(名前は kebab-case: `add ui bar-chart`。
178
178
  ブロックは `add block sales-overview`。未知名で実行すると候補が一覧表示される)。
179
179
 
180
+ ### 長い表はページングする(縦スクロール箱を作らない)
181
+
182
+ - 行が多い表は `DataTablePreset`(または `DataTable`)に `enablePagination` を付けて**ページング**する
183
+ (既定は 10 行 / ページ。ページングは既定で無効なので、付け忘れると全行が並ぶ)。素の `<table>` を
184
+ 手書きして全行を流し込まない。
185
+ - カードの中に `max-h-*` + `overflow-y-auto` の**縦スクロール領域を作らない**。ホイールやキー
186
+ (End / Space)がその箱に吸われ、ページ全体がスクロールしなくなる。高さは内容に任せる。
187
+ - 横に広い表の `overflow-x-auto` は構わない。`SheetGrid`(`.csv` / `.xlsx` のビューア)は
188
+ グリッドとして意図した入れ子スクロールなので対象外。
189
+
180
190
  ### グラフはまず型付きチャートで描く
181
191
 
182
192
  - 推移は `LineChart` / `AreaChart`、比較は `BarChart`、構成比は `DonutChart`。`data`(行の配列)・
@@ -6,8 +6,8 @@
6
6
  "name": "vibe-app",
7
7
  "dependencies": {
8
8
  "@squadbase/react": "^0.1.0",
9
- "@squadbase/vantage": "0.6.2-dev1.31accb1",
10
- "@squadbase/vantage-sdk": "0.1.1-dev1.28afad5",
9
+ "@squadbase/vantage": "0.6.2-dev1.05a930b",
10
+ "@squadbase/vantage-sdk": "0.1.1-dev1.29787f0",
11
11
  "date-fns": "^4.1.0",
12
12
  "lucide-react": "^0.468.0",
13
13
  "react": "^19.3.1",
@@ -114,7 +114,7 @@
114
114
 
115
115
  "@mermaid-js/parser": ["@mermaid-js/parser@1.2.1", "", { "dependencies": { "@chevrotain/types": "~11.1.2" } }, "sha512-n12NohV3mrUyUL2o93IgG/ifeW9FTyeJn3zDxkhwa8MJ9Fxg3HQMlA3RiGmD/3UnJvheztkjjQAjA2T4LmUcpw=="],
116
116
 
117
- "@oxc-project/types": ["@oxc-project/types@0.154.0", "", {}, "sha512-KYOUGQFlLWR+s4QMV2Q9ko38TZm7EdO87Qm7rvZLqk/webse/cDjtyxKhg/47l0byCbs/k46LT8qc4PKjAikzA=="],
117
+ "@oxc-project/types": ["@oxc-project/types@0.155.0", "", {}, "sha512-SeE8JPwW1xs1tHeNycjUtUBdhorRa8o4jlvjwk49R7/Z291i+4DQa7JJIgOm/TaYQZx+QrPQbVJ/9c8dMIZN8Q=="],
118
118
 
119
119
  "@radix-ui/primitive": ["@radix-ui/primitive@1.1.7", "", {}, "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q=="],
120
120
 
@@ -148,35 +148,35 @@
148
148
 
149
149
  "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-rDiah9wvtqihWtWz02XreeRKIxt2EJF8y5D9rtY9l5A2zxePAtcPiOMpDugNRw5bFHz+1/8viVoc7ZVKiJknCw=="],
150
150
 
151
- "@rolldown/binding-android-arm-eabi": ["@rolldown/binding-android-arm-eabi@1.2.14", "", { "os": "android", "cpu": "arm" }, "sha512-aWR+XKH9+rW7m5fWq/5360J3mHycrx/o6xZM2DQFdJnTMdyP7/WzxPjhtLbVWdKo2y612/4U1bWqb3HQhiF7vg=="],
151
+ "@rolldown/binding-android-arm-eabi": ["@rolldown/binding-android-arm-eabi@1.2.15", "", { "os": "android", "cpu": "arm" }, "sha512-aLcgK9gIJ3YRvrrtfSBbRa27F5WrpiTaPulnFpsHPxpUHgFlSgnU4854MHwoRGEBhfRJDWCS2WQ7lIjskE9Olw=="],
152
152
 
153
- "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.2.14", "", { "os": "android", "cpu": "arm64" }, "sha512-QiVxHofl6A8lQU7WAtOUY4QMVZXohxwz7WJJoeh8NBEUyKfIkMRKhYyq8az/1RypNiIXzNTtiS6KwHz0erxYRQ=="],
153
+ "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.2.15", "", { "os": "android", "cpu": "arm64" }, "sha512-xD7DdZCrbThKuxNg7ooQmvp07ys4J/pnSEMTG3/OdOWl6daC67m/Q4fL78sRHMxTH/N4fGUbYhcPM0MF5WFHkg=="],
154
154
 
155
- "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.2.14", "", { "os": "darwin", "cpu": "arm64" }, "sha512-lTQOzHuBIooh3jIr5XINQkjPGEuGy+Dsy0meXDr6Sjty0b99kYgGh4uBC/8mJX1T0kC8d6S0aPP1Y+bHkh6aBw=="],
155
+ "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.2.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-m2WuQ6r0B4OTYxyPpNLtXaMRbRDUbG8YQV2Q3KTdSSK8Th6KHCnC8jVCJt8bvYeSB5WivkkNbc85mKadaSdOxA=="],
156
156
 
157
- "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.2.14", "", { "os": "darwin", "cpu": "x64" }, "sha512-hyMKEGatjmBYCs/Q4sbIcL/GqpFEqsX3wc+HYVQ/85A4rxc1o7rPm4DUI4GV4k/h0ITddwFSGpqQunI14GXjTA=="],
157
+ "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.2.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-5RtnsHM36nodM9WbMy+zip0LGS/G+y5wro5RHrpOlr4ac24+1zSi9kiV2MP1myZeZ/ifEHfulQcWjnAXzc2C2A=="],
158
158
 
159
- "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.2.14", "", { "os": "freebsd", "cpu": "x64" }, "sha512-u371GbXoVXrEgP20PwzoGNOdAOqAMhzo19JzzY+V4YmmnC+DTOoqdbMu+O+54emM59Mik/UdrtWgcuEDKWRPKA=="],
159
+ "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.2.15", "", { "os": "freebsd", "cpu": "x64" }, "sha512-g3l09pyFqhu+135JKkbkwiKGQmwU5hUMUPJ4VdBADvuqR8d33L6H578rh8m7rtxbdp+VmX/+nzI7nvhSZlzpUQ=="],
160
160
 
161
- "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.2.14", "", { "os": "linux", "cpu": "arm" }, "sha512-BqwKsdZQf6EqVWr8fv2MrDYzAvOZ7+NYFSJESRGs87fkseEhs0TU9TXgRJTkZoRRkxTizyEg1LKUXfU3/78uqg=="],
161
+ "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.2.15", "", { "os": "linux", "cpu": "arm" }, "sha512-nIe3VugI97nGQtspPxDqA4sXMUpeiocKIP/CqpY/u/vgbT+alX+cVxu1C/oVBPKg4feNTmYovE91v4DgXvXNwQ=="],
162
162
 
163
- "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.2.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-SD9kY8M009x/7s57Z1dgUVlP5+uYBgKQK0Tkr1WIWngrtUJmRCW1RJFMY7QDla2khXhmg2i5Io7MUjcmZNZ/KQ=="],
163
+ "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.2.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-iTBjXx+B5QSvgI89lJfmqh4wTh8NabR1YZIIN3hNx4zSG0EVfVqkOvLulNpG4251pbd6/IAmZ/LefVk1Y4rPhw=="],
164
164
 
165
- "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.2.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-BJ2CL19M6uuWesMgnqJzgxoI+N+eV7b0A9O9vLV42y+gitC41fNa8KElau1tHVzuLWX5GzEuxMPhAO0tQXQMkg=="],
165
+ "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.2.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-uN26XOnVR7rqQupG3V9oAG2EGGatWjcJHOeDOHUFCh4TBWmP2tHTFoVN13aYbwfm0aHqJAEmcwhAu6sooIjfqQ=="],
166
166
 
167
- "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.2.14", "", { "os": "linux", "cpu": "ppc64" }, "sha512-4+Kl8daPV8NDHaD7mfCcc1pRM+Qg7ab8DyMm5FG5WoZgv4h2i3KFN1CybYBNbQqmuYm7ftJLvmMI1eHYJ22S/Q=="],
167
+ "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.2.15", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9nWkcdd+H+XhaA3fQIwpHnKIrfx/PQ6rO0pivr78Og07tXlUlctBBJoEYLPfhTmCR/QxEVdn6uPjHV5SymPhdw=="],
168
168
 
169
- "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.2.14", "", { "os": "linux", "cpu": "s390x" }, "sha512-V/h8ePq1fKDrmtFMz6xcCN3DKvuvMRrwwD1q0PkBeeo4DpK64biaKvqWGVxqCeYKqeRk7gE7/iKPxLCn08O97A=="],
169
+ "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.2.15", "", { "os": "linux", "cpu": "s390x" }, "sha512-Ggh0ciFjLwIusLSLz7LZYEaGdxOAUJXI+Qu5JCbhPJJHEt8vP+IjRATTJheiJsdzzTYgUiHUiw3X1Ea3ZpMBdA=="],
170
170
 
171
- "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.2.14", "", { "os": "linux", "cpu": "x64" }, "sha512-WxalXzgrLVpKC93xfpaJ3gCXUWzeCr+FRCbDVD2mUAy4L6Vo3lNVz0epUEUpxPWJkLNYUwM4OSIikv36OJ9mLg=="],
171
+ "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.2.15", "", { "os": "linux", "cpu": "x64" }, "sha512-FHg60rS6DBePAYZ9X9F67x+JObFdmgn6tupA1C/a3B+CufFmTD4mX+e8M75tCiAO3foR/6xwVBI0KQiI7sn/rw=="],
172
172
 
173
- "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.2.14", "", { "os": "linux", "cpu": "x64" }, "sha512-3G3tnAmTpdk9jO3GQtyHXqeFD2SlecfTws/Y3M2mmj9nMBwwuLT4kur8sZKeu7ENUHSpVO3cIsm5A52LEWWwlg=="],
173
+ "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.2.15", "", { "os": "linux", "cpu": "x64" }, "sha512-/cvV/7v5VGz/moiz0XKRj2dE8DzsKyhIu1KpOvJGPjRywg3yscON889UGvAe8zfiJLHup7VLjBduoGWi3HusWQ=="],
174
174
 
175
- "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.2.14", "", { "os": "none", "cpu": "arm64" }, "sha512-qyr5XiNGxm2ytx17vvtcJ9nXn6v1gENETi503bJx3OGwZ1tSonj7BosxbMa/xeAfSl5iS9X6KjEWYXLu6v1L7Q=="],
175
+ "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.2.15", "", { "os": "none", "cpu": "arm64" }, "sha512-MyzC5Hrv/sm+Z7TSGKP/j8001RO2xQm1zOpCNVL4j0cY/VhBZviludA2YFQ7i4lH6ZAXxQm0lcMdBzM+99mE7w=="],
176
176
 
177
- "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.2.14", "", { "os": "win32", "cpu": "arm64" }, "sha512-oA4o5o0S2yB199YyYXDzKUv1Z56Rc+mgOjwm7Z/vJ9Z8igiDPYvoOuDyo6g7AK/ZaF09nPhDKVnGBcYHO11GXA=="],
177
+ "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.2.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-+PgYiS+AH5omy6L45ZLxybasauJaj3M8buoT83D+nzPmCayXlxUV9hydhciCKVyIg0B/v0BtXPDE1uGqNwCPpg=="],
178
178
 
179
- "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.2.14", "", { "os": "win32", "cpu": "x64" }, "sha512-9mE94bf2GZUaR/fUSXCMpBoWkctu421Mi0ORpm4zqfbkjKep7RxzPFa5etcGs5h+2KApoWBOA/hAJa/7saGgEQ=="],
179
+ "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.2.15", "", { "os": "win32", "cpu": "x64" }, "sha512-HFZd2QNQYn1JbIi+Hpd7H+r/lkWxcfIu28nirZ6eCn2LafuvmExMlb/P96Sfx5tf7pPArw39IdhmCF8vmV+NhQ=="],
180
180
 
181
181
  "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="],
182
182
 
@@ -196,9 +196,9 @@
196
196
 
197
197
  "@squadbase/react": ["@squadbase/react@0.1.0", "", { "dependencies": { "zod": "^4.3.6" }, "peerDependencies": { "@types/react": "17.x || 18.x || 19.x", "react": "17.x || 18.x || 19.x" }, "optionalPeers": ["@types/react"] }, "sha512-k+Dpyj7eH+Yjydw54iEluCjYDae086d12hWgBjNMpw4wLbQGz3CrJ6Yipodg8n1cKUt7zvwddlcTHd79bgKsXg=="],
198
198
 
199
- "@squadbase/vantage": ["@squadbase/vantage@0.6.2-dev1.31accb1", "", { "dependencies": { "@base-ui/react": "^1.6.0", "@hono/node-server": "^2.1.5", "@streamdown/code": "^1.1.1", "@tailwindcss/vite": "^4.3.3", "@tanstack/react-query": "^5.101.2", "@tanstack/react-router": "^1.170.17", "@tanstack/react-table": "^8.21.3", "@vitejs/plugin-react": "^6.1.2", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", "date-fns": "^4.1.0", "echarts": "^6.0.0", "esbuild": "^0.28.1", "fflate": "^0.8.3", "hono": "^4.13.13", "html-to-image": "^1.11.13", "lucide-react": "^0.468.0", "papaparse": "^5.7.0", "picocolors": "^1.1.1", "react-day-picker": "^10.0.1", "read-excel-file": "^9.3.10", "streamdown": "^2.5.0", "tailwind-merge": "^3.0.0", "tailwindcss": "^4.3.3", "tw-animate-css": "^1.0.0", "vite": "^8.3.4" }, "peerDependencies": { "react": ">=18.2.0", "react-dom": ">=18.2.0" }, "bin": { "vantage": "dist/cli.js" } }, "sha512-3Ff9PRHHhqf7vxGqEiKuKpZfL/Dq2Q0mJP7NMapHCqUA928oG2Gn/wUJOUfzcCVoZRgb5m09PBr3sQC+BrEGkQ=="],
199
+ "@squadbase/vantage": ["@squadbase/vantage@0.6.2-dev1.05a930b", "", { "dependencies": { "@base-ui/react": "^1.6.0", "@hono/node-server": "^2.1.5", "@streamdown/code": "^1.1.1", "@tailwindcss/vite": "^4.3.3", "@tanstack/react-query": "^5.101.2", "@tanstack/react-router": "^1.170.17", "@tanstack/react-table": "^8.21.3", "@vitejs/plugin-react": "^6.1.2", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", "date-fns": "^4.1.0", "echarts": "^6.0.0", "esbuild": "0.28.1", "fflate": "^0.8.3", "hono": "^4.13.13", "html-to-image": "^1.11.13", "lucide-react": "^0.468.0", "papaparse": "^5.7.0", "picocolors": "^1.1.1", "react-day-picker": "^10.0.1", "read-excel-file": "^9.3.10", "shiki": "3.23.0", "streamdown": "^2.5.0", "tailwind-merge": "3.6.0", "tailwindcss": "^4.3.3", "tw-animate-css": "^1.0.0", "vite": "^8.3.4" }, "peerDependencies": { "react": ">=18.2.0", "react-dom": ">=18.2.0", "typescript": ">=5.0.0" }, "optionalPeers": ["typescript"], "bin": { "vantage": "dist/cli.js" } }, "sha512-hA8JC3AzYaWBpXlnlZmaP1kBwU71BIppqcfbuXv57VltJX5sLtpe7mon3jwSpCgwz9LbP1OudM50c60xSdKRPg=="],
200
200
 
201
- "@squadbase/vantage-sdk": ["@squadbase/vantage-sdk@0.1.1-dev1.28afad5", "", {}, "sha512-Y2d40uYKTARweiy1lslrmTkA35WyhMnco0e7eGSBYs/HVN+CjRBZQbuhZ4ebPw0L0JG+7B2nTQMzZmUdmyeG9w=="],
201
+ "@squadbase/vantage-sdk": ["@squadbase/vantage-sdk@0.1.1-dev1.29787f0", "", {}, "sha512-zvqv9Ctm3D62uXRQoOQCHQ/mtT6nczKDTjNfwthqHOlQ6TLcEzuDy9reBaS0/dvEJ9nTruTcru/dT45S+cCQFQ=="],
202
202
 
203
203
  "@streamdown/code": ["@streamdown/code@1.1.1", "", { "dependencies": { "shiki": "^3.19.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0" } }, "sha512-i7HTNuDgZWb+VdrNVOam9gQhIc5MSSDXKWXgbUrn/4vSRaSMM+Rtl10MQj4wLWPNpF7p80waJsAqFP8HZfb0Jg=="],
204
204
 
@@ -716,7 +716,7 @@
716
716
 
717
717
  "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="],
718
718
 
719
- "read-excel-file": ["read-excel-file@9.3.10", "", { "dependencies": { "fflate": "^0.8.3", "saxen": "^11.1.0", "unzipper-esm": "^0.13.3", "worker-f": "^0.1.12" } }, "sha512-zFcBdzunLCGBmLRT4Q3mLPJnhKPAXXfGAZ83feODlEqX2aRUbgF1h/n2oY0UF7I8tVwnJNRas6fTRD5veDUs+g=="],
719
+ "read-excel-file": ["read-excel-file@9.3.12", "", { "dependencies": { "fflate": "^0.8.3", "saxen": "^11.1.0", "unzipper-esm": "^0.13.3", "worker-f": "^0.1.12" } }, "sha512-benNAoQbe5HqoTi3qrQkR8wMeHq0gVWLt2Br73hVvmBPGdzMf9OLGsps/812AfZ+bvzVYsUyhF77Y3o4B51yCA=="],
720
720
 
721
721
  "regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="],
722
722
 
@@ -744,7 +744,7 @@
744
744
 
745
745
  "robust-predicates": ["robust-predicates@3.0.3", "", {}, "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA=="],
746
746
 
747
- "rolldown": ["rolldown@1.2.14", "", { "dependencies": { "@oxc-project/types": "=0.154.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm-eabi": "1.2.14", "@rolldown/binding-android-arm64": "1.2.14", "@rolldown/binding-darwin-arm64": "1.2.14", "@rolldown/binding-darwin-x64": "1.2.14", "@rolldown/binding-freebsd-x64": "1.2.14", "@rolldown/binding-linux-arm-gnueabihf": "1.2.14", "@rolldown/binding-linux-arm64-gnu": "1.2.14", "@rolldown/binding-linux-arm64-musl": "1.2.14", "@rolldown/binding-linux-ppc64-gnu": "1.2.14", "@rolldown/binding-linux-s390x-gnu": "1.2.14", "@rolldown/binding-linux-x64-gnu": "1.2.14", "@rolldown/binding-linux-x64-musl": "1.2.14", "@rolldown/binding-openharmony-arm64": "1.2.14", "@rolldown/binding-win32-arm64-msvc": "1.2.14", "@rolldown/binding-win32-x64-msvc": "1.2.14" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-eTBTTqMR6uHKLgcUqpJP8ntzDI6V4LYA8negAMBEI7gkgVvOdrx6VsvS2Ym+83KhOPdLCHWJu0w7UQFJZhHKNw=="],
747
+ "rolldown": ["rolldown@1.2.15", "", { "dependencies": { "@oxc-project/types": "=0.155.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm-eabi": "1.2.15", "@rolldown/binding-android-arm64": "1.2.15", "@rolldown/binding-darwin-arm64": "1.2.15", "@rolldown/binding-darwin-x64": "1.2.15", "@rolldown/binding-freebsd-x64": "1.2.15", "@rolldown/binding-linux-arm-gnueabihf": "1.2.15", "@rolldown/binding-linux-arm64-gnu": "1.2.15", "@rolldown/binding-linux-arm64-musl": "1.2.15", "@rolldown/binding-linux-ppc64-gnu": "1.2.15", "@rolldown/binding-linux-s390x-gnu": "1.2.15", "@rolldown/binding-linux-x64-gnu": "1.2.15", "@rolldown/binding-linux-x64-musl": "1.2.15", "@rolldown/binding-openharmony-arm64": "1.2.15", "@rolldown/binding-win32-arm64-msvc": "1.2.15", "@rolldown/binding-win32-x64-msvc": "1.2.15" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-6odND+fel5zVw3I4Yc2e69gvguDnX0CAAGiNDZSq4ik5WUcaLrkD2GLuog0XcODbor31hZ5ayEMgVDno+KPV5g=="],
748
748
 
749
749
  "roughjs": ["roughjs@4.6.6", "", { "dependencies": { "hachure-fill": "^0.5.2", "path-data-parser": "^0.1.0", "points-on-curve": "^0.2.0", "points-on-path": "^0.2.1" } }, "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ=="],
750
750
 
@@ -752,7 +752,7 @@
752
752
 
753
753
  "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="],
754
754
 
755
- "saxen": ["saxen@11.1.1", "", {}, "sha512-J4BkmJFaM7VgE7pgkFGsNEcqqM3h7+Mz80vfLWFhx7uNOCOXIu6LLjQHYWNejdst3pf/3JUaBIG9+pkk1umlow=="],
755
+ "saxen": ["saxen@11.2.0", "", {}, "sha512-2bG564AAJcm/3vyZC0Uhs/mACrH+4AHMaKHKAB2M5xMqomUZa27FQ7MYaQGFKVcJk/MVzSEQ34A8QZWW+eiWEw=="],
756
756
 
757
757
  "scheduler": ["scheduler@0.28.0", "", {}, "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw=="],
758
758
 
@@ -830,7 +830,7 @@
830
830
 
831
831
  "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="],
832
832
 
833
- "vite": ["vite@8.3.4", "", { "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.7", "postcss": "^8.5.29", "rolldown": "~1.2.12", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.7.1", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-IftLE8phJhdNRIGNEV4JCCvfNScoZQVfcXaN8TQropWGJtOHcu+iAB9pHcuGvmEb/kddw0LBe/jS8ztdC6xdiQ=="],
833
+ "vite": ["vite@8.3.5", "", { "dependencies": { "lightningcss": "^1.33.0", "picomatch": "^4.0.7", "postcss": "^8.5.30", "rolldown": "~1.2.14", "tinyglobby": "^0.2.17" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.7.1 || ^0.8.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "@vitejs/devtools", "esbuild", "jiti", "less", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-z1YpjieJ9g0ebTmQpLBv/oq9E3F9EIlOe+7LahW8SrE3LTiG3udBRUXQOlYXP9b+IC2q2phHeVxj9xGlw34OEQ=="],
834
834
 
835
835
  "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="],
836
836
 
@@ -0,0 +1,5 @@
1
+ # サプライチェーン対策: 公開から 48 時間(172800 秒)未満の版は install で解決しない。
2
+ # 自社パッケージは publish 直後の版を雛形が参照するため除外する。
3
+ [install]
4
+ minimumReleaseAge = 172800
5
+ minimumReleaseAgeExcludes = ["@squadbase/vantage", "@squadbase/vantage-sdk", "@squadbase/react"]
@@ -12,8 +12,8 @@
12
12
  },
13
13
  "dependencies": {
14
14
  "@squadbase/react": "^0.1.0",
15
- "@squadbase/vantage": "0.6.2-dev1.31accb1",
16
- "@squadbase/vantage-sdk": "0.1.1-dev1.28afad5",
15
+ "@squadbase/vantage": "0.6.2-dev1.05a930b",
16
+ "@squadbase/vantage-sdk": "0.1.1-dev1.29787f0",
17
17
  "date-fns": "^4.1.0",
18
18
  "lucide-react": "^0.468.0",
19
19
  "react": "^19.3.1",
@@ -7,15 +7,6 @@ import type { NavGroup } from "@squadbase/vantage/components"
7
7
  import { BuildRecovery } from "./components/build-recovery"
8
8
  import { UserCard } from "./components/user-card"
9
9
 
10
- /**
11
- * `AppShell` is router-agnostic and renders nav entries through whatever
12
- * component it is handed, using an `href` prop. Adapt it to the Vantage router
13
- * so navigation stays client-side.
14
- */
15
- function RouterLink({ href, ...props }: { href: string } & ComponentProps<"a">) {
16
- return <Link to={href} {...props} />
17
- }
18
-
19
10
  /**
20
11
  * The nav *is* the route list — adding a page file puts it in the nav, with no
21
12
  * second list to keep in sync. Dynamic routes (`sales/[id].tsx`) have no single
@@ -67,3 +58,12 @@ export default function RootLayout() {
67
58
  </SquadbaseProvider>
68
59
  )
69
60
  }
61
+
62
+ /**
63
+ * `AppShell` is router-agnostic and renders nav entries through whatever
64
+ * component it is handed, using an `href` prop. Adapt it to the Vantage router
65
+ * so navigation stays client-side.
66
+ */
67
+ function RouterLink({ href, ...props }: { href: string } & ComponentProps<"a">) {
68
+ return <Link to={href} {...props} />
69
+ }
@@ -29,6 +29,12 @@ import { Button, Spinner } from "@squadbase/vantage/ui"
29
29
  */
30
30
  const FRAMEWORK_ERROR_SELECTOR = '[role="alert"] h2'
31
31
 
32
+ /** `import.meta.hot` のうち、ここで使う分だけ。 */
33
+ interface ViteHot {
34
+ on(event: string, cb: () => void): void
35
+ off?(event: string, cb: () => void): void
36
+ }
37
+
32
38
  /** 書き込みの連鎖が落ち着くのを待ってからリロードする。 */
33
39
  const RELOAD_DELAY_MS = 1200
34
40
 
@@ -43,6 +49,21 @@ const SETTLE_MS = 5000
43
49
 
44
50
  const RELOAD_COUNT_KEY = "vantage:build-recovery-reloads"
45
51
 
52
+ /**
53
+ * 書きかけのファイルを下書きとして表示している間、Vantage のクライアントが立てる目印。
54
+ * 下書き中の描画エラーはフレームワークが直前に通った版へ戻すので、ここでは覆わず、
55
+ * リロードもしない(リロードすると下書きの表示ごと消える)。
56
+ */
57
+ declare global {
58
+ interface Window {
59
+ __VANTAGE_DRAFT__?: { active: boolean }
60
+ }
61
+ }
62
+
63
+ function isDraftActive(): boolean {
64
+ return window.__VANTAGE_DRAFT__?.active === true
65
+ }
66
+
46
67
  /** コピーしたことが伝わればいいので、確認表示はすぐ元に戻す。 */
47
68
  const COPIED_FEEDBACK_MS = 2000
48
69
 
@@ -90,6 +111,7 @@ export function RecoveringState({ error }: { error?: unknown }) {
90
111
  const attempts = readReloadCount()
91
112
  if (attempts < MAX_RELOADS) {
92
113
  const timer = setTimeout(() => {
114
+ if (isDraftActive()) return
93
115
  writeReloadCount(attempts + 1)
94
116
  window.location.reload()
95
117
  }, RELOAD_DELAY_MS)
@@ -181,6 +203,11 @@ export function BuildRecovery({ children }: { children: ReactNode }) {
181
203
  const host = hostRef.current
182
204
  if (!host) return
183
205
  const sync = () => {
206
+ // 下書きの表示中は覆わない。終わった時点でエラー画面が残っていれば、次の sync で覆う。
207
+ if (isDraftActive()) {
208
+ setMessage(null)
209
+ return
210
+ }
184
211
  const heading = host.querySelector(FRAMEWORK_ERROR_SELECTOR)
185
212
  // 覆って見えなくする以上、支援技術にも読ませない。`role="alert"` のままだと
186
213
  // 「Something went wrong」がそのまま読み上げられてしまう。属性は observe の
@@ -192,7 +219,16 @@ export function BuildRecovery({ children }: { children: ReactNode }) {
192
219
  sync()
193
220
  const observer = new MutationObserver(sync)
194
221
  observer.observe(host, { childList: true, subtree: true })
195
- return () => observer.disconnect()
222
+ // 下書きの有無が変わったときに見直す(目印の更新を待つため 1 tick 遅らせる)。
223
+ const onDraftState = () => setTimeout(sync, 0)
224
+ // 生成される `ImportMeta` は `env` しか宣言しない(HMR の型を入れるとアプリが vite/client を
225
+ // 入れたときに二重宣言になる)ので、ここだけ局所キャストで読む。
226
+ const hot = (import.meta as unknown as { hot?: ViteHot }).hot
227
+ hot?.on("vantage:draft-state", onDraftState)
228
+ return () => {
229
+ observer.disconnect()
230
+ hot?.off?.("vantage:draft-state", onDraftState)
231
+ }
196
232
  }, [])
197
233
 
198
234
  const crashed = message !== null
@@ -11,6 +11,16 @@ import {
11
11
  cn,
12
12
  } from "@squadbase/vantage/ui"
13
13
 
14
+ export function UserCard() {
15
+ const userState = useUser()
16
+
17
+ if (userState.status === "success") {
18
+ return <UserInfo user={userState.data} />
19
+ }
20
+
21
+ return <UserInfoFallback />
22
+ }
23
+
14
24
  /**
15
25
  * Squadbase-authenticated user chip for the app shell.
16
26
  *
@@ -120,13 +130,3 @@ function UserInfoFallback() {
120
130
  </div>
121
131
  )
122
132
  }
123
-
124
- export function UserCard() {
125
- const userState = useUser()
126
-
127
- if (userState.status === "success") {
128
- return <UserInfo user={userState.data} />
129
- }
130
-
131
- return <UserInfoFallback />
132
- }
@@ -18,12 +18,14 @@
18
18
  }
19
19
 
20
20
  /* Page typeface. Latin system faces first, then Japanese system faces so kana and
21
- kanji do not fall back to a thin locale default. Put a Latin webfont in front if
22
- you add one, but keep the Japanese families. */
21
+ kanji do not fall back to a thin locale default. `ui-sans-serif` / `system-ui`
22
+ come after the Japanese families on purpose: on Japanese Windows they resolve to
23
+ the condensed Yu Gothic UI, which would otherwise take the kana. Put a Latin
24
+ webfont in front if you add one, but keep the Japanese families. */
23
25
  body {
24
26
  font-family:
25
- ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Hiragino Sans",
26
- "Hiragino Kaku Gothic ProN", "Noto Sans JP", "Yu Gothic UI", Meiryo, sans-serif;
27
+ -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Hiragino Sans",
28
+ "Hiragino Kaku Gothic ProN", "Noto Sans JP", Meiryo, ui-sans-serif, system-ui, sans-serif;
27
29
  }
28
30
 
29
31
  /* vantage-template:chart-preset — regenerated by `chart`; edits here are lost */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squadbase/vantage-template",
3
- "version": "0.1.1-dev1.da9fa09",
3
+ "version": "0.1.1-dev1.e87430f",
4
4
  "description": "CLI tool for initializing and customizing Squadbase Vantage projects.",
5
5
  "keywords": [
6
6
  "squadbase",
@@ -36,19 +36,6 @@
36
36
  "access": "public",
37
37
  "registry": "https://registry.npmjs.org"
38
38
  },
39
- "scripts": {
40
- "sync-base": "rsync -a --delete --exclude='node_modules' --exclude='dist' --exclude='.vantage' --exclude='*.tsbuildinfo' --exclude='*.log' template/ base-template/ && mv base-template/.gitignore base-template/_gitignore",
41
- "build": "npm run sync-base && tsup",
42
- "type-check": "tsc",
43
- "agents": "node scripts/build-agents.cjs",
44
- "agents:check": "node scripts/build-agents.cjs --check",
45
- "template:install": "bun install --cwd template --frozen-lockfile",
46
- "template:build": "npm --prefix template run build",
47
- "prepublishOnly": "npm run agents:check && npm run build",
48
- "release": "npm publish --@squadbase:registry=https://registry.npmjs.org",
49
- "dev": "node scripts/dev.mjs",
50
- "screenshot": "node scripts/screenshot.mjs"
51
- },
52
39
  "devDependencies": {
53
40
  "@types/node": "^22.15.0",
54
41
  "playwright": "^1.49.0",
@@ -58,7 +45,20 @@
58
45
  "optionalDependencies": {
59
46
  "@ai-sdk/anthropic": "^2.0.0",
60
47
  "@ai-sdk/google": "^2.0.0",
61
- "@ai-sdk/openai": "^2.0.0",
48
+ "@ai-sdk/openai": "2.0.122",
62
49
  "ai": "^5.0.52"
50
+ },
51
+ "gitHead": "e87430fa6bccdd2c9b34ebfba984dd801e7d1420",
52
+ "scripts": {
53
+ "sync-base": "rsync -a --delete --exclude='node_modules' --exclude='dist' --exclude='.vantage' --exclude='*.tsbuildinfo' --exclude='*.log' template/ base-template/ && mv base-template/.gitignore base-template/_gitignore",
54
+ "build": "pnpm run sync-base && tsup",
55
+ "type-check": "tsc",
56
+ "agents": "node scripts/build-agents.cjs",
57
+ "agents:check": "node scripts/build-agents.cjs --check",
58
+ "template:install": "bun install --cwd template --frozen-lockfile",
59
+ "template:build": "bun run --cwd template build",
60
+ "release": "pnpm publish --@squadbase:registry=https://registry.npmjs.org",
61
+ "dev": "node scripts/dev.mjs",
62
+ "screenshot": "node scripts/screenshot.mjs"
63
63
  }
64
- }
64
+ }
@@ -104,6 +104,53 @@ const RANGE_OPTIONS: SegmentOption[] = [
104
104
  { label: "90 days", value: "90d" },
105
105
  ]
106
106
 
107
+ export default function HomePage() {
108
+ // Filter state belongs in the URL rather than `useState`: it survives a
109
+ // reload, and a pasted link reproduces exactly what you were looking at.
110
+ const [range, setRange] = useSearchParam("range", "30d")
111
+
112
+ // `search` goes into the query string *and* the cache key, so changing the
113
+ // range refetches instead of serving the previous window.
114
+ const summary = useApiQuery<KpiSummary>("/api/kpi-summary", { search: { range } })
115
+
116
+ return (
117
+ <PageShell>
118
+ <PageShellHeader>
119
+ <PageShellHeading>
120
+ <PageShellTitle>[Template] Performance overview</PageShellTitle>
121
+ <PageShellDescription>
122
+ Key metrics and trends for the selected period.
123
+ </PageShellDescription>
124
+ </PageShellHeading>
125
+ <PageShellHeaderEnd>
126
+ <SegmentedControl
127
+ options={RANGE_OPTIONS}
128
+ value={range}
129
+ onChange={setRange}
130
+ ariaLabel="Period"
131
+ />
132
+ </PageShellHeaderEnd>
133
+ </PageShellHeader>
134
+
135
+ <PageShellContent className="space-y-6">
136
+ {summary.isPending ? (
137
+ <Loading />
138
+ ) : summary.isError ? (
139
+ // `summary.error` is an `ApiError`: `message` is the text the handler
140
+ // passed to `HttpError`, so there is no cast and no generic wording.
141
+ <ErrorState
142
+ title="Could not load the dashboard"
143
+ message={summary.error.message}
144
+ action={<Button onClick={() => void summary.refetch()}>Retry</Button>}
145
+ />
146
+ ) : (
147
+ <DashboardBody data={summary.data} />
148
+ )}
149
+ </PageShellContent>
150
+ </PageShell>
151
+ )
152
+ }
153
+
107
154
  function DashboardBody({ data }: { data: KpiSummary }) {
108
155
  return (
109
156
  <>
@@ -151,50 +198,3 @@ function DashboardBody({ data }: { data: KpiSummary }) {
151
198
  </>
152
199
  )
153
200
  }
154
-
155
- export default function HomePage() {
156
- // Filter state belongs in the URL rather than `useState`: it survives a
157
- // reload, and a pasted link reproduces exactly what you were looking at.
158
- const [range, setRange] = useSearchParam("range", "30d")
159
-
160
- // `search` goes into the query string *and* the cache key, so changing the
161
- // range refetches instead of serving the previous window.
162
- const summary = useApiQuery<KpiSummary>("/api/kpi-summary", { search: { range } })
163
-
164
- return (
165
- <PageShell>
166
- <PageShellHeader>
167
- <PageShellHeading>
168
- <PageShellTitle>[Template] Performance overview</PageShellTitle>
169
- <PageShellDescription>
170
- Key metrics and trends for the selected period.
171
- </PageShellDescription>
172
- </PageShellHeading>
173
- <PageShellHeaderEnd>
174
- <SegmentedControl
175
- options={RANGE_OPTIONS}
176
- value={range}
177
- onChange={setRange}
178
- ariaLabel="Period"
179
- />
180
- </PageShellHeaderEnd>
181
- </PageShellHeader>
182
-
183
- <PageShellContent className="space-y-6">
184
- {summary.isPending ? (
185
- <Loading />
186
- ) : summary.isError ? (
187
- // `summary.error` is an `ApiError`: `message` is the text the handler
188
- // passed to `HttpError`, so there is no cast and no generic wording.
189
- <ErrorState
190
- title="Could not load the dashboard"
191
- message={summary.error.message}
192
- action={<Button onClick={() => void summary.refetch()}>Retry</Button>}
193
- />
194
- ) : (
195
- <DashboardBody data={summary.data} />
196
- )}
197
- </PageShellContent>
198
- </PageShell>
199
- )
200
- }
@@ -104,6 +104,53 @@ const RANGE_OPTIONS: SegmentOption[] = [
104
104
  { label: "90日", value: "90d" },
105
105
  ]
106
106
 
107
+ export default function HomePage() {
108
+ // 絞り込み状態は useState ではなく URL に置く。リロードで消えず、URL を
109
+ // そのまま共有すれば相手も同じ画面を見られる。
110
+ const [range, setRange] = useSearchParam("range", "30d")
111
+
112
+ // search はクエリ文字列とキャッシュキーの両方に入るので、期間を変えると
113
+ // 前の期間のデータを返さずに再取得される。
114
+ const summary = useApiQuery<KpiSummary>("/api/kpi-summary", { search: { range } })
115
+
116
+ return (
117
+ <PageShell>
118
+ <PageShellHeader>
119
+ <PageShellHeading>
120
+ <PageShellTitle>[テンプレート] パフォーマンス概要</PageShellTitle>
121
+ <PageShellDescription>
122
+ 選択期間の主要指標とトレンド。
123
+ </PageShellDescription>
124
+ </PageShellHeading>
125
+ <PageShellHeaderEnd>
126
+ <SegmentedControl
127
+ options={RANGE_OPTIONS}
128
+ value={range}
129
+ onChange={setRange}
130
+ ariaLabel="期間"
131
+ />
132
+ </PageShellHeaderEnd>
133
+ </PageShellHeader>
134
+
135
+ <PageShellContent className="space-y-6">
136
+ {summary.isPending ? (
137
+ <Loading />
138
+ ) : summary.isError ? (
139
+ // summary.error は ApiError。message にはハンドラが HttpError に
140
+ // 渡した文言がそのまま入るので、キャストも汎用文言も要らない。
141
+ <ErrorState
142
+ title="ダッシュボードを読み込めませんでした"
143
+ message={summary.error.message}
144
+ action={<Button onClick={() => void summary.refetch()}>再試行</Button>}
145
+ />
146
+ ) : (
147
+ <DashboardBody data={summary.data} />
148
+ )}
149
+ </PageShellContent>
150
+ </PageShell>
151
+ )
152
+ }
153
+
107
154
  function DashboardBody({ data }: { data: KpiSummary }) {
108
155
  return (
109
156
  <>
@@ -151,50 +198,3 @@ function DashboardBody({ data }: { data: KpiSummary }) {
151
198
  </>
152
199
  )
153
200
  }
154
-
155
- export default function HomePage() {
156
- // 絞り込み状態は useState ではなく URL に置く。リロードで消えず、URL を
157
- // そのまま共有すれば相手も同じ画面を見られる。
158
- const [range, setRange] = useSearchParam("range", "30d")
159
-
160
- // search はクエリ文字列とキャッシュキーの両方に入るので、期間を変えると
161
- // 前の期間のデータを返さずに再取得される。
162
- const summary = useApiQuery<KpiSummary>("/api/kpi-summary", { search: { range } })
163
-
164
- return (
165
- <PageShell>
166
- <PageShellHeader>
167
- <PageShellHeading>
168
- <PageShellTitle>[テンプレート] パフォーマンス概要</PageShellTitle>
169
- <PageShellDescription>
170
- 選択期間の主要指標とトレンド。
171
- </PageShellDescription>
172
- </PageShellHeading>
173
- <PageShellHeaderEnd>
174
- <SegmentedControl
175
- options={RANGE_OPTIONS}
176
- value={range}
177
- onChange={setRange}
178
- ariaLabel="期間"
179
- />
180
- </PageShellHeaderEnd>
181
- </PageShellHeader>
182
-
183
- <PageShellContent className="space-y-6">
184
- {summary.isPending ? (
185
- <Loading />
186
- ) : summary.isError ? (
187
- // summary.error は ApiError。message にはハンドラが HttpError に
188
- // 渡した文言がそのまま入るので、キャストも汎用文言も要らない。
189
- <ErrorState
190
- title="ダッシュボードを読み込めませんでした"
191
- message={summary.error.message}
192
- action={<Button onClick={() => void summary.refetch()}>再試行</Button>}
193
- />
194
- ) : (
195
- <DashboardBody data={summary.data} />
196
- )}
197
- </PageShellContent>
198
- </PageShell>
199
- )
200
- }