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
-
-
-
-
-
-
-
-
-
-
-
- } />
- } />
- } />
- } />
- } />
- } />
- } />
-
-
-
-
-
-
- )
-}
-
-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')
- }
- },
-});