@tanstack/cta-ui 0.10.0-alpha.24 → 0.10.0-alpha.27

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 (80) hide show
  1. package/README.md +20 -0
  2. package/dist/assets/index-DSKioOfX.css +1 -0
  3. package/dist/assets/index-DWTDdndE.js +213 -0
  4. package/dist/assets/index-DWTDdndE.js.map +1 -0
  5. package/dist/favicon.ico +0 -0
  6. package/dist/index.html +19 -0
  7. package/dist/logo-color-100w.png +0 -0
  8. package/dist/logo192.png +0 -0
  9. package/dist/logo512.png +0 -0
  10. package/dist/manifest.json +25 -0
  11. package/dist/robots.txt +3 -0
  12. package/dist/tailwind.svg +1 -0
  13. package/dist/tanstack.png +0 -0
  14. package/dist/typescript.svg +1 -0
  15. package/index.html +18 -0
  16. package/lib/engine-handling/add-to-app-wrapper.ts +134 -0
  17. package/{src → lib}/engine-handling/create-app-wrapper.ts +48 -39
  18. package/{src → lib}/engine-handling/file-helpers.ts +4 -2
  19. package/{src → lib}/engine-handling/generate-initial-payload.ts +58 -51
  20. package/lib/engine-handling/server-environment.ts +37 -0
  21. package/lib/index.ts +150 -34
  22. package/lib/types.d.ts +20 -0
  23. package/lib-dist/engine-handling/add-to-app-wrapper.d.ts +14 -0
  24. package/lib-dist/engine-handling/add-to-app-wrapper.js +78 -0
  25. package/lib-dist/engine-handling/create-app-wrapper.d.ts +14 -0
  26. package/lib-dist/engine-handling/create-app-wrapper.js +70 -0
  27. package/lib-dist/engine-handling/file-helpers.d.ts +2 -0
  28. package/lib-dist/engine-handling/file-helpers.js +21 -0
  29. package/lib-dist/engine-handling/framework-registration.d.ts +1 -0
  30. package/lib-dist/engine-handling/framework-registration.js +10 -0
  31. package/lib-dist/engine-handling/generate-initial-payload.d.ts +32 -0
  32. package/lib-dist/engine-handling/generate-initial-payload.js +88 -0
  33. package/lib-dist/engine-handling/server-environment.d.ts +17 -0
  34. package/lib-dist/engine-handling/server-environment.js +18 -0
  35. package/lib-dist/index.d.ts +5 -7
  36. package/lib-dist/index.js +124 -18
  37. package/package.json +12 -12
  38. package/public/logo-color-100w.png +0 -0
  39. package/src/components/background-animation.tsx +229 -0
  40. package/src/components/cta-sidebar.tsx +28 -33
  41. package/src/components/file-navigator.tsx +72 -74
  42. package/src/components/header.tsx +31 -0
  43. package/src/components/sidebar-items/add-ons.tsx +48 -45
  44. package/src/components/sidebar-items/mode-selector.tsx +6 -4
  45. package/src/components/sidebar-items/project-name.tsx +4 -5
  46. package/src/components/sidebar-items/typescript-switch.tsx +3 -3
  47. package/src/components/startup-dialog.tsx +4 -6
  48. package/src/components/ui/switch.tsx +6 -6
  49. package/src/hooks/use-mounted.ts +9 -0
  50. package/src/hooks/use-preferred-reduced-motion.ts +27 -0
  51. package/src/index.tsx +48 -0
  52. package/src/lib/api.ts +10 -8
  53. package/src/main.tsx +12 -0
  54. package/src/store/project.ts +36 -20
  55. package/src/styles.css +90 -18
  56. package/src/types.d.ts +1 -1
  57. package/tailwind.config.cjs +47 -0
  58. package/vite.config.ts +16 -0
  59. package/app.config.js +0 -22
  60. package/src/api.ts +0 -6
  61. package/src/client.tsx +0 -8
  62. package/src/engine-handling/add-to-app-wrapper.ts +0 -114
  63. package/src/engine-handling/server-environment.ts +0 -30
  64. package/src/integrations/tanstack-query/layout.tsx +0 -5
  65. package/src/integrations/tanstack-query/root-provider.tsx +0 -15
  66. package/src/logo.svg +0 -44
  67. package/src/routeTree.gen.ts +0 -88
  68. package/src/router.tsx +0 -32
  69. package/src/routes/__root.tsx +0 -86
  70. package/src/routes/api/add-to-app.ts +0 -21
  71. package/src/routes/api/create-app.ts +0 -21
  72. package/src/routes/api/dry-run-add-to-app.ts +0 -16
  73. package/src/routes/api/dry-run-create-app.ts +0 -16
  74. package/src/routes/api/initial-payload.ts +0 -10
  75. package/src/routes/api/load-remote-add-on.ts +0 -42
  76. package/src/routes/api/load-starter.ts +0 -47
  77. package/src/routes/api/shutdown.ts +0 -11
  78. package/src/routes/index.tsx +0 -17
  79. package/src/ssr.tsx +0 -12
  80. /package/{src → lib}/engine-handling/framework-registration.ts +0 -0
package/lib-dist/index.js CHANGED
@@ -1,23 +1,129 @@
1
1
  import { dirname, resolve } from 'node:path';
2
2
  import { fileURLToPath } from 'node:url';
3
- export function launchUI({ mode, addOns, options, forcedMode, forcedAddOns, }) {
4
- const projectPath = process.cwd();
5
- delete process.env.NODE_ENV;
6
- process.env.CTA_ADD_ONS = addOns?.join(',') || '';
7
- process.env.CTA_PROJECT_PATH = projectPath;
8
- process.env.CTA_OPTIONS = options ? JSON.stringify(options) : '';
9
- process.env.CTA_MODE = mode;
10
- if (forcedMode) {
11
- process.env.CTA_FORCED_ROUTER_MODE = forcedMode;
3
+ import express from 'express';
4
+ import cors from 'cors';
5
+ import chalk from 'chalk';
6
+ import { AddOnCompiledSchema, StarterCompiledSchema, } from '@tanstack/cta-engine';
7
+ import { addToAppWrapper } from './engine-handling/add-to-app-wrapper.js';
8
+ import { createAppWrapper } from './engine-handling/create-app-wrapper.js';
9
+ import { generateInitialPayload } from './engine-handling/generate-initial-payload.js';
10
+ import { setServerEnvironment } from './engine-handling/server-environment.js';
11
+ export function launchUI(options) {
12
+ const { port: requestedPort, ...rest } = options;
13
+ setServerEnvironment(rest);
14
+ const app = express();
15
+ app.use(cors());
16
+ app.use(express.json());
17
+ app.use(express.urlencoded({ extended: true }));
18
+ const launchUI = !process.env.CTA_DISABLE_UI;
19
+ if (launchUI) {
20
+ const packagePath = resolve(dirname(fileURLToPath(import.meta.url)), '..');
21
+ app.use(express.static(resolve(packagePath, 'dist')));
12
22
  }
13
- if (forcedAddOns) {
14
- process.env.CTA_FORCED_ADD_ONS = forcedAddOns.join(',');
15
- }
16
- const developerPath = resolve(dirname(fileURLToPath(import.meta.url)), '..');
17
- const configPath = resolve(developerPath, './app.config.js');
18
- process.chdir(developerPath);
19
- import(configPath).then(async (config) => {
20
- const out = await config.default;
21
- await out.dev();
23
+ app.post('/api/add-to-app', async (req, res) => {
24
+ await addToAppWrapper(req.body.addOns, {
25
+ response: res,
26
+ environmentFactory: options.environmentFactory,
27
+ });
28
+ });
29
+ app.post('/api/create-app', async (req, res) => {
30
+ await createAppWrapper(req.body.options, {
31
+ response: res,
32
+ environmentFactory: options.environmentFactory,
33
+ });
34
+ });
35
+ app.post('/api/dry-run-add-to-app', async (req, res) => {
36
+ res.send(await addToAppWrapper(req.body.addOns, {
37
+ dryRun: true,
38
+ environmentFactory: options.environmentFactory,
39
+ }));
40
+ });
41
+ app.post('/api/dry-run-create-app', async (req, res) => {
42
+ res.send(await createAppWrapper(req.body.options, {
43
+ dryRun: true,
44
+ environmentFactory: options.environmentFactory,
45
+ }));
46
+ });
47
+ app.get('/api/initial-payload', async (_req, res) => {
48
+ res.send(await generateInitialPayload());
49
+ });
50
+ app.get('/api/load-remote-add-on', async (req, res) => {
51
+ const { url } = req.query;
52
+ if (!url) {
53
+ res.status(400).send('URL is required');
54
+ return;
55
+ }
56
+ try {
57
+ const response = await fetch(url);
58
+ const data = await response.json();
59
+ const parsed = AddOnCompiledSchema.safeParse(data);
60
+ if (!parsed.success) {
61
+ res.status(400).json({ error: 'Invalid add-on data' });
62
+ }
63
+ else {
64
+ res.json({
65
+ id: url,
66
+ name: parsed.data.name,
67
+ description: parsed.data.description,
68
+ version: parsed.data.version,
69
+ author: parsed.data.author,
70
+ license: parsed.data.license,
71
+ link: parsed.data.link,
72
+ smallLogo: parsed.data.smallLogo,
73
+ logo: parsed.data.logo,
74
+ type: parsed.data.type,
75
+ modes: parsed.data.modes,
76
+ });
77
+ }
78
+ }
79
+ catch {
80
+ res.status(500).send('Failed to load add-on');
81
+ }
82
+ });
83
+ app.get('/api/load-starter', async (req, res) => {
84
+ const { url } = req.query;
85
+ if (!url) {
86
+ res.status(400).send('URL is required');
87
+ return;
88
+ }
89
+ try {
90
+ const response = await fetch(url);
91
+ const data = await response.json();
92
+ const parsed = StarterCompiledSchema.safeParse(data);
93
+ if (!parsed.success) {
94
+ res.status(400).json({ error: 'Invalid starter data' });
95
+ }
96
+ else {
97
+ res.json({
98
+ url,
99
+ id: parsed.data.id,
100
+ name: parsed.data.name,
101
+ description: parsed.data.description,
102
+ version: parsed.data.version,
103
+ author: parsed.data.author,
104
+ license: parsed.data.license,
105
+ dependsOn: parsed.data.dependsOn,
106
+ mode: parsed.data.mode,
107
+ typescript: parsed.data.typescript,
108
+ tailwind: parsed.data.tailwind,
109
+ banner: parsed.data.banner
110
+ ? url.replace('starter.json', parsed.data.banner)
111
+ : undefined,
112
+ });
113
+ }
114
+ }
115
+ catch {
116
+ res.status(500).send('Failed to load starter');
117
+ }
118
+ });
119
+ app.post('/api/shutdown', (_req, res) => {
120
+ setTimeout(() => {
121
+ process.exit(0);
122
+ }, 50);
123
+ res.send({ shutdown: true });
124
+ });
125
+ const port = requestedPort || process.env.PORT || 8080;
126
+ app.listen(port, () => {
127
+ console.log(`🔥 ${chalk.blueBright(`Create TanStack ${launchUI ? 'App' : 'API'}`)} is running on ${chalk.underline(`http://localhost:${port}`)}`);
22
128
  });
23
129
  }
package/package.json CHANGED
@@ -24,17 +24,15 @@
24
24
  "@tailwindcss/vite": "^4.0.6",
25
25
  "@tanstack/react-query": "^5.66.5",
26
26
  "@tanstack/react-query-devtools": "^5.66.5",
27
- "@tanstack/react-router": "^1.114.3",
28
- "@tanstack/react-router-devtools": "^1.114.3",
29
- "@tanstack/react-router-with-query": "^1.114.3",
30
- "@tanstack/react-start": "^1.114.3",
31
- "@tanstack/router-plugin": "^1.114.3",
32
27
  "@uiw/codemirror-theme-github": "^4.23.10",
33
28
  "@uiw/react-codemirror": "^4.23.10",
29
+ "chalk": "^5.4.1",
34
30
  "class-variance-authority": "^0.7.1",
35
31
  "clsx": "^2.1.1",
32
+ "cors": "^2.8.5",
36
33
  "embla-carousel-react": "^8.6.0",
37
34
  "execa": "^9.5.2",
35
+ "express": "^4.21.2",
38
36
  "jotai-tanstack-query": "^0.9.0",
39
37
  "lucide-react": "^0.476.0",
40
38
  "next-themes": "^0.4.6",
@@ -45,27 +43,29 @@
45
43
  "tailwind-merge": "^3.0.2",
46
44
  "tailwindcss": "^4.0.6",
47
45
  "tailwindcss-animate": "^1.0.7",
48
- "vinxi": "^0.5.3",
49
46
  "vite-tsconfig-paths": "^5.1.4",
50
47
  "zustand": "^5.0.3",
51
- "@tanstack/cta-engine": "0.10.0-alpha.21",
52
- "@tanstack/cta-framework-react-cra": "0.10.0-alpha.21",
53
- "@tanstack/cta-framework-solid": "0.10.0-alpha.21"
48
+ "@tanstack/cta-engine": "0.10.0-alpha.27",
49
+ "@tanstack/cta-framework-react-cra": "0.10.0-alpha.27",
50
+ "@tanstack/cta-framework-solid": "0.10.0-alpha.27"
54
51
  },
55
52
  "devDependencies": {
53
+ "@tailwindcss/typography": "^0.5.16",
56
54
  "@testing-library/dom": "^10.4.0",
57
55
  "@testing-library/react": "^16.2.0",
56
+ "@types/cors": "^2.8.17",
57
+ "@types/express": "^5.0.1",
58
58
  "@types/node": "^22.14.1",
59
59
  "@types/react": "^19.0.8",
60
60
  "@types/react-dom": "^19.0.3",
61
- "@vitejs/plugin-react": "^4.3.4",
61
+ "@vitejs/plugin-react": "^4.4.1",
62
62
  "@vitest/coverage-v8": "3.1.1",
63
63
  "jsdom": "^26.0.0",
64
64
  "typescript": "^5.7.2",
65
- "vite": "^6.1.0",
65
+ "vite": "^6.3.3",
66
66
  "vitest": "^3.0.5",
67
67
  "web-vitals": "^4.2.4"
68
68
  },
69
- "version": "0.10.0-alpha.24",
69
+ "version": "0.10.0-alpha.27",
70
70
  "scripts": {}
71
71
  }
Binary file
@@ -0,0 +1,229 @@
1
+ import * as React from 'react'
2
+ import { twMerge } from 'tailwind-merge'
3
+
4
+ import { useMounted } from '@/hooks/use-mounted'
5
+ import { usePrefersReducedMotion } from '@/hooks/use-preferred-reduced-motion'
6
+
7
+ export function BackgroundAnimation() {
8
+ const canvasRef = React.useRef<HTMLCanvasElement>(null)
9
+ const prefersReducedMotion = usePrefersReducedMotion()
10
+ const mounted = useMounted()
11
+ const isHomePage = false
12
+
13
+ React.useEffect(() => {
14
+ if (prefersReducedMotion !== false) {
15
+ return
16
+ }
17
+
18
+ const canvas = canvasRef.current
19
+
20
+ let morphDuration = 2000
21
+ const waitDuration = 1000 * 60 * 2
22
+
23
+ const easingFn = cubicBezier(0.645, 0.045, 0.355, 1.0)
24
+
25
+ if (canvas) {
26
+ const ctx = canvas.getContext('2d')!
27
+
28
+ let rafId: ReturnType<typeof requestAnimationFrame> | null = null
29
+ let timeout: ReturnType<typeof setTimeout> | null = null
30
+ let startTime = performance.now()
31
+
32
+ function createBlobs() {
33
+ return shuffle([
34
+ {
35
+ color: { h: 10, s: 100, l: 50 },
36
+ },
37
+ {
38
+ color: { h: 40, s: 100, l: 50 },
39
+ },
40
+ {
41
+ color: { h: 150, s: 100, l: 50 },
42
+ },
43
+ {
44
+ color: { h: 200, s: 100, l: 50 },
45
+ },
46
+ ]).map((blob) => ({
47
+ ...blob,
48
+ x: Math.random() * canvas!.width,
49
+ y: Math.random() * canvas!.height,
50
+ r: Math.random() * 500 + 700,
51
+ colorH: blob.color.h,
52
+ colorS: blob.color.s,
53
+ colorL: blob.color.l,
54
+ }))
55
+ }
56
+
57
+ function shuffle<T>(array: T[]) {
58
+ for (let i = array.length - 1; i > 0; i--) {
59
+ const j = Math.floor(Math.random() * (i + 1))
60
+ ;[array[i], array[j]] = [array[j], array[i]]
61
+ }
62
+ return array
63
+ }
64
+
65
+ let startBlobs = createBlobs()
66
+ let currentBlobs = startBlobs
67
+ let targetBlobs: ReturnType<typeof createBlobs> = []
68
+
69
+ function resizeHandler() {
70
+ // Create an offscreen canvas and copy the current content
71
+ const offscreen = document.createElement('canvas')
72
+ offscreen.width = canvas!.width
73
+ offscreen.height = canvas!.height
74
+ offscreen.getContext('2d')!.drawImage(canvas!, 0, 0)
75
+
76
+ // Resize the main canvas
77
+ canvas!.width = window.innerWidth
78
+ canvas!.height = window.innerHeight
79
+
80
+ // Stretch and redraw the saved content to fill the new size
81
+ ctx.drawImage(offscreen, 0, 0, canvas!.width, canvas!.height)
82
+ }
83
+
84
+ function start() {
85
+ if (timeout) {
86
+ clearTimeout(timeout)
87
+ }
88
+ if (rafId) {
89
+ cancelAnimationFrame(rafId)
90
+ }
91
+
92
+ startBlobs = JSON.parse(JSON.stringify(currentBlobs))
93
+ targetBlobs = createBlobs()
94
+ startTime = performance.now()
95
+ animate()
96
+ }
97
+
98
+ function animate() {
99
+ ctx.clearRect(0, 0, canvas!.width, canvas!.height)
100
+
101
+ const time = performance.now() - startTime
102
+ const progress = time / morphDuration
103
+ const easedProgress = easingFn(progress)
104
+
105
+ // Draw the blobs
106
+ startBlobs.forEach((startBlob, i) => {
107
+ const targetBlob = targetBlobs[i]
108
+
109
+ currentBlobs[i].x = interpolate(
110
+ startBlob.x,
111
+ targetBlob.x,
112
+ easedProgress,
113
+ )
114
+ currentBlobs[i].y = interpolate(
115
+ startBlob.y,
116
+ targetBlob.y,
117
+ easedProgress,
118
+ )
119
+
120
+ const gradient = ctx.createRadialGradient(
121
+ currentBlobs[i].x,
122
+ currentBlobs[i].y,
123
+ 0,
124
+ currentBlobs[i].x,
125
+ currentBlobs[i].y,
126
+ currentBlobs[i].r,
127
+ )
128
+
129
+ currentBlobs[i].colorH = interpolate(
130
+ startBlob.colorH,
131
+ targetBlob.colorH,
132
+ easedProgress,
133
+ )
134
+ currentBlobs[i].colorS = interpolate(
135
+ startBlob.colorS,
136
+ targetBlob.colorS,
137
+ easedProgress,
138
+ )
139
+ currentBlobs[i].colorL = interpolate(
140
+ startBlob.colorL,
141
+ targetBlob.colorL,
142
+ easedProgress,
143
+ )
144
+
145
+ gradient.addColorStop(
146
+ 0,
147
+ `hsla(${currentBlobs[i].colorH}, ${currentBlobs[i].colorS}%, ${currentBlobs[i].colorL}%, 1)`,
148
+ )
149
+ gradient.addColorStop(
150
+ 1,
151
+ `hsla(${currentBlobs[i].colorH}, ${currentBlobs[i].colorS}%, ${currentBlobs[i].colorL}%, 0)`,
152
+ )
153
+
154
+ ctx.fillStyle = gradient
155
+ ctx.beginPath()
156
+ ctx.arc(
157
+ currentBlobs[i].x,
158
+ currentBlobs[i].y,
159
+ currentBlobs[i].r,
160
+ 0,
161
+ Math.PI * 2,
162
+ )
163
+ ctx.fill()
164
+ })
165
+
166
+ if (progress < 1) {
167
+ rafId = requestAnimationFrame(animate)
168
+ } else {
169
+ timeout = setTimeout(() => {
170
+ morphDuration = 4000
171
+ start()
172
+ }, waitDuration)
173
+ }
174
+ }
175
+
176
+ resizeHandler()
177
+ start()
178
+ window.addEventListener('resize', resizeHandler)
179
+
180
+ return () => {
181
+ if (rafId) {
182
+ cancelAnimationFrame(rafId)
183
+ }
184
+ if (timeout) {
185
+ clearTimeout(timeout)
186
+ }
187
+ window.removeEventListener('resize', resizeHandler)
188
+ }
189
+ }
190
+ }, [prefersReducedMotion])
191
+
192
+ return (
193
+ <div
194
+ className={twMerge(
195
+ 'fixed inset-0 z-0 opacity-20 pointer-events-none',
196
+ 'transition-opacity duration-[2s] ease-linear',
197
+ '[&+*]:relative',
198
+ mounted
199
+ ? isHomePage
200
+ ? 'opacity-10 dark:opacity-20'
201
+ : 'opacity-10 dark:opacity-20'
202
+ : 'opacity-0',
203
+ )}
204
+ >
205
+ <canvas ref={canvasRef} />
206
+ </div>
207
+ )
208
+ }
209
+
210
+ function cubicBezier(p1x: number, p1y: number, p2x: number, p2y: number) {
211
+ return function (t: number) {
212
+ const cx = 3 * p1x
213
+ const bx = 3 * (p2x - p1x) - cx
214
+ const ax = 1 - cx - bx
215
+
216
+ const cy = 3 * p1y
217
+ const by = 3 * (p2y - p1y) - cy
218
+ const ay = 1 - cy - by
219
+
220
+ const x = ((ax * t + bx) * t + cx) * t
221
+ const y = ((ay * t + by) * t + cy) * t
222
+
223
+ return y
224
+ }
225
+ }
226
+
227
+ function interpolate(start: number, end: number, progress: number) {
228
+ return start + (end - start) * progress
229
+ }
@@ -1,11 +1,3 @@
1
- import {
2
- Sidebar,
3
- SidebarContent,
4
- SidebarFooter,
5
- SidebarGroup,
6
- SidebarHeader,
7
- } from '@/components/ui/sidebar'
8
-
9
1
  import SelectedAddOns from '@/components/sidebar-items/add-ons'
10
2
  import RunAddOns from '@/components/sidebar-items/run-add-ons'
11
3
  import RunCreateApp from '@/components/sidebar-items/run-create-app'
@@ -14,6 +6,8 @@ import ModeSelector from '@/components/sidebar-items/mode-selector'
14
6
  import TypescriptSwitch from '@/components/sidebar-items/typescript-switch'
15
7
  import StarterDialog from '@/components/sidebar-items/starter'
16
8
 
9
+ import { ChevronRightIcon } from 'lucide-react'
10
+
17
11
  import { useApplicationMode, useReady } from '@/store/project'
18
12
 
19
13
  export function AppSidebar() {
@@ -21,35 +15,36 @@ export function AppSidebar() {
21
15
  const mode = useApplicationMode()
22
16
 
23
17
  return (
24
- <Sidebar>
25
- <SidebarHeader className="flex justify-center items-center">
26
- <img src="/tanstack.png" className="w-3/5" />
27
- </SidebarHeader>
28
- <SidebarContent>
29
- {ready && (
30
- <>
31
- {mode === 'setup' && (
32
- <SidebarGroup>
18
+ <div className="flex flex-col gap-2">
19
+ {ready && (
20
+ <>
21
+ {mode === 'setup' && (
22
+ <div className="bg-white dark:bg-black/40 shadow-xl p-4 space-y-2 rounded-lg">
23
+ <div className="block p-4 bg-gray-500/10 hover:bg-gray-500/20 rounded-lg transition-colors space-y-4 active">
33
24
  <ProjectName />
25
+ </div>
26
+ <div className="block p-4 bg-gray-500/10 hover:bg-gray-500/20 rounded-lg transition-colors space-y-4 active">
34
27
  <ModeSelector />
28
+ </div>
29
+ <div className="block p-4 bg-gray-500/10 hover:bg-gray-500/20 rounded-lg transition-colors space-y-4 active">
35
30
  <TypescriptSwitch />
36
- </SidebarGroup>
37
- )}
38
- <SidebarGroup>
39
- <SelectedAddOns />
40
- </SidebarGroup>
41
- {mode === 'setup' && (
42
- <SidebarGroup>
43
- <StarterDialog />
44
- </SidebarGroup>
45
- )}
46
- </>
47
- )}
48
- </SidebarContent>
49
- <SidebarFooter className="mb-5">
31
+ </div>
32
+ </div>
33
+ )}
34
+ <div className="bg-white dark:bg-black/40 shadow-xl p-4 space-y-2 rounded-lg">
35
+ <SelectedAddOns />
36
+ </div>
37
+ {mode === 'setup' && (
38
+ <div className="bg-white dark:bg-black/40 shadow-xl p-4 space-y-2 rounded-lg">
39
+ <StarterDialog />
40
+ </div>
41
+ )}
42
+ </>
43
+ )}
44
+ <div className="mt-5">
50
45
  <RunAddOns />
51
46
  <RunCreateApp />
52
- </SidebarFooter>
53
- </Sidebar>
47
+ </div>
48
+ </div>
54
49
  )
55
50
  }