Merge remote-tracking branch 'origin/diagram'
This commit is contained in:
commit
e44bc95a4e
11 changed files with 183 additions and 46 deletions
15
web-app/components.json
Normal file
15
web-app/components.json
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"style": "default",
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.js",
|
||||
"css": "src/app/globals.css",
|
||||
"baseColor": "zinc",
|
||||
"cssVariables": true
|
||||
},
|
||||
"rsc": false,
|
||||
"tsx": false,
|
||||
"aliases": {
|
||||
"utils": "~/lib/utils",
|
||||
"components": "~/components"
|
||||
}
|
||||
}
|
||||
58
web-app/package-lock.json
generated
58
web-app/package-lock.json
generated
|
|
@ -13,6 +13,7 @@
|
|||
"@vitejs/plugin-react": "^5.0.4",
|
||||
"bootstrap": "^5.3.8",
|
||||
"bootstrap-icons": "^1.13.1",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^17.2.3",
|
||||
"express": "^5.1.0",
|
||||
|
|
@ -26,10 +27,12 @@
|
|||
"react-markdown": "^10.1.0",
|
||||
"react-router": "^7.9.4",
|
||||
"react-router-dom": "^7.9.4",
|
||||
"shadcn-ui": "^0.9.5",
|
||||
"vite-jsconfig-paths": "^2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.38.0",
|
||||
"daisyui": "^5.3.7",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
|
|
@ -2607,12 +2610,33 @@
|
|||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/class-variance-authority": {
|
||||
"version": "0.7.1",
|
||||
"resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
|
||||
"integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"clsx": "^2.1.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://polar.sh/cva"
|
||||
}
|
||||
},
|
||||
"node_modules/classnames": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
|
||||
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/clsx": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
|
||||
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
|
|
@ -2735,6 +2759,16 @@
|
|||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/daisyui": {
|
||||
"version": "5.3.7",
|
||||
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-5.3.7.tgz",
|
||||
"integrity": "sha512-0+8PaSGift0HlIQABCeZzWOBV5Nx/vsI2TihB9hbaEyZENPlZZz+se2JnAH5rz9gBYTyDLB7NJup8hkREr6WBw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://github.com/saadeghi/daisyui?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/data-view-buffer": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
|
||||
|
|
@ -7558,6 +7592,30 @@
|
|||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/shadcn-ui": {
|
||||
"version": "0.9.5",
|
||||
"resolved": "https://registry.npmjs.org/shadcn-ui/-/shadcn-ui-0.9.5.tgz",
|
||||
"integrity": "sha512-dsBQWpdLLYCdSdmvOmu53nJhhWnQD1OiblhuhkI4rPYxPKTyfbmZ2NTJHWMu1fXN9PTfN6IVK5vvh+BrjHJx2g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chalk": "^5.4.1"
|
||||
},
|
||||
"bin": {
|
||||
"shadcn-ui": "dist/index.js"
|
||||
}
|
||||
},
|
||||
"node_modules/shadcn-ui/node_modules/chalk": {
|
||||
"version": "5.6.2",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz",
|
||||
"integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@
|
|||
"@vitejs/plugin-react": "^5.0.4",
|
||||
"bootstrap": "^5.3.8",
|
||||
"bootstrap-icons": "^1.13.1",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^17.2.3",
|
||||
"express": "^5.1.0",
|
||||
|
|
@ -32,11 +33,13 @@
|
|||
"react-markdown": "^10.1.0",
|
||||
"react-router": "^7.9.4",
|
||||
"react-router-dom": "^7.9.4",
|
||||
"shadcn-ui": "^0.9.5",
|
||||
"vite-jsconfig-paths": "^2.0.1"
|
||||
},
|
||||
"packageManager": ">=npm@10.9.0",
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.38.0",
|
||||
"daisyui": "^5.3.7",
|
||||
"eslint": "^9.38.0",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ function App() {
|
|||
return (
|
||||
<div className="dark min-h-screen bg-gray-950 text-white flex justify-center pt-12">
|
||||
<ChatLayout />
|
||||
<div></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,20 +3,6 @@ import ChatHeader from "src/components/ui/chat/chat-header";
|
|||
import ChatWindow from "src/components/ui/chat/chat-window";
|
||||
import MessageInput from "src/components/ui/chat/message-input";
|
||||
|
||||
import { GoogleGenAI } from "@google/genai"
|
||||
|
||||
const ai = new GoogleGenAI({ apiKey: import.meta.env.GEMINI_API_KEY })
|
||||
|
||||
async function AIRepsponse(userInputArray) {
|
||||
const response = await ai.models.generateContent({
|
||||
model: "gemini-2.5-flash",
|
||||
contents: userInputArray
|
||||
})
|
||||
return response.text
|
||||
}
|
||||
|
||||
let userInput = []
|
||||
|
||||
export default function ChatLayout() {
|
||||
const [messages, setMessages] = useState([
|
||||
{
|
||||
|
|
@ -25,17 +11,15 @@ export default function ChatLayout() {
|
|||
},
|
||||
]);
|
||||
|
||||
async function handleSend(text) {
|
||||
userInput.push(text)
|
||||
const res = await AIRepsponse(userInput)
|
||||
|
||||
function handleSend(text) {
|
||||
const userMsg = { role: "user", content: text };
|
||||
setMessages((s) => [...s, userMsg]);
|
||||
|
||||
// fake assistant reply after short delay
|
||||
setTimeout(() => {
|
||||
setMessages((s) => [
|
||||
...s,
|
||||
{ role: "assistant", content: res },
|
||||
{ role: "assistant", content: `You said: ${text}` },
|
||||
]);
|
||||
}, 600);
|
||||
}
|
||||
|
|
@ -46,7 +30,7 @@ export default function ChatLayout() {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-4xl gap-4 p-4">
|
||||
<div className="flex flex-col flex-start w-full max-w-3xl gap-4 p-4">
|
||||
<ChatHeader />
|
||||
<ChatWindow messages={messages} />
|
||||
<MessageInput onSend={handleSend} onDeleteAll={handleDeleteAll} />
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ export default function FlameButton({ onClick }) {
|
|||
return (
|
||||
<motion.button
|
||||
onClick={onClick}
|
||||
className="bg-gray-700 p-2 rounded-2xl"
|
||||
className="bg-gray-700 cursor-pointer p-2 rounded-2xl border-2 border-gray-600"
|
||||
whileHover={{ scale: 1.1 }}
|
||||
whileTap={{ scale: 0.9 }}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ export default function DownButton({ onClick }) {
|
|||
return (
|
||||
<motion.button
|
||||
onClick={onClick}
|
||||
className="bg-gray-700 p-2 rounded-2xl"
|
||||
className="bg-gray-700 p-2 rounded-2xl file-input border-2 border-gray-600"
|
||||
whileHover={{ scale: 1.1 }}
|
||||
whileTap={{ scale: 0.9 }}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,16 +1,71 @@
|
|||
import React from "react";
|
||||
import React, { useState, useRef } from "react";
|
||||
import { X } from "lucide-react";
|
||||
import { motion } from "motion/react";
|
||||
import { FilePlus2 } from "lucide-react";
|
||||
|
||||
export default function SchematicButton({ onClick }) {
|
||||
export default function SchematicButton({ onFiles }) {
|
||||
const [filesList, setFilesList] = useState([]);
|
||||
const inputRef = useRef(null);
|
||||
|
||||
function handleFiles(e) {
|
||||
const files = Array.from(e.target.files || []);
|
||||
if (files.length === 0) return;
|
||||
|
||||
setFilesList((s) => [...s, ...files]);
|
||||
if (onFiles) onFiles(files);
|
||||
if (inputRef.current) inputRef.current.value = null;
|
||||
}
|
||||
|
||||
function removeFile(index) {
|
||||
setFilesList((s) => {
|
||||
const copy = [...s];
|
||||
copy.splice(index, 1);
|
||||
return copy;
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<motion.button
|
||||
onClick={onClick}
|
||||
className=" bg-gray-700 p-2 rounded-2xl"
|
||||
whileHover={{ scale: 1.1 }}
|
||||
whileTap={{ scale: 0.9 }}
|
||||
>
|
||||
<FilePlus2 />
|
||||
</motion.button>
|
||||
<div className="flex items-center gap-2">
|
||||
<label className="relative inline-block">
|
||||
<motion.input
|
||||
ref={inputRef}
|
||||
type="file"
|
||||
accept="image/*,application/pdf"
|
||||
multiple
|
||||
onChange={handleFiles}
|
||||
className="file-input hidden"
|
||||
whileHover={{ scale: 1.02 }}
|
||||
whileTap={{ scale: 0.98 }}
|
||||
/>
|
||||
<motion.div
|
||||
className="bg-gray-700 p-2 rounded-2xl cursor-pointer border-2 border-gray-600"
|
||||
whileHover={{ scale: 1.1 }}
|
||||
whileTap={{ scale: 0.9 }}
|
||||
onClick={() => inputRef.current && inputRef.current.click()}
|
||||
>
|
||||
<FilePlus2 />
|
||||
</motion.div>
|
||||
</label>
|
||||
|
||||
{filesList.length > 0 && (
|
||||
<div className="flex gap-2 items-center max-w-xs flex-wrap">
|
||||
{filesList.map((f, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex items-center gap-2 bg-gray-800 text-sm text-slate-200 px-2 py-1 rounded"
|
||||
>
|
||||
<span className="truncate max-w-[10rem]">{f.name}</span>
|
||||
<button
|
||||
onClick={() => removeFile(i)}
|
||||
className="bg-gray-900 text-white rounded-full w-6 h-6 flex items-center justify-center text-xs border-2 border-gray-700"
|
||||
aria-label="Remove file"
|
||||
>
|
||||
<X />
|
||||
</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
import React from "react";
|
||||
import DeleteButton from "src/components/ui/button/delete-button";
|
||||
import SchematicButton from "../button/schematic-button";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import { motion } from "motion/react";
|
||||
import { Rocket } from "lucide-react";
|
||||
|
|
@ -26,11 +29,15 @@ export default function ChatHeader({ title = "Title of Chat" }) {
|
|||
}
|
||||
return (
|
||||
<div className="w-full flex justify-center">
|
||||
<header className="text-slate-100 fixed top-4 max-w-3xl w-full px-4">
|
||||
<div className="flex justify-between items-center gap-4">
|
||||
<SchematicButton></SchematicButton>
|
||||
<header className="text-slate-100 fixed top-4 ">
|
||||
<div className="flex items-center gap-3">
|
||||
<h1 className="text-lg font-semibold shadow-md shadow-indigo-600 bg-gray-900 px-6 py-2 rounded-4xl border-2 border-gray-800">
|
||||
{title}
|
||||
</h1>
|
||||
<DeleteButton></DeleteButton>
|
||||
{isDebug && (
|
||||
<motion.button
|
||||
onClick={triggerDemoIngest}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
import React, { useState } from "react";
|
||||
import React, { useState, useRef, useEffect } from "react";
|
||||
import DeleteButton from "src/components/ui/button/delete-button";
|
||||
import DownButton from "src/components/ui/button/down-button";
|
||||
import SchematicButton from "src/components/ui/button/schematic-button";
|
||||
import { motion } from "motion/react";
|
||||
import { BotMessageSquare } from "lucide-react";
|
||||
|
||||
export default function MessageInput({ onSend }) {
|
||||
const [text, setText] = useState("");
|
||||
const textareaRef = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
// ensure correct initial height
|
||||
if (textareaRef.current) textareaRef.current.style.height = "auto";
|
||||
}, []);
|
||||
|
||||
function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
|
@ -17,29 +22,37 @@ export default function MessageInput({ onSend }) {
|
|||
|
||||
return (
|
||||
<div className="w-full flex justify-center">
|
||||
<footer className="fixed bottom-6 max-w-2xl w-full px-4">
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex justify-between">
|
||||
<div className="flex gap-2">
|
||||
<SchematicButton></SchematicButton>
|
||||
<DeleteButton></DeleteButton>
|
||||
<footer className="fixed bottom-6 max-w-3xl w-full px-4">
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex justify-between items-end">
|
||||
<div className="flex">
|
||||
<DownButton></DownButton>
|
||||
</div>
|
||||
<DownButton></DownButton>
|
||||
</div>
|
||||
<form
|
||||
onSubmit={handleSubmit}
|
||||
className="bg-gray-900 rounded-2xl border-2 border-gray-800 shadow-lg shadow-indigo-600"
|
||||
>
|
||||
<div className="flex p-2 shadow-xl">
|
||||
<input
|
||||
<div className="flex p-2 shadow-xl items-center">
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
value={text}
|
||||
onChange={(e) => setText(e.target.value)}
|
||||
onChange={(e) => {
|
||||
setText(e.target.value);
|
||||
// auto-resize
|
||||
const ta = textareaRef.current;
|
||||
if (ta) {
|
||||
ta.style.height = "auto";
|
||||
ta.style.height = `${ta.scrollHeight}px`;
|
||||
}
|
||||
}}
|
||||
placeholder="Type a message..."
|
||||
className="flex-1 mx-2 rounded-md shadow-2sx border-none focus:border-none focus:outline-none"
|
||||
rows={1}
|
||||
className="flex-1 mx-2 rounded-md shadow-2sx border-none focus:border-none focus:outline-none resize-none overflow-auto max-h-40"
|
||||
/>
|
||||
<motion.button
|
||||
type="submit"
|
||||
className="flex gap-2 px-4 py-2 bg-gray-700 rounded-xl ml-4"
|
||||
className="flex gap-2 px-4 py-2 bg-gray-700 rounded-xl ml-4 items-center"
|
||||
whileHover={{ scale: 1.1 }}
|
||||
whileTap={{ scale: 0.9 }}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
@import "tailwindcss";
|
||||
@plugin "daisyui";
|
||||
|
||||
.dark {
|
||||
--paragraph: 235, 236, 239;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue