Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
"react-dropdown": "^1.11.0",
"react-icons": "^5.5.0",
"react-markdown": "^9.0.1",
"react-resizable-panels": "^3.0.3",
"react-router-dom": "^6.24.1",
"react-shepherd": "^6.1.1",
"react-spring": "^9.7.4",
Expand Down
228 changes: 57 additions & 171 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
import { useEffect, useState } from "react";
import { App as AntdApp, Layout, Row, Col, Collapse, Spin } from "antd";
import { App as AntdApp, Layout, Spin } from "antd";
import { LoadingOutlined } from "@ant-design/icons";
import { Routes, Route, useSearchParams, useNavigate } from "react-router-dom";
import Navbar from "./components/Navbar";
import Footer from "./components/Footer";
import tour from "./components/Tour";
import AgreementData from "./editors/editorsContainer/AgreementData";
import LearnNow from "./pages/LearnNow";
import AgreementHtml from "./AgreementHtml";
import Errors from "./utils/helpers/Errors";
import TemplateMarkdown from "./editors/editorsContainer/TemplateMarkdown";
import TemplateModel from "./editors/editorsContainer/TemplateModel";
import useAppStore from "./store/store";
import SampleDropdown from "./components/SampleDropdown";
import UseShare from "./components/UseShare";
import LearnContent from "./components/Content";
import ResizableContainer from "./components/ResizableContainer";
import { AIChatPanel } from "./components/AIChatPanel";
import MainContainer from "./pages/MainContainer";
import PlaygroundSidebar from "./components/PlaygroundSidebar";
import "./styles/App.css";
import AIConfigPopup from "./components/AIConfigPopup";
import { loadConfigFromLocalStorage } from "./ai-assistant/chatRelay";

Expand All @@ -26,16 +19,13 @@ const App = () => {
const navigate = useNavigate();
const init = useAppStore((state) => state.init);
const loadFromLink = useAppStore((state) => state.loadFromLink);
const { isAIChatOpen, setAIChatOpen, isAIConfigOpen, setAIConfigOpen } = useAppStore((state) => ({
isAIChatOpen: state.isAIChatOpen,
setAIChatOpen: state.setAIChatOpen,
const { isAIConfigOpen, setAIConfigOpen } =
useAppStore((state) => ({
isAIConfigOpen: state.isAIConfigOpen,
setAIConfigOpen: state.setAIConfigOpen
}
));
setAIConfigOpen: state.setAIConfigOpen,
}));
const backgroundColor = useAppStore((state) => state.backgroundColor);
const textColor = useAppStore((state) => state.textColor);
const [activePanel, setActivePanel] = useState<string | string[]>();
const [loading, setLoading] = useState(true);
const [searchParams] = useSearchParams();

Expand All @@ -51,10 +41,6 @@ const App = () => {
setAIConfigOpen(false);
};

const onChange = (key: string | string[]) => {
setActivePanel(key);
};

useEffect(() => {
const initializeApp = async () => {
try {
Expand All @@ -74,7 +60,7 @@ const App = () => {
setLoading(false);
}
};
initializeApp();
void initializeApp();
}, [init, loadFromLink, searchParams, navigate]);

useEffect(() => {
Expand Down Expand Up @@ -109,167 +95,67 @@ const App = () => {

const showTour = searchParams.get("showTour") === "true";
if (showTour || !localStorage.getItem("hasVisited")) {
startTour();
void startTour();
}
}, [searchParams]);

const panels = [
{
key: "templateMark",
label: "TemplateMark",
children: <TemplateMarkdown />,
},
{
key: "model",
label: "Concerto Model",
children: <TemplateModel />,
},
{
key: "data",
label: "Preview Data",
children: <AgreementData />,
},
];


return (
<AntdApp>
<Layout style={{ minHeight: "100vh" }}>
<Layout style={{ height: "100vh" }}>
<Navbar scrollToFooter={scrollToFooter} />
<Content>
{loading ? (
<div
style={{
flex: 1,
display: "flex",
justifyContent: "center",
background: backgroundColor,
alignItems: "center",
minHeight: "calc(100vh - 64px - 70px)", // Adjust for Navbar and Footer height
}}
>
<Spinner />
</div>
) : (
<Routes>
<Route
path="/"
element={
<div
style={{
padding: 24,
paddingBottom: 24,
minHeight: 360,
background: backgroundColor,
}}
>
<Row>
<Col xs={24} sm={8}>
<Row
style={{
marginLeft: "25px",
display: "flex",
flexDirection: "row",
gap: "10px",
}}
>
<SampleDropdown setLoading={setLoading} />
<UseShare />
<button id="ai-assistant" onClick={() => setAIChatOpen(!isAIChatOpen)} className="flex cursor-pointer items-center justify-center px-4 py-1.5 h-8 text-sm bg-white border border-solid border-gray-300 rounded-md transition-all duration-400 ease-in-out hover:border-[#4096ff] hover:text-[#4096ff]">
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 45 20"
fill="none"
className="size-8"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={1.5}
stroke="currentColor"
d="M12 13.25c4.97 0 9-3.694 9-8.25s-4.03-8.25-9-8.25S3 0.444 3 5c0 2.104.859 4.023 2.273 5.48.432.447.74 1.04.586 1.641a4.483 4.483 0 01-.923 1.785A5.969 5.969 0 006 14c1.282 0 2.47-.402 3.445-1.087.81.22 1.668.337 2.555.337z"
transform="translate(0, 3)"
/>
<defs>
<linearGradient id="ai-gradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stopColor="#a78bfa" />
<stop offset="50%" stopColor="#ec4899" />
<stop offset="100%" stopColor="#ef4444" />
</linearGradient>
</defs>
<text
x="32"
y="6"
fontSize="16"
fontWeight="bold"
fill="url(#ai-gradient)"
textAnchor="middle"
>
AI
</text>
</svg>
<span>Assistant</span>
</button>
</Row>
</Col>
<Col span={18}>
<Errors />
</Col>
</Row>
<div
style={{
padding: 24,
minHeight: 360,
background: backgroundColor,
}}
>
<ResizableContainer
leftPane={
<Collapse
defaultActiveKey={activePanel}
onChange={onChange}
items={panels}
style={{ marginBottom: "24px" }}
/>
}
rightPane={<AgreementHtml loading={loading} isModal={false} />}
aiChatPane={isAIChatOpen ? <AIChatPanel /> : null}
initialLeftWidth={66}
minLeftWidth={30}
minRightWidth={30}
/>
</div>
</div>
}
/>
<Route path="/learn" element={<LearnNow />}>
<Route path="intro" element={<LearnContent file="intro.md" />} />
<Route path="module1" element={<LearnContent file="module1.md" />} />
<Route path="module2" element={<LearnContent file="module2.md" />} />
<Route path="module3" element={<LearnContent file="module3.md" />} />
</Route>
</Routes>
)}
</Content>
<AIConfigPopup
isOpen={isAIConfigOpen}
onClose={() => setAIConfigOpen(false)}
onSave={handleConfigSave}
/>
<Footer />
<Layout className="app-layout">
<Routes>
<Route
path="/"
element={
<>
<PlaygroundSidebar />
<Content>
{loading ? (
<div
className="app-content-loading"
style={{
background: backgroundColor,
}}
>
<Spinner />
</div>
) : (
<div
className="app-main-content"
style={{
background: backgroundColor,
}}
>
<MainContainer />
</div>
)}
</Content>
<AIConfigPopup
isOpen={isAIConfigOpen}
onClose={() => setAIConfigOpen(false)}
onSave={handleConfigSave}
/>
</>
}
/>
<Route path="/learn" element={<LearnNow />}>
<Route path="intro" element={<LearnContent file="intro.md" />} />
<Route path="module1" element={<LearnContent file="module1.md" />} />
<Route path="module2" element={<LearnContent file="module2.md" />} />
<Route path="module3" element={<LearnContent file="module3.md" />} />
</Route>
</Routes>
</Layout>
</Layout>
</AntdApp>
);
};

const Spinner = () => (
<div
style={{
flex: 1,
display: "flex",
justifyContent: "center",
alignItems: "center",
}}
>
<div className="app-spinner-container">
<Spin
indicator={<LoadingOutlined style={{ fontSize: 42, color: "#19c6c7" }} spin />}
/>
Expand Down
16 changes: 8 additions & 8 deletions src/components/AIChatPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const AIChatPanel = () => {
editorAgreementData: state.editorAgreementData,
}));

const { chatState, resetChat, aiConfig, setAIConfig, setAIConfigOpen, setAIChatOpen } = useAppStore.getState()
const { chatState, resetChat, aiConfig, setAIConfig, setAIConfigOpen, setAIChatOpen, textColor } = useAppStore.getState()

const latestMessageRef = useRef<HTMLDivElement>(null);

Expand All @@ -37,7 +37,7 @@ export const AIChatPanel = () => {
return;
}

let prompt = userInput;
const prompt = userInput;

setUserInput("");
setIsDropdownOpen(false);
Expand Down Expand Up @@ -101,7 +101,7 @@ export const AIChatPanel = () => {
return;
}
e.preventDefault();
handleSendMessage();
void handleSendMessage();
}
};

Expand Down Expand Up @@ -180,9 +180,9 @@ export const AIChatPanel = () => {
}, [chatState.messages, chatState.isLoading]);

return (
<div className="twp pl-4 pr-4 pt-3 flex flex-col border rounded-md h-[calc(100vh-150px)]">
<div className="twp pl-4 pr-4 pt-3 flex flex-col border rounded-md h-[calc(100vh-150px)] h-full">
<div className="flex justify-between items-center h-4">
<h2 className="text-lg font-bold">AI Assistant</h2>
<h2 className="text-lg font-bold" style={{ color: textColor }}>AI Assistant</h2>
<div className="flex items-center gap-2">
<button
onClick={() => setAIConfigOpen(true)}
Expand Down Expand Up @@ -277,7 +277,7 @@ export const AIChatPanel = () => {
}
>
<div className={`${message.role === 'assistant' ? 'bg-blue-100' : 'bg-gray-100'} p-3 rounded-lg`}>
<p className="text-xs font-semibold text-gray-600 mb-1">
<p className="text-xs font-semibold mb-1" style={{ color: textColor }}>
{message.role === 'assistant' ? 'Assistant' : 'You'}
</p>
{message.content && renderMessageContent(
Expand Down Expand Up @@ -332,7 +332,7 @@ export const AIChatPanel = () => {

{/* Context selection row */}
<div className="flex items-center justify-start px-2 gap-2">
<span className="text-xs text-gray-600 mr-1">Context:</span>
<span className="text-xs text-gray-600 mr-1" style={{color: textColor}}>Context:</span>
{/* TemplateMark Button */}
<div
onClick={() => handleTemplateMarkToggle(!includeTemplateMarkContent)}
Expand Down Expand Up @@ -524,7 +524,7 @@ export const AIChatPanel = () => {
</button>
) : (
<button
onClick={handleSendMessage}
onClick={() => void handleSendMessage()}
disabled={!userInput.trim()}
className="bg-blue-500 text-white px-4 py-2 rounded-lg hover:bg-blue-600 flex-shrink-0 disabled:bg-gray-400 disabled:cursor-not-allowed"
>
Expand Down
Loading