@tanstack/cta-ui 0.10.0-alpha.26 → 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.
- package/README.md +20 -0
- package/dist/assets/index-DSKioOfX.css +1 -0
- package/dist/assets/index-DWTDdndE.js +213 -0
- package/dist/assets/index-DWTDdndE.js.map +1 -0
- package/dist/index.html +9 -3
- package/dist/logo-color-100w.png +0 -0
- package/index.html +7 -1
- package/lib/engine-handling/add-to-app-wrapper.ts +39 -15
- package/lib/engine-handling/create-app-wrapper.ts +17 -6
- package/lib/engine-handling/file-helpers.ts +4 -2
- package/lib/engine-handling/generate-initial-payload.ts +58 -51
- package/lib/index.ts +11 -1
- package/lib/types.d.ts +18 -11
- package/lib-dist/engine-handling/add-to-app-wrapper.d.ts +2 -0
- package/lib-dist/engine-handling/add-to-app-wrapper.js +14 -9
- package/lib-dist/engine-handling/create-app-wrapper.d.ts +2 -1
- package/lib-dist/engine-handling/create-app-wrapper.js +6 -4
- package/lib-dist/engine-handling/file-helpers.js +3 -2
- package/lib-dist/engine-handling/generate-initial-payload.d.ts +14 -22
- package/lib-dist/engine-handling/generate-initial-payload.js +44 -49
- package/lib-dist/index.d.ts +2 -0
- package/lib-dist/index.js +6 -1
- package/package.json +6 -4
- package/public/logo-color-100w.png +0 -0
- package/src/components/background-animation.tsx +229 -0
- package/src/components/cta-sidebar.tsx +28 -33
- package/src/components/file-navigator.tsx +72 -74
- package/src/components/header.tsx +31 -0
- package/src/components/sidebar-items/add-ons.tsx +48 -45
- package/src/components/sidebar-items/mode-selector.tsx +6 -4
- package/src/components/sidebar-items/project-name.tsx +4 -5
- package/src/components/sidebar-items/typescript-switch.tsx +3 -3
- package/src/components/startup-dialog.tsx +4 -6
- package/src/components/ui/switch.tsx +6 -6
- package/src/hooks/use-mounted.ts +9 -0
- package/src/hooks/use-preferred-reduced-motion.ts +27 -0
- package/src/index.tsx +24 -20
- package/src/store/project.ts +36 -20
- package/src/styles.css +90 -18
- package/src/types.d.ts +1 -1
- package/tailwind.config.cjs +47 -0
- package/dist/assets/index-D0-fpgzI.js +0 -223
- package/dist/assets/index-D0-fpgzI.js.map +0 -1
- package/dist/assets/index-D5brMzJg.css +0 -1
|
@@ -1,29 +1,11 @@
|
|
|
1
1
|
import type { SerializedOptions } from '@tanstack/cta-engine';
|
|
2
|
-
import type {
|
|
2
|
+
import type { AddOnInfo } from '../types.js';
|
|
3
3
|
export declare function generateInitialPayload(): Promise<{
|
|
4
4
|
applicationMode: "add" | "setup";
|
|
5
5
|
localFiles: Record<string, string>;
|
|
6
6
|
addOns: {
|
|
7
|
-
'code-router':
|
|
8
|
-
|
|
9
|
-
name: string;
|
|
10
|
-
description: string;
|
|
11
|
-
type: "starter" | "add-on" | "example" | "toolchain";
|
|
12
|
-
smallLogo: string | undefined;
|
|
13
|
-
logo: string | undefined;
|
|
14
|
-
link: string | undefined;
|
|
15
|
-
dependsOn: string[] | undefined;
|
|
16
|
-
}[];
|
|
17
|
-
'file-router': {
|
|
18
|
-
id: string;
|
|
19
|
-
name: string;
|
|
20
|
-
description: string;
|
|
21
|
-
type: "starter" | "add-on" | "example" | "toolchain";
|
|
22
|
-
smallLogo: string | undefined;
|
|
23
|
-
logo: string | undefined;
|
|
24
|
-
link: string | undefined;
|
|
25
|
-
dependsOn: string[] | undefined;
|
|
26
|
-
}[];
|
|
7
|
+
'code-router': AddOnInfo[];
|
|
8
|
+
'file-router': AddOnInfo[];
|
|
27
9
|
};
|
|
28
10
|
options: SerializedOptions;
|
|
29
11
|
output: {
|
|
@@ -36,5 +18,15 @@ export declare function generateInitialPayload(): Promise<{
|
|
|
36
18
|
} | undefined;
|
|
37
19
|
forcedRouterMode: import("@tanstack/cta-engine").Mode | undefined;
|
|
38
20
|
forcedAddOns: string[];
|
|
39
|
-
registry:
|
|
21
|
+
registry: {
|
|
22
|
+
"add-ons": never[];
|
|
23
|
+
starters: {
|
|
24
|
+
name: string;
|
|
25
|
+
description: string;
|
|
26
|
+
url: string;
|
|
27
|
+
banner?: string;
|
|
28
|
+
mode: Mode;
|
|
29
|
+
framework: string;
|
|
30
|
+
}[];
|
|
31
|
+
};
|
|
40
32
|
}>;
|
|
@@ -1,16 +1,21 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs';
|
|
2
1
|
import { basename, resolve } from 'node:path';
|
|
3
|
-
import { createSerializedOptionsFromPersisted, getAllAddOns, getFrameworkById, recursivelyGatherFiles, } from '@tanstack/cta-engine';
|
|
2
|
+
import { createSerializedOptionsFromPersisted, getAllAddOns, getFrameworkById, getRawRegistry, getRegistryAddOns, readConfigFile, recursivelyGatherFiles, } from '@tanstack/cta-engine';
|
|
4
3
|
import { cleanUpFiles } from './file-helpers.js';
|
|
5
4
|
import { createAppWrapper } from './create-app-wrapper.js';
|
|
6
5
|
import { registerFrameworks } from './framework-registration.js';
|
|
7
|
-
import { getApplicationMode, getForcedAddOns, getForcedRouterMode, getProjectOptions, getProjectPath, getRegistry, } from './server-environment.js';
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
6
|
+
import { getApplicationMode, getForcedAddOns, getForcedRouterMode, getProjectOptions, getProjectPath, getRegistry as getRegistryURL, } from './server-environment.js';
|
|
7
|
+
function convertAddOnToAddOnInfo(addOn) {
|
|
8
|
+
return {
|
|
9
|
+
id: addOn.id,
|
|
10
|
+
name: addOn.name,
|
|
11
|
+
description: addOn.description,
|
|
12
|
+
modes: addOn.modes,
|
|
13
|
+
type: addOn.type,
|
|
14
|
+
smallLogo: addOn.smallLogo,
|
|
15
|
+
logo: addOn.logo,
|
|
16
|
+
link: addOn.link,
|
|
17
|
+
dependsOn: addOn.dependsOn,
|
|
18
|
+
};
|
|
14
19
|
}
|
|
15
20
|
export async function generateInitialPayload() {
|
|
16
21
|
registerFrameworks();
|
|
@@ -20,7 +25,7 @@ export async function generateInitialPayload() {
|
|
|
20
25
|
? await cleanUpFiles(await recursivelyGatherFiles(projectPath, false))
|
|
21
26
|
: {};
|
|
22
27
|
const forcedRouterMode = getForcedRouterMode();
|
|
23
|
-
function getSerializedOptions() {
|
|
28
|
+
async function getSerializedOptions() {
|
|
24
29
|
if (applicationMode === 'setup') {
|
|
25
30
|
const projectOptions = getProjectOptions();
|
|
26
31
|
return {
|
|
@@ -31,63 +36,53 @@ export async function generateInitialPayload() {
|
|
|
31
36
|
typescript: projectOptions.typescript || true,
|
|
32
37
|
tailwind: projectOptions.tailwind || true,
|
|
33
38
|
git: projectOptions.git || true,
|
|
39
|
+
targetDir: projectOptions.targetDir ||
|
|
40
|
+
resolve(projectPath, projectOptions.projectName),
|
|
34
41
|
};
|
|
35
42
|
}
|
|
36
43
|
else {
|
|
37
|
-
const persistedOptions =
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
const registryUrl = getRegistry();
|
|
42
|
-
let registry;
|
|
43
|
-
if (registryUrl) {
|
|
44
|
-
registry = (await fetch(registryUrl).then((res) => res.json()));
|
|
45
|
-
for (const addOn of registry['add-ons']) {
|
|
46
|
-
addOn.url = absolutizeUrl(registryUrl, addOn.url);
|
|
47
|
-
}
|
|
48
|
-
for (const starter of registry.starters) {
|
|
49
|
-
starter.url = absolutizeUrl(registryUrl, starter.url);
|
|
50
|
-
if (starter.banner) {
|
|
51
|
-
starter.banner = absolutizeUrl(registryUrl, starter.banner);
|
|
44
|
+
const persistedOptions = await readConfigFile(projectPath);
|
|
45
|
+
if (!persistedOptions) {
|
|
46
|
+
throw new Error('No config file found');
|
|
52
47
|
}
|
|
48
|
+
return createSerializedOptionsFromPersisted(persistedOptions);
|
|
53
49
|
}
|
|
54
50
|
}
|
|
55
|
-
const
|
|
51
|
+
const rawRegistry = await getRawRegistry(getRegistryURL());
|
|
52
|
+
const registryAddOns = await getRegistryAddOns(getRegistryURL());
|
|
53
|
+
const serializedOptions = await getSerializedOptions();
|
|
56
54
|
const output = await createAppWrapper(serializedOptions, {
|
|
57
55
|
dryRun: true,
|
|
58
56
|
});
|
|
59
57
|
const framework = await getFrameworkById(serializedOptions.framework);
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
link: addOn.link,
|
|
78
|
-
dependsOn: addOn.dependsOn,
|
|
79
|
-
}));
|
|
58
|
+
const codeRouterAddOns = getAllAddOns(framework, 'code-router').map(convertAddOnToAddOnInfo);
|
|
59
|
+
const fileRouterAddOns = getAllAddOns(framework, 'file-router').map(convertAddOnToAddOnInfo);
|
|
60
|
+
for (const addOnInfo of registryAddOns || []) {
|
|
61
|
+
const addOnFramework = rawRegistry?.['add-ons'].find((addOn) => addOn.url === addOnInfo.id);
|
|
62
|
+
if (addOnFramework?.framework === serializedOptions.framework) {
|
|
63
|
+
if (addOnInfo.modes.includes('code-router')) {
|
|
64
|
+
codeRouterAddOns.push(convertAddOnToAddOnInfo(addOnInfo));
|
|
65
|
+
}
|
|
66
|
+
if (addOnInfo.modes.includes('file-router')) {
|
|
67
|
+
fileRouterAddOns.push(convertAddOnToAddOnInfo(addOnInfo));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const serializedRegistry = {
|
|
72
|
+
['add-ons']: [],
|
|
73
|
+
starters: (rawRegistry?.starters || []).filter((starter) => starter.framework === serializedOptions.framework),
|
|
74
|
+
};
|
|
80
75
|
return {
|
|
81
76
|
applicationMode,
|
|
82
77
|
localFiles,
|
|
83
78
|
addOns: {
|
|
84
|
-
'code-router':
|
|
85
|
-
'file-router':
|
|
79
|
+
'code-router': codeRouterAddOns,
|
|
80
|
+
'file-router': fileRouterAddOns,
|
|
86
81
|
},
|
|
87
82
|
options: serializedOptions,
|
|
88
83
|
output,
|
|
89
84
|
forcedRouterMode,
|
|
90
85
|
forcedAddOns: getForcedAddOns(),
|
|
91
|
-
registry,
|
|
86
|
+
registry: serializedRegistry,
|
|
92
87
|
};
|
|
93
88
|
}
|
package/lib-dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { ServerEnvironment } from './engine-handling/server-environment.js';
|
|
2
|
+
import type { Environment } from '@tanstack/cta-engine';
|
|
2
3
|
export declare function launchUI(options: Partial<ServerEnvironment> & {
|
|
3
4
|
port?: number;
|
|
5
|
+
environmentFactory?: () => Environment;
|
|
4
6
|
}): void;
|
package/lib-dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { dirname, resolve } from 'node:path';
|
|
|
2
2
|
import { fileURLToPath } from 'node:url';
|
|
3
3
|
import express from 'express';
|
|
4
4
|
import cors from 'cors';
|
|
5
|
+
import chalk from 'chalk';
|
|
5
6
|
import { AddOnCompiledSchema, StarterCompiledSchema, } from '@tanstack/cta-engine';
|
|
6
7
|
import { addToAppWrapper } from './engine-handling/add-to-app-wrapper.js';
|
|
7
8
|
import { createAppWrapper } from './engine-handling/create-app-wrapper.js';
|
|
@@ -22,21 +23,25 @@ export function launchUI(options) {
|
|
|
22
23
|
app.post('/api/add-to-app', async (req, res) => {
|
|
23
24
|
await addToAppWrapper(req.body.addOns, {
|
|
24
25
|
response: res,
|
|
26
|
+
environmentFactory: options.environmentFactory,
|
|
25
27
|
});
|
|
26
28
|
});
|
|
27
29
|
app.post('/api/create-app', async (req, res) => {
|
|
28
30
|
await createAppWrapper(req.body.options, {
|
|
29
31
|
response: res,
|
|
32
|
+
environmentFactory: options.environmentFactory,
|
|
30
33
|
});
|
|
31
34
|
});
|
|
32
35
|
app.post('/api/dry-run-add-to-app', async (req, res) => {
|
|
33
36
|
res.send(await addToAppWrapper(req.body.addOns, {
|
|
34
37
|
dryRun: true,
|
|
38
|
+
environmentFactory: options.environmentFactory,
|
|
35
39
|
}));
|
|
36
40
|
});
|
|
37
41
|
app.post('/api/dry-run-create-app', async (req, res) => {
|
|
38
42
|
res.send(await createAppWrapper(req.body.options, {
|
|
39
43
|
dryRun: true,
|
|
44
|
+
environmentFactory: options.environmentFactory,
|
|
40
45
|
}));
|
|
41
46
|
});
|
|
42
47
|
app.get('/api/initial-payload', async (_req, res) => {
|
|
@@ -119,6 +124,6 @@ export function launchUI(options) {
|
|
|
119
124
|
});
|
|
120
125
|
const port = requestedPort || process.env.PORT || 8080;
|
|
121
126
|
app.listen(port, () => {
|
|
122
|
-
console.log(`Create TanStack ${launchUI ? 'App' : 'API'} is running on http://localhost:${port}`);
|
|
127
|
+
console.log(`🔥 ${chalk.blueBright(`Create TanStack ${launchUI ? 'App' : 'API'}`)} is running on ${chalk.underline(`http://localhost:${port}`)}`);
|
|
123
128
|
});
|
|
124
129
|
}
|
package/package.json
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"@tanstack/react-query-devtools": "^5.66.5",
|
|
27
27
|
"@uiw/codemirror-theme-github": "^4.23.10",
|
|
28
28
|
"@uiw/react-codemirror": "^4.23.10",
|
|
29
|
+
"chalk": "^5.4.1",
|
|
29
30
|
"class-variance-authority": "^0.7.1",
|
|
30
31
|
"clsx": "^2.1.1",
|
|
31
32
|
"cors": "^2.8.5",
|
|
@@ -44,11 +45,12 @@
|
|
|
44
45
|
"tailwindcss-animate": "^1.0.7",
|
|
45
46
|
"vite-tsconfig-paths": "^5.1.4",
|
|
46
47
|
"zustand": "^5.0.3",
|
|
47
|
-
"@tanstack/cta-engine": "0.10.0-alpha.
|
|
48
|
-
"@tanstack/cta-framework-
|
|
49
|
-
"@tanstack/cta-framework-
|
|
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"
|
|
50
51
|
},
|
|
51
52
|
"devDependencies": {
|
|
53
|
+
"@tailwindcss/typography": "^0.5.16",
|
|
52
54
|
"@testing-library/dom": "^10.4.0",
|
|
53
55
|
"@testing-library/react": "^16.2.0",
|
|
54
56
|
"@types/cors": "^2.8.17",
|
|
@@ -64,6 +66,6 @@
|
|
|
64
66
|
"vitest": "^3.0.5",
|
|
65
67
|
"web-vitals": "^4.2.4"
|
|
66
68
|
},
|
|
67
|
-
"version": "0.10.0-alpha.
|
|
69
|
+
"version": "0.10.0-alpha.27",
|
|
68
70
|
"scripts": {}
|
|
69
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
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
</
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
<
|
|
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
|
-
</
|
|
53
|
-
</
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
54
49
|
)
|
|
55
50
|
}
|