@squadbase/vantage 0.5.3-dev.71d2349 → 0.6.0-dev.3cc6138

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 (120) hide show
  1. package/NOTICE +73 -0
  2. package/README.md +3 -3
  3. package/dist/add-S62E5MSP.js +6 -0
  4. package/dist/add-S62E5MSP.js.map +1 -0
  5. package/dist/{build-756WIEVI.js → build-XXKKX6KB.js} +10 -10
  6. package/dist/{build-756WIEVI.js.map → build-XXKKX6KB.js.map} +1 -1
  7. package/dist/{check-KZ5PHCA6.js → check-X2TDOILH.js} +8 -8
  8. package/dist/{check-KZ5PHCA6.js.map → check-X2TDOILH.js.map} +1 -1
  9. package/dist/{chunk-L3TW4FNA.js → chunk-46PPUW6Y.js} +34 -13
  10. package/dist/chunk-46PPUW6Y.js.map +1 -0
  11. package/dist/chunk-5ZOP4SQR.js +47 -0
  12. package/dist/chunk-5ZOP4SQR.js.map +1 -0
  13. package/dist/{chunk-BAM33YZ6.js → chunk-B45WAO5Q.js} +6 -5
  14. package/dist/chunk-B45WAO5Q.js.map +1 -0
  15. package/dist/{chunk-HQDEYCDP.js → chunk-CIJWVFJD.js} +3 -3
  16. package/dist/{chunk-HQDEYCDP.js.map → chunk-CIJWVFJD.js.map} +1 -1
  17. package/dist/chunk-FVWU275X.js +91 -0
  18. package/dist/chunk-FVWU275X.js.map +1 -0
  19. package/dist/{chunk-VXAV2435.js → chunk-KLEIVDUV.js} +30 -7
  20. package/dist/chunk-KLEIVDUV.js.map +1 -0
  21. package/dist/{chunk-RA6Z3OX3.js → chunk-LCN36MYX.js} +61 -9
  22. package/dist/chunk-LCN36MYX.js.map +1 -0
  23. package/dist/{chunk-JC6MT5UU.js → chunk-MXI3PZFT.js} +3 -3
  24. package/dist/{chunk-JC6MT5UU.js.map → chunk-MXI3PZFT.js.map} +1 -1
  25. package/dist/chunk-PLST5Q7T.js +2221 -0
  26. package/dist/chunk-PLST5Q7T.js.map +1 -0
  27. package/dist/chunk-PVTHHV7X.js +105 -0
  28. package/dist/chunk-PVTHHV7X.js.map +1 -0
  29. package/dist/{chunk-EHPJXDJU.js → chunk-SVNOWE5U.js} +4 -2
  30. package/dist/chunk-SVNOWE5U.js.map +1 -0
  31. package/dist/{chunk-C4MQRWXS.js → chunk-TDUIW7LD.js} +17 -3
  32. package/dist/chunk-TDUIW7LD.js.map +1 -0
  33. package/dist/{chunk-X44ZTKID.js → chunk-V2FV5OJ6.js} +61 -9
  34. package/dist/chunk-V2FV5OJ6.js.map +1 -0
  35. package/dist/{chunk-PSEYFYAP.js → chunk-VVOXG6ZU.js} +68 -8
  36. package/dist/chunk-VVOXG6ZU.js.map +1 -0
  37. package/dist/{chunk-D4Y5RWJA.js → chunk-YE4CPCXX.js} +81 -21
  38. package/dist/chunk-YE4CPCXX.js.map +1 -0
  39. package/dist/cli.js +15 -14
  40. package/dist/cli.js.map +1 -1
  41. package/dist/client/index.d.ts +5 -35
  42. package/dist/client/index.js +1 -1
  43. package/dist/components/index.js +3 -2212
  44. package/dist/components/index.js.map +1 -1
  45. package/dist/{dev-EILX2VTF.js → dev-4MQQDHA4.js} +10 -10
  46. package/dist/{dev-EILX2VTF.js.map → dev-4MQQDHA4.js.map} +1 -1
  47. package/dist/{docs-GP2ZHIQF.js → docs-OTFWTCPW.js} +4 -4
  48. package/dist/{docs-GP2ZHIQF.js.map → docs-OTFWTCPW.js.map} +1 -1
  49. package/dist/{doctor-FXZLMGC3.js → doctor-QXA6DURZ.js} +8 -8
  50. package/dist/{doctor-FXZLMGC3.js.map → doctor-QXA6DURZ.js.map} +1 -1
  51. package/dist/manifest-ttGMFi-Z.d.ts +43 -0
  52. package/dist/{preview-WBQPCTTT.js → preview-HVOM27FS.js} +4 -4
  53. package/dist/{preview-WBQPCTTT.js.map → preview-HVOM27FS.js.map} +1 -1
  54. package/dist/query/index.js +1 -102
  55. package/dist/query/index.js.map +1 -1
  56. package/dist/router/index.d.ts +10 -0
  57. package/dist/router/index.js +2 -89
  58. package/dist/router/index.js.map +1 -1
  59. package/dist/{routes-WCN4KSZY.js → routes-2QRAKMGK.js} +17 -8
  60. package/dist/routes-2QRAKMGK.js.map +1 -0
  61. package/dist/{search-GLKCJXSI.js → search-7A43AZ7M.js} +4 -4
  62. package/dist/{search-GLKCJXSI.js.map → search-7A43AZ7M.js.map} +1 -1
  63. package/dist/server/node.d.ts +1 -1
  64. package/dist/server/node.js +6 -1
  65. package/dist/server/node.js.map +1 -1
  66. package/dist/slide/index.d.ts +230 -0
  67. package/dist/slide/index.js +1571 -0
  68. package/dist/slide/index.js.map +1 -0
  69. package/dist/table/index.d.ts +107 -0
  70. package/dist/table/index.js +680 -0
  71. package/dist/table/index.js.map +1 -0
  72. package/dist/types-DQjLpLCs.d.ts +121 -0
  73. package/dist/ui/index.js +3 -41
  74. package/dist/ui/index.js.map +1 -1
  75. package/dist/{upgrade-S64IXL42.js → upgrade-2QCP7UQQ.js} +9 -9
  76. package/dist/upgrade-2QCP7UQQ.js.map +1 -0
  77. package/dist/vite/index.d.ts +1 -1
  78. package/dist/vite/index.js +5 -5
  79. package/docs/en/agent-skills.md +19 -12
  80. package/docs/en/changelog.md +77 -0
  81. package/docs/en/cli-reference.md +8 -4
  82. package/docs/en/pages-and-metadata.md +17 -0
  83. package/docs/en/routing.md +75 -3
  84. package/docs/en/ui-and-theming.md +76 -0
  85. package/docs/index.json +2 -2
  86. package/docs/ja/agent-skills.md +18 -11
  87. package/docs/ja/changelog.md +75 -0
  88. package/docs/ja/cli-reference.md +8 -4
  89. package/docs/ja/pages-and-metadata.md +17 -0
  90. package/docs/ja/routing.md +71 -3
  91. package/docs/ja/ui-and-theming.md +74 -0
  92. package/package.json +17 -3
  93. package/skills/vantage-app/SKILL.md +4 -0
  94. package/skills/vantage-pitfalls/SKILL.md +41 -1
  95. package/skills/vantage-slide/SKILL.md +264 -0
  96. package/skills/vantage-slide/references/morph.md +102 -0
  97. package/skills/vantage-slide/references/steps.md +57 -0
  98. package/skills/vantage-slide/references/tokens.md +87 -0
  99. package/skills/vantage-slide/references/transitions.md +196 -0
  100. package/templates/AGENTS.md +28 -230
  101. package/templates/agents/api.md +49 -0
  102. package/templates/agents/csv.md +32 -0
  103. package/templates/agents/page.md +185 -0
  104. package/templates/agents/slide.md +34 -0
  105. package/templates/agents/xlsx.md +33 -0
  106. package/theme.css +28 -0
  107. package/dist/add-PPCBMWEN.js +0 -6
  108. package/dist/add-PPCBMWEN.js.map +0 -1
  109. package/dist/chunk-BAM33YZ6.js.map +0 -1
  110. package/dist/chunk-C4MQRWXS.js.map +0 -1
  111. package/dist/chunk-D4Y5RWJA.js.map +0 -1
  112. package/dist/chunk-EHPJXDJU.js.map +0 -1
  113. package/dist/chunk-L3TW4FNA.js.map +0 -1
  114. package/dist/chunk-PSEYFYAP.js.map +0 -1
  115. package/dist/chunk-RA6Z3OX3.js.map +0 -1
  116. package/dist/chunk-VXAV2435.js.map +0 -1
  117. package/dist/chunk-X44ZTKID.js.map +0 -1
  118. package/dist/routes-WCN4KSZY.js.map +0 -1
  119. package/dist/types-De3mo_hv.d.ts +0 -59
  120. package/dist/upgrade-S64IXL42.js.map +0 -1
@@ -43,6 +43,23 @@ export default function MonthlyAnalysis() {
43
43
  > `export const page = definePage({...})` の**エクスポート名は `page` 固定**です。別名にすると
44
44
  > メタデータが読まれません。
45
45
 
46
+ ## データファイルページのタイトル
47
+
48
+ `.csv` / `.xlsx` の[ファイルページ](routing#tsx-以外もページになる)に `definePage` は書けないので、
49
+ タイトルは**ファイル名から導かれます**。
50
+
51
+ | ファイル | `title` |
52
+ |---|---|
53
+ | `june.xlsx` | `June` |
54
+ | `sales_by_region.csv` | `Sales By Region` |
55
+ | `salesByRegion.csv` | `Sales By Region` |
56
+ | `kpi/index.csv` | `KPI`(`index` はディレクトリ名を借りる。全大文字は略語とみなして保つ) |
57
+ | ルート直下の `index.csv` | なし(`definePage` の無いルート `index.tsx` と同じ) |
58
+
59
+ `navLabel` は設定されないので、ナビの表示名も同じ値になります。名前を変えたいときは
60
+ **ファイルをリネーム**するか、`_` を前置してルート化を止め、tsx から `FilePageView` で描いて
61
+ そこに `definePage` を書きます。
62
+
46
63
  ## Fast Refresh との関係
47
64
 
48
65
  `page` のような非コンポーネントのエクスポートがあっても、Vantage はそれを内部で扱うため
@@ -1,6 +1,6 @@
1
1
  # ファイルベースルーティング
2
2
 
3
- > ファイルを置くだけでルートが生まれる。ページ・レイアウト・動的パラメータ・404/error の規約。
3
+ > ファイルを置くだけでルートが生まれる。ページ・レイアウト・動的パラメータ・404/error の規約と、tsx 以外のページ(csv / xlsx / スライド)。
4
4
 
5
5
  Vantage のルーティングは **ファイルの配置がそのまま URL** になります。ルーターの設定は書きません。
6
6
 
@@ -35,11 +35,75 @@ Vantage のルーティングは **ファイルの配置がそのまま URL**
35
35
  | `_layout.tsx` | そのディレクトリ配下のレイアウト(入れ子可) |
36
36
  | `_404.tsx` | not-found の UI(**ルートのみ**) |
37
37
  | `_error.tsx` | error の UI(**ルートのみ**) |
38
+ | `monthly-report.slide.tsx` | `/monthly-report`(スライドデッキ) |
39
+ | `sales-export.csv` | `/sales-export`(表として描かれる) |
40
+ | `data/june.xlsx` | `/data/june`(同上) |
38
41
 
39
42
  - `[name]` → 動的パラメータ、`[...name]` → catchall。
40
43
  - `index` は自身のディレクトリにマップ(名前は URL から落ちる)。
41
44
  - `_` で始まるファイルは予約(`_layout`/`_404`/`_error`)またはプライベート(ルーティングされない)。
42
45
 
46
+ ## tsx 以外もページになる
47
+
48
+ ルートになるのは `.tsx` / `.jsx` だけではありません。**`.csv` と `.xlsx`** はそのまま表として
49
+ 読めるページになり、**`<name>.slide.tsx`** はスライドデッキになります。どちらも URL の作り方は
50
+ tsx と同じで、`vantage routes` にも `useRoutes()` のナビにも同格で並びます。
51
+
52
+ ### データファイル(`.csv` / `.xlsx`)
53
+
54
+ `src/data/june.xlsx` を置いて `/data/june` を開くと、ダウンロードせずブラウザ上で表として
55
+ 読めます。xlsx は複数シートを切り替えられ、元ファイルをダウンロードする導線も付きます。
56
+
57
+ 描かれるのは**表計算ソフトそのものの見た目**です —— 列は A / B / C、左に行番号、ファイルの
58
+ 1 行目はヘッダとして固定され、罫線はデータの外まで続きます。シートのタブは下端に並びます。
59
+ 読み取り専用なので編集もソートもできません(セルを選んで `Cmd/Ctrl+C` でコピーはできます)。
60
+ 並べ替えや絞り込みが要るデータは、`server/api/` から配って `DataTable` で描いてください。
61
+
62
+ - **タイトルはファイル名から導かれます**(`june.xlsx` → 「June」、`index.csv` はディレクトリ名)。
63
+ データファイルに `definePage` は書けないので、名前を変えたいときはファイルをリネームします。
64
+ - **パースはブラウザで行われます。** dev・ビルド後・デプロイ後で同じコードが同じバイトを読むので、
65
+ 同じ URL が同じものを返します。25MB を超えるファイルはそのページにエラーが出ます
66
+ (`vantage check` が `FILE_PAGE_TOO_LARGE` で先に警告します)。
67
+ - 数式・グラフ・セル書式は再現されません。**値として読めれば十分**という設計です。
68
+ - ページにしたくないファイルは `_` を前置します(`_june.xlsx`)。tsx から自分で描きたいときは
69
+ こうしてルート化を止め、`@squadbase/vantage/table` の `FilePageView` を使います。
70
+
71
+ > [!WARNING]
72
+ > **ビューアの無い拡張子はルートになりません。** `.xls` / `.ods` / `.tsv` などを置いても
73
+ > 404 にすらならず、ナビにも `vantage routes` にも出ません。`vantage check` の
74
+ > `UNSUPPORTED_PAGE_FILE` 警告が唯一の告知点です。
75
+
76
+ ### スライド(`.slide.tsx`)
77
+
78
+ `.slide` は**ページ種別サフィックス**で、URL には出ません(`src/june.slide.tsx` → `/june`)。
79
+ 中身は `<SlideDeck>` を返す普通の React コンポーネントなので、`definePage` もナビも
80
+ 通常のページと同じように効きます。
81
+
82
+ ```tsx
83
+ import { definePage } from "@squadbase/vantage";
84
+ import { Slide, SlideDeck } from "@squadbase/vantage/slide";
85
+
86
+ export const page = definePage({ title: "6月度レビュー", navLabel: "6月度" });
87
+
88
+ export default function Deck() {
89
+ return (
90
+ <SlideDeck>
91
+ <Slide>…</Slide>
92
+ <Slide>…</Slide>
93
+ </SlideDeck>
94
+ );
95
+ }
96
+ ```
97
+
98
+ 1920×1080 の固定キャンバスに書き、画面サイズに合わせて縮小されます。キーボードとクリックで
99
+ 送れ、左のスライド一覧は `L` で開閉、`F` で全画面、ツールバーから pptx を書き出せます。詳しくは
100
+ [UI とテーマ](ui-and-theming)。
101
+
102
+ > [!WARNING]
103
+ > `.slide` を付けられるのは**ルートになるページモジュールだけ**です。`_layout.slide.tsx` や
104
+ > `deck.slide.ts` は `INVALID_PAGE_VARIANT` エラーになります。また `june.slide.tsx` と
105
+ > `june.tsx`、`sales.csv` と `sales.tsx` はいずれも同じ URL に解決するので `ROUTE_CONFLICT` です。
106
+
43
107
  ## ルーティングされないディレクトリ
44
108
 
45
109
  次のディレクトリは(どの階層でも)決して走査されません。ここには自由にコードを置けます。
@@ -149,6 +213,8 @@ export default function RootLayout() {
149
213
  | `index` | `boolean` | ディレクトリの index ルートか |
150
214
  | `label` | `string` | `navLabel` → `title` → `path` の順で決まる表示名 |
151
215
  | `title` / `description` / `navLabel` | `string \| undefined` | [`definePage`](pages-and-metadata) の値 |
216
+ | `kind` | `"module" \| "file"` | 何から描かれるか。`"file"` は `.csv` / `.xlsx` |
217
+ | `variant` | `"slide" \| undefined` | ページ種別サフィックス |
152
218
 
153
219
  `useCurrentRoute()` は今表示中のルートを返します(404 のときは `undefined`)。
154
220
  上の例のような「現在地」判定のほか、パンくずやページ見出しに使えます。
@@ -188,8 +254,10 @@ setSegments((prev) => [...prev, "enterprise"]);
188
254
 
189
255
  ## HMR とルート再生成
190
256
 
191
- `.tsx` ページや `server/api` ファイルを追加・削除すると、開発サーバーがルートを再生成して
192
- フルリロードします。ページ本体を編集した場合は React Fast Refresh がその場で反映します。
257
+ `.tsx` ページ・データファイル・`server/api` ファイルを追加・削除すると、開発サーバーがルートを
258
+ 再生成してフルリロードします。ページ本体を編集した場合は React Fast Refresh がその場で反映します。
259
+ `.csv` / `.xlsx` は**中身を書き換えたときもフルリロード**します(実行時に固定 URL から取得される
260
+ ので、モジュールグラフは変化しないためです)。
193
261
 
194
262
  ## 次に読む
195
263
 
@@ -40,6 +40,80 @@ export default function Overview() {
40
40
 
41
41
  各コンポーネントの props と使い方は [コンポーネント](components) タブにまとめてあります。
42
42
 
43
+ ## 専用サブパスの UI
44
+
45
+ 重い依存を持つ 3 つは、使うアプリだけが取り込むよう別サブパスに隔離してあります。
46
+
47
+ | サブパス | 何が入っているか |
48
+ |---|---|
49
+ | `@squadbase/vantage/markdown` | `MarkdownRenderer`(Shiki の全言語文法を引き込むため隔離) |
50
+ | `@squadbase/vantage/table` | `TableViewer` / `FilePageView`(csv・xlsx のパーサを動的 import) |
51
+ | `@squadbase/vantage/slide` | スライドのプリミティブ(pptx 書き出しを動的 import) |
52
+
53
+ `/components` の barrel に混ぜると、`DataTable` を使うだけのアプリにも import の辺ができて
54
+ バンドラが落とせなくなるためです。
55
+
56
+ ### 表ビューア(`/table`)
57
+
58
+ `.csv` / `.xlsx` を `src/` に置けば[自動でページになります](routing)。自分でタイトルを付けたい、
59
+ ダッシュボードの一部として表を埋めたい、といったときだけ直接使います。
60
+
61
+ ```tsx
62
+ import { FilePageView, TableViewer, SheetGrid, loadWorkbook } from "@squadbase/vantage/table";
63
+ ```
64
+
65
+ | 名前 | 役割 |
66
+ |---|---|
67
+ | `FilePageView` | ファイルページそのもの。URL を渡すと取得・パース・表示まで面倒を見る |
68
+ | `TableViewer` | 1 シートを枠付きで描く(ビューポートの残りを埋め、下端にシートタブと行数) |
69
+ | `SheetGrid` | 表計算ソフトのグリッド本体。高さの決まった親の中に置く |
70
+ | `loadWorkbook` | URL → パース済みのシート配列 |
71
+
72
+ 見た目は**表計算ソフトそのもの**です —— 列は A / B / C、左に行番号、ファイルの 1 行目は
73
+ ヘッダとして固定、罫線はデータの外まで続きます。読み取り専用で、ツールバーもソートも
74
+ ページネーションもありません(セルを選んで `Cmd/Ctrl+C` でコピーはできます)。並べ替えや
75
+ 絞り込みが要るなら、それは表ビューアではなく [`DataTable`](parts/data-table) の
76
+ 仕事です。配色はアプリのテーマトークンで塗られるので、ダークモードにも追従します。
77
+
78
+ ### スライド(`/slide`)
79
+
80
+ `<name>.slide.tsx` を[置くとスライドデッキのページになります](routing)。
81
+
82
+ ```tsx
83
+ import {
84
+ MorphElement, Slide, SlideDeck, Step, Steps, useSlideNumber,
85
+ } from "@squadbase/vantage/slide";
86
+ ```
87
+
88
+ | 名前 | 役割 |
89
+ |---|---|
90
+ | `SlideDeck` | デッキの器。`theme` でトークンを上書き、`embedded` でページ内に埋め込む、`rail={false}` で一覧を閉じた状態から始める |
91
+ | `Slide` | 1 枚。`transition` で enter / exit を宣言する |
92
+ | `Steps` / `Step` | 段階表示。`→` で 1 つずつ開く |
93
+ | `MorphElement` | 2 枚にまたがる同一要素を連続して動かす(同じ `id` を両方に付ける) |
94
+ | `useSlideNumber` | `{ current, total }`。ページ番号のフッター用 |
95
+ | `useIsActiveSlide` | 聴衆が見ているインスタンスかどうか。入場アニメーションの門番に使う |
96
+
97
+ 読み手は見え方を 3 通りに切り替えられます — **一覧 + スライド**(既定。左にサムネイル列)、
98
+ **スライドのみ**(`L` で一覧を閉じ、表示領域いっぱいに描く)、**全画面**(`F`。一覧は退く)。
99
+ どれもコードでは決めず、開閉の状態は `?rail=` に入るのでその状態のリンクを共有できます。
100
+ 閉じた状態から始めたいデッキだけ `<SlideDeck rail={false}>` と書きます(`embedded` は既定で閉)。
101
+ サムネイル列はデッキ自身の `--slide-bg` / `--slide-fg` の混色だけで塗られるので、色味を持たず
102
+ デッキのテーマにそのまま馴染みます。
103
+
104
+ スライドは **1920×1080 の固定キャンバス**に絶対 px で書き、画面サイズに合わせて縮小されます。
105
+ 配色と書体は `--slide-*` トークン(下の「テーマトークン」と同じ仕組みで、既定はアプリのトークンへの
106
+ 別名なので**ダークモードにも自動で追従します**)。
107
+
108
+ 書き方の作法 — キャンバスの縦方向の予算、タイプスケール、トランジションの設計原則、モーフの規則、
109
+ pptx 書き出しが壊れる条件 — は同梱の **`vantage-slide` スキル**にあります
110
+ ([エージェント Skill](agent-skills))。
111
+
112
+ > [!NOTE]
113
+ > スライドの中で `@squadbase/vantage/ui` のプリミティブを使わないでください。UI キットは rem と
114
+ > Tailwind でダッシュボード向けに組まれているので、1920×1080 を縮小投影すると `text-sm` のボタンは
115
+ > 実効 8px 程度になって読めません。
116
+
43
117
  ## Tailwind はそのまま使える
44
118
 
45
119
  `className` に Tailwind ユーティリティを書くだけで効きます。`tailwind.config` も `postcss.config`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squadbase/vantage",
3
- "version": "0.5.3-dev.71d2349",
3
+ "version": "0.6.0-dev.3cc6138",
4
4
  "description": "Data dashboard framework for Squadbase Editor. Vite-powered, config-free, one-file first.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -59,6 +59,14 @@
59
59
  "types": "./dist/markdown/index.d.ts",
60
60
  "default": "./dist/markdown/index.js"
61
61
  },
62
+ "./table": {
63
+ "types": "./dist/table/index.d.ts",
64
+ "default": "./dist/table/index.js"
65
+ },
66
+ "./slide": {
67
+ "types": "./dist/slide/index.d.ts",
68
+ "default": "./dist/slide/index.js"
69
+ },
62
70
  "./server": {
63
71
  "types": "./dist/server/index.d.ts",
64
72
  "default": "./dist/server/index.js"
@@ -84,7 +92,8 @@
84
92
  "theme.css",
85
93
  "registry",
86
94
  "skills",
87
- "templates"
95
+ "templates",
96
+ "NOTICE"
88
97
  ],
89
98
  "scripts": {
90
99
  "build": "node scripts/build-docs.mjs && node scripts/build.mjs",
@@ -116,10 +125,14 @@
116
125
  "date-fns": "^4.1.0",
117
126
  "echarts": "^6.0.0",
118
127
  "esbuild": "^0.28.1",
119
- "hono": "^4.12.30",
128
+ "fflate": "^0.8.3",
129
+ "hono": "^4.13.5",
130
+ "html-to-image": "^1.11.13",
120
131
  "lucide-react": "^0.468.0",
132
+ "papaparse": "^5.7.0",
121
133
  "picocolors": "^1.1.1",
122
134
  "react-day-picker": "^10.0.1",
135
+ "read-excel-file": "^9.3.10",
123
136
  "streamdown": "^2.5.0",
124
137
  "tailwind-merge": "^3.0.0",
125
138
  "tailwindcss": "^4.3.2",
@@ -132,6 +145,7 @@
132
145
  },
133
146
  "devDependencies": {
134
147
  "@types/node": "^22.10.0",
148
+ "@types/papaparse": "^5.5.2",
135
149
  "@types/react": "^19.2.0",
136
150
  "@types/react-dom": "^19.2.0",
137
151
  "react": "^19.2.7",
@@ -75,6 +75,10 @@ vantage add skill # 配置済みの skill(このファイルの仲間)と、そ
75
75
  - 共通の枠(ヘッダ・サイドバー)は `_layout.tsx`、Not Found と例外は `_404.tsx` / `_error.tsx`。
76
76
  - 一覧 → 詳細を作るなら、**動的パラメータの「3 つの綴り」を先に決めてから**両方のファイルを
77
77
  書く。後から変えると静かにマッチしなくなる。
78
+ - **ページになるのは tsx だけではない。** `.csv` / `.xlsx` を `src/` に置けば表として読めるページに
79
+ なり、`<name>.slide.tsx` はスライドデッキになる(どちらもナビに同格で並ぶ)。ダッシュボード・
80
+ スライド・データを 1 つのアプリに並べたいときは、まずこの 2 つで足りないか考える —— 表を出すために
81
+ ページを書く必要は無い。スライドの書き方は `vantage-slide` スキル。
78
82
 
79
83
  ## Step 3 — データを繋ぐ
80
84
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: vantage-pitfalls
3
- description: Vantage(@squadbase/vantage)アプリを書くときに静かに壊れる落とし穴のリファレンス。Base UI は Radix ではない(asChild 無し・render プロップ)、Select の value/label、クライアントから server/ を import しない、EChart の配色と高さ、動的パラメータの3綴り同期、PUBLIC_ env、HttpError など。Vantage の UI コンポーネントやルーティングが思った通りに動かないとき、ビルドやスタイルが静かに欠落するときに参照する。
3
+ description: Vantage(@squadbase/vantage)アプリを書くときに静かに壊れる落とし穴のリファレンス。Base UI は Radix ではない(asChild 無し・render プロップ)、Select の value/label、クライアントから server/ を import しない、EChart の配色と高さ、動的パラメータの3綴り同期、PUBLIC_ env、HttpError、csv/xlsx ページの URL 衝突とサイズ上限、スライドの data-waitfor など。Vantage の UI コンポーネントやルーティングが思った通りに動かないとき、ビルドやスタイルが静かに欠落するときに参照する。
4
4
  ---
5
5
 
6
6
  # Vantage アプリの落とし穴リファレンス
@@ -153,6 +153,46 @@ import { type ColumnDef } from "@squadbase/vantage/components"
153
153
  const columns: ColumnDef<Row>[] = [ … ] // 注釈しないと accessorKey が string に広がり弾かれる
154
154
  ```
155
155
 
156
+ ## データファイルページ:同名の tsx と衝突する / ブラウザでパースされる
157
+
158
+ `src/` に置いた `.csv` / `.xlsx` はページになる。ここで静かに壊れるのは 3 つ。
159
+
160
+ - **`sales.csv` と `sales.tsx` はどちらも `/sales`** に解決するので `ROUTE_CONFLICT` エラー。
161
+ `.slide.tsx` も同じ(`june.slide.tsx` と `june.tsx`)。
162
+ - **パースはブラウザで走る。** 25MB を超えるファイルはそのページがエラー表示になる
163
+ (`vantage check` が `FILE_PAGE_TOO_LARGE` で先に警告する)。大きいデータは `server/api/` から
164
+ 配って `DataTable` で描く。
165
+ - **ビューアの無い拡張子(`.xls`・`.ods`・`.tsv` など)はルートにならない。** 404 ですらなく、
166
+ ナビにも `vantage routes` にも出ない。`vantage check` の `UNSUPPORTED_PAGE_FILE` 警告が
167
+ 唯一の告知点なので、「置いたのに出てこない」ときはまず `check` を見る。
168
+
169
+ ビューアは**表計算ソフトの見た目の読み取り専用グリッド**(列は A / B / C、左に行番号、1 行目は
170
+ 固定、シートのタブは下端)。**ソート・フィルタ・ページネーションは持たない** —— 並べ替えや
171
+ 絞り込みが要るなら `server/api/` から配って `DataTable` で描く。数式・グラフ・セル書式は
172
+ 再現されない(値として読めれば十分、という設計)。
173
+
174
+ ## スライド:`data-waitfor` を忘れるとチャートが白紙で書き出される
175
+
176
+ `.slide.tsx` の pptx 書き出しはスライドを画像として焼く。**canvas に描くもの(`EChart`)は
177
+ 描き終わりを待たせないと、描画前に撮られてそのスライドだけ白紙になる**:
178
+
179
+ ```tsx
180
+ <div data-waitfor="canvas">
181
+ <EChart option={option} style={{ width: 1400, height: 600 }} />
182
+ </div>
183
+ ```
184
+
185
+ ほかに 2 つ。
186
+
187
+ - **`@squadbase/vantage/ui` のプリミティブをスライドに置かない。** UI キットは rem と Tailwind で
188
+ 組まれているので、1920×1080 を縮小投影すると `text-sm` のボタンが実効 8px 程度になって読めない。
189
+ スライドは絶対 px のインラインスタイルで書く。
190
+ - **dev サーバーを起動したあとに最初の `.slide.tsx` を新規作成した場合、初回の pptx 書き出しで
191
+ 1 回だけリロードが入る。** 書き出し用の依存はプロジェクトがデッキを持つときだけ宣言される仕組みで、
192
+ その判定は dev の起動時に 1 度しか走らないため。`vantage dev` を再起動すれば解消する。
193
+
194
+ 書き方の作法(キャンバスの縦方向の予算、段階表示、トランジション、モーフ)は `vantage-slide` スキル。
195
+
156
196
  ## 生成物:`.vantage/` と `dist/` は触らない
157
197
 
158
198
  どちらも gitignore 済みの生成物。編集しても次のコマンドで上書きされる。`.vantage/` は任意の CLI
@@ -0,0 +1,264 @@
1
+ ---
2
+ name: vantage-slide
3
+ description: Vantage(@squadbase/vantage)で `.slide.tsx` のスライドデッキを書く / 直すときの技術リファレンス。ファイル契約、1920×1080 固定キャンバスと縦方向の予算、タイプスケールとレイアウト、デザイントークン、段階表示(Steps/Step)、ページ間トランジションとモーフ、pptx 書き出しの前提条件を扱う。`src/**/*.slide.tsx` を書く・触る前に読む。「スライドを作って」「デッキを1本」「この頁のレイアウトを直して」「1つずつ出したい」「pptx で出したい」で起動。
4
+ ---
5
+
6
+ # `.slide.tsx` でスライドを書く
7
+
8
+ このスキルは**技術リファレンス**です。Vantage アプリの一般規約(ページの置き場所・import サブパス・
9
+ 禁止ファイル)は `AGENTS.md` が正本で、ここでは繰り返しません。スライド固有のことだけを扱います。
10
+
11
+ 関連: 新しいページ / API / UI を 1 つ足す定型は `vantage-add-feature`、静かに壊れる落とし穴の
12
+ 一覧は `vantage-pitfalls`。
13
+
14
+ ## プリミティブのリファレンス
15
+
16
+ 各プリミティブには `references/` に個別のファイルがあります。**使う前にそのファイルを読むこと。**
17
+
18
+ | プリミティブ | 読むべき場面 | ファイル |
19
+ | --- | --- | --- |
20
+ | `<Steps>` / `<Step>` | 段階表示を作るとき | `references/steps.md` |
21
+ | `SlideTransition` | enter / exit のアニメーションを書くとき | `references/transitions.md` |
22
+ | `MorphElement` + `morph` | 2 枚にまたがる同一要素を動かすとき | `references/morph.md` |
23
+ | `<SlideDeck theme>` / `var(--slide-*)` / webfont | 配色・書体を決めるとき、pptx を出すとき | `references/tokens.md` |
24
+
25
+ ## ファイル契約
26
+
27
+ ```tsx
28
+ // src/june.slide.tsx → /june(`.slide` は URL に出ない)
29
+ import { definePage } from "@squadbase/vantage"
30
+ import { Slide, SlideDeck, Step, Steps, useSlideNumber } from "@squadbase/vantage/slide"
31
+
32
+ export const page = definePage({ title: "6月度レビュー", navLabel: "6月度" })
33
+
34
+ export default function JuneDeck() {
35
+ return (
36
+ <SlideDeck theme={{ bg: "#0f172a", fg: "#f8fafc", accent: "#fbbf24" }}>
37
+ <Slide>…</Slide>
38
+ <Slide transition={RISE}>…</Slide>
39
+ </SlideDeck>
40
+ )
41
+ }
42
+ ```
43
+
44
+ - **1 ファイル = 1 デッキ = 1 ルート。** `export default` は `<SlideDeck>` を返す React コンポーネント
45
+ (通常のページと同じ契約なので、`vantage check` も `vantage routes` も特別扱いをしません)。
46
+ - **スライドは `<SlideDeck>` の直下の `<Slide>`。** 深くネストした `<Slide>` は拾われません。
47
+ - `definePage` の `title` / `navLabel` はビルド時に静的抽出されるので**リテラルで書く**(変数・
48
+ テンプレート補間は不可)。ナビの表示名はこれで決まります。
49
+ - **`.slide` を付けられるのはルートになるページモジュールだけ。** `_layout.slide.tsx` や
50
+ `deck.slide.ts` は `INVALID_PAGE_VARIANT` エラーになります。
51
+ - **`june.slide.tsx` と `june.tsx` は同じ URL** になるので `ROUTE_CONFLICT` です。どちらかを消すこと。
52
+ - **デッキファイル以外を作らない。** ヘルパーコンポーネントも定数も同じ `.slide.tsx` の中に書く。
53
+ スライド専用のコンポーネントを `components/` に切り出すと、デッキ 1 本を読むのに 2 ファイル
54
+ 追わせることになります。
55
+
56
+ ## import してよいもの
57
+
58
+ - `@squadbase/vantage/slide` — `SlideDeck` `Slide` `Steps` `Step` `MorphElement` `useSlideNumber`
59
+ `useIsActiveSlide` `SlideCanvas` と、その型
60
+ - `@squadbase/vantage` — `definePage`
61
+ - `@squadbase/vantage/components` の `EChart` — **チャートを載せるときはこれ**(→「チャート」)
62
+ - `lucide-react` のアイコン
63
+
64
+ **`@squadbase/vantage/ui` のプリミティブはスライドに使わない。** UI キットは rem と Tailwind で
65
+ ダッシュボード向けに組まれているので、1920×1080 を縮小投影すると `text-sm` のボタンは実効 8px 程度に
66
+ なって読めません。スライドは**絶対 px のインラインスタイル**で書きます。
67
+
68
+ ## キャンバス
69
+
70
+ 各スライドは **1920 × 1080** の固定キャンバスに描かれ、フレームワークが画面に合わせて縮小します。
71
+ ビューポートが文字どおり 1920×1080 だと思って設計してください。
72
+
73
+ - `font-size`・padding・位置は**絶対 px**。`rem`・`vw`/`vh`・文字サイズの `%` は使わない
74
+ - 各スライドのルート要素はキャンバスを埋める(`width: "100%"; height: "100%"`)
75
+ - インライン `style={{ … }}` を使う。読み込んだ CSS はグローバルです
76
+
77
+ ### タイプスケール(ここから始めて調整する)
78
+
79
+ | 要素 | サイズ |
80
+ | --- | --- |
81
+ | ヒーロータイトル | 140–200px |
82
+ | セクション見出し | 80–120px |
83
+ | ページ見出し | 56–80px |
84
+ | 本文 | 32–44px |
85
+ | キャプション / ラベル | 22–28px |
86
+
87
+ ### 余白
88
+
89
+ - コンテンツの padding: キャンバス端から **100–160px**。文字を端に触れさせない
90
+ - line-height: 見出し 1.2、本文 1.5–1.7
91
+ - 要素間: 32–64px
92
+
93
+ ### 縦方向の予算 — 中身は 1080px に収まらなければならない
94
+
95
+ キャンバスは**スクロールしません**。1080px の下端を越えたものは黙って切れます。JSX を書く前に
96
+ 計算して収まることを確かめること。**これがスライドが壊れる原因の第 1 位**で、確かめない限り
97
+ 溢れていると思ってください。
98
+
99
+ **使える高さ** = `1080 − 上 padding − 下 padding`。上下 120px なら **840px**、160px なら **760px**。
100
+ 先に padding を決めて、その予算の中で設計します。
101
+
102
+ **要素の高さ** = `文字サイズ × line-height × 行数`。2 行に折り返す箇条書きは 2 行と数えます。
103
+ 次の要素を足す前に、その下の gap(32–64px)を加算してください。
104
+
105
+ **計算例 — 本文 1 枚、padding 120px(予算 840px):**
106
+
107
+ | 要素 | 高さ |
108
+ | --- | --- |
109
+ | 見出し: 80px × 1.2 × 1 行 | 96px |
110
+ | gap | 64px |
111
+ | 本文: 40px × 1.6 × 3 行 | 192px |
112
+ | gap | 48px |
113
+ | 箇条書き 5 行: 40px × 1.6 × 1 行 | 320px(5 × 64px) |
114
+ | 箇条書き間の gap 4 つ: 24px | 96px |
115
+ | **合計** | **816px ✅ 840 に収まる** |
116
+
117
+ 見出しを 120px にするか箇条書きを 6 つにすると溢れます。**書く前に毎ページこの検算をすること。**
118
+
119
+ **ページ単位の規則:**
120
+
121
+ - 「見出し + 本文」か「見出し + 5 つ以内の短い箇条書き」のどちらか。両方は載せない
122
+ - 箇条書きは選んだ文字サイズで 1 行に収まること。折り返すなら短くするか別ページに割る
123
+ - ヒーロータイトル(140–200px)はタイトル + サブタイトル 1 行 + アイブロウまで。それ以外は載せない
124
+ - セクション見出し(80–120px)のページにはほとんど何も足さない
125
+ - 収めるために padding を上げる・スケールの下限より小さい文字にする・本文の line-height を 1.4 未満に
126
+ 詰める、をやりたくなったら**ページを 2 枚に割る**。予算が厳しいときは分割が常に正解
127
+
128
+ **`overflow: auto/scroll`・負のマージン・transform で溢れを隠さないこと。** キャンバスは固定で、
129
+ 切れた中身は消えています。
130
+
131
+ ## ビジュアルの方向
132
+
133
+ 一貫した見た目を選び、全ページで保つこと。
134
+
135
+ - **配色** — 背景 1・本文 1・アクセント 1・ミュート 1。前の 3 つは `<SlideDeck theme>` に入れ、
136
+ それ以外はプレーンな const にする(→ `references/tokens.md`)
137
+ - **書体** — ディスプレイ 1 + 本文 1。指定がなければシステムスタック。見出しは太く(800–900)、
138
+ 本文は通常(400–500)
139
+ - **レイアウトグリッド** — padding を 1 つ決めて(例: 120px)貫く。左揃えは編集的、中央揃えは儀礼的
140
+ - **美学のコミットメント** — 1 つ選ぶ: ミニマル / マキシマル / エディトリアル / レトロ /
141
+ ブルータリスト / ソフト・パステル / ネオン / 紙・印刷。混ぜない
142
+
143
+ ## 繰り返す要素は `map` ではなくコンポーネント
144
+
145
+ カード・ロゴの列・タイル・行など**視覚的に繰り返す**要素は、小さなコンポーネントを定義して
146
+ **1 件につき 1 回インスタンス化**します。データ配列を `map` して 1 つのテンプレートから描かない。
147
+
148
+ ```tsx
149
+ // ✅ 各カードが独自の JSX ノードを持つ
150
+ const Card = ({ label, value }: { label: string; value: string }) => (
151
+ <div style={{ flex: 1 }}>
152
+ <div style={{ fontSize: 28, opacity: 0.6 }}>{label}</div>
153
+ <div style={{ fontSize: 120, fontWeight: 800 }}>{value}</div>
154
+ </div>
155
+ )
156
+
157
+ <div style={{ display: "flex", gap: 96 }}>
158
+ <Card label="売上" value="¥5.2億" />
159
+ <Card label="解約率" value="1.4%" />
160
+ </div>
161
+ ```
162
+
163
+ `map` の本体は**ソース上 1 箇所**なので、あとから 1 枚だけ文言や色を変えたいときに全部が一緒に
164
+ 変わります。コンポーネント定義がレイアウトの単一の正本のまま、インスタンスごとの値だけが
165
+ 呼び出し側に出る形が正しい。
166
+
167
+ これは**視覚要素が繰り返すとき**の話で、データが繰り返すときの話ではありません。純粋な
168
+ テキストの箇条書き(`<ul><li>`)は各 `<li>` が既に独立した JSX ノードなので、そのままで正しい。
169
+
170
+ ## ページ番号
171
+
172
+ フッターに現在位置を出すなら `useSlideNumber()` から読む。**ハードコードしない。**
173
+
174
+ ```tsx
175
+ const { current, total } = useSlideNumber() // current は 1 始まり
176
+ ```
177
+
178
+ スライドを差し込む・並べ替える・消すたびに全フッターを直す羽目になるのを避けられます。
179
+
180
+ ## チャート
181
+
182
+ チャートは `@squadbase/vantage/components` の `EChart` を使います。**canvas に描かれるので、
183
+ pptx 書き出しのために描き終わりを待たせる必要があります**:
184
+
185
+ ```tsx
186
+ <div data-waitfor="canvas">
187
+ <EChart option={option} style={{ width: 1400, height: 600 }} />
188
+ </div>
189
+ ```
190
+
191
+ `data-waitfor` を付けないと、書き出しがチャートの描画前にスクリーンショットを撮り、
192
+ **そのスライドだけチャートが白紙**で出ます。
193
+
194
+ チャートに色をハードコードしないこと(`option.color` / `itemStyle.color` / `theme`)。
195
+ `EChart` はテーマトークンに追従します。
196
+
197
+ ## 実行時に無料で付いてくるもの
198
+
199
+ - `src/june.slide.tsx` → `/june`。ナビ(`useRoutes()`)に通常のページと同格で並ぶ
200
+ - `→ ↓ Space PageDown` で次、`← ↑ PageUp` で前、`Home` / `End` で端へ
201
+ - 画面の左右 30% のクリックでも送れる(中央は不活性。リンク・ボタンの上は素通し)
202
+ - `F` で全画面、`Escape` で全画面を抜ける / デッキから戻る
203
+ - `L` でスライド一覧(左のサムネイル列)を開閉。クリックでその枚へ飛ぶ
204
+ - 現在位置は URL の `?p=` に入る(履歴は汚れない)。`?p=4` のリンクをそのまま共有できる
205
+ - ツールバーから **pptx 書き出し**
206
+ - 画面サイズを変えても縮尺が変わるだけでレイアウトは崩れない
207
+
208
+ 見え方は 3 通りで、切り替えはすべて実行時(コードでは決めない):
209
+
210
+ | 見え方 | 出し方 |
211
+ |---|---|
212
+ | 全画面 | `F` / ツールバー。スライドだけになり、一覧は退く |
213
+ | 一覧 + スライド(既定) | 左にサムネイル、右にスライド |
214
+ | スライドのみ | `L` / ツールバー で一覧を閉じる。表示領域いっぱいに 16:9 を描く |
215
+
216
+ 一覧の開閉は `?rail=` にも入るので、閉じた状態のリンクも共有できます。
217
+ 閉じた状態から始めたいデッキだけ `<SlideDeck rail={false}>` と書きます
218
+ (`embedded` のデッキは幅が無いので既定で閉じています)。サムネイルの配色はデッキ自身の
219
+ `--slide-bg` / `--slide-fg` の混色だけで作られていて、色味は持ちません。
220
+
221
+ ## pptx 書き出しの前提
222
+
223
+ デッキは**画像として**書き出されます。1 スライド = 1 枚の PNG(3840×2160)。文字は入らないので
224
+ 受け取り側にフォントが要らず、日本語も欠けません。代わりに**書き出した pptx の文字は検索・編集
225
+ できません**(ブラウザで見ている間は DOM なので選択できます)。
226
+
227
+ 書き出しが壊れる典型は 2 つ:
228
+
229
+ 1. **`data-waitfor` を付けないチャート**(→「チャート」)
230
+ 2. **サブセットしていない日本語 webfont**(→ `references/tokens.md`)
231
+
232
+ 段階表示は**全部開いた状態**で書き出されます(`<Steps>` はホストが無いと全開)。
233
+
234
+ ## 仕上げ前のセルフレビュー
235
+
236
+ - [ ] `export default` が `<SlideDeck>` を返している
237
+ - [ ] `<Slide>` が `<SlideDeck>` の直下にある
238
+ - [ ] 各スライドのルート要素が `100% × 100%` を埋めている
239
+ - [ ] 文字が padding の内側にある(端に触れていない)
240
+ - [ ] **全ページで(文字サイズ × line-height × 行数)+ gap + padding×2 ≤ 1080px。** 際どいなら分割
241
+ - [ ] 箇条書きが 2 行目に折り返していない
242
+ - [ ] 配色とタイプスケールが全ページで一貫している
243
+ - [ ] 1 ページ 1 メッセージ
244
+ - [ ] 繰り返す視覚要素が `array.map` ではなく明示的な `<Component />` になっている
245
+ - [ ] ページ番号が `useSlideNumber()` から来ている(ハードコードしていない)
246
+ - [ ] チャートが `data-waitfor="canvas"` で包まれている
247
+ - [ ] `<Steps>` を使ったページが、`?p=` で直接開いても完成した絵として読める
248
+ - [ ] トランジションを宣言したなら、全ページが 1 つの family に収まっている(→ `references/transitions.md`)
249
+ - [ ] `definePage` の値がリテラルである
250
+ - [ ] `.slide.tsx` 以外のファイルを作っていない
251
+
252
+ ## アンチパターン
253
+
254
+ - ❌ 文字の壁。1 ページ 40 語を超えたら分割する
255
+ - ❌ キャンバス全面を本文で使う。padding 100–160px を守る
256
+ - ❌ 縦に 1080px を溢れさせる。切れた中身は見えない — 分割する
257
+ - ❌ `overflow: auto` / `scroll` / `hidden` で溢れを「隠す」。バグを隠しただけ
258
+ - ❌ 詰め込むためにスケールの下限より小さい文字にする・padding を 100px 未満にする
259
+ - ❌ 本文 28px 未満。プロジェクタで読めない
260
+ - ❌ `@squadbase/vantage/ui` のプリミティブをスライドに置く(縮小されて読めなくなる)
261
+ - ❌ 依存を追加する。使えるのは `@squadbase/vantage/*` と `lucide-react` と標準 Web API だけ
262
+ - ❌ スライド用のコンポーネントを別ファイルに切り出す
263
+ - ❌ チャートに `data-waitfor="canvas"` を付け忘れる(pptx が白紙になる)
264
+ - ❌ プリミティブを `references/*.md` を読まずに使う