@squadbase/vantage-template 0.1.1 → 0.2.0-dev1.a9938fc
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -9
- package/base-template/.squadbase/skills/vantage-add-feature/SKILL.md +14 -7
- package/base-template/.squadbase/skills/vantage-app/SKILL.md +4 -0
- package/base-template/.squadbase/skills/vantage-design/SKILL.md +62 -52
- package/base-template/.squadbase/skills/vantage-design/references/extract.md +26 -27
- package/base-template/.squadbase/skills/vantage-design/references/format.md +12 -172
- package/base-template/.squadbase/skills/vantage-design/references/presets.md +50 -110
- package/base-template/.squadbase/skills/vantage-pitfalls/SKILL.md +50 -1
- package/base-template/.squadbase/skills/vantage-slide/SKILL.md +299 -0
- package/base-template/.squadbase/skills/vantage-slide/references/morph.md +102 -0
- package/base-template/.squadbase/skills/vantage-slide/references/steps.md +57 -0
- package/base-template/.squadbase/skills/vantage-slide/references/tokens.md +87 -0
- package/base-template/.squadbase/skills/vantage-slide/references/transitions.md +196 -0
- package/base-template/AGENTS.md +224 -17
- package/base-template/agents/api.md +36 -0
- package/base-template/agents/csv.md +2 -1
- package/base-template/agents/doc.md +52 -3
- package/base-template/agents/page.md +91 -10
- package/base-template/agents/slide.md +90 -4
- package/base-template/agents/xlsx.md +2 -1
- package/base-template/bun.lock +917 -0
- package/base-template/bunfig.toml +5 -0
- package/base-template/package.json +6 -6
- package/base-template/squadbase.yml +1 -0
- package/base-template/src/_layout.tsx +16 -7
- package/base-template/src/components/build-recovery.tsx +37 -1
- package/base-template/src/components/user-card.tsx +11 -11
- package/base-template/src/index.tsx +8 -3
- package/base-template/src/styles.css +108 -12
- package/dist/index.js +971 -6
- package/package.json +20 -17
- package/ui-templates/CLAUDE.md +2 -0
- package/ui-templates/kpi-chart-simple/pages/index.tsx +48 -48
- package/ui-templates/kpi-chart-simple-ja/pages/index.tsx +48 -48
- package/base-template/DESIGN.md +0 -108
- package/base-template/package-lock.json +0 -5841
|
@@ -12,17 +12,17 @@
|
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"@squadbase/react": "^0.1.0",
|
|
15
|
-
"@squadbase/vantage": "0.
|
|
16
|
-
"@squadbase/vantage-sdk": "0.1.
|
|
15
|
+
"@squadbase/vantage": "0.7.0-dev1.275b4ed",
|
|
16
|
+
"@squadbase/vantage-sdk": "0.1.2-dev1.db5abf9",
|
|
17
17
|
"date-fns": "^4.1.0",
|
|
18
18
|
"lucide-react": "^0.468.0",
|
|
19
|
-
"react": "^19.
|
|
20
|
-
"react-dom": "^19.
|
|
19
|
+
"react": "^19.3.1",
|
|
20
|
+
"react-dom": "^19.3.1"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/node": "^22.15.0",
|
|
24
|
-
"@types/react": "^19.
|
|
25
|
-
"@types/react-dom": "^19.
|
|
24
|
+
"@types/react": "^19.3.0",
|
|
25
|
+
"@types/react-dom": "^19.3.0",
|
|
26
26
|
"typescript": "~5.7.0"
|
|
27
27
|
}
|
|
28
28
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ComponentProps } from "react"
|
|
2
2
|
import { SquadbaseProvider } from "@squadbase/react"
|
|
3
|
-
import { Link, Outlet, useCurrentRoute, useRoutes } from "@squadbase/vantage/router"
|
|
3
|
+
import { Link, Outlet, useCurrentRoute, useNavigate, useRoutes } from "@squadbase/vantage/router"
|
|
4
4
|
import { AppShell } from "@squadbase/vantage/components"
|
|
5
5
|
import type { NavGroup } from "@squadbase/vantage/components"
|
|
6
6
|
|
|
@@ -8,13 +8,11 @@ import { BuildRecovery } from "./components/build-recovery"
|
|
|
8
8
|
import { UserCard } from "./components/user-card"
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* The app's display language, written to `<html lang>` (a BCP 47 literal, e.g.
|
|
12
|
+
* "ja"). Set it to the language of the app's content: it also switches the slide
|
|
13
|
+
* toolbar messages and enables Japanese phrase-aware line breaking in slides.
|
|
14
14
|
*/
|
|
15
|
-
|
|
16
|
-
return <Link to={href} {...props} />
|
|
17
|
-
}
|
|
15
|
+
export const lang = "en"
|
|
18
16
|
|
|
19
17
|
/**
|
|
20
18
|
* The nav *is* the route list — adding a page file puts it in the nav, with no
|
|
@@ -50,6 +48,7 @@ function useNavGroups(): NavGroup[] {
|
|
|
50
48
|
* 「Something went wrong」のままリロードするまで残る。
|
|
51
49
|
*/
|
|
52
50
|
export default function RootLayout() {
|
|
51
|
+
const navigate = useNavigate()
|
|
53
52
|
return (
|
|
54
53
|
<SquadbaseProvider>
|
|
55
54
|
<AppShell
|
|
@@ -57,6 +56,7 @@ export default function RootLayout() {
|
|
|
57
56
|
groups={useNavGroups()}
|
|
58
57
|
actions={<UserCard />}
|
|
59
58
|
linkComponent={RouterLink}
|
|
59
|
+
onNavigate={(href) => navigate({ to: href })}
|
|
60
60
|
>
|
|
61
61
|
<BuildRecovery>
|
|
62
62
|
<Outlet />
|
|
@@ -65,3 +65,12 @@ export default function RootLayout() {
|
|
|
65
65
|
</SquadbaseProvider>
|
|
66
66
|
)
|
|
67
67
|
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* `AppShell` is router-agnostic and renders nav entries through whatever
|
|
71
|
+
* component it is handed, using an `href` prop. Adapt it to the Vantage router
|
|
72
|
+
* so navigation stays client-side.
|
|
73
|
+
*/
|
|
74
|
+
function RouterLink({ href, ...props }: { href: string } & ComponentProps<"a">) {
|
|
75
|
+
return <Link to={href} {...props} />
|
|
76
|
+
}
|
|
@@ -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
|
-
|
|
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
|
*
|
|
@@ -47,7 +57,7 @@ function DetailRow({ label, children }: { label: string; children: React.ReactNo
|
|
|
47
57
|
|
|
48
58
|
function UserInfo({ user }: { user: User }) {
|
|
49
59
|
const fullName = `${user.firstName} ${user.lastName}`
|
|
50
|
-
const rolesHref =
|
|
60
|
+
const rolesHref = document.documentElement.lang.startsWith("ja")
|
|
51
61
|
? "https://www.squadbase.dev/ja/docs/access-control#プロジェクトロール"
|
|
52
62
|
: "https://www.squadbase.dev/docs/access-control#project-roles"
|
|
53
63
|
|
|
@@ -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
|
-
}
|
|
@@ -17,6 +17,7 @@ export const page = definePage({
|
|
|
17
17
|
title: "Dashboard",
|
|
18
18
|
description: "Sample data — replace with your own to get started.",
|
|
19
19
|
navLabel: "Home",
|
|
20
|
+
placeholder: true,
|
|
20
21
|
})
|
|
21
22
|
|
|
22
23
|
const METRICS = [
|
|
@@ -65,14 +66,18 @@ const CHARTS = [
|
|
|
65
66
|
]
|
|
66
67
|
|
|
67
68
|
export default function HomePage() {
|
|
69
|
+
// `data-vantage-sample` marks this page as the untouched template sample (the coding
|
|
70
|
+
// agent looks for it). Remove it together with the sample content.
|
|
71
|
+
const sampleNote = document.documentElement.lang.startsWith("ja")
|
|
72
|
+
? "サンプルデータです。お手元のデータに置き換えて始めましょう。"
|
|
73
|
+
: "Sample data — replace with your own to get started."
|
|
74
|
+
|
|
68
75
|
return (
|
|
69
76
|
<PageShell>
|
|
70
77
|
<PageShellHeader>
|
|
71
78
|
<PageShellHeading>
|
|
72
79
|
<PageShellTitle>Dashboard</PageShellTitle>
|
|
73
|
-
<PageShellDescription>
|
|
74
|
-
Sample data — replace with your own to get started.
|
|
75
|
-
</PageShellDescription>
|
|
80
|
+
<PageShellDescription data-vantage-sample="home">{sampleNote}</PageShellDescription>
|
|
76
81
|
</PageShellHeading>
|
|
77
82
|
</PageShellHeader>
|
|
78
83
|
|
|
@@ -17,21 +17,117 @@
|
|
|
17
17
|
/* --radius: 0.625rem; */
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
/*
|
|
20
|
+
/* Page typeface. `--font-sans` (Vantage theme.css, overridable by the theme range
|
|
21
|
+
below or here) already lists Latin system faces and then Japanese ones, so kana
|
|
22
|
+
and kanji do not fall back to a thin locale default. Change the typeface by
|
|
23
|
+
overriding `--font-sans`; keep the Japanese families if you put a webfont in
|
|
24
|
+
front. */
|
|
25
|
+
body {
|
|
26
|
+
font-family: var(--font-sans);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/* squadkit-theme — regenerated by `theme`; edits here are lost */
|
|
30
|
+
/* theme: neutral */
|
|
21
31
|
:root {
|
|
22
|
-
--
|
|
23
|
-
--
|
|
24
|
-
--
|
|
25
|
-
--
|
|
26
|
-
--
|
|
32
|
+
--radius: 0.5rem;
|
|
33
|
+
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Hiragino Sans", "Hiragino Kaku Gothic ProN", "Noto Sans JP", Meiryo, ui-sans-serif, system-ui, sans-serif;
|
|
34
|
+
--background: oklch(0.975 0.004 255);
|
|
35
|
+
--card: oklch(1 0 0);
|
|
36
|
+
--popover: oklch(1 0 0);
|
|
37
|
+
--secondary: oklch(0.965 0.005 255);
|
|
38
|
+
--muted: oklch(0.965 0.005 255);
|
|
39
|
+
--accent: oklch(0.955 0.01 255);
|
|
40
|
+
--sidebar: oklch(0.985 0.003 255);
|
|
41
|
+
--sidebar-accent: oklch(0.955 0.01 255);
|
|
42
|
+
--border: oklch(0.93 0.006 255);
|
|
43
|
+
--input: oklch(0.9 0.01 255);
|
|
44
|
+
--sidebar-border: oklch(0.915 0.008 255);
|
|
45
|
+
--foreground: oklch(0.21 0.02 260);
|
|
46
|
+
--card-foreground: oklch(0.21 0.02 260);
|
|
47
|
+
--popover-foreground: oklch(0.21 0.02 260);
|
|
48
|
+
--secondary-foreground: oklch(0.25 0.02 260);
|
|
49
|
+
--muted-foreground: oklch(0.5 0.02 258);
|
|
50
|
+
--accent-foreground: oklch(0.25 0.02 260);
|
|
51
|
+
--sidebar-foreground: oklch(0.21 0.02 260);
|
|
52
|
+
--sidebar-accent-foreground: oklch(0.25 0.02 260);
|
|
53
|
+
--primary: oklch(0.55 0.2 262);
|
|
54
|
+
--primary-foreground: oklch(0.985 0 0);
|
|
55
|
+
--ring: oklch(0.7 0.12 262);
|
|
56
|
+
--sidebar-primary: oklch(0.55 0.2 262);
|
|
57
|
+
--sidebar-primary-foreground: oklch(0.985 0 0);
|
|
58
|
+
--sidebar-ring: oklch(0.7 0.12 262);
|
|
59
|
+
--chart-1: oklch(0.55 0.2 262);
|
|
60
|
+
--chart-2: oklch(0.62 0.17 118);
|
|
61
|
+
--chart-3: oklch(0.5 0.19 352);
|
|
62
|
+
--chart-4: oklch(0.62 0.1 195);
|
|
63
|
+
--chart-5: oklch(0.44 0.18 300);
|
|
64
|
+
--positive: oklch(0.52 0.14 150);
|
|
65
|
+
--positive-subtle: oklch(0.96 0.04 150);
|
|
66
|
+
--positive-fill: oklch(0.62 0.15 150);
|
|
67
|
+
--negative: oklch(0.53 0.19 25);
|
|
68
|
+
--negative-subtle: oklch(0.96 0.03 25);
|
|
69
|
+
--negative-fill: oklch(0.62 0.17 25);
|
|
70
|
+
--warning: oklch(0.53 0.12 70);
|
|
71
|
+
--warning-subtle: oklch(0.96 0.05 85);
|
|
72
|
+
--warning-fill: oklch(0.79 0.16 82);
|
|
73
|
+
--info: oklch(0.52 0.16 255);
|
|
74
|
+
--info-subtle: oklch(0.96 0.03 250);
|
|
75
|
+
--info-fill: oklch(0.62 0.15 250);
|
|
76
|
+
--neutral: oklch(0.52 0 0);
|
|
77
|
+
--neutral-subtle: oklch(0.96 0 0);
|
|
78
|
+
--neutral-fill: oklch(0.62 0 0);
|
|
79
|
+
--destructive: oklch(0.53 0.21 27.325);
|
|
80
|
+
--destructive-foreground: oklch(0.985 0 0);
|
|
27
81
|
}
|
|
28
82
|
|
|
29
83
|
.dark,
|
|
30
84
|
[data-theme="dark"] {
|
|
31
|
-
--
|
|
32
|
-
--
|
|
33
|
-
--
|
|
34
|
-
--
|
|
35
|
-
--
|
|
85
|
+
--background: oklch(0.16 0.012 262);
|
|
86
|
+
--card: oklch(0.215 0.015 262);
|
|
87
|
+
--popover: oklch(0.235 0.016 262);
|
|
88
|
+
--secondary: oklch(0.27 0.016 262);
|
|
89
|
+
--muted: oklch(0.27 0.016 262);
|
|
90
|
+
--accent: oklch(0.3 0.02 262);
|
|
91
|
+
--sidebar: oklch(0.2 0.014 262);
|
|
92
|
+
--sidebar-accent: oklch(0.3 0.02 262);
|
|
93
|
+
--border: oklch(1 0 0 / 10%);
|
|
94
|
+
--input: oklch(1 0 0 / 15%);
|
|
95
|
+
--sidebar-border: oklch(1 0 0 / 10%);
|
|
96
|
+
--foreground: oklch(0.96 0.005 255);
|
|
97
|
+
--card-foreground: oklch(0.96 0.005 255);
|
|
98
|
+
--popover-foreground: oklch(0.96 0.005 255);
|
|
99
|
+
--secondary-foreground: oklch(0.96 0.005 255);
|
|
100
|
+
--muted-foreground: oklch(0.72 0.02 258);
|
|
101
|
+
--accent-foreground: oklch(0.96 0.005 255);
|
|
102
|
+
--sidebar-foreground: oklch(0.96 0.005 255);
|
|
103
|
+
--sidebar-accent-foreground: oklch(0.96 0.005 255);
|
|
104
|
+
--primary: oklch(0.68 0.19 262);
|
|
105
|
+
--primary-foreground: oklch(0.145 0.02 262);
|
|
106
|
+
--ring: oklch(0.6 0.15 262);
|
|
107
|
+
--sidebar-primary: oklch(0.68 0.19 262);
|
|
108
|
+
--sidebar-primary-foreground: oklch(0.145 0.02 262);
|
|
109
|
+
--sidebar-ring: oklch(0.6 0.15 262);
|
|
110
|
+
--chart-1: oklch(0.56 0.19 262);
|
|
111
|
+
--chart-2: oklch(0.75 0.17 118);
|
|
112
|
+
--chart-3: oklch(0.64 0.19 352);
|
|
113
|
+
--chart-4: oklch(0.78 0.105 195);
|
|
114
|
+
--chart-5: oklch(0.71 0.17 300);
|
|
115
|
+
--positive: oklch(0.76 0.15 150);
|
|
116
|
+
--positive-subtle: oklch(0.27 0.05 150);
|
|
117
|
+
--positive-fill: oklch(0.56 0.13 150);
|
|
118
|
+
--negative: oklch(0.74 0.15 22);
|
|
119
|
+
--negative-subtle: oklch(0.27 0.06 25);
|
|
120
|
+
--negative-fill: oklch(0.56 0.15 22);
|
|
121
|
+
--warning: oklch(0.8 0.14 80);
|
|
122
|
+
--warning-subtle: oklch(0.27 0.05 75);
|
|
123
|
+
--warning-fill: oklch(0.73 0.145 78);
|
|
124
|
+
--info: oklch(0.75 0.12 250);
|
|
125
|
+
--info-subtle: oklch(0.27 0.06 255);
|
|
126
|
+
--info-fill: oklch(0.56 0.13 250);
|
|
127
|
+
--neutral: oklch(0.85 0 0);
|
|
128
|
+
--neutral-subtle: oklch(0.27 0 0);
|
|
129
|
+
--neutral-fill: oklch(0.56 0 0);
|
|
130
|
+
--destructive: oklch(0.704 0.191 22.216);
|
|
131
|
+
--destructive-foreground: oklch(0.145 0.02 262);
|
|
36
132
|
}
|
|
37
|
-
/*
|
|
133
|
+
/* squadkit-theme:end */
|