@squadbase/vantage 0.6.2-dev1.54bde93 → 0.6.2-dev1.56da87f

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 (89) hide show
  1. package/dist/{build-A23M4GCH.js → build-U7GO26HR.js} +3 -3
  2. package/dist/{build-A23M4GCH.js.map → build-U7GO26HR.js.map} +1 -1
  3. package/dist/{check-FBHT6W6J.js → check-MRVAIHG5.js} +3 -3
  4. package/dist/{check-FBHT6W6J.js.map → check-MRVAIHG5.js.map} +1 -1
  5. package/dist/{chunk-57GHR5LN.js → chunk-3RJEOP3J.js} +29 -18
  6. package/dist/chunk-3RJEOP3J.js.map +1 -0
  7. package/dist/{chunk-QOXXOFGL.js → chunk-77CISBMG.js} +994 -464
  8. package/dist/chunk-77CISBMG.js.map +1 -0
  9. package/dist/{chunk-JXFCSF3K.js → chunk-EG4PX726.js} +12 -7
  10. package/dist/chunk-EG4PX726.js.map +1 -0
  11. package/dist/{chunk-2N72FKLB.js → chunk-NZ5QIIG4.js} +33 -11
  12. package/dist/chunk-NZ5QIIG4.js.map +1 -0
  13. package/dist/{chunk-5C2SEDPH.js → chunk-PJ4SPXHS.js} +4 -4
  14. package/dist/{chunk-5C2SEDPH.js.map → chunk-PJ4SPXHS.js.map} +1 -1
  15. package/dist/{chunk-7S52M2SL.js → chunk-QNOX7WLZ.js} +3 -3
  16. package/dist/{chunk-7S52M2SL.js.map → chunk-QNOX7WLZ.js.map} +1 -1
  17. package/dist/{chunk-PDOBHMUQ.js → chunk-ROHNXP7J.js} +72 -3
  18. package/dist/chunk-ROHNXP7J.js.map +1 -0
  19. package/dist/{chunk-6OTTGAPO.js → chunk-TRMHNMMZ.js} +24 -7
  20. package/dist/chunk-TRMHNMMZ.js.map +1 -0
  21. package/dist/cli.js +4 -4
  22. package/dist/components/index.d.ts +49 -29
  23. package/dist/components/index.js +5 -5
  24. package/dist/{delta-text-CvwBGJ95.d.ts → delta-text-CbrOaW83.d.ts} +4 -2
  25. package/dist/{dev-H4I2JL63.js → dev-O42JFQ64.js} +3 -3
  26. package/dist/{dev-H4I2JL63.js.map → dev-O42JFQ64.js.map} +1 -1
  27. package/dist/doc/index.d.ts +14 -14
  28. package/dist/doc/index.js +198 -84
  29. package/dist/doc/index.js.map +1 -1
  30. package/dist/{doctor-B2O3GJDU.js → doctor-YD3XAAOT.js} +3 -3
  31. package/dist/{doctor-B2O3GJDU.js.map → doctor-YD3XAAOT.js.map} +1 -1
  32. package/dist/{format-U0cQl069.d.ts → format-DihQkL1N.d.ts} +17 -11
  33. package/dist/slide/index.d.ts +2 -2
  34. package/dist/slide/index.js +95 -44
  35. package/dist/slide/index.js.map +1 -1
  36. package/dist/table/index.js +6 -6
  37. package/dist/ui/index.d.ts +1 -1
  38. package/dist/ui/index.js +5 -5
  39. package/docs/en/agent-skills.md +1 -1
  40. package/docs/ja/agent-skills.md +1 -1
  41. package/package.json +2 -2
  42. package/registry/ui/app-shell.tsx +138 -1
  43. package/registry/ui/area-chart.tsx +344 -85
  44. package/registry/ui/attention-list.tsx +177 -15
  45. package/registry/ui/bar-chart.tsx +341 -85
  46. package/registry/ui/bar-list.tsx +332 -77
  47. package/registry/ui/box-plot-chart.tsx +405 -97
  48. package/registry/ui/category-bar.tsx +248 -40
  49. package/registry/ui/chart-frame.tsx +257 -46
  50. package/registry/ui/data-table.tsx +229 -37
  51. package/registry/ui/date-range-picker.tsx +190 -36
  52. package/registry/ui/delta.tsx +171 -12
  53. package/registry/ui/donut-chart.tsx +339 -84
  54. package/registry/ui/echart.tsx +142 -4
  55. package/registry/ui/filter-bar.tsx +179 -20
  56. package/registry/ui/funnel-steps.tsx +354 -80
  57. package/registry/ui/heatmap.tsx +293 -52
  58. package/registry/ui/histogram-chart.tsx +395 -97
  59. package/registry/ui/kpi-card.tsx +245 -44
  60. package/registry/ui/legend.tsx +330 -76
  61. package/registry/ui/line-chart.tsx +344 -85
  62. package/registry/ui/multi-select.tsx +159 -9
  63. package/registry/ui/page-shell.tsx +141 -3
  64. package/registry/ui/paired-chart.tsx +349 -90
  65. package/registry/ui/progress-circle.tsx +205 -39
  66. package/registry/ui/sample-data.tsx +153 -9
  67. package/registry/ui/scatter-chart.tsx +391 -100
  68. package/registry/ui/searchable-select.tsx +145 -7
  69. package/registry/ui/states.tsx +170 -13
  70. package/registry/ui/status-badge.tsx +193 -28
  71. package/registry/ui/tracker.tsx +205 -39
  72. package/registry/ui/trend-indicator.tsx +149 -6
  73. package/registry/ui/updated-at.tsx +170 -16
  74. package/registry/ui/waterfall-chart.tsx +415 -102
  75. package/skills/vantage-add-feature/SKILL.md +5 -0
  76. package/skills/vantage-design/SKILL.md +25 -27
  77. package/skills/vantage-design/references/extract.md +10 -13
  78. package/skills/vantage-design/references/format.md +12 -173
  79. package/skills/vantage-design/references/presets.md +0 -10
  80. package/templates/AGENTS.md +29 -15
  81. package/templates/agents/api.md +7 -0
  82. package/templates/agents/page.md +37 -1
  83. package/templates/agents/slide.md +7 -1
  84. package/dist/chunk-2N72FKLB.js.map +0 -1
  85. package/dist/chunk-57GHR5LN.js.map +0 -1
  86. package/dist/chunk-6OTTGAPO.js.map +0 -1
  87. package/dist/chunk-JXFCSF3K.js.map +0 -1
  88. package/dist/chunk-PDOBHMUQ.js.map +0 -1
  89. package/dist/chunk-QOXXOFGL.js.map +0 -1
@@ -1,13 +1,13 @@
1
1
  import { FILE_PAGE_MAX_ROWS, FILE_PAGE_MAX_BYTES } from '../chunk-C2CX6GLX.js';
2
2
  import { useQuery } from '../chunk-ANZLVSEP.js';
3
3
  import '../chunk-CKWPK4RO.js';
4
- import { Loading, ErrorState } from '../chunk-2N72FKLB.js';
5
- import { PageShell, PageShellHeader, PageShellHeading, PageShellTitle, PageShellMeta, PageShellActions, PageShellContent } from '../chunk-QOXXOFGL.js';
6
- import '../chunk-JXFCSF3K.js';
4
+ import { Loading, ErrorState } from '../chunk-NZ5QIIG4.js';
5
+ import { PageShell, PageShellHeader, PageShellHeading, PageShellTitle, PageShellMeta, PageShellActions, PageShellContent } from '../chunk-77CISBMG.js';
6
+ import '../chunk-EG4PX726.js';
7
7
  import '../chunk-2IAPIOPS.js';
8
- import '../chunk-57GHR5LN.js';
9
- import '../chunk-7S52M2SL.js';
10
- import { Button } from '../chunk-PDOBHMUQ.js';
8
+ import '../chunk-3RJEOP3J.js';
9
+ import '../chunk-QNOX7WLZ.js';
10
+ import { Button } from '../chunk-ROHNXP7J.js';
11
11
  import { cn } from '../chunk-U736PVHY.js';
12
12
  import { useRef, useState, useMemo, useLayoutEffect, useEffect, useCallback } from 'react';
13
13
  import { DownloadIcon } from 'lucide-react';
@@ -460,7 +460,7 @@ declare function ErrorState({ title, message, details, action, className, }: {
460
460
  title?: string;
461
461
  /** What happened and what to do next, in plain words. */
462
462
  message?: ReactNode;
463
- /** Technical detail (error message, status code), folded behind "詳細を表示". */
463
+ /** Technical detail (error message, status code), folded behind "Show details" (ja: "詳細を表示"). */
464
464
  details?: ReactNode;
465
465
  action?: ReactNode;
466
466
  className?: string;
package/dist/ui/index.js CHANGED
@@ -1,8 +1,8 @@
1
- export { Empty, ErrorState, Loading } from '../chunk-2N72FKLB.js';
2
- import { InputGroup, InputGroupInput, InputGroupAddon, InputGroupButton, Separator } from '../chunk-7S52M2SL.js';
3
- export { Badge, Calendar, CalendarDayButton, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Textarea, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, badgeVariants, toggleVariants, useIsMobile, useSidebar } from '../chunk-7S52M2SL.js';
4
- import { Button } from '../chunk-PDOBHMUQ.js';
5
- export { Button, buttonVariants } from '../chunk-PDOBHMUQ.js';
1
+ export { Empty, ErrorState, Loading } from '../chunk-NZ5QIIG4.js';
2
+ import { InputGroup, InputGroupInput, InputGroupAddon, InputGroupButton, Separator } from '../chunk-QNOX7WLZ.js';
3
+ export { Badge, Calendar, CalendarDayButton, Card, CardAction, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger, Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, Input, InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, Popover, PopoverContent, PopoverDescription, PopoverHeader, PopoverTitle, PopoverTrigger, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, Skeleton, Table, TableBody, TableCaption, TableCell, TableFooter, TableHead, TableHeader, TableRow, Textarea, Toggle, ToggleGroup, ToggleGroupItem, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, badgeVariants, toggleVariants, useIsMobile, useSidebar } from '../chunk-QNOX7WLZ.js';
4
+ import { Button } from '../chunk-ROHNXP7J.js';
5
+ export { Button, buttonVariants } from '../chunk-ROHNXP7J.js';
6
6
  import { cn } from '../chunk-U736PVHY.js';
7
7
  export { cn } from '../chunk-U736PVHY.js';
8
8
  import { Accordion as Accordion$1 } from '@base-ui/react/accordion';
@@ -15,7 +15,7 @@ All of these target an agent that is **building a Vantage app**.
15
15
  | `vantage-app` | Creating an app, and growing a single-file `index.tsx` SPA into a fullstack app with `server/api`, nested layouts, dynamic routes, and 404/error pages |
16
16
  | `vantage-add-feature` | Adding a page / API / UI / block (`vantage add` through to `check` / `routes`) |
17
17
  | `vantage-slide` | Writing a slide deck in `.slide.tsx` (the 1920×1080 canvas's vertical budget, the type scale, stepped reveals, transitions and morph, what a pptx export needs) |
18
- | `vantage-design` | Creating / updating `DESIGN.md` (the design intent) and `styles.css` (the values) together — colors, chart palette, fonts, matching a brand from a URL, logo, document, or an external DESIGN.md, and checking contrast |
18
+ | `vantage-design` | Choosing and applying the theme (`styles.css` values) — colors, chart palette, fonts, matching a brand from a URL, logo, document, or an external DESIGN.md, and checking contrast |
19
19
  | `vantage-pitfalls` | A pitfalls reference (Base UI ≠ Radix, `SelectValue` behavior, never importing `server/` from the client, and more) |
20
20
  | `project-storage` | Working with Squadbase Project Storage (uploaded files) from the browser — listing, embedding images / video / PDFs, uploading. For apps running on Squadbase |
21
21
 
@@ -15,7 +15,7 @@ Vantage は **Claude Code の Agent Skills** を同梱しています。Skill
15
15
  | `vantage-app` | アプリの新規作成と、`index.tsx` 単体の SPA から `server/api`・ネストレイアウト・動的ルート・404/error を備えた fullstack への拡張 |
16
16
  | `vantage-add-feature` | ページ / API / UI / ブロックの追加(`vantage add` + `check` / `routes` までの一連) |
17
17
  | `vantage-slide` | `.slide.tsx` でスライドデッキを書く(1920×1080 キャンバスの縦方向の予算、タイプスケール、段階表示、トランジションとモーフ、pptx 書き出しの前提) |
18
- | `vantage-design` | 見た目の意図 `DESIGN.md` と値を持つ `styles.css` をまとめて作る / 更新する(配色・チャートの色・書体・ブランド合わせ。URL・ロゴ・資料・外部の DESIGN.md からの取り込み、コントラストの確認) |
18
+ | `vantage-design` | テーマ(`styles.css` の値と配色・チャートの色・書体)を決めて適用する(ブランド合わせ。URL・ロゴ・資料・外部の DESIGN.md からの取り込み、コントラストの確認) |
19
19
  | `vantage-pitfalls` | 落とし穴リファレンス(Base UI ≠ Radix、`SelectValue` の挙動、クライアントから `server/` を import しない など) |
20
20
  | `project-storage` | Squadbase の Project Storage(アップロード済みファイル)をブラウザから扱う(一覧、画像 / 動画 / PDF の埋め込み、アップロード)。Squadbase 上で動くアプリ向け |
21
21
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squadbase/vantage",
3
- "version": "0.6.2-dev1.54bde93",
3
+ "version": "0.6.2-dev1.56da87f",
4
4
  "description": "Data dashboard framework for Squadbase Editor. Vite-powered, config-free, one-file first.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -152,7 +152,7 @@
152
152
  "vitest": "^5.0.5",
153
153
  "@squadbase/squadkit": "0.0.0"
154
154
  },
155
- "gitHead": "54bde936ab6acec5570032d647be1d2ac1e50797",
155
+ "gitHead": "56da87f089eb31d3b80f128df925e11f748485db",
156
156
  "scripts": {
157
157
  "build": "node scripts/build-docs.mjs && node scripts/build.mjs",
158
158
  "build:js": "tsup --no-dts",
@@ -44,6 +44,142 @@ import {
44
44
  SidebarTrigger,
45
45
  } from "@squadbase/vantage/ui"
46
46
  import type { NavGroup, NavItem } from "@squadbase/vantage/components"
47
+ import { useSyncExternalStore } from "react"
48
+
49
+ /* ---------- inlined from @squadbase/squadkit registry/lib/locale.ts (internal to @squadbase/vantage) ---------- */
50
+
51
+ /**
52
+ * The display language of the parts and the defaults that follow it.
53
+ *
54
+ * The language comes from `<html lang>`: a tag starting with `ja` reads Japanese, anything else English.
55
+ * Without a usable `lang` (missing, invalid, or no `document` — SSR, Node) the locale is `FALLBACK_LOCALE`
56
+ * (`en-US`). A `locale` passed explicitly (a prop, an argument) always wins.
57
+ *
58
+ * Parts read the locale through `useLocale` (`hooks/use-locale`) and pass it down; the pure helpers here
59
+ * and in `format` / `comparison` / `chart-*` take it as an argument and fall back to `documentLocale()`.
60
+ */
61
+
62
+ /** The language of the built-in wording: Japanese or English. */
63
+ type UiLanguage = "ja" | "en"
64
+
65
+ /** The locale used when `<html lang>` gives none (and on the server). */
66
+ const FALLBACK_LOCALE = "en-US"
67
+
68
+ /** `"ja"` when `locale` starts with `ja` (`ja`, `ja-JP`), else `"en"`. A missing locale is `"en"`. */
69
+ function uiLanguage(locale: string | null | undefined): UiLanguage {
70
+ return typeof locale === "string" && locale.trim().toLowerCase().startsWith("ja") ? "ja" : "en"
71
+ }
72
+
73
+ /** A BCP 47 tag in its canonical form (`en-gb` → `en-GB`), or `FALLBACK_LOCALE` when it is empty or invalid. */
74
+ function normalizeLocale(tag: string | null | undefined): string {
75
+ const value = tag?.trim()
76
+ if (!value) return FALLBACK_LOCALE
77
+ try {
78
+ return Intl.getCanonicalLocales(value)[0] ?? FALLBACK_LOCALE
79
+ } catch {
80
+ return FALLBACK_LOCALE
81
+ }
82
+ }
83
+
84
+ /**
85
+ * The locale of the page: `document.documentElement.lang`, canonicalized. `FALLBACK_LOCALE` when it is empty or
86
+ * invalid, or there is no `document`. Parts read it through `useLocale`, never while rendering.
87
+ */
88
+ function documentLocale(): string {
89
+ if (typeof document === "undefined") return FALLBACK_LOCALE
90
+ return normalizeLocale(document.documentElement?.getAttribute("lang"))
91
+ }
92
+
93
+ /** `locale` when given, else the page's locale (`documentLocale()`). */
94
+ function resolveLocale(locale?: string | null): string {
95
+ return locale || documentLocale()
96
+ }
97
+
98
+ /** Picks the wording table of the locale's language. */
99
+ function pickLocale<T>(table: { ja: T; en: T }, locale?: string | null): T {
100
+ return table[uiLanguage(resolveLocale(locale))]
101
+ }
102
+
103
+ /** The currency `format="currency"` uses without a `currency`: JPY for Japanese, USD otherwise. */
104
+ function defaultCurrency(locale?: string | null): string {
105
+ return uiLanguage(resolveLocale(locale)) === "ja" ? "JPY" : "USD"
106
+ }
107
+
108
+ /** The month a fiscal year starts in (1–12) when none is given: April for Japanese, January otherwise. */
109
+ function defaultFiscalYearStartMonth(locale?: string | null): number {
110
+ return uiLanguage(resolveLocale(locale)) === "ja" ? 4 : 1
111
+ }
112
+
113
+ /** A day of the week, 0 = Sunday … 6 = Saturday (the `Date#getDay` / date-fns numbering). */
114
+ type WeekDay = 0 | 1 | 2 | 3 | 4 | 5 | 6
115
+
116
+ /** Locales whose week starts on Sunday when the runtime has no week data. */
117
+ const SUNDAY_FIRST = new Set(["en", "en-US", "en-CA"])
118
+
119
+ /**
120
+ * The first day of the week: Monday for Japanese; otherwise the locale's convention from `Intl.Locale`
121
+ * (`en-US` Sunday, `en-GB` Monday). Without week data in the runtime: Sunday for `en` / `en-US` / `en-CA`,
122
+ * Monday for the rest.
123
+ */
124
+ function weekStartsOn(locale?: string | null): WeekDay {
125
+ const tag = normalizeLocale(resolveLocale(locale))
126
+ if (uiLanguage(tag) === "ja") return 1
127
+ try {
128
+ const info = new Intl.Locale(tag) as Intl.Locale & {
129
+ getWeekInfo?: () => { firstDay: number }
130
+ weekInfo?: { firstDay: number }
131
+ }
132
+ const firstDay = info.getWeekInfo?.().firstDay ?? info.weekInfo?.firstDay
133
+ if (typeof firstDay === "number" && firstDay >= 1 && firstDay <= 7) return (firstDay % 7) as WeekDay
134
+ } catch {
135
+ // Fall through to the table below.
136
+ }
137
+ return SUNDAY_FIRST.has(tag) ? 0 : 1
138
+ }
139
+
140
+ /* ---------- inlined from @squadbase/squadkit registry/hooks/use-locale.ts (internal to @squadbase/vantage) ---------- */
141
+
142
+ const listeners = new Set<() => void>()
143
+ let observer: MutationObserver | null = null
144
+
145
+ /** One observer on `<html lang>` for every subscribed part. */
146
+ function subscribe(onChange: () => void): () => void {
147
+ if (typeof document === "undefined" || typeof MutationObserver === "undefined") return () => {}
148
+ listeners.add(onChange)
149
+ if (!observer) {
150
+ observer = new MutationObserver(() => {
151
+ for (const listener of [...listeners]) listener()
152
+ })
153
+ observer.observe(document.documentElement, { attributes: true, attributeFilter: ["lang"] })
154
+ }
155
+ return () => {
156
+ listeners.delete(onChange)
157
+ if (listeners.size === 0 && observer) {
158
+ observer.disconnect()
159
+ observer = null
160
+ }
161
+ }
162
+ }
163
+
164
+ const serverLocale = () => FALLBACK_LOCALE
165
+
166
+ /**
167
+ * The page's locale (`<html lang>`), following changes made while the page runs. On the server and during
168
+ * hydration it is `FALLBACK_LOCALE` (`en-US`), so the markup matches; it switches right after.
169
+ */
170
+ function useDocumentLocale(): string {
171
+ return useSyncExternalStore(subscribe, documentLocale, serverLocale)
172
+ }
173
+
174
+ /** The locale a part renders in: its `locale` prop when given, else the page's (`useDocumentLocale`). */
175
+ function useLocale(locale?: string | null): string {
176
+ const page = useDocumentLocale()
177
+ return locale || page
178
+ }
179
+
180
+ /* ---------- end of inlined code ---------- */
181
+
182
+ const PAGE_PLACEHOLDER = { ja: "ページを選択", en: "Select a page" }
47
183
 
48
184
  export interface AppShellSidebarProps {
49
185
  variant?: "sidebar"
@@ -90,6 +226,7 @@ function NavSelect({
90
226
  // Base UI's SelectValue renders the raw value (the href) unless `items` maps
91
227
  // it to a label, so pass the page names explicitly.
92
228
  const selectItems = allItems.map((item) => ({ value: item.href, label: item.label }))
229
+ const placeholder = PAGE_PLACEHOLDER[uiLanguage(useLocale())]
93
230
 
94
231
  return (
95
232
  <Select
@@ -102,7 +239,7 @@ function NavSelect({
102
239
  }}
103
240
  >
104
241
  <SelectTrigger size="sm" className="w-48">
105
- <SelectValue placeholder="ページを選択" />
242
+ <SelectValue placeholder={placeholder} />
106
243
  </SelectTrigger>
107
244
  {/*
108
245
  Page titles are author-written and often longer than the 12rem trigger,