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