diff --git a/app/globals.css b/app/globals.css deleted file mode 100644 index c871d07..0000000 --- a/app/globals.css +++ /dev/null @@ -1,92 +0,0 @@ -@import 'tailwindcss'; -@import "tw-animate-css"; - -@import '../src/styles/theme.css'; - -/* - The default border color has changed to `currentColor` in Tailwind CSS v4, - so we've added these compatibility styles to make sure everything still - looks the same as it did with Tailwind CSS v3. -*/ -@layer base { - *, - ::after, - ::before, - ::backdrop, - ::file-selector-button { - border-color: var(--color-gray-200, currentColor); - } - - * { - @apply border-border - } -} - -:root { - --radius: 0.5rem; - --background: oklch(0.08 0.01 265); - --foreground: oklch(0.95 0.01 265); - --card: oklch(0.15 0.01 265); - --card-foreground: oklch(0.98 0 0); - --popover: oklch(0.15 0.01 265); - --popover-foreground: oklch(0.98 0 0); - --primary: oklch(0.35 0.15 265); - --primary-foreground: oklch(0.98 0 0); - --secondary: oklch(0.25 0.01 265); - --secondary-foreground: oklch(0.95 0.01 265); - --muted: oklch(0.20 0.01 265); - --muted-foreground: oklch(0.65 0.01 265); - --accent: oklch(0.75 0.15 195); - --accent-foreground: oklch(0.15 0.01 265); - --destructive: oklch(0.577 0.245 27.325); - --destructive-foreground: oklch(0.98 0 0); - --border: oklch(0.25 0.01 265); - --input: oklch(0.28 0.02 265); - --ring: oklch(0.75 0.15 195); - --chart-1: oklch(0.70 0.20 10); - --chart-2: oklch(0.70 0.20 160); - --chart-3: oklch(0.70 0.20 200); - --chart-4: oklch(0.70 0.20 240); - --chart-5: oklch(0.70 0.20 280); -} - -@theme { - --color-background: var(--background); - --color-foreground: var(--foreground); - --color-card: var(--card); - --color-card-foreground: var(--card-foreground); - --color-popover: var(--popover); - --color-popover-foreground: var(--popover-foreground); - --color-primary: var(--primary); - --color-primary-foreground: var(--primary-foreground); - --color-secondary: var(--secondary); - --color-secondary-foreground: var(--secondary-foreground); - --color-muted: var(--muted); - --color-muted-foreground: var(--muted-foreground); - --color-accent: var(--accent); - --color-accent-foreground: var(--accent-foreground); - --color-destructive: var(--destructive); - --color-destructive-foreground: var(--destructive-foreground); - --color-border: var(--border); - --color-input: var(--input); - --color-ring: var(--ring); - - --radius-sm: calc(var(--radius) * 0.5); - --radius-md: var(--radius); - --radius-lg: calc(var(--radius) * 1.5); - --radius-xl: calc(var(--radius) * 2); - --radius-2xl: calc(var(--radius) * 3); - --radius-full: 9999px; -} - -h1, h2, h3, h4, h5, h6 { - font-family: var(--font-inter), sans-serif; -} - -body { - font-family: var(--font-inter), sans-serif; -} - -code, pre { - font-family: var(--font-jetbrains-mono), monospace; -} diff --git a/index.html b/index.html deleted file mode 100644 index a1a2dd9..0000000 --- a/index.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - CodeSnippet - Share & Run Code (Python, React & More) - - - - - - - -
- - - - diff --git a/next.config.js b/next.config.js new file mode 100644 index 0000000..b74fb38 --- /dev/null +++ b/next.config.js @@ -0,0 +1,14 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + output: process.env.BUILD_STATIC ? 'export' : 'standalone', + basePath: process.env.NEXT_PUBLIC_BASE_PATH || '', + turbopack: {}, + images: { + unoptimized: true, + }, + experimental: { + optimizePackageImports: ['@radix-ui/react-icons', '@phosphor-icons/react'], + }, +}; + +export default nextConfig; diff --git a/next.config.ts b/next.config.ts deleted file mode 100644 index f5e033c..0000000 --- a/next.config.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type { NextConfig } from 'next'; - -const nextConfig: NextConfig = { - // Output as static HTML for GitHub Pages, or standalone for Docker - output: process.env.BUILD_STATIC ? 'export' : 'standalone', - - // Base path for GitHub Pages deployment - // Set to '/' for custom domain or root deployment - // Set to '/repo-name/' for GitHub Pages at username.github.io/repo-name/ - basePath: process.env.NEXT_PUBLIC_BASE_PATH || '', - - // Configure webpack for browser-only modules - webpack: (config, { isServer }) => { - if (!isServer) { - // Externalize node modules for browser - config.resolve.fallback = { - ...config.resolve.fallback, - fs: false, - path: false, - crypto: false, - 'node:url': false, - 'node:fs': false, - 'node:fs/promises': false, - 'node:vm': false, - 'node:path': false, - 'node:crypto': false, - 'node:child_process': false, - }; - } - return config; - }, - - // Environment variables that should be available on the client - env: { - NEXT_PUBLIC_FLASK_BACKEND_URL: process.env.NEXT_PUBLIC_FLASK_BACKEND_URL || '', - }, - - // Image optimization - images: { - unoptimized: true, // Required for static export - }, - - // Experimental features - experimental: { - optimizePackageImports: ['@radix-ui/react-icons', '@phosphor-icons/react'], - }, -}; - -export default nextConfig; diff --git a/nginx.conf b/nginx.conf deleted file mode 100644 index ae726e1..0000000 --- a/nginx.conf +++ /dev/null @@ -1,29 +0,0 @@ -server { - listen 3000; - server_name localhost; - root /usr/share/nginx/html; - index index.html; - - location / { - try_files $uri $uri/ /index.html; - - add_header Cache-Control "no-cache, no-store, must-revalidate"; - add_header Pragma "no-cache"; - add_header Expires "0"; - } - - location /api { - proxy_pass http://backend:5000; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_cache_bypass $http_upgrade; - - proxy_buffering off; - proxy_request_buffering off; - } -} diff --git a/package-lock.json b/package-lock.json index ab12fe3..2dbf27b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -70,7 +70,6 @@ "react-router-dom": "^7.12.0", "recharts": "^2.15.1", "sonner": "^2.0.1", - "sql.js": "^1.13.0", "tailwind-merge": "^3.0.2", "three": "^0.175.0", "tw-animate-css": "^1.2.4", @@ -80,9 +79,10 @@ }, "devDependencies": { "@eslint/js": "^9.21.0", - "@tailwindcss/postcss": "^4.1.8", + "@tailwindcss/postcss": "^4.1.18", "@types/react": "^19.0.10", "@types/react-dom": "^19.0.4", + "autoprefixer": "^10.4.23", "eslint": "^9.28.0", "eslint-config-next": "^16.1.3", "eslint-plugin-react-hooks": "^5.2.0", @@ -4726,9 +4726,9 @@ } }, "node_modules/@tailwindcss/node": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.17.tgz", - "integrity": "sha512-csIkHIgLb3JisEFQ0vxr2Y57GUNYh447C8xzwj89U/8fdW8LhProdxvnVH6U8M2Y73QKiTIH+LWbK3V2BBZsAg==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz", + "integrity": "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4738,37 +4738,37 @@ "lightningcss": "1.30.2", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.17.tgz", - "integrity": "sha512-F0F7d01fmkQhsTjXezGBLdrl1KresJTcI3DB8EkScCldyKp3Msz4hub4uyYaVnk88BAS1g5DQjjF6F5qczheLA==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.18.tgz", + "integrity": "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==", "dev": true, "license": "MIT", "engines": { "node": ">= 10" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.17", - "@tailwindcss/oxide-darwin-arm64": "4.1.17", - "@tailwindcss/oxide-darwin-x64": "4.1.17", - "@tailwindcss/oxide-freebsd-x64": "4.1.17", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.17", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.17", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.17", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.17", - "@tailwindcss/oxide-linux-x64-musl": "4.1.17", - "@tailwindcss/oxide-wasm32-wasi": "4.1.17", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.17", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.17" + "@tailwindcss/oxide-android-arm64": "4.1.18", + "@tailwindcss/oxide-darwin-arm64": "4.1.18", + "@tailwindcss/oxide-darwin-x64": "4.1.18", + "@tailwindcss/oxide-freebsd-x64": "4.1.18", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", + "@tailwindcss/oxide-linux-x64-musl": "4.1.18", + "@tailwindcss/oxide-wasm32-wasi": "4.1.18", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.17.tgz", - "integrity": "sha512-BMqpkJHgOZ5z78qqiGE6ZIRExyaHyuxjgrJ6eBO5+hfrfGkuya0lYfw8fRHG77gdTjWkNWEEm+qeG2cDMxArLQ==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz", + "integrity": "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==", "cpu": [ "arm64" ], @@ -4783,9 +4783,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.17.tgz", - "integrity": "sha512-EquyumkQweUBNk1zGEU/wfZo2qkp/nQKRZM8bUYO0J+Lums5+wl2CcG1f9BgAjn/u9pJzdYddHWBiFXJTcxmOg==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.18.tgz", + "integrity": "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==", "cpu": [ "arm64" ], @@ -4800,9 +4800,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.17.tgz", - "integrity": "sha512-gdhEPLzke2Pog8s12oADwYu0IAw04Y2tlmgVzIN0+046ytcgx8uZmCzEg4VcQh+AHKiS7xaL8kGo/QTiNEGRog==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz", + "integrity": "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==", "cpu": [ "x64" ], @@ -4817,9 +4817,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.17.tgz", - "integrity": "sha512-hxGS81KskMxML9DXsaXT1H0DyA+ZBIbyG/sSAjWNe2EDl7TkPOBI42GBV3u38itzGUOmFfCzk1iAjDXds8Oh0g==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz", + "integrity": "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==", "cpu": [ "x64" ], @@ -4834,9 +4834,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.17.tgz", - "integrity": "sha512-k7jWk5E3ldAdw0cNglhjSgv501u7yrMf8oeZ0cElhxU6Y2o7f8yqelOp3fhf7evjIS6ujTI3U8pKUXV2I4iXHQ==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz", + "integrity": "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==", "cpu": [ "arm" ], @@ -4851,9 +4851,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.17.tgz", - "integrity": "sha512-HVDOm/mxK6+TbARwdW17WrgDYEGzmoYayrCgmLEw7FxTPLcp/glBisuyWkFz/jb7ZfiAXAXUACfyItn+nTgsdQ==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz", + "integrity": "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==", "cpu": [ "arm64" ], @@ -4868,9 +4868,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.17.tgz", - "integrity": "sha512-HvZLfGr42i5anKtIeQzxdkw/wPqIbpeZqe7vd3V9vI3RQxe3xU1fLjss0TjyhxWcBaipk7NYwSrwTwK1hJARMg==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz", + "integrity": "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==", "cpu": [ "arm64" ], @@ -4885,9 +4885,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.17.tgz", - "integrity": "sha512-M3XZuORCGB7VPOEDH+nzpJ21XPvK5PyjlkSFkFziNHGLc5d6g3di2McAAblmaSUNl8IOmzYwLx9NsE7bplNkwQ==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz", + "integrity": "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==", "cpu": [ "x64" ], @@ -4902,9 +4902,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.17.tgz", - "integrity": "sha512-k7f+pf9eXLEey4pBlw+8dgfJHY4PZ5qOUFDyNf7SI6lHjQ9Zt7+NcscjpwdCEbYi6FI5c2KDTDWyf2iHcCSyyQ==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz", + "integrity": "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==", "cpu": [ "x64" ], @@ -4919,9 +4919,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.17.tgz", - "integrity": "sha512-cEytGqSSoy7zK4JRWiTCx43FsKP/zGr0CsuMawhH67ONlH+T79VteQeJQRO/X7L0juEUA8ZyuYikcRBf0vsxhg==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz", + "integrity": "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -4937,10 +4937,10 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.6.0", - "@emnapi/runtime": "^1.6.0", + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1", "@emnapi/wasi-threads": "^1.1.0", - "@napi-rs/wasm-runtime": "^1.0.7", + "@napi-rs/wasm-runtime": "^1.1.0", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.4.0" }, @@ -4948,70 +4948,10 @@ "node": ">=14.0.0" } }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.6.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.1.0", - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.6.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { - "version": "1.0.7", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.5.0", - "@emnapi/runtime": "^1.5.0", - "@tybys/wasm-util": "^0.10.1" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "dev": true, - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { - "version": "2.8.1", - "dev": true, - "inBundle": true, - "license": "0BSD", - "optional": true - }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.17.tgz", - "integrity": "sha512-JU5AHr7gKbZlOGvMdb4722/0aYbU+tN6lv1kONx0JK2cGsh7g148zVWLM0IKR3NeKLv+L90chBVYcJ8uJWbC9A==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz", + "integrity": "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==", "cpu": [ "arm64" ], @@ -5026,9 +4966,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.17.tgz", - "integrity": "sha512-SKWM4waLuqx0IH+FMDUw6R66Hu4OuTALFgnleKbqhgGU30DY20NORZMZUKgLRjQXNN2TLzKvh48QXTig4h4bGw==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz", + "integrity": "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==", "cpu": [ "x64" ], @@ -5043,17 +4983,17 @@ } }, "node_modules/@tailwindcss/postcss": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.17.tgz", - "integrity": "sha512-+nKl9N9mN5uJ+M7dBOOCzINw94MPstNR/GtIhz1fpZysxL/4a+No64jCBD6CPN+bIHWFx3KWuu8XJRrj/572Dw==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.18.tgz", + "integrity": "sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g==", "dev": true, "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.1.17", - "@tailwindcss/oxide": "4.1.17", + "@tailwindcss/node": "4.1.18", + "@tailwindcss/oxide": "4.1.18", "postcss": "^8.4.41", - "tailwindcss": "4.1.17" + "tailwindcss": "4.1.18" } }, "node_modules/@tanstack/query-core": { @@ -6142,6 +6082,43 @@ "node": ">= 0.4" } }, + "node_modules/autoprefixer": { + "version": "10.4.23", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz", + "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001760", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -7236,9 +7213,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.18.3", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.3.tgz", - "integrity": "sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==", + "version": "5.18.4", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", + "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==", "dev": true, "license": "MIT", "dependencies": { @@ -8417,6 +8394,20 @@ "node": ">= 0.6" } }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, "node_modules/framer-motion": { "version": "12.23.25", "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.25.tgz", @@ -10494,6 +10485,13 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -11513,12 +11511,6 @@ "node": ">=0.10.0" } }, - "node_modules/sql.js": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/sql.js/-/sql.js-1.13.0.tgz", - "integrity": "sha512-RJbVP1HRDlUUXahJ7VMTcu9Rm1Nzw+EBpoPr94vnbD4LwR715F3CcxE2G2k45PewcaZ57pjetYa+LoSJLAASgA==", - "license": "MIT" - }, "node_modules/stable-hash": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz", @@ -11751,9 +11743,9 @@ } }, "node_modules/tailwindcss": { - "version": "4.1.17", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.17.tgz", - "integrity": "sha512-j9Ee2YjuQqYT9bbRTfTZht9W/ytp5H+jJpZKiYdP/bpnXARAuELt9ofP0lPnmHjbga7SNQIxdTAXCmtKVYjN+Q==", + "version": "4.1.18", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", + "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", "license": "MIT" }, "node_modules/tapable": { diff --git a/package.json b/package.json index 8765625..be224c9 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,6 @@ "react-router-dom": "^7.12.0", "recharts": "^2.15.1", "sonner": "^2.0.1", - "sql.js": "^1.13.0", "tailwind-merge": "^3.0.2", "three": "^0.175.0", "tw-animate-css": "^1.2.4", @@ -82,9 +81,10 @@ }, "devDependencies": { "@eslint/js": "^9.21.0", - "@tailwindcss/postcss": "^4.1.8", + "@tailwindcss/postcss": "^4.1.18", "@types/react": "^19.0.10", "@types/react-dom": "^19.0.4", + "autoprefixer": "^10.4.23", "eslint": "^9.28.0", "eslint-config-next": "^16.1.3", "eslint-plugin-react-hooks": "^5.2.0", diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..52b9b4b --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,5 @@ +module.exports = { + plugins: { + '@tailwindcss/postcss': {}, + }, +} diff --git a/src/App.tsx b/src/App.tsx deleted file mode 100644 index f92ed60..0000000 --- a/src/App.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { BrowserRouter as Router, Routes, Route } from 'react-router-dom' -import { motion } from 'framer-motion' -import { Code } from '@phosphor-icons/react' -import { Navigation } from '@/components/layout/navigation/Navigation' -import { NavigationProvider } from '@/components/layout/navigation/NavigationProvider' -import { NavigationSidebar } from '@/components/layout/navigation/NavigationSidebar' -import { useNavigation } from '@/components/layout/navigation/useNavigation' -import { BackendIndicator } from '@/components/layout/BackendIndicator' -import { HomePage } from '@/pages/HomePage' -import { DemoPage } from '@/pages/DemoPage' -import { AtomsPage } from '@/pages/AtomsPage' -import { MoleculesPage } from '@/pages/MoleculesPage' -import { OrganismsPage } from '@/pages/OrganismsPage' -import { TemplatesPage } from '@/pages/TemplatesPage' -import { SettingsPage } from '@/pages/SettingsPage' - -function AppContent() { - const { menuOpen } = useNavigation() - - return ( -
-
- - - - -
-
-
- - -
- -
-

- CodeSnippet -

-
- - - -
-
-
- -
- - } /> - } /> - } /> - } /> - } /> - } /> - } /> - -
- -
-
-
-

Save, organize, and share your code snippets with beautiful syntax highlighting and live execution

-

Supports React preview and Python execution via Pyodide

-
-
-
-
-
- ) -} - -function App() { - return ( - - - - - - ) -} - -export default App diff --git a/app/PageLayout.tsx b/src/app/PageLayout.tsx similarity index 100% rename from app/PageLayout.tsx rename to src/app/PageLayout.tsx diff --git a/app/atoms/page.tsx b/src/app/atoms/page.tsx similarity index 100% rename from app/atoms/page.tsx rename to src/app/atoms/page.tsx diff --git a/app/demo/page.tsx b/src/app/demo/page.tsx similarity index 94% rename from app/demo/page.tsx rename to src/app/demo/page.tsx index 7c34d05..d6f4142 100644 --- a/app/demo/page.tsx +++ b/src/app/demo/page.tsx @@ -5,8 +5,8 @@ import { motion } from 'framer-motion'; import { SplitScreenEditor } from '@/components/features/snippet-editor/SplitScreenEditor'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Sparkle } from '@phosphor-icons/react'; -import { DEMO_CODE } from '@/pages/demo-constants'; -import { DemoFeatureCards } from '@/pages/DemoFeatureCards'; +import { DEMO_CODE } from '@/components/demo/demo-constants'; +import { DemoFeatureCards } from '@/components/demo/DemoFeatureCards'; import { PageLayout } from '../PageLayout'; export default function DemoPage() { diff --git a/src/app/globals.css b/src/app/globals.css new file mode 100644 index 0000000..b59ced0 --- /dev/null +++ b/src/app/globals.css @@ -0,0 +1,49 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 222.2 84% 4.9%; + --foreground: 210 40% 98%; + --card: 222.2 84% 4.9%; + --card-foreground: 210 40% 98%; + --popover: 222.2 84% 4.9%; + --popover-foreground: 210 40% 98%; + --primary: 263 70% 50%; + --primary-foreground: 210 40% 98%; + --secondary: 217.2 32.6% 17.5%; + --secondary-foreground: 210 40% 98%; + --muted: 217.2 32.6% 17.5%; + --muted-foreground: 215 20.2% 65.1%; + --accent: 195 100% 70%; + --accent-foreground: 222.2 84% 4.9%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 40% 98%; + --border: 217.2 32.6% 17.5%; + --input: 217.2 32.6% 17.5%; + --ring: 195 100% 70%; + --radius: 0.5rem; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} + +h1, h2, h3, h4, h5, h6 { + font-family: var(--font-inter), 'Inter', sans-serif; +} + +body { + font-family: var(--font-inter), 'Inter', sans-serif; +} + +code, pre { + font-family: var(--font-jetbrains-mono), 'JetBrains Mono', monospace; +} diff --git a/app/layout.tsx b/src/app/layout.tsx similarity index 54% rename from app/layout.tsx rename to src/app/layout.tsx index 49d9aea..932bca6 100644 --- a/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,23 +1,7 @@ import type { Metadata } from 'next'; -import { Inter, JetBrains_Mono, Bricolage_Grotesque } from 'next/font/google'; import './globals.css'; import { Providers } from './providers'; -const inter = Inter({ - subsets: ['latin'], - variable: '--font-inter', -}); - -const jetbrainsMono = JetBrains_Mono({ - subsets: ['latin'], - variable: '--font-jetbrains-mono', -}); - -const bricolageGrotesque = Bricolage_Grotesque({ - subsets: ['latin'], - variable: '--font-bricolage-grotesque', -}); - export const metadata: Metadata = { title: 'CodeSnippet - Share & Run Code (Python, React & More)', description: 'Save, organize, and share your code snippets with beautiful syntax highlighting and live execution', @@ -30,7 +14,12 @@ export default function RootLayout({ }>) { return ( - + + + + + + {children} diff --git a/app/molecules/page.tsx b/src/app/molecules/page.tsx similarity index 100% rename from app/molecules/page.tsx rename to src/app/molecules/page.tsx diff --git a/app/organisms/page.tsx b/src/app/organisms/page.tsx similarity index 100% rename from app/organisms/page.tsx rename to src/app/organisms/page.tsx diff --git a/app/page.tsx b/src/app/page.tsx similarity index 100% rename from app/page.tsx rename to src/app/page.tsx diff --git a/app/providers.tsx b/src/app/providers.tsx similarity index 100% rename from app/providers.tsx rename to src/app/providers.tsx diff --git a/app/settings/page.tsx b/src/app/settings/page.tsx similarity index 100% rename from app/settings/page.tsx rename to src/app/settings/page.tsx diff --git a/app/templates/page.tsx b/src/app/templates/page.tsx similarity index 100% rename from app/templates/page.tsx rename to src/app/templates/page.tsx diff --git a/src/app/theme.css b/src/app/theme.css new file mode 100644 index 0000000..8da23f2 --- /dev/null +++ b/src/app/theme.css @@ -0,0 +1,262 @@ +@import "tailwindcss"; + +@import '@radix-ui/colors/sage-dark.css' layer(base); +@import '@radix-ui/colors/olive.css' layer(base); +@import '@radix-ui/colors/olive-dark.css' layer(base); +@import '@radix-ui/colors/sand.css' layer(base); +@import '@radix-ui/colors/sand-dark.css' layer(base); +@import '@radix-ui/colors/red.css' layer(base); +@import '@radix-ui/colors/red-dark.css' layer(base); +@import '@radix-ui/colors/ruby.css' layer(base); +@import '@radix-ui/colors/ruby-dark.css' layer(base); +@import '@radix-ui/colors/crimson.css' layer(base); +@import '@radix-ui/colors/crimson-dark.css' layer(base); +@import '@radix-ui/colors/pink.css' layer(base); +@import '@radix-ui/colors/pink-dark.css' layer(base); +@import '@radix-ui/colors/plum.css' layer(base); +@import '@radix-ui/colors/plum-dark.css' layer(base); +@import '@radix-ui/colors/purple.css' layer(base); +@import '@radix-ui/colors/purple-dark.css' layer(base); +@import '@radix-ui/colors/violet.css' layer(base); +@import '@radix-ui/colors/violet-dark.css' layer(base); +@import '@radix-ui/colors/iris.css' layer(base); +@import '@radix-ui/colors/iris-dark.css' layer(base); +@import '@radix-ui/colors/indigo.css' layer(base); +@import '@radix-ui/colors/indigo-dark.css' layer(base); +@import '@radix-ui/colors/blue.css' layer(base); +@import '@radix-ui/colors/blue-dark.css' layer(base); +@import '@radix-ui/colors/cyan.css' layer(base); +@import '@radix-ui/colors/cyan-dark.css' layer(base); +@import '@radix-ui/colors/teal.css' layer(base); +@import '@radix-ui/colors/teal-dark.css' layer(base); +@import '@radix-ui/colors/jade.css' layer(base); +@import '@radix-ui/colors/jade-dark.css' layer(base); +@import '@radix-ui/colors/green.css' layer(base); +@import '@radix-ui/colors/green-dark.css' layer(base); +@import '@radix-ui/colors/grass.css' layer(base); +@import '@radix-ui/colors/grass-dark.css' layer(base); +@import '@radix-ui/colors/bronze.css' layer(base); +@import '@radix-ui/colors/bronze-dark.css' layer(base); +@import '@radix-ui/colors/gold.css' layer(base); +@import '@radix-ui/colors/gold-dark.css' layer(base); +@import '@radix-ui/colors/brown.css' layer(base); +@import '@radix-ui/colors/brown-dark.css' layer(base); +@import '@radix-ui/colors/orange.css' layer(base); +@import '@radix-ui/colors/orange-dark.css' layer(base); +@import '@radix-ui/colors/amber.css' layer(base); +@import '@radix-ui/colors/amber-dark.css' layer(base); +@import '@radix-ui/colors/yellow.css' layer(base); +@import '@radix-ui/colors/yellow-dark.css' layer(base); +@import '@radix-ui/colors/lime.css' layer(base); +@import '@radix-ui/colors/lime-dark.css' layer(base); +@import '@radix-ui/colors/mint.css' layer(base); +@import '@radix-ui/colors/mint-dark.css' layer(base); +@import '@radix-ui/colors/sky.css' layer(base); +@import '@radix-ui/colors/sky-dark.css' layer(base); +@import '@radix-ui/colors/tomato.css' layer(base); +@import '@radix-ui/colors/tomato-dark.css' layer(base); +@import '@radix-ui/colors/gray.css' layer(base); +@import '@radix-ui/colors/gray-dark.css' layer(base); +@import '@radix-ui/colors/mauve.css' layer(base); +@import '@radix-ui/colors/mauve-dark.css' layer(base); +@import '@radix-ui/colors/slate.css' layer(base); +@import '@radix-ui/colors/slate-dark.css' layer(base); +@import '@radix-ui/colors/slate-alpha.css' layer(base); +@import '@radix-ui/colors/slate-dark-alpha.css' layer(base); + +@import 'tailwindcss/theme' layer(theme); + +@import 'tailwindcss/preflight' layer(base); + +/* + The default border color has changed to `currentColor` in Tailwind CSS v4, + so we've added these compatibility styles to make sure everything still + looks the same as it did with Tailwind CSS v3. + + If we ever want to remove these styles, we need to add an explicit border + color utility to any element that depends on these defaults. +*/ +@layer base { + *, + ::after, + ::before, + ::backdrop, + ::file-selector-button { + border-color: var(--color-gray-200, currentColor); + } +} + +@layer base { + #spark-app { + --tomato-contrast: #fff; + --red-contrast: #fff; + --ruby-contrast: #fff; + --crimson-contrast: #fff; + --pink-contrast: #fff; + --plum-contrast: #fff; + --purple-contrast: #fff; + --violet-contrast: #fff; + --iris-contrast: #fff; + --indigo-contrast: #fff; + --blue-contrast: #fff; + --cyan-contrast: #fff; + --teal-contrast: #fff; + --jade-contrast: #fff; + --green-contrast: #fff; + --grass-contrast: #fff; + --bronze-contrast: #fff; + --gold-contrast: #fff; + --brown-contrast: #fff; + --orange-contrast: #fff; + --amber-contrast: #000; + --yellow-contrast: #000; + --lime-contrast: #000; + --mint-contrast: #000; + --sky-contrast: #000; + --gray-contrast: #fff; + --mauve-contrast: #fff; + --slate-contrast: #fff; + --sage-contrast: #fff; + --olive-contrast: #fff; + --sand-contrast: #fff; + + /** + * Spacing scale + * + * These variables define a spacing scale based on Tailwind's default. + * We've introduced a --size-scale variable as a multiplier. + * By adjusting this single value, we can proportionally + * scale all spacing throughout the entire application. + * + * https://tailwindcss.com/docs/customizing-spacing#default-spacing-scale + */ + --size-scale: 1; + --size-0: 0px; + --size-px: 1px; + --size-0-5: calc(0.125rem * var(--size-scale)); + --size-1: calc(0.25rem * var(--size-scale)); + --size-1-5: calc(0.375rem * var(--size-scale)); + --size-2: calc(0.5rem * var(--size-scale)); + --size-2-5: calc(0.625rem * var(--size-scale)); + --size-3: calc(0.75rem * var(--size-scale)); + --size-3-5: calc(0.875rem * var(--size-scale)); + --size-4: calc(1rem * var(--size-scale)); + --size-5: calc(1.25rem * var(--size-scale)); + --size-6: calc(1.5rem * var(--size-scale)); + --size-7: calc(1.75rem * var(--size-scale)); + --size-8: calc(2rem * var(--size-scale)); + --size-9: calc(2.25rem * var(--size-scale)); + --size-10: calc(2.5rem * var(--size-scale)); + --size-11: calc(2.75rem * var(--size-scale)); + --size-12: calc(3rem * var(--size-scale)); + --size-14: calc(3.5rem * var(--size-scale)); + --size-16: calc(4rem * var(--size-scale)); + --size-20: calc(5rem * var(--size-scale)); + --size-24: calc(6rem * var(--size-scale)); + --size-28: calc(7rem * var(--size-scale)); + --size-32: calc(8rem * var(--size-scale)); + --size-36: calc(9rem * var(--size-scale)); + --size-40: calc(10rem * var(--size-scale)); + --size-44: calc(11rem * var(--size-scale)); + --size-48: calc(12rem * var(--size-scale)); + --size-52: calc(13rem * var(--size-scale)); + --size-56: calc(14rem * var(--size-scale)); + --size-60: calc(15rem * var(--size-scale)); + --size-64: calc(16rem * var(--size-scale)); + --size-72: calc(18rem * var(--size-scale)); + --size-80: calc(20rem * var(--size-scale)); + --size-96: calc(24rem * var(--size-scale)); + + /* Border radii */ + --radius-factor: 1; + --radius-sm: calc(2px * var(--radius-factor) * var(--size-scale)); + --radius-md: calc(6px * var(--radius-factor) * var(--size-scale)); + --radius-lg: calc(8px * var(--radius-factor) * var(--size-scale)); + --radius-xl: calc(12px * var(--radius-factor) * var(--size-scale)); + --radius-2xl: calc(16px * var(--radius-factor) * var(--size-scale)); + --radius-full: 9999px; + + /* Neutral colors */ + --color-neutral-1: var(--slate-1); + --color-neutral-2: var(--slate-2); + --color-neutral-3: var(--slate-3); + --color-neutral-4: var(--slate-4); + --color-neutral-5: var(--slate-5); + --color-neutral-6: var(--slate-6); + --color-neutral-7: var(--slate-7); + --color-neutral-8: var(--slate-8); + --color-neutral-9: var(--slate-9); + --color-neutral-10: var(--slate-10); + --color-neutral-11: var(--slate-11); + --color-neutral-12: var(--slate-12); + --color-neutral-a1: var(--slate-a1); + --color-neutral-a2: var(--slate-a2); + --color-neutral-a3: var(--slate-a3); + --color-neutral-a4: var(--slate-a4); + --color-neutral-a5: var(--slate-a5); + --color-neutral-a6: var(--slate-a6); + --color-neutral-a7: var(--slate-a7); + --color-neutral-a8: var(--slate-a8); + --color-neutral-a9: var(--slate-a9); + --color-neutral-a10: var(--slate-a10); + --color-neutral-a11: var(--slate-a11); + --color-neutral-a12: var(--slate-a12); + --color-neutral-contrast: var(--slate-contrast); + + /* Accent colors */ + --color-accent-1: var(--blue-1); + --color-accent-2: var(--blue-2); + --color-accent-3: var(--blue-3); + --color-accent-4: var(--blue-4); + --color-accent-5: var(--blue-5); + --color-accent-6: var(--blue-6); + --color-accent-7: var(--blue-7); + --color-accent-8: var(--blue-8); + --color-accent-9: var(--blue-9); + --color-accent-10: var(--blue-10); + --color-accent-11: var(--blue-11); + --color-accent-12: var(--blue-12); + --color-accent-contrast: var(--blue-contrast); + + /* Secondary accent colors */ + --color-accent-secondary-1: var(--violet-1); + --color-accent-secondary-2: var(--violet-2); + --color-accent-secondary-3: var(--violet-3); + --color-accent-secondary-4: var(--violet-4); + --color-accent-secondary-5: var(--violet-5); + --color-accent-secondary-6: var(--violet-6); + --color-accent-secondary-7: var(--violet-7); + --color-accent-secondary-8: var(--violet-8); + --color-accent-secondary-9: var(--violet-9); + --color-accent-secondary-10: var(--violet-10); + --color-accent-secondary-11: var(--violet-11); + --color-accent-secondary-12: var(--violet-12); + --color-accent-secondary-contrast: var(--violet-contrast); + + /* Foreground colors */ + --color-fg: var(--color-neutral-12); + --color-fg-secondary: var(--color-neutral-a11); + + /* Background colors */ + --color-bg: #ffffff; + --color-bg-inset: var(--color-neutral-2); + --color-bg-overlay: #ffffff; + + /* Focus ring */ + --color-focus-ring: var(--color-accent-9); + + /* Fonts */ + --font-sans-serif: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", + "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-monospace: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, + "Liberation Mono", "Courier New", monospace; + --font-family: var(--font-sans-serif); + } + + #spark-app.dark-theme { + --color-bg: var(--color-neutral-1); + --color-bg-inset: #000000; + --color-bg-overlay: var(--color-neutral-3); + } +} \ No newline at end of file diff --git a/src/pages/DemoFeatureCards.tsx b/src/components/demo/DemoFeatureCards.tsx similarity index 100% rename from src/pages/DemoFeatureCards.tsx rename to src/components/demo/DemoFeatureCards.tsx diff --git a/src/pages/demo-constants.ts b/src/components/demo/demo-constants.ts similarity index 100% rename from src/pages/demo-constants.ts rename to src/components/demo/demo-constants.ts diff --git a/src/components/features/namespace-manager/NamespaceSelector.tsx b/src/components/features/namespace-manager/NamespaceSelector.tsx index 0d034c1..629ba05 100644 --- a/src/components/features/namespace-manager/NamespaceSelector.tsx +++ b/src/components/features/namespace-manager/NamespaceSelector.tsx @@ -59,7 +59,13 @@ export function NamespaceSelector({ selectedNamespaceId, onNamespaceChange }: Na setLoading(true) try { - const newNamespace = await createNamespace(newNamespaceName.trim()) + const newNamespace: Namespace = { + id: Date.now().toString(), + name: newNamespaceName.trim(), + createdAt: Date.now(), + isDefault: false, + } + await createNamespace(newNamespace) setNamespaces(prev => [...prev, newNamespace]) setNewNamespaceName('') setCreateDialogOpen(false) diff --git a/src/components/features/snippet-viewer/SnippetViewer.tsx b/src/components/features/snippet-viewer/SnippetViewer.tsx index 3815fe9..0ab2827 100644 --- a/src/components/features/snippet-viewer/SnippetViewer.tsx +++ b/src/components/features/snippet-viewer/SnippetViewer.tsx @@ -34,7 +34,7 @@ export function SnippetViewer({ snippet, open, onOpenChange, onEdit, onCopy }: S onEdit(snippet) } - const canPreview = snippet.hasPreview && appConfig.previewEnabledLanguages.includes(snippet.language) + const canPreview = !!(snippet.hasPreview && appConfig.previewEnabledLanguages.includes(snippet.language)) const isPython = snippet.language === 'Python' return ( diff --git a/src/hooks/useDatabaseOperations.ts b/src/hooks/useDatabaseOperations.ts index ef74549..56cfc99 100644 --- a/src/hooks/useDatabaseOperations.ts +++ b/src/hooks/useDatabaseOperations.ts @@ -37,11 +37,7 @@ export function useDatabaseOperations() { setCheckingSchema(true) try { const result = await validateDatabaseSchema() - setSchemaHealth(result.valid ? 'healthy' : 'corrupted') - - if (!result.valid) { - console.warn('Schema validation failed:', result.issues) - } + setSchemaHealth(result ? 'healthy' : 'corrupted') } catch (error) { console.error('Schema check failed:', error) setSchemaHealth('corrupted') @@ -52,12 +48,12 @@ export function useDatabaseOperations() { const handleExport = useCallback(async () => { try { - const data = await exportDatabase() - const blob = new Blob([new Uint8Array(data)], { type: 'application/octet-stream' }) + const jsonData = await exportDatabase() + const blob = new Blob([jsonData], { type: 'application/json' }) const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url - a.download = `codesnippet-backup-${Date.now()}.db` + a.download = `codesnippet-backup-${Date.now()}.json` document.body.appendChild(a) a.click() document.body.removeChild(a) @@ -74,9 +70,8 @@ export function useDatabaseOperations() { if (!file) return try { - const arrayBuffer = await file.arrayBuffer() - const data = new Uint8Array(arrayBuffer) - await importDatabase(data) + const text = await file.text() + await importDatabase(text) toast.success('Database imported successfully') await loadStats() } catch (error) { diff --git a/src/hooks/useSettingsState.ts b/src/hooks/useSettingsState.ts index cf99495..cc206c6 100644 --- a/src/hooks/useSettingsState.ts +++ b/src/hooks/useSettingsState.ts @@ -51,6 +51,10 @@ export function useSettingsState() { await migrateToFlask(flaskUrl, loadStats) } + const handleMigrateToIndexedDBWrapper = async () => { + await handleMigrateToIndexedDB(flaskUrl) + } + return { stats, loading, @@ -72,7 +76,7 @@ export function useSettingsState() { handleTestConnection, handleSaveStorageConfig, handleMigrateToFlask, - handleMigrateToIndexedDB, + handleMigrateToIndexedDB: handleMigrateToIndexedDBWrapper, checkSchemaHealth, } } diff --git a/src/lib/db-constants.ts b/src/lib/db-constants.ts deleted file mode 100644 index eff5427..0000000 --- a/src/lib/db-constants.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Database constants shared across modules - */ - -export const DB_KEY = 'codesnippet-db' -export const IDB_NAME = 'CodeSnippetDB' -export const IDB_STORE = 'database' -export const IDB_VERSION = 1 diff --git a/src/lib/db-core/clearDatabase.ts b/src/lib/db-core/clearDatabase.ts deleted file mode 100644 index 300fbca..0000000 --- a/src/lib/db-core/clearDatabase.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { deleteFromIndexedDB } from '../db-indexeddb' -import { deleteFromLocalStorage } from '../db-localstorage' -import { getFlaskAdapter } from './getFlaskAdapter' -import { initDB } from './initDB' -import { dbState } from './state' - -export async function clearDatabase(): Promise { - const adapter = getFlaskAdapter() - if (adapter) { - await adapter.wipeDatabase() - return - } - - await deleteFromIndexedDB() - deleteFromLocalStorage() - - dbState.dbInstance = null - await initDB() -} diff --git a/src/lib/db-core/exportDatabase.ts b/src/lib/db-core/exportDatabase.ts deleted file mode 100644 index 94937d2..0000000 --- a/src/lib/db-core/exportDatabase.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { initDB } from './initDB' - -export async function exportDatabase(): Promise { - const db = await initDB() - return db.export() -} diff --git a/src/lib/db-core/getDatabaseStats.ts b/src/lib/db-core/getDatabaseStats.ts deleted file mode 100644 index d545113..0000000 --- a/src/lib/db-core/getDatabaseStats.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { openIndexedDB } from '../db-indexeddb' -import { DB_KEY } from '../db-constants' -import { initDB } from './initDB' - -export async function getDatabaseStats(): Promise<{ - snippetCount: number - templateCount: number - storageType: 'indexeddb' | 'localstorage' | 'none' - databaseSize: number -}> { - const db = await initDB() - - const snippetResult = db.exec('SELECT COUNT(*) as count FROM snippets') - const templateResult = db.exec('SELECT COUNT(*) as count FROM snippet_templates') - - const snippetCount = snippetResult[0]?.values[0]?.[0] as number || 0 - const templateCount = templateResult[0]?.values[0]?.[0] as number || 0 - - const data = db.export() - const databaseSize = data.length - - const hasIDB = await openIndexedDB() - const hasLocalStorage = typeof localStorage !== 'undefined' && localStorage.getItem(DB_KEY) !== null - const storageType = hasIDB ? 'indexeddb' : (hasLocalStorage ? 'localstorage' : 'none') - - return { - snippetCount, - templateCount, - storageType, - databaseSize, - } -} diff --git a/src/lib/db-core/getFlaskAdapter.ts b/src/lib/db-core/getFlaskAdapter.ts deleted file mode 100644 index c2bd475..0000000 --- a/src/lib/db-core/getFlaskAdapter.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { FlaskStorageAdapter, getStorageConfig, loadStorageConfig } from '../storage' -import { dbState } from './state' - -export function getFlaskAdapter(): FlaskStorageAdapter | null { - if (!dbState.configLoaded) { - loadStorageConfig() - dbState.configLoaded = true - } - - const config = getStorageConfig() - if (config.backend === 'flask' && config.flaskUrl) { - try { - if (!dbState.flaskAdapter || dbState.flaskAdapter['baseUrl'] !== config.flaskUrl) { - dbState.flaskAdapter = new FlaskStorageAdapter(config.flaskUrl) - } - return dbState.flaskAdapter - } catch (error) { - console.warn('Failed to create Flask adapter:', error) - return null - } - } - return null -} diff --git a/src/lib/db-core/importDatabase.ts b/src/lib/db-core/importDatabase.ts deleted file mode 100644 index 32ddc0a..0000000 --- a/src/lib/db-core/importDatabase.ts +++ /dev/null @@ -1,19 +0,0 @@ -import initSqlJs from 'sql.js' -import { saveDB } from './saveDB' -import { dbState } from './state' - -export async function importDatabase(data: Uint8Array): Promise { - if (!dbState.sqlInstance) { - dbState.sqlInstance = await initSqlJs({ - locateFile: (file) => `https://sql.js.org/dist/${file}`, - }) - } - - try { - dbState.dbInstance = new dbState.sqlInstance.Database(data) - await saveDB() - } catch (error) { - console.error('Failed to import database:', error) - throw error - } -} diff --git a/src/lib/db-core/initDB.ts b/src/lib/db-core/initDB.ts deleted file mode 100644 index 62bd660..0000000 --- a/src/lib/db-core/initDB.ts +++ /dev/null @@ -1,57 +0,0 @@ -import initSqlJs, { Database } from 'sql.js' -import { loadFromIndexedDB } from '../db-indexeddb' -import { loadFromLocalStorage } from '../db-localstorage' -import { createTables, validateSchema } from '../db-schema' -import { saveDB } from './saveDB' -import { dbState } from './state' -import { wipeAndRecreateDB } from './wipeAndRecreateDB' - -export async function initDB(): Promise { - if (dbState.dbInstance) return dbState.dbInstance - - if (!dbState.sqlInstance) { - dbState.sqlInstance = await initSqlJs({ - locateFile: (file) => `https://sql.js.org/dist/${file}`, - }) - } - - let loadedData: Uint8Array | null = null - let schemaValid = false - - loadedData = await loadFromIndexedDB() - - if (!loadedData) { - loadedData = loadFromLocalStorage() - } - - if (loadedData && loadedData.length > 0) { - try { - const testDb = new dbState.sqlInstance.Database(loadedData) - schemaValid = await validateSchema(testDb) - - if (schemaValid) { - dbState.dbInstance = testDb - } else { - console.warn('Schema validation failed, wiping database') - testDb.close() - await wipeAndRecreateDB() - dbState.dbInstance = new dbState.sqlInstance.Database() - } - } catch (error) { - console.error('Failed to load saved database, creating new one:', error) - await wipeAndRecreateDB() - dbState.dbInstance = new dbState.sqlInstance.Database() - } - } else { - dbState.dbInstance = new dbState.sqlInstance.Database() - } - - if (!dbState.dbInstance) { - throw new Error('Failed to initialize database') - } - - createTables(dbState.dbInstance) - await saveDB() - - return dbState.dbInstance -} diff --git a/src/lib/db-core/saveDB.ts b/src/lib/db-core/saveDB.ts deleted file mode 100644 index 8584a4d..0000000 --- a/src/lib/db-core/saveDB.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { saveToIndexedDB } from '../db-indexeddb' -import { saveToLocalStorage } from '../db-localstorage' -import { dbState } from './state' - -export async function saveDB() { - if (!dbState.dbInstance) return - - try { - const data = dbState.dbInstance.export() - - const savedToIDB = await saveToIndexedDB(data) - - if (!savedToIDB) { - saveToLocalStorage(data) - } - } catch (error) { - console.error('Failed to save database:', error) - } -} diff --git a/src/lib/db-core/state.ts b/src/lib/db-core/state.ts deleted file mode 100644 index 3524251..0000000 --- a/src/lib/db-core/state.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { Database } from 'sql.js' -import type { FlaskStorageAdapter } from '../storage' - -export const dbState = { - dbInstance: null as Database | null, - sqlInstance: null as any, - flaskAdapter: null as FlaskStorageAdapter | null, - configLoaded: false, -} diff --git a/src/lib/db-core/wipeAndRecreateDB.ts b/src/lib/db-core/wipeAndRecreateDB.ts deleted file mode 100644 index 0765b00..0000000 --- a/src/lib/db-core/wipeAndRecreateDB.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { deleteFromIndexedDB, saveToIndexedDB } from '../db-indexeddb' -import { deleteFromLocalStorage, saveToLocalStorage } from '../db-localstorage' -import { dbState } from './state' - -export async function wipeAndRecreateDB(): Promise { - console.warn('Wiping corrupted database and creating fresh schema...') - - await saveToIndexedDB(new Uint8Array()) - saveToLocalStorage(new Uint8Array()) - - await deleteFromIndexedDB() - deleteFromLocalStorage() - - dbState.dbInstance = null -} diff --git a/src/lib/db-indexeddb.ts b/src/lib/db-indexeddb.ts deleted file mode 100644 index 197de78..0000000 --- a/src/lib/db-indexeddb.ts +++ /dev/null @@ -1,100 +0,0 @@ -/** - * IndexedDB operations for database persistence - */ - -import { DB_KEY, IDB_NAME, IDB_STORE, IDB_VERSION } from './db-constants' - -export async function openIndexedDB(): Promise { - if (typeof indexedDB === 'undefined') return null - - return new Promise((resolve) => { - try { - const request = indexedDB.open(IDB_NAME, IDB_VERSION) - - request.onerror = () => { - console.warn('IndexedDB not available, falling back to localStorage') - resolve(null) - } - - request.onupgradeneeded = (event) => { - const db = (event.target as IDBOpenDBRequest).result - if (!db.objectStoreNames.contains(IDB_STORE)) { - db.createObjectStore(IDB_STORE) - } - } - - request.onsuccess = (event) => { - resolve((event.target as IDBOpenDBRequest).result) - } - } catch (error) { - console.warn('IndexedDB error:', error) - resolve(null) - } - }) -} - -export async function loadFromIndexedDB(): Promise { - const db = await openIndexedDB() - if (!db) return null - - return new Promise((resolve) => { - try { - const transaction = db.transaction([IDB_STORE], 'readonly') - const store = transaction.objectStore(IDB_STORE) - const request = store.get(DB_KEY) - - request.onsuccess = () => { - const data = request.result - resolve(data ? new Uint8Array(data) : null) - } - - request.onerror = () => { - console.warn('Failed to load from IndexedDB') - resolve(null) - } - } catch (error) { - console.warn('IndexedDB read error:', error) - resolve(null) - } - }) -} - -export async function saveToIndexedDB(data: Uint8Array): Promise { - const db = await openIndexedDB() - if (!db) return false - - return new Promise((resolve) => { - try { - const transaction = db.transaction([IDB_STORE], 'readwrite') - const store = transaction.objectStore(IDB_STORE) - const request = store.put(data, DB_KEY) - - request.onsuccess = () => resolve(true) - request.onerror = () => { - console.warn('Failed to save to IndexedDB') - resolve(false) - } - } catch (error) { - console.warn('IndexedDB write error:', error) - resolve(false) - } - }) -} - -export async function deleteFromIndexedDB(): Promise { - const db = await openIndexedDB() - if (!db) return - - return new Promise((resolve) => { - try { - const transaction = db.transaction([IDB_STORE], 'readwrite') - const store = transaction.objectStore(IDB_STORE) - const request = store.delete(DB_KEY) - request.onsuccess = () => resolve() - request.onerror = () => resolve() - } catch (error) { - console.warn('Error clearing IndexedDB:', error) - resolve() - } - }) -} diff --git a/src/lib/db-localstorage.ts b/src/lib/db-localstorage.ts deleted file mode 100644 index 10d2020..0000000 --- a/src/lib/db-localstorage.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * LocalStorage operations for database persistence - */ - -import { DB_KEY } from './db-constants' - -export function loadFromLocalStorage(): Uint8Array | null { - try { - const savedData = localStorage.getItem(DB_KEY) - if (savedData) { - return new Uint8Array(JSON.parse(savedData)) - } - } catch (error) { - console.warn('Failed to load from localStorage:', error) - } - return null -} - -export function saveToLocalStorage(data: Uint8Array): boolean { - try { - const dataArray = Array.from(data) - localStorage.setItem(DB_KEY, JSON.stringify(dataArray)) - return true - } catch (error) { - console.warn('Failed to save to localStorage (quota exceeded?):', error) - return false - } -} - -export function deleteFromLocalStorage(): void { - try { - localStorage.removeItem(DB_KEY) - } catch (error) { - console.warn('Error clearing localStorage:', error) - } -} diff --git a/src/lib/db-mapper.ts b/src/lib/db-mapper.ts deleted file mode 100644 index b561c27..0000000 --- a/src/lib/db-mapper.ts +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Database row-to-object mapping utilities - * Handles conversion of SQL query results to typed objects - */ - -/** - * Maps a SQL query result row to a typed object - * Handles special conversions for boolean and JSON fields - */ -export function mapRowToObject(row: any[], columns: string[]): T { - const obj: any = {} - - columns.forEach((col, idx) => { - const value = row[idx] - - // Convert integer boolean fields to actual booleans - if (col === 'hasPreview' || col === 'isDefault') { - obj[col] = value === 1 - } - // Parse JSON string fields with error handling - else if (col === 'inputParameters') { - if (value) { - try { - obj[col] = JSON.parse(value as string) - } catch (error) { - console.warn(`Failed to parse JSON for ${col}:`, error) - obj[col] = undefined - } - } else { - obj[col] = undefined - } - } - // All other fields pass through as-is - else { - obj[col] = value - } - }) - - return obj as T -} - -/** - * Maps multiple SQL result rows to an array of typed objects - */ -export function mapRowsToObjects(results: any[]): T[] { - if (results.length === 0) return [] - - const columns = results[0].columns - const values = results[0].values - - return values.map(row => mapRowToObject(row, columns)) -} diff --git a/src/lib/db-namespaces/createNamespace.ts b/src/lib/db-namespaces/createNamespace.ts deleted file mode 100644 index 5079e22..0000000 --- a/src/lib/db-namespaces/createNamespace.ts +++ /dev/null @@ -1,30 +0,0 @@ -import type { Namespace } from '../types' -import { initDB } from '../db-core/initDB' -import { saveDB } from '../db-core/saveDB' -import { getFlaskAdapter } from '../db-core/getFlaskAdapter' - -export async function createNamespace(name: string): Promise { - const namespace: Namespace = { - id: Date.now().toString(), - name, - createdAt: Date.now(), - isDefault: false, - } - - const adapter = getFlaskAdapter() - if (adapter) { - await adapter.createNamespace(namespace) - return namespace - } - - const db = await initDB() - - db.run( - `INSERT INTO namespaces (id, name, createdAt, isDefault) - VALUES (?, ?, ?, ?)`, - [namespace.id, namespace.name, namespace.createdAt, namespace.isDefault ? 1 : 0] - ) - - await saveDB() - return namespace -} diff --git a/src/lib/db-namespaces/deleteNamespace.ts b/src/lib/db-namespaces/deleteNamespace.ts deleted file mode 100644 index 5435acc..0000000 --- a/src/lib/db-namespaces/deleteNamespace.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { initDB } from '../db-core/initDB' -import { saveDB } from '../db-core/saveDB' -import { getFlaskAdapter } from '../db-core/getFlaskAdapter' - -export async function deleteNamespace(id: string): Promise { - const adapter = getFlaskAdapter() - if (adapter) { - return await adapter.deleteNamespace(id) - } - - const db = await initDB() - - const defaultNamespace = db.exec('SELECT id FROM namespaces WHERE isDefault = 1') - if (defaultNamespace.length === 0 || defaultNamespace[0].values.length === 0) { - throw new Error('Default namespace not found') - } - - const defaultId = defaultNamespace[0].values[0][0] as string - - const checkDefault = db.exec('SELECT isDefault FROM namespaces WHERE id = ?', [id]) - if (checkDefault.length > 0 && checkDefault[0].values[0]?.[0] === 1) { - throw new Error('Cannot delete default namespace') - } - - db.run('UPDATE snippets SET namespaceId = ? WHERE namespaceId = ?', [defaultId, id]) - - db.run('DELETE FROM namespaces WHERE id = ?', [id]) - - await saveDB() -} diff --git a/src/lib/db-namespaces/ensureDefaultNamespace.ts b/src/lib/db-namespaces/ensureDefaultNamespace.ts deleted file mode 100644 index 5f8a78f..0000000 --- a/src/lib/db-namespaces/ensureDefaultNamespace.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { Namespace } from '../types' -import { initDB } from '../db-core/initDB' -import { saveDB } from '../db-core/saveDB' - -export async function ensureDefaultNamespace(): Promise { - const db = await initDB() - - const results = db.exec('SELECT COUNT(*) as count FROM namespaces WHERE isDefault = 1') - const count = results[0]?.values[0]?.[0] as number || 0 - - if (count === 0) { - const defaultNamespace: Namespace = { - id: 'default', - name: 'Default', - createdAt: Date.now(), - isDefault: true, - } - - db.run( - `INSERT INTO namespaces (id, name, createdAt, isDefault) - VALUES (?, ?, ?, ?)`, - [defaultNamespace.id, defaultNamespace.name, defaultNamespace.createdAt, 1] - ) - - await saveDB() - } -} diff --git a/src/lib/db-namespaces/getAllNamespaces.ts b/src/lib/db-namespaces/getAllNamespaces.ts deleted file mode 100644 index 0001875..0000000 --- a/src/lib/db-namespaces/getAllNamespaces.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { Namespace } from '../types' -import { initDB } from '../db-core/initDB' -import { getFlaskAdapter } from '../db-core/getFlaskAdapter' -import { mapRowsToObjects } from '../db-mapper' - -export async function getAllNamespaces(): Promise { - const adapter = getFlaskAdapter() - if (adapter) { - return await adapter.getAllNamespaces() - } - - const db = await initDB() - const results = db.exec('SELECT * FROM namespaces ORDER BY isDefault DESC, name ASC') - - return mapRowsToObjects(results) -} diff --git a/src/lib/db-namespaces/getNamespaceById.ts b/src/lib/db-namespaces/getNamespaceById.ts deleted file mode 100644 index d05ca11..0000000 --- a/src/lib/db-namespaces/getNamespaceById.ts +++ /dev/null @@ -1,15 +0,0 @@ -import type { Namespace } from '../types' -import { initDB } from '../db-core/initDB' -import { mapRowToObject } from '../db-mapper' - -export async function getNamespaceById(id: string): Promise { - const db = await initDB() - const results = db.exec('SELECT * FROM namespaces WHERE id = ?', [id]) - - if (results.length === 0 || results[0].values.length === 0) return null - - const columns = results[0].columns - const row = results[0].values[0] - - return mapRowToObject(row, columns) -} diff --git a/src/lib/db-schema.ts b/src/lib/db-schema.ts deleted file mode 100644 index 835d8f4..0000000 --- a/src/lib/db-schema.ts +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Database schema management and validation - */ - -import type { Database } from 'sql.js' - -export async function validateSchema(db: Database): Promise { - try { - const snippetsCheck = db.exec("PRAGMA table_info(snippets)") - if (snippetsCheck.length === 0) return true - - const columns = snippetsCheck[0].values.map(row => row[1] as string) - const requiredColumns = ['id', 'title', 'code', 'language', 'category', 'namespaceId', 'createdAt', 'updatedAt'] - - for (const col of requiredColumns) { - if (!columns.includes(col)) { - console.warn(`Schema validation failed: missing column '${col}'`) - return false - } - } - - const namespacesCheck = db.exec("PRAGMA table_info(namespaces)") - if (namespacesCheck.length === 0) { - console.warn('Schema validation failed: namespaces table missing') - return false - } - - return true - } catch (error) { - console.error('Schema validation error:', error) - return false - } -} - -export function createTables(db: Database): void { - db.run(` - CREATE TABLE IF NOT EXISTS namespaces ( - id TEXT PRIMARY KEY, - name TEXT NOT NULL, - createdAt INTEGER NOT NULL, - isDefault INTEGER DEFAULT 0 - ) - `) - - db.run(` - CREATE TABLE IF NOT EXISTS snippets ( - id TEXT PRIMARY KEY, - title TEXT NOT NULL, - description TEXT, - code TEXT NOT NULL, - language TEXT NOT NULL, - category TEXT NOT NULL, - namespaceId TEXT, - hasPreview INTEGER DEFAULT 0, - functionName TEXT, - inputParameters TEXT, - createdAt INTEGER NOT NULL, - updatedAt INTEGER NOT NULL, - FOREIGN KEY (namespaceId) REFERENCES namespaces(id) - ) - `) - - db.run(` - CREATE TABLE IF NOT EXISTS snippet_templates ( - id TEXT PRIMARY KEY, - title TEXT NOT NULL, - description TEXT, - code TEXT NOT NULL, - language TEXT NOT NULL, - category TEXT NOT NULL, - hasPreview INTEGER DEFAULT 0, - functionName TEXT, - inputParameters TEXT - ) - `) -} - -export async function validateDatabaseSchema(db: Database): Promise<{ valid: boolean; issues: string[] }> { - try { - const issues: string[] = [] - - const snippetsCheck = db.exec("PRAGMA table_info(snippets)") - if (snippetsCheck.length === 0) { - issues.push('Snippets table missing') - return { valid: false, issues } - } - - const columns = snippetsCheck[0].values.map(row => row[1] as string) - const requiredColumns = ['id', 'title', 'code', 'language', 'category', 'namespaceId', 'createdAt', 'updatedAt'] - - for (const col of requiredColumns) { - if (!columns.includes(col)) { - issues.push(`Missing column '${col}' in snippets table`) - } - } - - const namespacesCheck = db.exec("SELECT name FROM sqlite_master WHERE type='table' AND name='namespaces'") - if (namespacesCheck.length === 0) { - issues.push('Namespaces table missing') - } - - return { valid: issues.length === 0, issues } - } catch (error) { - return { valid: false, issues: ['Failed to validate schema: ' + (error as Error).message] } - } -} diff --git a/src/lib/db-snippets/bulkMoveSnippets.ts b/src/lib/db-snippets/bulkMoveSnippets.ts deleted file mode 100644 index 262701c..0000000 --- a/src/lib/db-snippets/bulkMoveSnippets.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { initDB } from '../db-core/initDB' -import { saveDB } from '../db-core/saveDB' -import { getFlaskAdapter } from '../db-core/getFlaskAdapter' - -export async function bulkMoveSnippets(snippetIds: string[], targetNamespaceId: string): Promise { - const adapter = getFlaskAdapter() - if (adapter) { - await adapter.bulkMoveSnippets(snippetIds, targetNamespaceId) - return - } - - const db = await initDB() - const now = Date.now() - - for (const snippetId of snippetIds) { - db.run( - 'UPDATE snippets SET namespaceId = ?, updatedAt = ? WHERE id = ?', - [targetNamespaceId, now, snippetId] - ) - } - - await saveDB() -} diff --git a/src/lib/db-snippets/createSnippet.ts b/src/lib/db-snippets/createSnippet.ts deleted file mode 100644 index acc8c90..0000000 --- a/src/lib/db-snippets/createSnippet.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { Snippet } from '../types' -import { initDB } from '../db-core/initDB' -import { saveDB } from '../db-core/saveDB' -import { getFlaskAdapter } from '../db-core/getFlaskAdapter' - -export async function createSnippet(snippet: Snippet): Promise { - const adapter = getFlaskAdapter() - if (adapter) { - return await adapter.createSnippet(snippet) - } - - const db = await initDB() - - db.run( - `INSERT INTO snippets (id, title, description, code, language, category, namespaceId, hasPreview, functionName, inputParameters, createdAt, updatedAt) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, - [ - snippet.id, - snippet.title, - snippet.description, - snippet.code, - snippet.language, - snippet.category, - snippet.namespaceId || null, - snippet.hasPreview ? 1 : 0, - snippet.functionName || null, - snippet.inputParameters ? JSON.stringify(snippet.inputParameters) : null, - snippet.createdAt, - snippet.updatedAt, - ] - ) - - await saveDB() -} diff --git a/src/lib/db-snippets/createTemplate.ts b/src/lib/db-snippets/createTemplate.ts deleted file mode 100644 index 7014b4c..0000000 --- a/src/lib/db-snippets/createTemplate.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type { SnippetTemplate } from '../types' -import { initDB } from '../db-core/initDB' -import { saveDB } from '../db-core/saveDB' - -export async function createTemplate(template: SnippetTemplate): Promise { - const db = await initDB() - - db.run( - `INSERT INTO snippet_templates (id, title, description, code, language, category, hasPreview, functionName, inputParameters) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, - [ - template.id, - template.title, - template.description, - template.code, - template.language, - template.category, - template.hasPreview ? 1 : 0, - template.functionName || null, - template.inputParameters ? JSON.stringify(template.inputParameters) : null, - ] - ) - - await saveDB() -} diff --git a/src/lib/db-snippets/deleteSnippet.ts b/src/lib/db-snippets/deleteSnippet.ts deleted file mode 100644 index bb89acc..0000000 --- a/src/lib/db-snippets/deleteSnippet.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { initDB } from '../db-core/initDB' -import { saveDB } from '../db-core/saveDB' -import { getFlaskAdapter } from '../db-core/getFlaskAdapter' - -export async function deleteSnippet(id: string): Promise { - const adapter = getFlaskAdapter() - if (adapter) { - return await adapter.deleteSnippet(id) - } - - const db = await initDB() - - db.run('DELETE FROM snippets WHERE id = ?', [id]) - - await saveDB() -} diff --git a/src/lib/db-snippets/getAllSnippets.ts b/src/lib/db-snippets/getAllSnippets.ts deleted file mode 100644 index d924b4e..0000000 --- a/src/lib/db-snippets/getAllSnippets.ts +++ /dev/null @@ -1,16 +0,0 @@ -import type { Snippet } from '../types' -import { initDB } from '../db-core/initDB' -import { getFlaskAdapter } from '../db-core/getFlaskAdapter' -import { mapRowsToObjects } from '../db-mapper' - -export async function getAllSnippets(): Promise { - const adapter = getFlaskAdapter() - if (adapter) { - return await adapter.getAllSnippets() - } - - const db = await initDB() - const results = db.exec('SELECT * FROM snippets ORDER BY updatedAt DESC') - - return mapRowsToObjects(results) -} diff --git a/src/lib/db-snippets/getAllTemplates.ts b/src/lib/db-snippets/getAllTemplates.ts deleted file mode 100644 index 247af39..0000000 --- a/src/lib/db-snippets/getAllTemplates.ts +++ /dev/null @@ -1,10 +0,0 @@ -import type { SnippetTemplate } from '../types' -import { initDB } from '../db-core/initDB' -import { mapRowsToObjects } from '../db-mapper' - -export async function getAllTemplates(): Promise { - const db = await initDB() - const results = db.exec('SELECT * FROM snippet_templates') - - return mapRowsToObjects(results) -} diff --git a/src/lib/db-snippets/getSnippet.ts b/src/lib/db-snippets/getSnippet.ts deleted file mode 100644 index 2943335..0000000 --- a/src/lib/db-snippets/getSnippet.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { Snippet } from '../types' -import { initDB } from '../db-core/initDB' -import { getFlaskAdapter } from '../db-core/getFlaskAdapter' -import { mapRowToObject } from '../db-mapper' - -export async function getSnippet(id: string): Promise { - const adapter = getFlaskAdapter() - if (adapter) { - return await adapter.getSnippet(id) - } - - const db = await initDB() - const results = db.exec('SELECT * FROM snippets WHERE id = ?', [id]) - - if (results.length === 0 || results[0].values.length === 0) return null - - const columns = results[0].columns - const row = results[0].values[0] - - return mapRowToObject(row, columns) -} diff --git a/src/lib/db-snippets/getSnippetsByNamespace.ts b/src/lib/db-snippets/getSnippetsByNamespace.ts deleted file mode 100644 index ffc2dff..0000000 --- a/src/lib/db-snippets/getSnippetsByNamespace.ts +++ /dev/null @@ -1,13 +0,0 @@ -import type { Snippet } from '../types' -import { initDB } from '../db-core/initDB' -import { mapRowsToObjects } from '../db-mapper' - -export async function getSnippetsByNamespace(namespaceId: string): Promise { - const db = await initDB() - const results = db.exec( - 'SELECT * FROM snippets WHERE namespaceId = ? OR (namespaceId IS NULL AND ? = (SELECT id FROM namespaces WHERE isDefault = 1)) ORDER BY updatedAt DESC', - [namespaceId, namespaceId] - ) - - return mapRowsToObjects(results) -} diff --git a/src/lib/db-snippets/moveSnippetToNamespace.ts b/src/lib/db-snippets/moveSnippetToNamespace.ts deleted file mode 100644 index bf87927..0000000 --- a/src/lib/db-snippets/moveSnippetToNamespace.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { initDB } from '../db-core/initDB' -import { saveDB } from '../db-core/saveDB' -import { getFlaskAdapter } from '../db-core/getFlaskAdapter' - -export async function moveSnippetToNamespace(snippetId: string, targetNamespaceId: string): Promise { - const adapter = getFlaskAdapter() - if (adapter) { - const snippet = await adapter.getSnippet(snippetId) - if (snippet) { - snippet.namespaceId = targetNamespaceId - snippet.updatedAt = Date.now() - await adapter.updateSnippet(snippet) - } - return - } - - const db = await initDB() - - db.run( - 'UPDATE snippets SET namespaceId = ?, updatedAt = ? WHERE id = ?', - [targetNamespaceId, Date.now(), snippetId] - ) - - await saveDB() -} diff --git a/src/lib/db-snippets/seedDatabase.ts b/src/lib/db-snippets/seedDatabase.ts deleted file mode 100644 index 5c2647a..0000000 --- a/src/lib/db-snippets/seedDatabase.ts +++ /dev/null @@ -1,41 +0,0 @@ -import type { Snippet, SnippetTemplate } from '../types' -import { initDB } from '../db-core/initDB' -import { createSnippet } from './createSnippet' -import { createTemplate } from './createTemplate' -import { ensureDefaultNamespace } from '../db-namespaces/ensureDefaultNamespace' -import seedSnippetsData from '@/data/seed-snippets.json' -import seedTemplatesData from '@/data/seed-templates.json' - -export async function seedDatabase(): Promise { - const db = await initDB() - - await ensureDefaultNamespace() - - const checkSnippets = db.exec('SELECT COUNT(*) as count FROM snippets') - const snippetCount = checkSnippets[0]?.values[0]?.[0] as number - - if (snippetCount > 0) { - return - } - - const now = Date.now() - - const seedSnippets: Snippet[] = seedSnippetsData.map((snippet, index) => { - const timestamp = now - index * 1000 - return { - ...snippet, - createdAt: timestamp, - updatedAt: timestamp, - } - }) - - for (const snippet of seedSnippets) { - await createSnippet(snippet) - } - - const seedTemplates: SnippetTemplate[] = seedTemplatesData - - for (const template of seedTemplates) { - await createTemplate(template) - } -} diff --git a/src/lib/db-snippets/syncTemplatesFromJSON.ts b/src/lib/db-snippets/syncTemplatesFromJSON.ts deleted file mode 100644 index cb31d95..0000000 --- a/src/lib/db-snippets/syncTemplatesFromJSON.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { SnippetTemplate } from '../types' -import { initDB } from '../db-core/initDB' -import { createTemplate } from './createTemplate' - -export async function syncTemplatesFromJSON(templates: SnippetTemplate[]): Promise { - const db = await initDB() - - const existingTemplates = db.exec('SELECT id FROM snippet_templates') - const existingIds = new Set( - existingTemplates[0]?.values.map(row => row[0] as string) || [] - ) - - let addedCount = 0 - for (const template of templates) { - if (!existingIds.has(template.id)) { - await createTemplate(template) - addedCount++ - } - } -} diff --git a/src/lib/db-snippets/updateSnippet.ts b/src/lib/db-snippets/updateSnippet.ts deleted file mode 100644 index a8afcb3..0000000 --- a/src/lib/db-snippets/updateSnippet.ts +++ /dev/null @@ -1,34 +0,0 @@ -import type { Snippet } from '../types' -import { initDB } from '../db-core/initDB' -import { saveDB } from '../db-core/saveDB' -import { getFlaskAdapter } from '../db-core/getFlaskAdapter' - -export async function updateSnippet(snippet: Snippet): Promise { - const adapter = getFlaskAdapter() - if (adapter) { - return await adapter.updateSnippet(snippet) - } - - const db = await initDB() - - db.run( - `UPDATE snippets - SET title = ?, description = ?, code = ?, language = ?, category = ?, namespaceId = ?, hasPreview = ?, functionName = ?, inputParameters = ?, updatedAt = ? - WHERE id = ?`, - [ - snippet.title, - snippet.description, - snippet.code, - snippet.language, - snippet.category, - snippet.namespaceId || null, - snippet.hasPreview ? 1 : 0, - snippet.functionName || null, - snippet.inputParameters ? JSON.stringify(snippet.inputParameters) : null, - snippet.updatedAt, - snippet.id, - ] - ) - - await saveDB() -} diff --git a/src/lib/db.ts b/src/lib/db.ts index eeab88e..d693302 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -1,38 +1,223 @@ /** - * Main database module - Re-exports from focused modules - * This file maintains backward compatibility while delegating to specialized modules + * Unified storage interface - routes to IndexedDB or Flask based on configuration */ -// Re-export core database functions -export { initDB } from './db-core/initDB' -export { saveDB } from './db-core/saveDB' -export { exportDatabase } from './db-core/exportDatabase' -export { importDatabase } from './db-core/importDatabase' -export { getDatabaseStats } from './db-core/getDatabaseStats' -export { clearDatabase } from './db-core/clearDatabase' +import type { Snippet, Namespace } from './types'; +import { getStorageConfig, FlaskStorageAdapter } from './storage'; +import * as IndexedDBStorage from './indexeddb-storage'; -// Re-export snippet operations -export { getAllSnippets } from './db-snippets/getAllSnippets' -export { getSnippet } from './db-snippets/getSnippet' -export { createSnippet } from './db-snippets/createSnippet' -export { updateSnippet } from './db-snippets/updateSnippet' -export { deleteSnippet } from './db-snippets/deleteSnippet' -export { getSnippetsByNamespace } from './db-snippets/getSnippetsByNamespace' -export { moveSnippetToNamespace } from './db-snippets/moveSnippetToNamespace' -export { bulkMoveSnippets } from './db-snippets/bulkMoveSnippets' -export { getAllTemplates } from './db-snippets/getAllTemplates' -export { createTemplate } from './db-snippets/createTemplate' -export { syncTemplatesFromJSON } from './db-snippets/syncTemplatesFromJSON' -export { seedDatabase } from './db-snippets/seedDatabase' +// Helper to get the active storage backend +function getActiveStorage() { + const config = getStorageConfig(); + + if (config.backend === 'flask' && config.flaskUrl) { + return new FlaskStorageAdapter(config.flaskUrl); + } + + return null; // Use IndexedDB +} -// Re-export namespace operations -export { getAllNamespaces } from './db-namespaces/getAllNamespaces' -export { createNamespace } from './db-namespaces/createNamespace' -export { deleteNamespace } from './db-namespaces/deleteNamespace' -export { ensureDefaultNamespace } from './db-namespaces/ensureDefaultNamespace' -export { getNamespaceById } from './db-namespaces/getNamespaceById' +// Snippet operations +export async function getAllSnippets(): Promise { + const flask = getActiveStorage(); + if (flask) { + return await flask.getAllSnippets(); + } + return await IndexedDBStorage.getAllSnippets(); +} -// Re-export schema validation -export { validateDatabaseSchema } from './db-schema' +export async function getSnippet(id: string): Promise { + const flask = getActiveStorage(); + if (flask) { + return await flask.getSnippet(id); + } + return await IndexedDBStorage.getSnippet(id); +} -// Note: saveDB is intentionally not exported as it's used internally by the modules +export async function createSnippet(snippet: Snippet): Promise { + const flask = getActiveStorage(); + if (flask) { + return await flask.createSnippet(snippet); + } + return await IndexedDBStorage.createSnippet(snippet); +} + +export async function updateSnippet(snippet: Snippet): Promise { + const flask = getActiveStorage(); + if (flask) { + return await flask.updateSnippet(snippet); + } + return await IndexedDBStorage.updateSnippet(snippet); +} + +export async function deleteSnippet(id: string): Promise { + const flask = getActiveStorage(); + if (flask) { + return await flask.deleteSnippet(id); + } + return await IndexedDBStorage.deleteSnippet(id); +} + +export async function getSnippetsByNamespace(namespaceId: string): Promise { + const flask = getActiveStorage(); + if (flask) { + return await flask.getSnippetsByNamespace(namespaceId); + } + return await IndexedDBStorage.getSnippetsByNamespace(namespaceId); +} + +export async function moveSnippetToNamespace(snippetId: string, namespaceId: string): Promise { + const snippet = await getSnippet(snippetId); + if (!snippet) throw new Error('Snippet not found'); + + snippet.namespaceId = namespaceId; + snippet.updatedAt = Date.now(); + + await updateSnippet(snippet); +} + +export async function bulkMoveSnippets(snippetIds: string[], namespaceId: string): Promise { + for (const id of snippetIds) { + await moveSnippetToNamespace(id, namespaceId); + } +} + +export async function getAllTemplates(): Promise { + const snippets = await getAllSnippets(); + return snippets.filter(s => s.isTemplate); +} + +export async function createTemplate(snippet: Omit): Promise { + const template: Snippet = { + ...snippet, + id: Date.now().toString(), + createdAt: Date.now(), + updatedAt: Date.now(), + isTemplate: true, + }; + await createSnippet(template); +} + +export async function syncTemplatesFromJSON(templates: any[]): Promise { + // This would sync predefined templates - implement as needed + console.log('Syncing templates', templates.length); +} + +export async function seedDatabase(): Promise { + // Seed with default namespace if needed + const namespaces = await getAllNamespaces(); + if (namespaces.length === 0) { + await ensureDefaultNamespace(); + } +} + +// Namespace operations +export async function getAllNamespaces(): Promise { + const flask = getActiveStorage(); + if (flask) { + return await flask.getAllNamespaces(); + } + return await IndexedDBStorage.getAllNamespaces(); +} + +export async function getNamespaceById(id: string): Promise { + const flask = getActiveStorage(); + if (flask) { + return await flask.getNamespace(id); + } + return await IndexedDBStorage.getNamespace(id); +} + +export async function createNamespace(namespace: Namespace): Promise { + const flask = getActiveStorage(); + if (flask) { + return await flask.createNamespace(namespace); + } + return await IndexedDBStorage.createNamespace(namespace); +} + +export async function deleteNamespace(id: string): Promise { + const flask = getActiveStorage(); + if (flask) { + return await flask.deleteNamespace(id); + } + return await IndexedDBStorage.deleteNamespace(id); +} + +export async function ensureDefaultNamespace(): Promise { + const namespaces = await getAllNamespaces(); + let defaultNs = namespaces.find(ns => ns.isDefault); + + if (!defaultNs) { + defaultNs = { + id: 'default', + name: 'Default', + createdAt: Date.now(), + isDefault: true, + }; + await createNamespace(defaultNs); + } + + return defaultNs; +} + +// Database operations +export async function initDB(): Promise { + // Initialize IndexedDB or verify Flask connection + const flask = getActiveStorage(); + if (flask) { + const connected = await flask.testConnection(); + if (!connected) { + throw new Error('Failed to connect to Flask backend'); + } + } else { + // Initialize IndexedDB + await IndexedDBStorage.openDB(); + } + + // Ensure default namespace exists + await ensureDefaultNamespace(); +} + +export async function clearDatabase(): Promise { + const flask = getActiveStorage(); + if (flask) { + return await flask.clearDatabase(); + } + return await IndexedDBStorage.clearDatabase(); +} + +export async function getDatabaseStats() { + const flask = getActiveStorage(); + if (flask) { + return await flask.getStats(); + } + return await IndexedDBStorage.getDatabaseStats(); +} + +export async function exportDatabase(): Promise { + const flask = getActiveStorage(); + if (flask) { + const data = await flask.exportDatabase(); + return JSON.stringify(data, null, 2); + } + const data = await IndexedDBStorage.exportDatabase(); + return JSON.stringify(data, null, 2); +} + +export async function importDatabase(jsonData: string): Promise { + const data = JSON.parse(jsonData); + const flask = getActiveStorage(); + if (flask) { + return await flask.importDatabase(data); + } + await IndexedDBStorage.importDatabase(data); +} + +export function validateDatabaseSchema(): Promise { + // With IndexedDB, schema is always valid + return Promise.resolve(true); +} + +// For backward compatibility +export const saveDB = async () => { /* No-op with IndexedDB */ }; diff --git a/src/lib/db.ts.old b/src/lib/db.ts.old new file mode 100644 index 0000000..eeab88e --- /dev/null +++ b/src/lib/db.ts.old @@ -0,0 +1,38 @@ +/** + * Main database module - Re-exports from focused modules + * This file maintains backward compatibility while delegating to specialized modules + */ + +// Re-export core database functions +export { initDB } from './db-core/initDB' +export { saveDB } from './db-core/saveDB' +export { exportDatabase } from './db-core/exportDatabase' +export { importDatabase } from './db-core/importDatabase' +export { getDatabaseStats } from './db-core/getDatabaseStats' +export { clearDatabase } from './db-core/clearDatabase' + +// Re-export snippet operations +export { getAllSnippets } from './db-snippets/getAllSnippets' +export { getSnippet } from './db-snippets/getSnippet' +export { createSnippet } from './db-snippets/createSnippet' +export { updateSnippet } from './db-snippets/updateSnippet' +export { deleteSnippet } from './db-snippets/deleteSnippet' +export { getSnippetsByNamespace } from './db-snippets/getSnippetsByNamespace' +export { moveSnippetToNamespace } from './db-snippets/moveSnippetToNamespace' +export { bulkMoveSnippets } from './db-snippets/bulkMoveSnippets' +export { getAllTemplates } from './db-snippets/getAllTemplates' +export { createTemplate } from './db-snippets/createTemplate' +export { syncTemplatesFromJSON } from './db-snippets/syncTemplatesFromJSON' +export { seedDatabase } from './db-snippets/seedDatabase' + +// Re-export namespace operations +export { getAllNamespaces } from './db-namespaces/getAllNamespaces' +export { createNamespace } from './db-namespaces/createNamespace' +export { deleteNamespace } from './db-namespaces/deleteNamespace' +export { ensureDefaultNamespace } from './db-namespaces/ensureDefaultNamespace' +export { getNamespaceById } from './db-namespaces/getNamespaceById' + +// Re-export schema validation +export { validateDatabaseSchema } from './db-schema' + +// Note: saveDB is intentionally not exported as it's used internally by the modules diff --git a/src/lib/indexeddb-storage.ts b/src/lib/indexeddb-storage.ts new file mode 100644 index 0000000..561e8db --- /dev/null +++ b/src/lib/indexeddb-storage.ts @@ -0,0 +1,241 @@ +/** + * IndexedDB Storage - Direct storage of snippets and namespaces + */ + +import type { Snippet, Namespace } from './types'; + +const DB_NAME = 'codesnippet-db'; +const DB_VERSION = 2; +const SNIPPETS_STORE = 'snippets'; +const NAMESPACES_STORE = 'namespaces'; + +let dbInstance: IDBDatabase | null = null; + +export async function openDB(): Promise { + if (dbInstance) return dbInstance; + + return new Promise((resolve, reject) => { + const request = indexedDB.open(DB_NAME, DB_VERSION); + + request.onerror = () => reject(request.error); + + request.onupgradeneeded = (event) => { + const db = (event.target as IDBOpenDBRequest).result; + + // Create snippets store if it doesn't exist + if (!db.objectStoreNames.contains(SNIPPETS_STORE)) { + const snippetsStore = db.createObjectStore(SNIPPETS_STORE, { keyPath: 'id' }); + snippetsStore.createIndex('namespaceId', 'namespaceId', { unique: false }); + snippetsStore.createIndex('createdAt', 'createdAt', { unique: false }); + } + + // Create namespaces store if it doesn't exist + if (!db.objectStoreNames.contains(NAMESPACES_STORE)) { + db.createObjectStore(NAMESPACES_STORE, { keyPath: 'id' }); + } + }; + + request.onsuccess = () => { + dbInstance = request.result; + resolve(dbInstance); + }; + }); +} + +// Snippet operations +export async function getAllSnippets(): Promise { + const db = await openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([SNIPPETS_STORE], 'readonly'); + const store = transaction.objectStore(SNIPPETS_STORE); + const request = store.getAll(); + + request.onerror = () => reject(request.error); + request.onsuccess = () => resolve(request.result); + }); +} + +export async function getSnippet(id: string): Promise { + const db = await openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([SNIPPETS_STORE], 'readonly'); + const store = transaction.objectStore(SNIPPETS_STORE); + const request = store.get(id); + + request.onerror = () => reject(request.error); + request.onsuccess = () => resolve(request.result || null); + }); +} + +export async function createSnippet(snippet: Snippet): Promise { + const db = await openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([SNIPPETS_STORE], 'readwrite'); + const store = transaction.objectStore(SNIPPETS_STORE); + const request = store.add(snippet); + + request.onerror = () => reject(request.error); + request.onsuccess = () => resolve(); + }); +} + +export async function updateSnippet(snippet: Snippet): Promise { + const db = await openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([SNIPPETS_STORE], 'readwrite'); + const store = transaction.objectStore(SNIPPETS_STORE); + const request = store.put(snippet); + + request.onerror = () => reject(request.error); + request.onsuccess = () => resolve(); + }); +} + +export async function deleteSnippet(id: string): Promise { + const db = await openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([SNIPPETS_STORE], 'readwrite'); + const store = transaction.objectStore(SNIPPETS_STORE); + const request = store.delete(id); + + request.onerror = () => reject(request.error); + request.onsuccess = () => resolve(); + }); +} + +export async function getSnippetsByNamespace(namespaceId: string): Promise { + const db = await openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([SNIPPETS_STORE], 'readonly'); + const store = transaction.objectStore(SNIPPETS_STORE); + const index = store.index('namespaceId'); + const request = index.getAll(namespaceId); + + request.onerror = () => reject(request.error); + request.onsuccess = () => resolve(request.result); + }); +} + +// Namespace operations +export async function getAllNamespaces(): Promise { + const db = await openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([NAMESPACES_STORE], 'readonly'); + const store = transaction.objectStore(NAMESPACES_STORE); + const request = store.getAll(); + + request.onerror = () => reject(request.error); + request.onsuccess = () => resolve(request.result); + }); +} + +export async function getNamespace(id: string): Promise { + const db = await openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([NAMESPACES_STORE], 'readonly'); + const store = transaction.objectStore(NAMESPACES_STORE); + const request = store.get(id); + + request.onerror = () => reject(request.error); + request.onsuccess = () => resolve(request.result || null); + }); +} + +export async function createNamespace(namespace: Namespace): Promise { + const db = await openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([NAMESPACES_STORE], 'readwrite'); + const store = transaction.objectStore(NAMESPACES_STORE); + const request = store.add(namespace); + + request.onerror = () => reject(request.error); + request.onsuccess = () => resolve(); + }); +} + +export async function updateNamespace(namespace: Namespace): Promise { + const db = await openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([NAMESPACES_STORE], 'readwrite'); + const store = transaction.objectStore(NAMESPACES_STORE); + const request = store.put(namespace); + + request.onerror = () => reject(request.error); + request.onsuccess = () => resolve(); + }); +} + +export async function deleteNamespace(id: string): Promise { + const db = await openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([NAMESPACES_STORE], 'readwrite'); + const store = transaction.objectStore(NAMESPACES_STORE); + const request = store.delete(id); + + request.onerror = () => reject(request.error); + request.onsuccess = () => resolve(); + }); +} + +// Database operations +export async function clearDatabase(): Promise { + const db = await openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([SNIPPETS_STORE, NAMESPACES_STORE], 'readwrite'); + + const snippetsStore = transaction.objectStore(SNIPPETS_STORE); + const namespacesStore = transaction.objectStore(NAMESPACES_STORE); + + snippetsStore.clear(); + namespacesStore.clear(); + + transaction.onerror = () => reject(transaction.error); + transaction.oncomplete = () => resolve(); + }); +} + +export async function getDatabaseStats() { + const snippets = await getAllSnippets(); + const namespaces = await getAllNamespaces(); + const templates = snippets.filter(s => s.isTemplate); + + return { + snippetCount: snippets.length, + templateCount: templates.length, + namespaceCount: namespaces.length, + storageType: 'indexeddb' as const, + databaseSize: 0, // IndexedDB doesn't provide easy size calculation + }; +} + +// Export/Import +export async function exportDatabase(): Promise<{ snippets: Snippet[]; namespaces: Namespace[] }> { + const snippets = await getAllSnippets(); + const namespaces = await getAllNamespaces(); + return { snippets, namespaces }; +} + +export async function importDatabase(data: { snippets: Snippet[]; namespaces: Namespace[] }): Promise { + await clearDatabase(); + + const db = await openDB(); + + return new Promise((resolve, reject) => { + const transaction = db.transaction([SNIPPETS_STORE, NAMESPACES_STORE], 'readwrite'); + const snippetsStore = transaction.objectStore(SNIPPETS_STORE); + const namespacesStore = transaction.objectStore(NAMESPACES_STORE); + + // Import namespaces + for (const namespace of data.namespaces) { + namespacesStore.add(namespace); + } + + // Import snippets + for (const snippet of data.snippets) { + snippetsStore.add(snippet); + } + + transaction.onerror = () => reject(transaction.error); + transaction.oncomplete = () => resolve(); + }); +} diff --git a/src/lib/storage.ts b/src/lib/storage.ts index 65cd56d..dbce4b5 100644 --- a/src/lib/storage.ts +++ b/src/lib/storage.ts @@ -249,4 +249,49 @@ export class FlaskStorageAdapter { throw new Error(`Failed to bulk move snippets: ${response.statusText}`) } } + + async getSnippetsByNamespace(namespaceId: string): Promise { + const snippets = await this.getAllSnippets(); + return snippets.filter(s => s.namespaceId === namespaceId); + } + + async getNamespace(id: string): Promise { + const namespaces = await this.getAllNamespaces(); + return namespaces.find(ns => ns.id === id) || null; + } + + async clearDatabase(): Promise { + return this.wipeDatabase(); + } + + async getStats() { + const snippets = await this.getAllSnippets(); + const namespaces = await this.getAllNamespaces(); + const templates = snippets.filter(s => s.isTemplate); + return { + snippetCount: snippets.length, + templateCount: templates.length, + namespaceCount: namespaces.length, + storageType: 'indexeddb' as const, + databaseSize: 0, + }; + } + + async exportDatabase(): Promise<{ snippets: Snippet[]; namespaces: import('./types').Namespace[] }> { + const snippets = await this.getAllSnippets(); + const namespaces = await this.getAllNamespaces(); + return { snippets, namespaces }; + } + + async importDatabase(data: { snippets: Snippet[]; namespaces: import('./types').Namespace[] }): Promise { + await this.wipeDatabase(); + + for (const namespace of data.namespaces) { + await this.createNamespace(namespace); + } + + for (const snippet of data.snippets) { + await this.createSnippet(snippet); + } + } } diff --git a/src/lib/types.ts b/src/lib/types.ts index db9d42e..973e955 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -16,6 +16,7 @@ export interface Snippet { category: string namespaceId?: string hasPreview?: boolean + isTemplate?: boolean functionName?: string inputParameters?: InputParameter[] createdAt: number diff --git a/src/main.tsx b/src/main.tsx deleted file mode 100644 index 0f43ff0..0000000 --- a/src/main.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { createRoot } from 'react-dom/client' -import { ErrorBoundary } from "react-error-boundary"; -import { Provider } from 'react-redux' -import "@github/spark/spark" -import { Toaster } from '@/components/ui/sonner' -import { loadStorageConfig } from '@/lib/storage' -import { store } from '@/store' - -import App from './App.tsx' -import { ErrorFallback } from './components/error/ErrorFallback.tsx' - -import "./main.css" -import "./styles/theme.css" -import "./index.css" - -loadStorageConfig() - -const logErrorToConsole = (error: Error, info: { componentStack?: string }) => { - console.error('Application Error:', error); - if (info.componentStack) { - console.error('Component Stack:', info.componentStack); - } -}; - -createRoot(document.getElementById('root')!).render( - - - - - - -) diff --git a/src/pages/AtomsPage.tsx b/src/pages/AtomsPage.tsx deleted file mode 100644 index b3a1f19..0000000 --- a/src/pages/AtomsPage.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { motion } from 'framer-motion' -import { AtomsSection } from '@/components/atoms/AtomsSection' -import type { Snippet } from '@/lib/types' -import { useCallback } from 'react' -import { toast } from 'sonner' -import { createSnippet } from '@/lib/db' - -export function AtomsPage() { - const handleSaveSnippet = useCallback(async (snippetData: Omit) => { - try { - const newSnippet: Snippet = { - ...snippetData, - id: Date.now().toString(), - createdAt: Date.now(), - updatedAt: Date.now(), - } - await createSnippet(newSnippet) - toast.success('Component saved as snippet!') - } catch (error) { - console.error('Failed to save snippet:', error) - toast.error('Failed to save snippet') - } - }, []) - - return ( - -
-

Atoms

-

Fundamental building blocks - basic HTML elements styled as reusable components

-
- -
- ) -} diff --git a/src/pages/DemoPage.tsx b/src/pages/DemoPage.tsx deleted file mode 100644 index ce0a9da..0000000 --- a/src/pages/DemoPage.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { useState } from 'react' -import { motion } from 'framer-motion' -import { SplitScreenEditor } from '@/components/features/snippet-editor/SplitScreenEditor' -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' -import { Sparkle } from '@phosphor-icons/react' -import { DEMO_CODE } from './demo-constants' -import { DemoFeatureCards } from './DemoFeatureCards' - -export function DemoPage() { - const [code, setCode] = useState(DEMO_CODE) - - return ( - -
-
-
- -
-

Split-Screen Demo

-
-

- Experience live React component editing with real-time preview. Edit the code on the left and watch it update instantly on the right. -

-
- - - - - - Interactive Code Editor - - - This editor supports JSX, TSX, JavaScript, and TypeScript with live preview. - Try switching between Code, Split, and Preview modes using the buttons above the editor. - - - - - - - - -
- ) -} diff --git a/src/pages/HomePage.tsx b/src/pages/HomePage.tsx deleted file mode 100644 index 1d2df8e..0000000 --- a/src/pages/HomePage.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { motion } from 'framer-motion' -import { SnippetManagerRedux } from '@/components/SnippetManagerRedux' - -export function HomePage() { - return ( - -
-

My Snippets

-

Save, organize, and share your code snippets

-
- -
- ) -} diff --git a/src/pages/MoleculesPage.tsx b/src/pages/MoleculesPage.tsx deleted file mode 100644 index 54bb3ba..0000000 --- a/src/pages/MoleculesPage.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { motion } from 'framer-motion' -import { MoleculesSection } from '@/components/molecules/MoleculesSection' -import type { Snippet } from '@/lib/types' -import { useCallback } from 'react' -import { toast } from 'sonner' -import { createSnippet } from '@/lib/db' - -export function MoleculesPage() { - const handleSaveSnippet = useCallback(async (snippetData: Omit) => { - try { - const newSnippet: Snippet = { - ...snippetData, - id: Date.now().toString(), - createdAt: Date.now(), - updatedAt: Date.now(), - } - await createSnippet(newSnippet) - toast.success('Component saved as snippet!') - } catch (error) { - console.error('Failed to save snippet:', error) - toast.error('Failed to save snippet') - } - }, []) - - return ( - -
-

Molecules

-

Simple combinations of atoms that work together as functional units

-
- -
- ) -} diff --git a/src/pages/OrganismsPage.tsx b/src/pages/OrganismsPage.tsx deleted file mode 100644 index 5787c19..0000000 --- a/src/pages/OrganismsPage.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { motion } from 'framer-motion' -import { OrganismsSection } from '@/components/organisms/OrganismsSection' -import type { Snippet } from '@/lib/types' -import { useCallback } from 'react' -import { toast } from 'sonner' -import { createSnippet } from '@/lib/db' - -export function OrganismsPage() { - const handleSaveSnippet = useCallback(async (snippetData: Omit) => { - try { - const newSnippet: Snippet = { - ...snippetData, - id: Date.now().toString(), - createdAt: Date.now(), - updatedAt: Date.now(), - } - await createSnippet(newSnippet) - toast.success('Component saved as snippet!') - } catch (error) { - console.error('Failed to save snippet:', error) - toast.error('Failed to save snippet') - } - }, []) - - return ( - -
-

Organisms

-

Complex UI components composed of molecules and atoms

-
- -
- ) -} diff --git a/src/pages/SettingsPage.tsx b/src/pages/SettingsPage.tsx deleted file mode 100644 index 50142c8..0000000 --- a/src/pages/SettingsPage.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import { motion } from 'framer-motion' -import { PersistenceSettings } from '@/components/demo/PersistenceSettings' -import { SchemaHealthCard } from '@/components/settings/SchemaHealthCard' -import { BackendAutoConfigCard } from '@/components/settings/BackendAutoConfigCard' -import { StorageBackendCard } from '@/components/settings/StorageBackendCard' -import { DatabaseStatsCard } from '@/components/settings/DatabaseStatsCard' -import { StorageInfoCard } from '@/components/settings/StorageInfoCard' -import { DatabaseActionsCard } from '@/components/settings/DatabaseActionsCard' -import { useSettingsState } from '@/hooks/useSettingsState' - -export function SettingsPage() { - const { - stats, - loading, - storageBackend, - setStorageBackend, - flaskUrl, - setFlaskUrl, - flaskConnectionStatus, - setFlaskConnectionStatus, - testingConnection, - envVarSet, - schemaHealth, - checkingSchema, - handleExport, - handleImport, - handleClear, - handleSeed, - formatBytes, - handleTestConnection, - handleSaveStorageConfig, - handleMigrateToFlask, - handleMigrateToIndexedDB, - checkSchemaHealth, - } = useSettingsState() - - return ( - -
-

Settings

-

Manage your database and application settings

-
- -
- - - - - - - { - setFlaskUrl(url) - setFlaskConnectionStatus('unknown') - }} - onTestConnection={handleTestConnection} - onSaveConfig={handleSaveStorageConfig} - onMigrateToFlask={handleMigrateToFlask} - onMigrateToIndexedDB={handleMigrateToIndexedDB} - /> - - - - - - -
-
- ) -} diff --git a/src/pages/TemplatesPage.tsx b/src/pages/TemplatesPage.tsx deleted file mode 100644 index 784e779..0000000 --- a/src/pages/TemplatesPage.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { motion } from 'framer-motion' -import { TemplatesSection } from '@/components/templates/TemplatesSection' -import type { Snippet } from '@/lib/types' -import { useCallback } from 'react' -import { toast } from 'sonner' -import { createSnippet } from '@/lib/db' - -export function TemplatesPage() { - const handleSaveSnippet = useCallback(async (snippetData: Omit) => { - try { - const newSnippet: Snippet = { - ...snippetData, - id: Date.now().toString(), - createdAt: Date.now(), - updatedAt: Date.now(), - } - await createSnippet(newSnippet) - toast.success('Component saved as snippet!') - } catch (error) { - console.error('Failed to save snippet:', error) - toast.error('Failed to save snippet') - } - }, []) - - return ( - -
-

Templates

-

Page-level layouts that combine organisms into complete interfaces

-
- -
- ) -} diff --git a/src/store/slices/namespacesSlice.ts b/src/store/slices/namespacesSlice.ts index 36f1f4c..b440d08 100644 --- a/src/store/slices/namespacesSlice.ts +++ b/src/store/slices/namespacesSlice.ts @@ -32,7 +32,14 @@ export const fetchNamespaces = createAsyncThunk( export const createNamespace = createAsyncThunk( 'namespaces/create', async (name: string) => { - return await createNamespaceDB(name) + const namespace: Namespace = { + id: Date.now().toString(), + name, + createdAt: Date.now(), + isDefault: false, + } + await createNamespaceDB(namespace) + return namespace } ) diff --git a/tailwind.config.js b/tailwind.config.js index 8f44d5e..f2d44a8 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,147 +1,62 @@ -import fs from "fs"; - /** @type {import('tailwindcss').Config} */ - -let theme = {}; -try { - const themePath = "./theme.json"; - - if (fs.existsSync(themePath)) { - theme = JSON.parse(fs.readFileSync(themePath, "utf-8")); - } -} catch (err) { - console.error('failed to parse custom styles', err) +module.exports = { + darkMode: ["class"], + content: [ + './src/pages/**/*.{ts,tsx}', + './src/components/**/*.{ts,tsx}', + './src/app/**/*.{ts,tsx}', + './src/**/*.{ts,tsx}', + ], + theme: { + container: { + center: true, + padding: "2rem", + screens: { + "2xl": "1400px", + }, + }, + extend: { + colors: { + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + }, + }, + plugins: [], } -const defaultTheme = { - container: { - center: true, - padding: "2rem", - }, - extend: { - screens: { - coarse: { raw: "(pointer: coarse)" }, - fine: { raw: "(pointer: fine)" }, - pwa: { raw: "(display-mode: standalone)" }, - }, - colors: { - neutral: { - 1: "var(--color-neutral-1)", - 2: "var(--color-neutral-2)", - 3: "var(--color-neutral-3)", - 4: "var(--color-neutral-4)", - 5: "var(--color-neutral-5)", - 6: "var(--color-neutral-6)", - 7: "var(--color-neutral-7)", - 8: "var(--color-neutral-8)", - 9: "var(--color-neutral-9)", - 10: "var(--color-neutral-10)", - 11: "var(--color-neutral-11)", - 12: "var(--color-neutral-12)", - a1: "var(--color-neutral-a1)", - a2: "var(--color-neutral-a2)", - a3: "var(--color-neutral-a3)", - a4: "var(--color-neutral-a4)", - a5: "var(--color-neutral-a5)", - a6: "var(--color-neutral-a6)", - a7: "var(--color-neutral-a7)", - a8: "var(--color-neutral-a8)", - a9: "var(--color-neutral-a9)", - a10: "var(--color-neutral-a10)", - a11: "var(--color-neutral-a11)", - a12: "var(--color-neutral-a12)", - contrast: "var(--color-neutral-contrast)", - }, - accent: { - 1: "var(--color-accent-1)", - 2: "var(--color-accent-2)", - 3: "var(--color-accent-3)", - 4: "var(--color-accent-4)", - 5: "var(--color-accent-5)", - 6: "var(--color-accent-6)", - 7: "var(--color-accent-7)", - 8: "var(--color-accent-8)", - 9: "var(--color-accent-9)", - 10: "var(--color-accent-10)", - 11: "var(--color-accent-11)", - 12: "var(--color-accent-12)", - contrast: "var(--color-accent-contrast)", - }, - "accent-secondary": { - 1: "var(--color-accent-secondary-1)", - 2: "var(--color-accent-secondary-2)", - 3: "var(--color-accent-secondary-3)", - 4: "var(--color-accent-secondary-4)", - 5: "var(--color-accent-secondary-5)", - 6: "var(--color-accent-secondary-6)", - 7: "var(--color-accent-secondary-7)", - 8: "var(--color-accent-secondary-8)", - 9: "var(--color-accent-secondary-9)", - 10: "var(--color-accent-secondary-10)", - 11: "var(--color-accent-secondary-11)", - 12: "var(--color-accent-secondary-12)", - contrast: "var(--color-accent-secondary-contrast)", - }, - fg: { - DEFAULT: "var(--color-fg)", - secondary: "var(--color-fg-secondary)", - }, - bg: { - DEFAULT: "var(--color-bg)", - inset: "var(--color-bg-inset)", - overlay: "var(--color-bg-overlay)", - }, - "focus-ring": "var(--color-focus-ring)", - }, - borderRadius: { - sm: "var(--radius-sm)", - md: "var(--radius-md)", - lg: "var(--radius-lg)", - xl: "var(--radius-xl)", - "2xl": "var(--radius-2xl)", - full: "var(--radius-full)", - }, - }, - spacing: { - px: "var(--size-px)", - 0: "var(--size-0)", - 0.5: "var(--size-0-5)", - 1: "var(--size-1)", - 1.5: "var(--size-1-5)", - 2: "var(--size-2)", - 2.5: "var(--size-2-5)", - 3: "var(--size-3)", - 3.5: "var(--size-3-5)", - 4: "var(--size-4)", - 5: "var(--size-5)", - 6: "var(--size-6)", - 7: "var(--size-7)", - 8: "var(--size-8)", - 9: "var(--size-9)", - 10: "var(--size-10)", - 11: "var(--size-11)", - 12: "var(--size-12)", - 14: "var(--size-14)", - 16: "var(--size-16)", - 20: "var(--size-20)", - 24: "var(--size-24)", - 28: "var(--size-28)", - 32: "var(--size-32)", - 36: "var(--size-36)", - 40: "var(--size-40)", - 44: "var(--size-44)", - 48: "var(--size-48)", - 52: "var(--size-52)", - 56: "var(--size-56)", - 60: "var(--size-60)", - 64: "var(--size-64)", - 72: "var(--size-72)", - 80: "var(--size-80)", - 96: "var(--size-96)", - }, - darkMode: ["selector", '[data-appearance="dark"]'], -} - -export default { - content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], - theme: { ...defaultTheme, ...theme }, -}; \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 70a33cc..30346b7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,7 @@ "DOM", "DOM.Iterable" ], - "jsx": "preserve", + "jsx": "react-jsx", "module": "ESNext", "moduleResolution": "bundler", "resolveJsonModule": true, @@ -36,7 +36,8 @@ "next-env.d.ts", "**/*.ts", "**/*.tsx", - ".next/types/**/*.ts" + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts" ], "exclude": [ "node_modules" diff --git a/vite.config.ts b/vite.config.ts deleted file mode 100644 index a73d354..0000000 --- a/vite.config.ts +++ /dev/null @@ -1,29 +0,0 @@ -import tailwindcss from "@tailwindcss/vite"; -import react from "@vitejs/plugin-react-swc"; -import { defineConfig, PluginOption } from "vite"; - -import sparkPlugin from "@github/spark/spark-vite-plugin"; -import createIconImportProxy from "@github/spark/vitePhosphorIconProxyPlugin"; -import { resolve } from 'path' - -const projectRoot = process.env.PROJECT_ROOT || import.meta.dirname - -// https://vite.dev/config/ -export default defineConfig({ - // Base path for GitHub Pages deployment - // Set to '/' for custom domain or root deployment - // Set to '/repo-name/' for GitHub Pages at username.github.io/repo-name/ - base: process.env.VITE_BASE_PATH || '/', - plugins: [ - react(), - tailwindcss(), - // DO NOT REMOVE - createIconImportProxy() as PluginOption, - sparkPlugin() as PluginOption, - ], - resolve: { - alias: { - '@': resolve(projectRoot, 'src') - } - }, -});