@donotdev/cli 0.0.20 → 0.0.21
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 +31 -0
- package/dependencies-matrix.json +86 -19
- package/dist/bin/commands/agent-setup.js +2 -2
- package/dist/bin/commands/build.js +6 -6
- package/dist/bin/commands/bump.js +491 -69
- package/dist/bin/commands/cacheout.js +6 -6
- package/dist/bin/commands/coach.js +6 -6
- package/dist/bin/commands/create-app.js +23 -15
- package/dist/bin/commands/create-project.js +101 -16
- package/dist/bin/commands/db.js +142136 -0
- package/dist/bin/commands/deploy.js +336 -126
- package/dist/bin/commands/dev.js +6 -6
- package/dist/bin/commands/doctor.js +140 -33
- package/dist/bin/commands/emu.js +6 -6
- package/dist/bin/commands/format.js +6 -6
- package/dist/bin/commands/get-demo.js +11 -6
- package/dist/bin/commands/make-admin.js +14210 -13770
- package/dist/bin/commands/preview.js +6 -6
- package/dist/bin/commands/seed.js +142426 -0
- package/dist/bin/commands/setup-cicd.js +8904 -0
- package/dist/bin/commands/setup.js +256 -212
- package/dist/bin/commands/staging.js +343 -127
- package/dist/bin/commands/sync-secrets.js +55 -33
- package/dist/bin/commands/type-check.js +6 -6
- package/dist/bin/commands/wai.js +6 -6
- package/dist/bin/dndev.js +76 -11
- package/dist/bin/donotdev.js +21 -12
- package/dist/index.js +437 -142
- package/package.json +1 -1
- package/templates/app-demo/.env.example +1 -0
- package/templates/{root-consumer → app-demo}/entities/ExampleEntity.ts.example +15 -9
- package/templates/app-demo/index.html.example +1 -1
- package/templates/app-dndev/index.html.example +164 -0
- package/templates/app-dndev/public/logo.svg.example +1 -0
- package/templates/app-dndev/public/manifest.json.example +10 -0
- package/templates/app-dndev/src/App.tsx.example +35 -0
- package/templates/app-dndev/src/components/CockpitLayout.css.example +181 -0
- package/templates/app-dndev/src/components/CockpitLayout.tsx.example +209 -0
- package/templates/app-dndev/src/components/Kanban.css.example +385 -0
- package/templates/app-dndev/src/components/ModeToggle.tsx.example +32 -0
- package/templates/app-dndev/src/components/OverlaySlot.tsx.example +68 -0
- package/templates/app-dndev/src/components/TerminalPanel.css.example +228 -0
- package/templates/app-dndev/src/components/TerminalPanel.tsx.example +714 -0
- package/templates/app-dndev/src/components/markdown-prose.css.example +49 -0
- package/templates/app-dndev/src/components/phases/CaptainLog.tsx.example +107 -0
- package/templates/app-dndev/src/components/phases/ContextTabs.tsx.example +352 -0
- package/templates/app-dndev/src/components/phases/PhaseCard.tsx.example +126 -0
- package/templates/app-dndev/src/components/phases/PhaseDetail.tsx.example +147 -0
- package/templates/app-dndev/src/components/phases/ReviewPanel.tsx.example +115 -0
- package/templates/app-dndev/src/components/phases/phaseData.ts.example +366 -0
- package/templates/app-dndev/src/config/app.ts.example +103 -0
- package/templates/app-dndev/src/config/commands.ts.example +171 -0
- package/templates/app-dndev/src/config/legal.ts.example +170 -0
- package/templates/app-dndev/src/config/providers.ts.example +7 -0
- package/templates/app-dndev/src/globals.css.example +10 -0
- package/templates/app-dndev/src/hooks/useDndevFile.ts.example +144 -0
- package/templates/app-dndev/src/main.tsx.example +21 -0
- package/templates/app-dndev/src/pages/BoardPage.tsx.example +640 -0
- package/templates/app-dndev/src/pages/GrillPage.tsx.example +658 -0
- package/templates/app-dndev/src/pages/HomePage.tsx.example +347 -0
- package/templates/app-dndev/src/pages/NotFoundPage.tsx.example +33 -0
- package/templates/app-dndev/src/pages/PhasesPage.tsx.example +137 -0
- package/templates/app-dndev/src/pages/SettingsPage.tsx.example +64 -0
- package/templates/app-dndev/src/pages/legal/LegalNoticePage.tsx.example +75 -0
- package/templates/app-dndev/src/pages/legal/PrivacyPage.tsx.example +69 -0
- package/templates/app-dndev/src/pages/legal/TermsPage.tsx.example +71 -0
- package/templates/app-dndev/src/stores/dndevStore.ts.example +386 -0
- package/templates/app-dndev/src/themes.css.example +161 -0
- package/templates/app-dndev/terminal-sidecar.cjs.example +341 -0
- package/templates/app-dndev/tsconfig.json.example +9 -0
- package/templates/app-dndev/vite.config.ts.example +24 -0
- package/templates/app-next/src/locales/home_en.json.example +6 -6
- package/templates/app-vite/index.html.example +1 -1
- package/templates/app-vite/src/locales/home_en.json.example +6 -6
- package/templates/functions-supabase/supabase/functions/.env.example +0 -2
- package/templates/root-consumer/.claude/commands/grill.md.example +86 -8
- package/templates/root-consumer/.dndev.secrets.example +32 -0
- package/templates/root-consumer/.gitignore.example +3 -0
- package/templates/root-consumer/AI.md.example +4 -0
- package/templates/root-consumer/entities/index.ts.example +2 -5
- package/templates/root-consumer/guides/dndev/COMPONENTS_ATOMIC.md.example +4 -0
- package/templates/root-consumer/guides/dndev/ENV_SETUP.md.example +23 -20
- package/templates/root-consumer/guides/dndev/INDEX.md.example +1 -0
- package/templates/root-consumer/guides/dndev/SETUP_BILLING.md.example +3 -7
- package/templates/root-consumer/guides/dndev/SETUP_CICD.md.example +115 -0
- package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +41 -0
- package/templates/root-consumer/guides/dndev/SETUP_SUPABASE.md.example +13 -18
- package/templates/root-consumer/guides/dndev/SETUP_VERCEL.md.example +17 -12
- package/templates/root-consumer/guides/dndev/advanced/COOKIE_REFERENCE.md.example +252 -252
- package/templates/root-consumer/guides/dndev/advanced/VERSION_CONTROL.md.example +174 -174
- package/templates/root-consumer/guides/wai-way/WAI_WAY_CLI.md.example +185 -251
- package/templates/root-consumer/guides/wai-way/agents/extractor.md.example +26 -8
- package/templates/root-consumer/guides/wai-way/blueprints/0_brainstorm.md.example +66 -49
- package/templates/root-consumer/guides/wai-way/blueprints/1_scaffold.md.example +6 -5
- package/templates/root-consumer/guides/wai-way/blueprints/2_entities.md.example +9 -9
- package/templates/root-consumer/guides/wai-way/blueprints/3_compose.md.example +1 -1
- package/templates/root-consumer/guides/wai-way/blueprints/4_configure.md.example +7 -6
- package/templates/root-consumer/guides/wai-way/context_map.json.example +51 -20
- package/templates/root-consumer/guides/wai-way/hld_template.md.example +138 -0
- package/templates/root-consumer/guides/wai-way/lld_template.md.example +103 -0
- package/templates/root-consumer/guides/wai-way/prd_template.md.example +140 -0
- /package/templates/{root-consumer → app-demo}/entities/Contact.ts.example +0 -0
- /package/templates/{root-consumer → app-demo}/entities/demo.ts.example +0 -0
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
VITE_AUTH_PARTNERS=password, google, github, apple, discord, facebook, linkedin, reddit, twitch, twitter, microsoft, spotify, yahoo
|
|
@@ -32,13 +32,16 @@ export const productEntity = defineEntity({
|
|
|
32
32
|
listCardFields: ['images', 'name', 'price', 'category'],
|
|
33
33
|
|
|
34
34
|
// ==========================================================================
|
|
35
|
-
// ACCESS RULES - Who can do what
|
|
35
|
+
// ACCESS RULES - Who can do what (maps to Supabase RLS policies)
|
|
36
36
|
// ==========================================================================
|
|
37
37
|
//
|
|
38
38
|
// 'guest' → Anyone (including not logged in)
|
|
39
39
|
// 'user' → Any logged-in user
|
|
40
|
-
// 'owner' → Only the document owner (createdById matches)
|
|
41
40
|
// 'admin' → Only admin role
|
|
41
|
+
// 'super' → Only super admin
|
|
42
|
+
//
|
|
43
|
+
// NOTE: These are DB-level roles. For owner-based logic, use field-level
|
|
44
|
+
// 'editable' / 'visibility' settings (see FIELD DEFINITIONS below).
|
|
42
45
|
//
|
|
43
46
|
access: {
|
|
44
47
|
create: 'admin', // Only admins can create products
|
|
@@ -67,16 +70,19 @@ export const productEntity = defineEntity({
|
|
|
67
70
|
// 'images' → Image upload (multiple)
|
|
68
71
|
// 'reference' → Link to another entity
|
|
69
72
|
//
|
|
70
|
-
// VISIBILITY:
|
|
71
|
-
// 'guest'
|
|
72
|
-
// 'user'
|
|
73
|
-
// '
|
|
74
|
-
// '
|
|
73
|
+
// VISIBILITY (field-level, UI only):
|
|
74
|
+
// 'guest' → Everyone can see this field
|
|
75
|
+
// 'user' → Only logged-in users
|
|
76
|
+
// 'admin' → Only admins
|
|
77
|
+
// 'super' → Only super admins
|
|
78
|
+
// 'hidden' → Never sent to client (server-only)
|
|
75
79
|
//
|
|
76
|
-
// EDITABLE:
|
|
80
|
+
// EDITABLE (field-level, UI only):
|
|
77
81
|
// 'admin' → Only admins can edit
|
|
78
|
-
// 'owner' → Only
|
|
82
|
+
// 'owner' → Only document creator can edit
|
|
83
|
+
// 'user' → Any authenticated user can edit
|
|
79
84
|
// 'create-only' → Set on creation, read-only after
|
|
85
|
+
// false → Never editable (read-only)
|
|
80
86
|
//
|
|
81
87
|
fields: {
|
|
82
88
|
// ==========================================================================
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
box-sizing: border-box;
|
|
21
21
|
}
|
|
22
22
|
body {
|
|
23
|
-
font-family:
|
|
23
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
|
|
24
24
|
line-height: 1.5;
|
|
25
25
|
-webkit-font-smoothing: antialiased;
|
|
26
26
|
-moz-osx-font-smoothing: grayscale;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
|
|
6
|
+
<!-- ✅ CORE: Performance-critical meta tags first -->
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
|
+
<meta name="color-scheme" content="light dark" />
|
|
9
|
+
|
|
10
|
+
<!-- ✅ DnDev: Framework will inject FaviconHead here -->
|
|
11
|
+
<!-- All favicon links will be added by FaviconHead component -->
|
|
12
|
+
|
|
13
|
+
<!-- ✅ DnDev: Asset manifest injection point -->
|
|
14
|
+
<!-- window.__DNDEV_ASSETS__ and __DNDEV_ASSET_MANIFEST__ injected here -->
|
|
15
|
+
|
|
16
|
+
<!-- ✅ PWA: Manifest link (if exists) -->
|
|
17
|
+
<link rel="manifest" href="/manifest.json" />
|
|
18
|
+
|
|
19
|
+
<!-- Fonts: Inter, Space Grotesk, Playfair, Roboto loaded via @donotdev/ui/dndev.css (bundled). -->
|
|
20
|
+
<!-- DNDEV_FONT_PRELOADS -->
|
|
21
|
+
|
|
22
|
+
<!-- ✅ PERFORMANCE: Preconnect to external domains (OAuth providers) -->
|
|
23
|
+
<!-- GitHub OAuth -->
|
|
24
|
+
<link rel="preconnect" href="https://github.com">
|
|
25
|
+
<link rel="preconnect" href="https://api.github.com">
|
|
26
|
+
<!-- Google OAuth (remove if not using) -->
|
|
27
|
+
<link rel="preconnect" href="https://accounts.google.com">
|
|
28
|
+
<link rel="preconnect" href="https://oauth2.googleapis.com">
|
|
29
|
+
<link rel="preconnect" href="https://apis.google.com">
|
|
30
|
+
<link rel="preconnect" href="https://www.googleapis.com">
|
|
31
|
+
|
|
32
|
+
<!-- ✅ SEO: Basic meta tags (AutoMetaTags will override these) -->
|
|
33
|
+
<title>Loading...</title>
|
|
34
|
+
<meta name="description" content="Modern web application powered by DoNotDev" />
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
<!-- ✅ PERFORMANCE: Critical CSS inlined here by build -->
|
|
38
|
+
<style>
|
|
39
|
+
/* Critical above-the-fold styles */
|
|
40
|
+
html, body {
|
|
41
|
+
margin: 0;
|
|
42
|
+
padding: 0;
|
|
43
|
+
box-sizing: border-box;
|
|
44
|
+
}
|
|
45
|
+
body {
|
|
46
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
|
|
47
|
+
line-height: 1.5;
|
|
48
|
+
-webkit-font-smoothing: antialiased;
|
|
49
|
+
-moz-osx-font-smoothing: grayscale;
|
|
50
|
+
background: #ffffff;
|
|
51
|
+
color: #000000;
|
|
52
|
+
}
|
|
53
|
+
@media (prefers-color-scheme: dark) {
|
|
54
|
+
body {
|
|
55
|
+
background: #000000;
|
|
56
|
+
color: #ffffff;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
/* Critical shell loader styles - pure CSS, instant render */
|
|
60
|
+
#shell-loader {
|
|
61
|
+
position: fixed;
|
|
62
|
+
inset: 0;
|
|
63
|
+
background: #000000;
|
|
64
|
+
display: flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
justify-content: center;
|
|
67
|
+
z-index: 9999;
|
|
68
|
+
opacity: 1;
|
|
69
|
+
transition: opacity 0.3s ease-out;
|
|
70
|
+
will-change: opacity;
|
|
71
|
+
/* Isolate from framework CSS */
|
|
72
|
+
margin: 0;
|
|
73
|
+
padding: 0;
|
|
74
|
+
box-sizing: border-box;
|
|
75
|
+
}
|
|
76
|
+
#shell-loader.shell-loader--fading {
|
|
77
|
+
opacity: 0;
|
|
78
|
+
pointer-events: none;
|
|
79
|
+
}
|
|
80
|
+
.shell-loader__content {
|
|
81
|
+
text-align: center;
|
|
82
|
+
color: white;
|
|
83
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
|
|
84
|
+
width: 100%;
|
|
85
|
+
max-width: 500px;
|
|
86
|
+
/* Prevent layout shifts from framework CSS */
|
|
87
|
+
margin: 0;
|
|
88
|
+
padding: 0;
|
|
89
|
+
box-sizing: border-box;
|
|
90
|
+
line-height: 1.2;
|
|
91
|
+
}
|
|
92
|
+
.shell-loader__brand {
|
|
93
|
+
display: block;
|
|
94
|
+
font-size: clamp(3rem, 10vw, 5rem);
|
|
95
|
+
font-weight: 800;
|
|
96
|
+
letter-spacing: -0.05em;
|
|
97
|
+
margin: 0 0 1rem 0;
|
|
98
|
+
padding: 0;
|
|
99
|
+
box-sizing: border-box;
|
|
100
|
+
}
|
|
101
|
+
/* ECG Waveform Animation - stroke-dasharray (performant, cross-browser, standard SVG technique) */
|
|
102
|
+
.shell-loader__dots {
|
|
103
|
+
display: block;
|
|
104
|
+
width: 300px;
|
|
105
|
+
height: 60px;
|
|
106
|
+
margin: 0 auto;
|
|
107
|
+
padding: 0;
|
|
108
|
+
box-sizing: border-box;
|
|
109
|
+
}
|
|
110
|
+
.shell-loader__dots svg {
|
|
111
|
+
width: 100%;
|
|
112
|
+
height: 100%;
|
|
113
|
+
display: block;
|
|
114
|
+
}
|
|
115
|
+
.shell-loader__dots path {
|
|
116
|
+
stroke: #667eea;
|
|
117
|
+
stroke-width: 3;
|
|
118
|
+
fill: none;
|
|
119
|
+
stroke-linecap: round;
|
|
120
|
+
stroke-linejoin: round;
|
|
121
|
+
stroke-dasharray: 400;
|
|
122
|
+
stroke-dashoffset: 400;
|
|
123
|
+
animation: ecg-draw 2s linear infinite;
|
|
124
|
+
}
|
|
125
|
+
@keyframes ecg-draw {
|
|
126
|
+
0% {
|
|
127
|
+
stroke-dashoffset: 400;
|
|
128
|
+
opacity: 0;
|
|
129
|
+
}
|
|
130
|
+
10% {
|
|
131
|
+
opacity: 1;
|
|
132
|
+
}
|
|
133
|
+
70% {
|
|
134
|
+
stroke-dashoffset: 0;
|
|
135
|
+
opacity: 1;
|
|
136
|
+
}
|
|
137
|
+
90%, 100% {
|
|
138
|
+
stroke-dashoffset: 0;
|
|
139
|
+
opacity: 0;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
</style>
|
|
143
|
+
</head>
|
|
144
|
+
<body>
|
|
145
|
+
|
|
146
|
+
<!-- ✅ SHELL LOADER: Branded loading experience before React loads -->
|
|
147
|
+
<div id="shell-loader">
|
|
148
|
+
<div class="shell-loader__content">
|
|
149
|
+
<span class="shell-loader__brand">DoNotDev</span>
|
|
150
|
+
<span class="shell-loader__dots">
|
|
151
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 300 60" preserveAspectRatio="xMidYMid meet">
|
|
152
|
+
<path d="M0,30 L100,30 L110,20 L120,30 L130,30 L140,5 L150,55 L160,30 L175,30 L190,20 L205,30 L300,30" />
|
|
153
|
+
</svg>
|
|
154
|
+
</span>
|
|
155
|
+
</div>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<!-- ✅ APP: React app container -->
|
|
159
|
+
<div id="root"></div>
|
|
160
|
+
|
|
161
|
+
<!-- ✅ PERFORMANCE: Module script with proper attributes -->
|
|
162
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
163
|
+
</body>
|
|
164
|
+
</html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500" xmlns:v="https://vecta.io/nano"><path d="M11.995 68.133c-.747.14-2.114 1.276-2.986 2.409l-1.557 2.199-.062 104.273.684 251.717c.935 2.835 3.423 4.749 6.347 4.89 2.366.142 3.61-.566 10.767-5.599l42.311-30.054L112.3 366.14l16.366-11.768c3.234-2.268 6.098-4.749 6.346-5.599 1.369-3.828 9.271-32.679 9.023-32.891-.124-.213-1.743.78-3.547 2.198-1.803 1.347-8.773 6.309-15.432 10.916L87.1 355.862l-24.891 17.65-23.831 16.729c-5.413 3.898-10.143 7.017-10.454 7.017-.685 0-.809-291.837-.063-292.334.249-.141 1.619.497 3.049 1.559l51.521 37.711 18.668 13.61 20.222 14.674 13.69 9.996 9.333 6.734 12.756 9.215c10.455 7.798 15.992 11.767 16.553 11.908.374.073 1.99-4.889 3.609-11.058l3.048-11.202-5.537-4.038-10.641-7.799c-2.863-1.984-10.391-7.584-16.8-12.405l-23.21-17.154-20.721-15.24L89.4 121.157c-2.613-1.986-7.653-5.671-11.2-8.223L23.506 73.096c-6.471-4.679-8.774-5.671-11.512-4.962l.001-.001zm235.829 4.466c-1.057.708-2.302 2.055-2.737 2.977-.622 1.276-.809 14.673-.746 62.733l.062 61.104 4.915 8.01 10.206 16.871 5.225 8.861.187-62.308 1.057-61.812c.56.212 6.098 4.111 12.321 8.648l43.806 31.97c1.307.991 1.431.708 5.973-9.641l4.667-10.705-1.618-1.346-14.747-10.845-20.844-15.313-36.65-26.864c-5.788-4.182-7.841-4.608-11.077-2.34zm-34.784 196.85l-7.279 15.807-11.511 25.166c.123.141 5.351-3.474 11.697-7.939l18.356-13.042 8.027-5.813c1.057-.922.685-1.701-6.347-13.469l-7.466-12.546-5.477 11.838v-.002z" fill="var(--primary,#dc143c)"/><path d="M376.752 57.145l-4.977 11.2c-1.432 2.907-4.792 10.066-7.468 15.95l-9.955 21.619-25.388 54.583-11.2 24.101-20.658 43.949-11.7 24.81-7.84 16.304c-.809 1.347-.621 1.631-15.679-23.747l-26.072-43.949-22.213-37.71c-8.587-14.815-10.267-16.73-12.322-14.319-.435.566-2.676 8.009-4.978 16.586l-22.711 82.228-13.378 47.494-8.153 29.064-18.232 64.86-12.133 43.382-4.355 16.162c.186 0 3.671-7.443 14.871-31.898l10.578-22.684 8.088-17.013 8.774-18.43 9.771-20.556 7.901-16.659 9.333-19.848 9.957-21.267 10.765-23.038c6.844-14.957 9.397-20.061 10.08-20.273.374-.142 3.485 4.606 6.968 10.562l24.828 41.255 31.981 52.809c5.289 8.932 6.846 11.06 8.153 11.2.87.142 1.929-.212 2.302-.779.685-.851 19.6-65.499 44.054-150.421l25.823-88.889 17.798-62.168c-.125-.141-1.308 2.34-2.615 5.53h.002z" fill="var(--accent,#ffd700)"/><path d="M359.081 153.904l-3.111 11.555c-1.369 4.89-2.302 9.074-2.053 9.287s5.041 3.757 10.703 7.869l33.289 24.314 37.645 27.505 19.601 14.318 6.409 4.536c2.489 1.49 5.538-.991-32.542 26.369l-38.269 27.432-31.236 22.402-33.913 24.456-28.187 20.273-12.134 8.648c-10.019 7.16-19.413 13.681-19.848 13.681-.188 0-.374-14.248-.374-31.757v-31.686l-4.419-7.088-10.019-16.518c-3.111-5.102-5.849-9.356-6.098-9.356s-.375 11.909-.249 26.37l.249 64.223v37.711l2.054 2.623c1.866 2.268 2.489 2.553 4.914 2.553 3.237 0 4.169-.568 18.855-11.202l76.535-55.291 21.78-15.807 120.403-86.411c3.67-2.552 5.102-4.748 5.475-8.434.188-2.409 0-3.971-.746-5.599-1.244-2.623-2.677-3.686-48.908-37.711l-51.023-37.642-14.624-10.774-12.88-9.427c-2.986-2.199-5.724-4.041-6.035-4.041-.374 0-.934 1.206-1.245 2.622l.001-.003z" fill="var(--primary,#dc143c)"/></svg>
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Main application component
|
|
3
|
+
*
|
|
4
|
+
* Admin preset with cockpit layout wrapper.
|
|
5
|
+
* Single unified store (DoNotDashStore) for all app state.
|
|
6
|
+
* Terminal embedded inside CockpitLayout (route-aware split panes).
|
|
7
|
+
* Ctrl+K navigation uses framework GoToWrapper (rendered by DnDevLayout).
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { ViteAppProviders } from '@donotdev/ui/vite';
|
|
11
|
+
|
|
12
|
+
import { CockpitLayout } from './components/CockpitLayout';
|
|
13
|
+
import './components/TerminalPanel.css';
|
|
14
|
+
import './components/CockpitLayout.css';
|
|
15
|
+
import './components/Kanban.css';
|
|
16
|
+
import './components/markdown-prose.css';
|
|
17
|
+
import './config/providers';
|
|
18
|
+
import { appConfig } from './config/app';
|
|
19
|
+
import { useDoNotDashStore } from './stores/dndevStore';
|
|
20
|
+
|
|
21
|
+
export function App() {
|
|
22
|
+
return (
|
|
23
|
+
<ViteAppProviders
|
|
24
|
+
config={appConfig}
|
|
25
|
+
layout={{
|
|
26
|
+
breadcrumbs: 'smart',
|
|
27
|
+
footer: () => null,
|
|
28
|
+
wrapper: ({ children }) => <CockpitLayout>{children}</CockpitLayout>,
|
|
29
|
+
}}
|
|
30
|
+
customStores={[
|
|
31
|
+
{ name: 'donotdash', type: 'regular', store: useDoNotDashStore },
|
|
32
|
+
]}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/* ===========================
|
|
2
|
+
COCKPIT LAYOUT
|
|
3
|
+
Route-aware split panes via react-resizable-panels
|
|
4
|
+
=========================== */
|
|
5
|
+
|
|
6
|
+
.dndev-cockpit {
|
|
7
|
+
display: flex;
|
|
8
|
+
flex-direction: column;
|
|
9
|
+
height: calc(100vh - var(--header-height, 56px));
|
|
10
|
+
min-height: 0;
|
|
11
|
+
overflow: hidden;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/* Panel groups fill cockpit */
|
|
15
|
+
.dndev-cockpit > [data-panel-group] {
|
|
16
|
+
flex: 1;
|
|
17
|
+
min-height: 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/* Panels must shrink below content intrinsic width */
|
|
21
|
+
.dndev-cockpit [data-panel] {
|
|
22
|
+
min-width: 0;
|
|
23
|
+
min-height: 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* Kill breakthrough CSS inside cockpit panels.
|
|
27
|
+
Sections use width: calc(100dvw - var(--sidebar-width)) to break out of PageContainer,
|
|
28
|
+
but inside react-resizable-panels the panel IS the boundary, not the viewport.
|
|
29
|
+
Without this, Sections render at 100dvw and bleed under the terminal panel. */
|
|
30
|
+
.dndev-cockpit .dndev-container > .dndev-section-full-width {
|
|
31
|
+
width: 100%;
|
|
32
|
+
max-width: 100%;
|
|
33
|
+
margin-inline: 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* ===========================
|
|
37
|
+
PANES
|
|
38
|
+
=========================== */
|
|
39
|
+
|
|
40
|
+
.dndev-cockpit-pane {
|
|
41
|
+
height: 100%;
|
|
42
|
+
min-height: 0;
|
|
43
|
+
min-width: 0;
|
|
44
|
+
overflow: hidden;
|
|
45
|
+
display: flex;
|
|
46
|
+
flex-direction: column;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.dndev-cockpit-pane--content {
|
|
50
|
+
overflow: hidden;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/* Scrollable area for page content — below the toolbar */
|
|
54
|
+
.dndev-cockpit-scroll {
|
|
55
|
+
flex: 1;
|
|
56
|
+
min-height: 0;
|
|
57
|
+
min-width: 0;
|
|
58
|
+
overflow-x: hidden;
|
|
59
|
+
overflow-y: auto;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* ===========================
|
|
63
|
+
COCKPIT TOOLBAR — real header row above content
|
|
64
|
+
=========================== */
|
|
65
|
+
|
|
66
|
+
.dndev-cockpit-toolbar {
|
|
67
|
+
flex-shrink: 0;
|
|
68
|
+
display: flex;
|
|
69
|
+
justify-content: space-between;
|
|
70
|
+
align-items: center;
|
|
71
|
+
padding: 4px 8px;
|
|
72
|
+
gap: 8px;
|
|
73
|
+
border-bottom: 1px solid var(--border);
|
|
74
|
+
background: var(--card);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.dndev-cockpit-toolbar-start,
|
|
78
|
+
.dndev-cockpit-toolbar-end {
|
|
79
|
+
display: flex;
|
|
80
|
+
align-items: center;
|
|
81
|
+
gap: 4px;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/* OverlaySlotTarget center area — pages inject controls here */
|
|
85
|
+
.dndev-cockpit-overlays-center {
|
|
86
|
+
display: flex;
|
|
87
|
+
align-items: center;
|
|
88
|
+
gap: 4px;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* GoTo search — constrain width so it doesn't stretch to fill flex space */
|
|
92
|
+
.dndev-cockpit-goto {
|
|
93
|
+
max-width: 320px;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.dndev-cockpit-pane--terminal {
|
|
97
|
+
background: #0a0a0a;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.dndev-cockpit-pane--left-stack {
|
|
101
|
+
border-inline-end: none;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.dndev-cockpit-pane--explorer {
|
|
105
|
+
overflow-y: auto;
|
|
106
|
+
background: var(--card);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* ===========================
|
|
110
|
+
RESIZE HANDLES
|
|
111
|
+
=========================== */
|
|
112
|
+
|
|
113
|
+
.dndev-cockpit-handle {
|
|
114
|
+
flex-shrink: 0;
|
|
115
|
+
transition: background var(--dur-fast, 100ms);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.dndev-cockpit-handle:hover,
|
|
119
|
+
.dndev-cockpit-handle:focus-visible,
|
|
120
|
+
.dndev-cockpit-handle[data-resize-handle-active] {
|
|
121
|
+
background: var(--primary);
|
|
122
|
+
outline: none;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/* Vertical handle (between left/right panes) */
|
|
126
|
+
.dndev-cockpit-handle--vertical {
|
|
127
|
+
width: 4px;
|
|
128
|
+
cursor: col-resize;
|
|
129
|
+
background: var(--border);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* Horizontal handle (between top/bottom panes) */
|
|
133
|
+
.dndev-cockpit-handle--horizontal {
|
|
134
|
+
height: 4px;
|
|
135
|
+
cursor: row-resize;
|
|
136
|
+
background: var(--border);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
@media (hover: none) {
|
|
140
|
+
.dndev-cockpit-handle--vertical { width: 8px; }
|
|
141
|
+
.dndev-cockpit-handle--horizontal { height: 8px; }
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* ===========================
|
|
145
|
+
FULLSCREEN — CSS-only, no React tree change
|
|
146
|
+
Terminal breaks out via position:fixed, content hidden
|
|
147
|
+
=========================== */
|
|
148
|
+
|
|
149
|
+
.dndev-cockpit[data-fullscreen] .dndev-cockpit-pane--content,
|
|
150
|
+
.dndev-cockpit[data-fullscreen] .dndev-cockpit-pane--explorer,
|
|
151
|
+
.dndev-cockpit[data-fullscreen] .dndev-cockpit-handle {
|
|
152
|
+
display: none !important;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* Terminal in Panel (horizontal/left-stack) */
|
|
156
|
+
.dndev-cockpit[data-fullscreen] .dndev-cockpit-pane--terminal {
|
|
157
|
+
position: fixed;
|
|
158
|
+
inset: 0;
|
|
159
|
+
z-index: 200;
|
|
160
|
+
height: 100vh !important;
|
|
161
|
+
max-height: none !important;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/* Terminal direct child (bottom-bar) */
|
|
165
|
+
.dndev-cockpit[data-fullscreen] > .dndev-terminal-panel {
|
|
166
|
+
position: fixed;
|
|
167
|
+
inset: 0;
|
|
168
|
+
z-index: 200;
|
|
169
|
+
height: 100vh !important;
|
|
170
|
+
max-height: none !important;
|
|
171
|
+
min-height: 0 !important;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/* ===========================
|
|
175
|
+
REMOVE FRAMEWORK COMPENSATION
|
|
176
|
+
The terminal is no longer fixed — it's inside the layout.
|
|
177
|
+
=========================== */
|
|
178
|
+
|
|
179
|
+
.dndev-layout {
|
|
180
|
+
padding-bottom: 0 !important;
|
|
181
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Cockpit layout wrapper — route-aware split panes via react-resizable-panels v4
|
|
3
|
+
*
|
|
4
|
+
* Reads current route, maps to a LayoutPreset, and renders the appropriate
|
|
5
|
+
* split arrangement with terminal as a contextual slot.
|
|
6
|
+
*
|
|
7
|
+
* Two presets:
|
|
8
|
+
* - bottom-bar: content on top, terminal at bottom (observer pages)
|
|
9
|
+
* - horizontal: content left, terminal right (operator pages)
|
|
10
|
+
*
|
|
11
|
+
* Terminal is rendered ONCE per layout — never conditionally swapped.
|
|
12
|
+
* Fullscreen is CSS-only (data-fullscreen attribute), no React tree change.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { useCallback, useEffect, useRef } from 'react';
|
|
16
|
+
import { Group, Panel, Separator, usePanelRef } from 'react-resizable-panels';
|
|
17
|
+
|
|
18
|
+
import { LanguageSelector } from '@donotdev/core';
|
|
19
|
+
import { GoTo, ThemeToggle, useLocation } from '@donotdev/ui';
|
|
20
|
+
|
|
21
|
+
import { useDoNotDashStore } from '../stores/dndevStore';
|
|
22
|
+
import { ModeToggle } from './ModeToggle';
|
|
23
|
+
import { OverlaySlotProvider, OverlaySlotTarget } from './OverlaySlot';
|
|
24
|
+
import { TerminalPanel } from './TerminalPanel';
|
|
25
|
+
|
|
26
|
+
import type { ReactNode } from 'react';
|
|
27
|
+
import type { Layout } from 'react-resizable-panels';
|
|
28
|
+
|
|
29
|
+
// ============================================================================
|
|
30
|
+
// CONTENT PANE — scrollable children + fixed overlays (GoTo, ModeToggle)
|
|
31
|
+
// ============================================================================
|
|
32
|
+
|
|
33
|
+
function ContentPane({ children }: { children: ReactNode }): ReactNode {
|
|
34
|
+
const { pathname } = useLocation();
|
|
35
|
+
const scrollRef = useRef<HTMLDivElement>(null);
|
|
36
|
+
|
|
37
|
+
// Reset scroll position on route change
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (scrollRef.current) scrollRef.current.scrollTop = 0;
|
|
40
|
+
}, [pathname]);
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<div className="dndev-cockpit-pane dndev-cockpit-pane--content">
|
|
44
|
+
<div className="dndev-cockpit-toolbar">
|
|
45
|
+
<div className="dndev-cockpit-toolbar-start">
|
|
46
|
+
<ThemeToggle display="compact" />
|
|
47
|
+
<LanguageSelector display="compact" />
|
|
48
|
+
<GoTo className="dndev-cockpit-goto" />
|
|
49
|
+
</div>
|
|
50
|
+
<OverlaySlotTarget />
|
|
51
|
+
<div className="dndev-cockpit-toolbar-end">
|
|
52
|
+
<ModeToggle />
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
<div ref={scrollRef} className="dndev-cockpit-scroll">
|
|
56
|
+
{children}
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ============================================================================
|
|
63
|
+
// LAYOUT PRESETS
|
|
64
|
+
// ============================================================================
|
|
65
|
+
|
|
66
|
+
type LayoutPreset = 'bottom-bar' | 'horizontal';
|
|
67
|
+
|
|
68
|
+
const LAYOUT_MAP: Record<string, LayoutPreset> = {
|
|
69
|
+
'/': 'bottom-bar',
|
|
70
|
+
'/phases': 'horizontal',
|
|
71
|
+
'/board': 'bottom-bar',
|
|
72
|
+
'/grill': 'horizontal',
|
|
73
|
+
'/settings': 'bottom-bar',
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
function getPreset(pathname: string): LayoutPreset {
|
|
77
|
+
if (LAYOUT_MAP[pathname]) return LAYOUT_MAP[pathname];
|
|
78
|
+
const base = '/' + pathname.split('/').filter(Boolean)[0];
|
|
79
|
+
return LAYOUT_MAP[base] ?? 'bottom-bar';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ============================================================================
|
|
83
|
+
// RESIZE HANDLE
|
|
84
|
+
// ============================================================================
|
|
85
|
+
|
|
86
|
+
function ResizeHandle({ direction }: { direction: 'horizontal' | 'vertical' }) {
|
|
87
|
+
return (
|
|
88
|
+
<Separator
|
|
89
|
+
className={`dndev-cockpit-handle dndev-cockpit-handle--${direction}`}
|
|
90
|
+
/>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ============================================================================
|
|
95
|
+
// COMPONENT
|
|
96
|
+
// ============================================================================
|
|
97
|
+
|
|
98
|
+
interface CockpitLayoutProps {
|
|
99
|
+
children: ReactNode;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export function CockpitLayout({ children }: CockpitLayoutProps): ReactNode {
|
|
103
|
+
const { pathname } = useLocation();
|
|
104
|
+
const preset = getPreset(pathname);
|
|
105
|
+
const isFullscreen = useDoNotDashStore((s) => s.isFullscreen);
|
|
106
|
+
const isExpanded = useDoNotDashStore((s) => s.isExpanded);
|
|
107
|
+
const terminalPanelRef = usePanelRef();
|
|
108
|
+
|
|
109
|
+
// Sync store isExpanded ↔ panel collapse/expand (bidirectional, loop-safe)
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
const panel = terminalPanelRef.current;
|
|
112
|
+
if (!panel) return;
|
|
113
|
+
const panelCollapsed = panel.isCollapsed();
|
|
114
|
+
if (isExpanded && panelCollapsed) panel.expand();
|
|
115
|
+
else if (!isExpanded && !panelCollapsed) panel.collapse();
|
|
116
|
+
}, [isExpanded, terminalPanelRef]);
|
|
117
|
+
|
|
118
|
+
// Sync panel collapse/expand → store (handles manual drag past minSize)
|
|
119
|
+
const syncCollapseToStore = useCallback(() => {
|
|
120
|
+
const panel = terminalPanelRef.current;
|
|
121
|
+
if (!panel) return;
|
|
122
|
+
const collapsed = panel.isCollapsed();
|
|
123
|
+
const storeExpanded = useDoNotDashStore.getState().isExpanded;
|
|
124
|
+
if (collapsed && storeExpanded) useDoNotDashStore.getState().setExpanded(false);
|
|
125
|
+
else if (!collapsed && !storeExpanded) useDoNotDashStore.getState().setExpanded(true);
|
|
126
|
+
}, [terminalPanelRef]);
|
|
127
|
+
|
|
128
|
+
// GOTCHA: onLayoutChanged fires on initial render. Without debounce:
|
|
129
|
+
// saveSizes → writes .dndev/dashboard.json → file watcher HMR event → re-render → loop.
|
|
130
|
+
// 500ms debounce breaks the cycle — initial render fires are swallowed.
|
|
131
|
+
const saveTimer = useRef<ReturnType<typeof setTimeout>>(null);
|
|
132
|
+
const saveSizes = useCallback((layout: Layout) => {
|
|
133
|
+
if (saveTimer.current) clearTimeout(saveTimer.current);
|
|
134
|
+
saveTimer.current = setTimeout(() => {
|
|
135
|
+
const sizes = Object.values(layout);
|
|
136
|
+
useDoNotDashStore.getState().setPanelSizes(pathname, sizes);
|
|
137
|
+
}, 500);
|
|
138
|
+
}, [pathname]);
|
|
139
|
+
|
|
140
|
+
// Clean up debounce timer on unmount or pathname change
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
return () => {
|
|
143
|
+
if (saveTimer.current) clearTimeout(saveTimer.current);
|
|
144
|
+
};
|
|
145
|
+
}, [pathname]);
|
|
146
|
+
|
|
147
|
+
const direction = preset === 'horizontal' ? 'horizontal' : 'vertical';
|
|
148
|
+
|
|
149
|
+
if (preset === 'horizontal') {
|
|
150
|
+
return (
|
|
151
|
+
<OverlaySlotProvider>
|
|
152
|
+
<div className="dndev-cockpit" data-preset="horizontal" data-fullscreen={isFullscreen || undefined}>
|
|
153
|
+
<Group
|
|
154
|
+
orientation="horizontal"
|
|
155
|
+
onLayoutChanged={saveSizes}
|
|
156
|
+
id={`cockpit-${pathname}`}
|
|
157
|
+
>
|
|
158
|
+
<Panel defaultSize="65%" minSize="30%">
|
|
159
|
+
<ContentPane>{children}</ContentPane>
|
|
160
|
+
</Panel>
|
|
161
|
+
<ResizeHandle direction="vertical" />
|
|
162
|
+
<Panel
|
|
163
|
+
defaultSize="35%"
|
|
164
|
+
minSize="20%"
|
|
165
|
+
collapsible
|
|
166
|
+
collapsedSize="34px"
|
|
167
|
+
panelRef={terminalPanelRef}
|
|
168
|
+
onResize={syncCollapseToStore}
|
|
169
|
+
>
|
|
170
|
+
<div className="dndev-cockpit-pane dndev-cockpit-pane--terminal">
|
|
171
|
+
<TerminalPanel embedded direction={direction} />
|
|
172
|
+
</div>
|
|
173
|
+
</Panel>
|
|
174
|
+
</Group>
|
|
175
|
+
</div>
|
|
176
|
+
</OverlaySlotProvider>
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// bottom-bar (default) — content on top, terminal at bottom, resizable
|
|
181
|
+
return (
|
|
182
|
+
<OverlaySlotProvider>
|
|
183
|
+
<div className="dndev-cockpit" data-preset="bottom-bar" data-fullscreen={isFullscreen || undefined}>
|
|
184
|
+
<Group
|
|
185
|
+
orientation="vertical"
|
|
186
|
+
onLayoutChanged={saveSizes}
|
|
187
|
+
id={`cockpit-${pathname}`}
|
|
188
|
+
>
|
|
189
|
+
<Panel defaultSize="75%" minSize="20%">
|
|
190
|
+
<ContentPane>{children}</ContentPane>
|
|
191
|
+
</Panel>
|
|
192
|
+
<ResizeHandle direction="horizontal" />
|
|
193
|
+
<Panel
|
|
194
|
+
defaultSize="25%"
|
|
195
|
+
minSize="80px"
|
|
196
|
+
collapsible
|
|
197
|
+
collapsedSize="34px"
|
|
198
|
+
panelRef={terminalPanelRef}
|
|
199
|
+
onResize={syncCollapseToStore}
|
|
200
|
+
>
|
|
201
|
+
<div className="dndev-cockpit-pane dndev-cockpit-pane--terminal">
|
|
202
|
+
<TerminalPanel embedded direction={direction} />
|
|
203
|
+
</div>
|
|
204
|
+
</Panel>
|
|
205
|
+
</Group>
|
|
206
|
+
</div>
|
|
207
|
+
</OverlaySlotProvider>
|
|
208
|
+
);
|
|
209
|
+
}
|