Initial import
This commit is contained in:
commit
c126b6f264
139
base44-site-template-main/.gitignore
vendored
Normal file
139
base44-site-template-main/.gitignore
vendored
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
.temp
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Sveltekit cache directory
|
||||||
|
.svelte-kit/
|
||||||
|
|
||||||
|
# vitepress build output
|
||||||
|
**/.vitepress/dist
|
||||||
|
|
||||||
|
# vitepress cache directory
|
||||||
|
**/.vitepress/cache
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# Firebase cache directory
|
||||||
|
.firebase/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v3
|
||||||
|
.pnp.*
|
||||||
|
.yarn/*
|
||||||
|
!.yarn/patches
|
||||||
|
!.yarn/plugins
|
||||||
|
!.yarn/releases
|
||||||
|
!.yarn/sdks
|
||||||
|
!.yarn/versions
|
||||||
|
|
||||||
|
# Vite logs files
|
||||||
|
vite.config.js.timestamp-*
|
||||||
|
vite.config.ts.timestamp-*
|
||||||
21
base44-site-template-main/LICENSE
Normal file
21
base44-site-template-main/LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 Yair Morgenstern
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
43
base44-site-template-main/README.md
Normal file
43
base44-site-template-main/README.md
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# Base44 Site Template
|
||||||
|
|
||||||
|
## What?
|
||||||
|
|
||||||
|
[base44](https://base44.com/) creates really beautiful sites, which is a lifesaver for someone like me who can’t design. But the sites are all online and not under your control. This is how you can make them run locally!
|
||||||
|
|
||||||
|
## How?
|
||||||
|
|
||||||
|
Make this site run:
|
||||||
|
|
||||||
|
- `npm install`
|
||||||
|
- `npm start` to check
|
||||||
|
|
||||||
|
Make your site run:
|
||||||
|
|
||||||
|
- Copy the jsx files from base44 (you can see them in base44 under Workspace > Code) into the `src/` directory
|
||||||
|
- Fix imports between the files, if you didn't keep the original file structure
|
||||||
|
- Add any missing npm packages (in your jsx files, but not in template)
|
||||||
|
- Install any missing shadcn components — e.g. `npx shadcn@latest add button input dialog`
|
||||||
|
- Remove base44 “entity access” code (or comment out, to replace with in-memory key-value storage later)
|
||||||
|
|
||||||
|
That's it!
|
||||||
|
|
||||||
|
|
||||||
|
## Usage instructions
|
||||||
|
|
||||||
|
- `npm start` — This will spawn a development server with a default port of `5173`.
|
||||||
|
- `npm run build` — This will output a production build in the `dist` directory.
|
||||||
|
- `npm run preview` — This will run the production build locally with a default port of `5173` (this will not work if you haven't generated the production build yet).
|
||||||
|
|
||||||
|
## Custom port
|
||||||
|
|
||||||
|
You can use the `-p` flag to specify a port for development. To do this, you can either run `npm start` with an additional flag:
|
||||||
|
|
||||||
|
```
|
||||||
|
npm start -- --port 3000
|
||||||
|
```
|
||||||
|
|
||||||
|
Or edit the `start` script directly:
|
||||||
|
|
||||||
|
```
|
||||||
|
vite --port 3000
|
||||||
|
```
|
||||||
21
base44-site-template-main/components.json
Normal file
21
base44-site-template-main/components.json
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://ui.shadcn.com/schema.json",
|
||||||
|
"style": "new-york",
|
||||||
|
"rsc": false,
|
||||||
|
"tsx": true,
|
||||||
|
"tailwind": {
|
||||||
|
"config": "",
|
||||||
|
"css": "index.css",
|
||||||
|
"baseColor": "neutral",
|
||||||
|
"cssVariables": true,
|
||||||
|
"prefix": ""
|
||||||
|
},
|
||||||
|
"aliases": {
|
||||||
|
"components": "@/components",
|
||||||
|
"utils": "@/lib/utils",
|
||||||
|
"ui": "@/components/ui",
|
||||||
|
"lib": "@/lib",
|
||||||
|
"hooks": "@/hooks"
|
||||||
|
},
|
||||||
|
"iconLibrary": "lucide"
|
||||||
|
}
|
||||||
120
base44-site-template-main/index.css
Normal file
120
base44-site-template-main/index.css
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
@import "tailwindcss";
|
||||||
|
@import "tw-animate-css";
|
||||||
|
|
||||||
|
@custom-variant dark (&:is(.dark *));
|
||||||
|
|
||||||
|
@theme inline {
|
||||||
|
--radius-sm: calc(var(--radius) - 4px);
|
||||||
|
--radius-md: calc(var(--radius) - 2px);
|
||||||
|
--radius-lg: var(--radius);
|
||||||
|
--radius-xl: calc(var(--radius) + 4px);
|
||||||
|
--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-border: var(--border);
|
||||||
|
--color-input: var(--input);
|
||||||
|
--color-ring: var(--ring);
|
||||||
|
--color-chart-1: var(--chart-1);
|
||||||
|
--color-chart-2: var(--chart-2);
|
||||||
|
--color-chart-3: var(--chart-3);
|
||||||
|
--color-chart-4: var(--chart-4);
|
||||||
|
--color-chart-5: var(--chart-5);
|
||||||
|
--color-sidebar: var(--sidebar);
|
||||||
|
--color-sidebar-foreground: var(--sidebar-foreground);
|
||||||
|
--color-sidebar-primary: var(--sidebar-primary);
|
||||||
|
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
||||||
|
--color-sidebar-accent: var(--sidebar-accent);
|
||||||
|
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
||||||
|
--color-sidebar-border: var(--sidebar-border);
|
||||||
|
--color-sidebar-ring: var(--sidebar-ring);
|
||||||
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--radius: 0.625rem;
|
||||||
|
--background: oklch(1 0 0);
|
||||||
|
--foreground: oklch(0.145 0 0);
|
||||||
|
--card: oklch(1 0 0);
|
||||||
|
--card-foreground: oklch(0.145 0 0);
|
||||||
|
--popover: oklch(1 0 0);
|
||||||
|
--popover-foreground: oklch(0.145 0 0);
|
||||||
|
--primary: oklch(0.205 0 0);
|
||||||
|
--primary-foreground: oklch(0.985 0 0);
|
||||||
|
--secondary: oklch(0.97 0 0);
|
||||||
|
--secondary-foreground: oklch(0.205 0 0);
|
||||||
|
--muted: oklch(0.97 0 0);
|
||||||
|
--muted-foreground: oklch(0.556 0 0);
|
||||||
|
--accent: oklch(0.97 0 0);
|
||||||
|
--accent-foreground: oklch(0.205 0 0);
|
||||||
|
--destructive: oklch(0.577 0.245 27.325);
|
||||||
|
--border: oklch(0.922 0 0);
|
||||||
|
--input: oklch(0.922 0 0);
|
||||||
|
--ring: oklch(0.708 0 0);
|
||||||
|
--chart-1: oklch(0.646 0.222 41.116);
|
||||||
|
--chart-2: oklch(0.6 0.118 184.704);
|
||||||
|
--chart-3: oklch(0.398 0.07 227.392);
|
||||||
|
--chart-4: oklch(0.828 0.189 84.429);
|
||||||
|
--chart-5: oklch(0.769 0.188 70.08);
|
||||||
|
--sidebar: oklch(0.985 0 0);
|
||||||
|
--sidebar-foreground: oklch(0.145 0 0);
|
||||||
|
--sidebar-primary: oklch(0.205 0 0);
|
||||||
|
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||||
|
--sidebar-accent: oklch(0.97 0 0);
|
||||||
|
--sidebar-accent-foreground: oklch(0.205 0 0);
|
||||||
|
--sidebar-border: oklch(0.922 0 0);
|
||||||
|
--sidebar-ring: oklch(0.708 0 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark {
|
||||||
|
--background: oklch(0.145 0 0);
|
||||||
|
--foreground: oklch(0.985 0 0);
|
||||||
|
--card: oklch(0.205 0 0);
|
||||||
|
--card-foreground: oklch(0.985 0 0);
|
||||||
|
--popover: oklch(0.205 0 0);
|
||||||
|
--popover-foreground: oklch(0.985 0 0);
|
||||||
|
--primary: oklch(0.922 0 0);
|
||||||
|
--primary-foreground: oklch(0.205 0 0);
|
||||||
|
--secondary: oklch(0.269 0 0);
|
||||||
|
--secondary-foreground: oklch(0.985 0 0);
|
||||||
|
--muted: oklch(0.269 0 0);
|
||||||
|
--muted-foreground: oklch(0.708 0 0);
|
||||||
|
--accent: oklch(0.269 0 0);
|
||||||
|
--accent-foreground: oklch(0.985 0 0);
|
||||||
|
--destructive: oklch(0.704 0.191 22.216);
|
||||||
|
--border: oklch(1 0 0 / 10%);
|
||||||
|
--input: oklch(1 0 0 / 15%);
|
||||||
|
--ring: oklch(0.556 0 0);
|
||||||
|
--chart-1: oklch(0.488 0.243 264.376);
|
||||||
|
--chart-2: oklch(0.696 0.17 162.48);
|
||||||
|
--chart-3: oklch(0.769 0.188 70.08);
|
||||||
|
--chart-4: oklch(0.627 0.265 303.9);
|
||||||
|
--chart-5: oklch(0.645 0.246 16.439);
|
||||||
|
--sidebar: oklch(0.205 0 0);
|
||||||
|
--sidebar-foreground: oklch(0.985 0 0);
|
||||||
|
--sidebar-primary: oklch(0.488 0.243 264.376);
|
||||||
|
--sidebar-primary-foreground: oklch(0.985 0 0);
|
||||||
|
--sidebar-accent: oklch(0.269 0 0);
|
||||||
|
--sidebar-accent-foreground: oklch(0.985 0 0);
|
||||||
|
--sidebar-border: oklch(1 0 0 / 10%);
|
||||||
|
--sidebar-ring: oklch(0.556 0 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
* {
|
||||||
|
@apply border-border outline-ring/50;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
@apply bg-background text-foreground;
|
||||||
|
}
|
||||||
|
}
|
||||||
11
base44-site-template-main/index.html
Normal file
11
base44-site-template-main/index.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="./index.css" />
|
||||||
|
<title>React Vite Micro App</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="./src/index.jsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
4384
base44-site-template-main/package-lock.json
generated
Normal file
4384
base44-site-template-main/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
33
base44-site-template-main/package.json
Normal file
33
base44-site-template-main/package.json
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"name": "static",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "",
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"start": "vite",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"build": "vite build"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/react-dialog": "^1.1.14",
|
||||||
|
"@radix-ui/react-slot": "^1.2.3",
|
||||||
|
"@tailwindcss/vite": "^4.1.7",
|
||||||
|
"@vitejs/plugin-react": "^4.5.0",
|
||||||
|
"axios": "^1.9.0",
|
||||||
|
"class-variance-authority": "^0.7.1",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"lucide-react": "^0.511.0",
|
||||||
|
"mermaid": "^11.6.0",
|
||||||
|
"monaco-editor": "^0.52.2",
|
||||||
|
"react": "^19.0.0",
|
||||||
|
"react-dom": "^19.0.0",
|
||||||
|
"svg-pan-zoom": "^3.6.2",
|
||||||
|
"tailwind-merge": "^3.3.0",
|
||||||
|
"tailwindcss": "^4.1.7"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^22.15.21",
|
||||||
|
"tw-animate-css": "^1.3.0",
|
||||||
|
"vite": "^6.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
8
base44-site-template-main/src/App.jsx
Normal file
8
base44-site-template-main/src/App.jsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import React from "react";
|
||||||
|
import CodeVisualizerPage from "./CodeVisualizer";
|
||||||
|
|
||||||
|
export default () => (
|
||||||
|
<>
|
||||||
|
<CodeVisualizerPage/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
127
base44-site-template-main/src/CodeEditor.jsx
Normal file
127
base44-site-template-main/src/CodeEditor.jsx
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
import * as monaco from 'monaco-editor';
|
||||||
|
|
||||||
|
export default function CodeEditor({ code, onChangeContent, onChangeCursorPosition: onChangeCursorOffset }) {
|
||||||
|
const containerRef = useRef(null);
|
||||||
|
const [editor, setEditor] = useState(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!containerRef.current) return;
|
||||||
|
|
||||||
|
// Define Kotlin language
|
||||||
|
if (!monaco.languages.getLanguages().some(({ id }) => id === 'kotlin')) {
|
||||||
|
monaco.languages.register({ id: 'kotlin' });
|
||||||
|
|
||||||
|
monaco.languages.setMonarchTokensProvider('kotlin', {
|
||||||
|
tokenizer: {
|
||||||
|
root: [
|
||||||
|
[/\b(package|import|class|interface|object|fun|val|var|when|if|else|for|while|return|constructor|companion|data|sealed|enum|override|open|private|protected|public|internal|final|abstract|suspend|lateinit|inline|get|set|this|super)\b/, 'keyword'],
|
||||||
|
[/\b(Int|String|Boolean|Float|Double|Long|Short|Byte|Any|Unit|Nothing)\b/, 'type'],
|
||||||
|
[/\/\/.*$/, 'comment'],
|
||||||
|
[/\/\*/, 'comment', '@comment'],
|
||||||
|
[/"/, 'string', '@string_double'],
|
||||||
|
[/'[^']*'/, 'string'],
|
||||||
|
[/\d+/, 'number'],
|
||||||
|
[/@\w+/, 'annotation'],
|
||||||
|
],
|
||||||
|
comment: [
|
||||||
|
[/[^/*]+/, 'comment'],
|
||||||
|
[/\/\*/, 'comment', '@push'],
|
||||||
|
[/\*\//, 'comment', '@pop'],
|
||||||
|
[/[/*]/, 'comment']
|
||||||
|
],
|
||||||
|
string_double: [
|
||||||
|
[/[^\\"]+/, 'string'],
|
||||||
|
[/\\./, 'string.escape'],
|
||||||
|
[/"/, 'string', '@pop']
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Define a dark dracula-inspired theme
|
||||||
|
monaco.editor.defineTheme('kotlinDarkDracula', {
|
||||||
|
base: 'vs-dark',
|
||||||
|
inherit: true,
|
||||||
|
rules: [
|
||||||
|
{ token: 'comment', foreground: '6272a4' },
|
||||||
|
{ token: 'keyword', foreground: 'ff79c6', fontStyle: 'bold' },
|
||||||
|
{ token: 'string', foreground: 'f1fa8c' },
|
||||||
|
{ token: 'number', foreground: 'bd93f9' },
|
||||||
|
{ token: 'type', foreground: '8be9fd' },
|
||||||
|
{ token: 'annotation', foreground: '50fa7b' },
|
||||||
|
],
|
||||||
|
colors: {
|
||||||
|
'editor.background': '#1a1c25',
|
||||||
|
'editor.foreground': '#f8f8f2',
|
||||||
|
'editor.lineHighlightBackground': '#2d303e',
|
||||||
|
'editor.selectionBackground': '#44475a',
|
||||||
|
'editorCursor.foreground': '#f8f8f2',
|
||||||
|
'editorWhitespace.foreground': '#3B3A32',
|
||||||
|
'editorIndentGuide.activeBackground': '#9D550FB0',
|
||||||
|
'editor.selectionHighlightBorder': '#222218'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize Monaco editor
|
||||||
|
const editorInstance = monaco.editor.create(containerRef.current, {
|
||||||
|
value: code,
|
||||||
|
language: 'kotlin',
|
||||||
|
theme: 'kotlinDarkDracula',
|
||||||
|
automaticLayout: true,
|
||||||
|
minimap: { enabled: true },
|
||||||
|
scrollBeyondLastLine: false,
|
||||||
|
fontSize: 14,
|
||||||
|
fontFamily: "'JetBrains Mono', 'Fira Code', Menlo, Monaco, 'Courier New', monospace",
|
||||||
|
lineNumbers: 'on',
|
||||||
|
renderLineHighlight: 'all',
|
||||||
|
scrollbar: {
|
||||||
|
useShadows: true,
|
||||||
|
verticalScrollbarSize: 10,
|
||||||
|
horizontalScrollbarSize: 10
|
||||||
|
},
|
||||||
|
padding: { top: 16 },
|
||||||
|
bracketPairColorization: { enabled: true },
|
||||||
|
autoClosingBrackets: 'always'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add event listener for content changes
|
||||||
|
const disposable = editorInstance.onDidChangeModelContent(() => {
|
||||||
|
onChangeContent(editorInstance.getValue());
|
||||||
|
});
|
||||||
|
|
||||||
|
editorInstance.onDidChangeCursorPosition((e) => {
|
||||||
|
// The editor speaks in position (line/column), the IR element speaks in offset (absolute int)
|
||||||
|
// The callback is in "IrSpeak" so we need to convert it
|
||||||
|
const offset = editorInstance.getModel().getOffsetAt(e.position)
|
||||||
|
onChangeCursorOffset(offset)
|
||||||
|
})
|
||||||
|
|
||||||
|
setEditor(editorInstance);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
return () => {
|
||||||
|
disposable.dispose();
|
||||||
|
editorInstance.dispose();
|
||||||
|
};
|
||||||
|
}, [containerRef]);
|
||||||
|
|
||||||
|
// Update editor value when code prop changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (editor && code !== editor.getValue()) {
|
||||||
|
editor.setValue(code);
|
||||||
|
}
|
||||||
|
}, [code, editor]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={containerRef}
|
||||||
|
className="h-full w-full overflow-hidden flex-grow"
|
||||||
|
style={{
|
||||||
|
backgroundColor: '#1a1c25',
|
||||||
|
border: 'none',
|
||||||
|
outline: 'none'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
158
base44-site-template-main/src/CodeVisualizer.jsx
Normal file
158
base44-site-template-main/src/CodeVisualizer.jsx
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
import React, { useState, useEffect, useRef } from "react";
|
||||||
|
import CodeEditor from "./CodeEditor";
|
||||||
|
import GraphViewer from "./GraphViewer";
|
||||||
|
import { Loader2 } from "lucide-react";
|
||||||
|
import KotlinLogo from './KotlinLogo.svg';
|
||||||
|
import axios from "axios";
|
||||||
|
import {editor} from "monaco-editor";
|
||||||
|
|
||||||
|
|
||||||
|
export default function CodeVisualizerPage() {
|
||||||
|
// Initial example Kotlin code
|
||||||
|
const [code, setCode] = useState(`// Example Kotlin code
|
||||||
|
fun main() {
|
||||||
|
var x = 5
|
||||||
|
if (x > 1 + 2) {
|
||||||
|
x += 1
|
||||||
|
listOf(1, 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
const [mermaidGraphText, setMermaidGraphText] = useState("");
|
||||||
|
|
||||||
|
const [cursorPositionOffset, setCursorPositionOffset] = useState(0)
|
||||||
|
const [mermaidGraphTextColored, setMermaidGraphTextColored] = useState("");
|
||||||
|
|
||||||
|
const [compilerMessages, setCompilerMessages] = useState([]);
|
||||||
|
const [isProcessing, setIsProcessing] = useState(false);
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
const timerRef = useRef(null);
|
||||||
|
|
||||||
|
const processCode = async (codeToProcess) => {
|
||||||
|
setIsProcessing(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// In a real implementation, you'd send `codeToProcess` to your backend
|
||||||
|
// which would return the Mermaid graph *text representation*.
|
||||||
|
// TODO: How can I auto-recognize when this is run via npm run start, so I can know to use the localhost? :think:
|
||||||
|
const response = await axios.post("/api/kotlinToMermaid?withOffsetComment=true", codeToProcess)
|
||||||
|
// const response = await axios.post("http://localhost:8080/api/kotlinToMermaid?withOffsetComment=true", codeToProcess)
|
||||||
|
const graphText = response.data.mermaidGraph
|
||||||
|
const messages = response.data.messages
|
||||||
|
setMermaidGraphText(graphText)
|
||||||
|
setCompilerMessages(messages)
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error processing code:", err)
|
||||||
|
setError("Failed to generate graph. Please ensure your backend is running or check the placeholder logic.")
|
||||||
|
} finally {
|
||||||
|
setIsProcessing(false)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Debounce of 250ms, to render the graph after the user stops typing
|
||||||
|
useEffect(() => {
|
||||||
|
if (code.trim() === '') {
|
||||||
|
setMermaidGraphText('classDiagram\n Empty["Type some Kotlin code to see the diagram"]\n classDef default fill:#2a2a3f,stroke:#6272a4,stroke-width:1px,color:#f8f8f2');
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timerRef.current) {
|
||||||
|
clearTimeout(timerRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
timerRef.current = setTimeout(() => {
|
||||||
|
processCode(code);
|
||||||
|
}, 250);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (timerRef.current) {
|
||||||
|
clearTimeout(timerRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [code]);
|
||||||
|
|
||||||
|
const handleCodeChange = (newCode) => {
|
||||||
|
setCode(newCode);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCursorOffsetChange = (offset) => {
|
||||||
|
setCursorPositionOffset(offset)
|
||||||
|
};
|
||||||
|
|
||||||
|
// The actual displayed graph is the graph we got plus coloring on all elements which contain the cursor position
|
||||||
|
useEffect(() => {
|
||||||
|
if (!mermaidGraphText){ // null or empty
|
||||||
|
setMermaidGraphTextColored(null)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const newMermaidGraphTextLines = mermaidGraphText.split("\n")
|
||||||
|
.map(line => {
|
||||||
|
// Offset comment looks like: " # Offset: ${element.startOffset}-${element.endOffset}"
|
||||||
|
const offsetSplit = line.split(" %% Offset: ")
|
||||||
|
if (offsetSplit.length < 2) return line // No comment
|
||||||
|
|
||||||
|
const positions = offsetSplit[1].split("-").map(num => parseInt(num))
|
||||||
|
if (cursorPositionOffset > positions[1] || cursorPositionOffset < positions[0]) return offsetSplit[0]
|
||||||
|
|
||||||
|
return offsetSplit[0] + ":::selected" // Coloring classdef
|
||||||
|
})
|
||||||
|
|
||||||
|
const newMermaidGraphText = newMermaidGraphTextLines.join("\n") + "\n\n classDef selected stroke:#f00"
|
||||||
|
|
||||||
|
setMermaidGraphTextColored(newMermaidGraphText)
|
||||||
|
},
|
||||||
|
[cursorPositionOffset, mermaidGraphText]
|
||||||
|
)
|
||||||
|
|
||||||
|
const onWarningLocationClick = (location) => {
|
||||||
|
if (!location) return;
|
||||||
|
|
||||||
|
// Use Monaco editor's API to reveal the line
|
||||||
|
const lineNumber = location.line
|
||||||
|
const columnNumber = location.column
|
||||||
|
|
||||||
|
const editorInstance = editor.getEditors()[0]; // Assuming there's only one editor instance
|
||||||
|
if (editorInstance) {
|
||||||
|
editorInstance.revealLineInCenter(lineNumber);
|
||||||
|
editorInstance.setPosition({ lineNumber, column: columnNumber });
|
||||||
|
editorInstance.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col h-screen bg-[#0f1117] text-slate-100">
|
||||||
|
<header className="sticky top-0 z-10 bg-[#1a1c25] border-b border-slate-800 px-4 py-3 flex items-center justify-between shadow-md">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<div className="h-6 w-6 mr-2">
|
||||||
|
<img src={KotlinLogo}/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h1 className="text-xl font-semibold text-slate-100 mr-4">Kotlin IR Explorer</h1>
|
||||||
|
{isProcessing && (
|
||||||
|
<div className="flex items-center text-sky-400">
|
||||||
|
<Loader2 className="h-4 w-4 animate-spin mr-2" />
|
||||||
|
<span className="text-sm">Processing...</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div className="flex flex-1 overflow-hidden">
|
||||||
|
<div className="w-1/2 border-r border-slate-800 bg-[#1a1c25]">
|
||||||
|
<CodeEditor code={code} onChangeContent={handleCodeChange} onChangeCursorPosition={handleCursorOffsetChange} />
|
||||||
|
</div>
|
||||||
|
<div className="w-1/2 bg-[#1a1c25] p-4 overflow-auto">
|
||||||
|
<GraphViewer
|
||||||
|
mermaidGraphText={mermaidGraphTextColored}
|
||||||
|
compilerMessages={compilerMessages}
|
||||||
|
isProcessing={isProcessing}
|
||||||
|
error={error}
|
||||||
|
onWarningLocationClick={onWarningLocationClick}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
140
base44-site-template-main/src/GraphViewer.jsx
Normal file
140
base44-site-template-main/src/GraphViewer.jsx
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
import React, {useEffect, useLayoutEffect, useRef, useState} from 'react';
|
||||||
|
import { Loader2, AlertTriangle, ImageOff } from 'lucide-react';
|
||||||
|
import mermaid from 'mermaid';
|
||||||
|
import svgPanZoom from 'svg-pan-zoom';
|
||||||
|
|
||||||
|
mermaid.initialize(
|
||||||
|
{
|
||||||
|
startOnLoad: false, // We will render manually
|
||||||
|
theme: 'dark', // Apply dark theme globally
|
||||||
|
// Dark theme configuration similar to Dracula
|
||||||
|
themeVariables: {
|
||||||
|
background: '#0f1117',
|
||||||
|
primaryColor: '#1a1c25', // Node background
|
||||||
|
primaryTextColor: '#f8f8f2',
|
||||||
|
primaryBorderColor: '#6272a4',
|
||||||
|
// lineColor: '#44475a',
|
||||||
|
secondaryColor: '#2d303e',
|
||||||
|
tertiaryColor: '#2d303e',
|
||||||
|
fontSize: '14px',
|
||||||
|
fontFamily: 'JetBrains Mono, Fira Code, Menlo, Monaco, Courier New, monospace',
|
||||||
|
|
||||||
|
// Specific for class diagrams
|
||||||
|
classText: '#f8f8f2',
|
||||||
|
},
|
||||||
|
securityLevel: 'loose', // Allow clicks etc
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export default function GraphViewer({ mermaidGraphText, compilerMessages, isProcessing,
|
||||||
|
error, onWarningLocationClick }) {
|
||||||
|
const svgContainerRef = useRef(null);
|
||||||
|
const [renderError, setRenderError] = useState(null);
|
||||||
|
|
||||||
|
const [svg, setSvg] = useState(null);
|
||||||
|
|
||||||
|
const graphId = `mermaid-graph-${Date.now()}`;
|
||||||
|
|
||||||
|
// Render graph when text or library readiness changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (!mermaidGraphText || isProcessing) return
|
||||||
|
|
||||||
|
setRenderError(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// mermaid.render() returns a promise with the SVG
|
||||||
|
mermaid.render(graphId, mermaidGraphText)
|
||||||
|
.then(({ svg, bindFunctions }) => {
|
||||||
|
if (svgContainerRef.current) {
|
||||||
|
svgContainerRef.current.innerHTML = svg;
|
||||||
|
if (bindFunctions) {
|
||||||
|
bindFunctions(svgContainerRef.current); // For interactivity if any
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setSvg(svg);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error("Mermaid rendering error:", err);
|
||||||
|
setRenderError(`Diagram error: ${err.message || 'Could not render graph.'}`);
|
||||||
|
if (svgContainerRef.current) {
|
||||||
|
svgContainerRef.current.innerHTML = `<div class="text-red-400 p-4">Error rendering diagram. Check console.</div>`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Synchronous Mermaid error:", err);
|
||||||
|
setRenderError(`Diagram error: ${err.message || 'Critical error.'}`);
|
||||||
|
if (svgContainerRef.current) {
|
||||||
|
svgContainerRef.current.innerHTML = `<div class="text-red-400 p-4">Critical diagram rendering error.</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [mermaidGraphText, isProcessing]);
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
if (!svgContainerRef.current || !svg) return;
|
||||||
|
const svgElement = svgContainerRef.current.children[0];
|
||||||
|
// make svg size to its current size - required so it's not a tiny thing
|
||||||
|
svgElement.style.width = '100%';
|
||||||
|
svgElement.style.height = '100%';
|
||||||
|
svgElement.style.visibility = 'hidden'; // Hide until pan-zoom is applied
|
||||||
|
svgPanZoom(svgElement, {
|
||||||
|
controlIconsEnabled: true
|
||||||
|
})
|
||||||
|
svgElement.style.visibility = 'visible'; // Show the SVG after pan-zoom is applied
|
||||||
|
})
|
||||||
|
|
||||||
|
let content;
|
||||||
|
|
||||||
|
if (isProcessing) {
|
||||||
|
content = (
|
||||||
|
<div className="h-full flex flex-col items-center justify-center text-slate-400">
|
||||||
|
<Loader2 className="h-12 w-12 animate-spin mb-4 text-sky-400" />
|
||||||
|
<p>{isProcessing ? "Processing Code..." : "Rendering Diagram..."}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (error || renderError) {
|
||||||
|
content = (
|
||||||
|
<div className="h-full flex flex-col items-center justify-center text-red-400">
|
||||||
|
<AlertTriangle className="h-12 w-12 mb-4" />
|
||||||
|
<p className="font-semibold">Error</p>
|
||||||
|
<p className="text-sm text-red-500 mt-1">{error || renderError}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else if (!mermaidGraphText || mermaidGraphText.trim() === '') {
|
||||||
|
// This condition checks for specific placeholder texts in the mermaid string
|
||||||
|
content = (
|
||||||
|
<div className="h-full w-full flex flex-col items-center justify-center text-slate-400">
|
||||||
|
<ImageOff className="h-16 w-16 mb-4 text-slate-500" />
|
||||||
|
{compilerMessages.map(message =>
|
||||||
|
<div className="mt-4 text-sm bg-[#2d303e] p-3 rounded-md text-left max-w w-full"
|
||||||
|
style={{cursor: 'pointer'}} // as
|
||||||
|
onClick={() => onWarningLocationClick(message.location)}>
|
||||||
|
{/*<p className="font-medium text-slate-300">Try a simple class:</p>*/}
|
||||||
|
<pre className="mt-2 overflow-x-auto text-xs bg-[#0f1117] p-2 rounded-md text-sky-300">
|
||||||
|
{
|
||||||
|
message.severity
|
||||||
|
+ (message.location ? ` (${message.location.line}:${message.location.column})` : '')
|
||||||
|
+": "+message.message
|
||||||
|
}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// The div will be populated by the useEffect hook
|
||||||
|
content = (
|
||||||
|
<div ref={svgContainerRef}
|
||||||
|
dangerouslySetInnerHTML={{__html: svg}}
|
||||||
|
className="w-full h-full flex justify-center items-center overflow-auto p-2 mermaid-live-container">
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="h-full w-full bg-[#0f1117] rounded-lg border border-slate-800 flex justify-center items-center overflow-hidden">
|
||||||
|
{content}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
19
base44-site-template-main/src/KotlinLogo.svg
Normal file
19
base44-site-template-main/src/KotlinLogo.svg
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Generator: Adobe Illustrator 24.1.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||||
|
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||||
|
viewBox="0 0 500 500" style="enable-background:new 0 0 500 500;" xml:space="preserve">
|
||||||
|
<style type="text/css">
|
||||||
|
.st0{fill:url(#SVGID_1_);}
|
||||||
|
</style>
|
||||||
|
<g id="Logotypes">
|
||||||
|
<g>
|
||||||
|
|
||||||
|
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="500.0035" y1="579.1058" x2="-9.653803e-02" y2="1079.2058" gradientTransform="matrix(0.9998 0 0 0.9998 9.651873e-02 -578.99)">
|
||||||
|
<stop offset="3.435144e-03" style="stop-color:#E44857"/>
|
||||||
|
<stop offset="0.4689" style="stop-color:#C711E1"/>
|
||||||
|
<stop offset="1" style="stop-color:#7F52FF"/>
|
||||||
|
</linearGradient>
|
||||||
|
<polygon class="st0" points="500,500 0,500 0,0 500,0 250,250 "/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 904 B |
59
base44-site-template-main/src/components/ui/button.tsx
Normal file
59
base44-site-template-main/src/components/ui/button.tsx
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import { Slot } from "@radix-ui/react-slot"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const buttonVariants = cva(
|
||||||
|
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default:
|
||||||
|
"bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
|
||||||
|
destructive:
|
||||||
|
"bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
|
||||||
|
outline:
|
||||||
|
"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
|
||||||
|
secondary:
|
||||||
|
"bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
|
||||||
|
ghost:
|
||||||
|
"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
|
||||||
|
link: "text-primary underline-offset-4 hover:underline",
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
default: "h-9 px-4 py-2 has-[>svg]:px-3",
|
||||||
|
sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
|
||||||
|
lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
|
||||||
|
icon: "size-9",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
size: "default",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
function Button({
|
||||||
|
className,
|
||||||
|
variant,
|
||||||
|
size,
|
||||||
|
asChild = false,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<"button"> &
|
||||||
|
VariantProps<typeof buttonVariants> & {
|
||||||
|
asChild?: boolean
|
||||||
|
}) {
|
||||||
|
const Comp = asChild ? Slot : "button"
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Comp
|
||||||
|
data-slot="button"
|
||||||
|
className={cn(buttonVariants({ variant, size, className }))}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Button, buttonVariants }
|
||||||
133
base44-site-template-main/src/components/ui/dialog.tsx
Normal file
133
base44-site-template-main/src/components/ui/dialog.tsx
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
||||||
|
import { XIcon } from "lucide-react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Dialog({
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Root>) {
|
||||||
|
return <DialogPrimitive.Root data-slot="dialog" {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogTrigger({
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {
|
||||||
|
return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogPortal({
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Portal>) {
|
||||||
|
return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogClose({
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Close>) {
|
||||||
|
return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogOverlay({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {
|
||||||
|
return (
|
||||||
|
<DialogPrimitive.Overlay
|
||||||
|
data-slot="dialog-overlay"
|
||||||
|
className={cn(
|
||||||
|
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogContent({
|
||||||
|
className,
|
||||||
|
children,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Content>) {
|
||||||
|
return (
|
||||||
|
<DialogPortal data-slot="dialog-portal">
|
||||||
|
<DialogOverlay />
|
||||||
|
<DialogPrimitive.Content
|
||||||
|
data-slot="dialog-content"
|
||||||
|
className={cn(
|
||||||
|
"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<DialogPrimitive.Close className="ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4">
|
||||||
|
<XIcon />
|
||||||
|
<span className="sr-only">Close</span>
|
||||||
|
</DialogPrimitive.Close>
|
||||||
|
</DialogPrimitive.Content>
|
||||||
|
</DialogPortal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="dialog-header"
|
||||||
|
className={cn("flex flex-col gap-2 text-center sm:text-left", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
data-slot="dialog-footer"
|
||||||
|
className={cn(
|
||||||
|
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogTitle({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Title>) {
|
||||||
|
return (
|
||||||
|
<DialogPrimitive.Title
|
||||||
|
data-slot="dialog-title"
|
||||||
|
className={cn("text-lg leading-none font-semibold", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function DialogDescription({
|
||||||
|
className,
|
||||||
|
...props
|
||||||
|
}: React.ComponentProps<typeof DialogPrimitive.Description>) {
|
||||||
|
return (
|
||||||
|
<DialogPrimitive.Description
|
||||||
|
data-slot="dialog-description"
|
||||||
|
className={cn("text-muted-foreground text-sm", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
Dialog,
|
||||||
|
DialogClose,
|
||||||
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
|
DialogOverlay,
|
||||||
|
DialogPortal,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
}
|
||||||
21
base44-site-template-main/src/components/ui/input.tsx
Normal file
21
base44-site-template-main/src/components/ui/input.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type={type}
|
||||||
|
data-slot="input"
|
||||||
|
className={cn(
|
||||||
|
"file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||||
|
"focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
|
||||||
|
"aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Input }
|
||||||
5
base44-site-template-main/src/index.jsx
Normal file
5
base44-site-template-main/src/index.jsx
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { createRoot } from 'react-dom/client';
|
||||||
|
import App from "./App";
|
||||||
|
|
||||||
|
createRoot(document.getElementById('root')).render(<App/>)
|
||||||
6
base44-site-template-main/src/lib/utils.ts
Normal file
6
base44-site-template-main/src/lib/utils.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { clsx, type ClassValue } from "clsx"
|
||||||
|
import { twMerge } from "tailwind-merge"
|
||||||
|
|
||||||
|
export function cn(...inputs: ClassValue[]) {
|
||||||
|
return twMerge(clsx(inputs))
|
||||||
|
}
|
||||||
10
base44-site-template-main/tsconfig.app.json
Normal file
10
base44-site-template-main/tsconfig.app.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"./src/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
base44-site-template-main/tsconfig.json
Normal file
14
base44-site-template-main/tsconfig.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.app.json"
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"compilerOptions": {
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
base44-site-template-main/vite.config.mjs
Normal file
14
base44-site-template-main/vite.config.mjs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import path from "path"
|
||||||
|
import tailwindcss from "@tailwindcss/vite"
|
||||||
|
import react from "@vitejs/plugin-react"
|
||||||
|
import { defineConfig } from "vite"
|
||||||
|
|
||||||
|
// https://vite.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react(), tailwindcss()],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": path.resolve(__dirname, "./src"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
Loading…
x
Reference in New Issue
Block a user