Refactor folder to support different vendors (#743)
Signed-off-by: Xinyao Wang <xinyao.wang@intel.com> Signed-off-by: chensuyue <suyue.chen@intel.com>
This commit is contained in:
42
CodeGen/ui/react/src/App.scss
Normal file
42
CodeGen/ui/react/src/App.scss
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
@import "./styles/styles";
|
||||
|
||||
.root {
|
||||
@include flex(row, nowrap, flex-start, flex-start);
|
||||
}
|
||||
|
||||
.layout-wrapper {
|
||||
@include absolutes;
|
||||
|
||||
display: grid;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
grid-template-columns: 80px auto;
|
||||
grid-template-rows: 1fr;
|
||||
}
|
||||
|
||||
/* ===== Scrollbar CSS ===== */
|
||||
/* Firefox */
|
||||
* {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #d6d6d6 #ffffff;
|
||||
}
|
||||
|
||||
/* Chrome, Edge, and Safari */
|
||||
*::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background-color: #d6d6d6;
|
||||
border-radius: 16px;
|
||||
border: 4px double #dedede;
|
||||
}
|
||||
32
CodeGen/ui/react/src/App.tsx
Normal file
32
CodeGen/ui/react/src/App.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import "./App.scss"
|
||||
import { MantineProvider } from "@mantine/core"
|
||||
import '@mantine/notifications/styles.css';
|
||||
import { SideNavbar, SidebarNavList } from "./components/sidebar/sidebar"
|
||||
import { IconMessages } from "@tabler/icons-react"
|
||||
import { Notifications } from '@mantine/notifications';
|
||||
import CodeGen from "./components/CodeGen/CodeGen";
|
||||
|
||||
const title = "Code Gen"
|
||||
const navList: SidebarNavList = [
|
||||
{ icon: IconMessages, label: title }
|
||||
]
|
||||
|
||||
function App() {
|
||||
|
||||
return (
|
||||
<MantineProvider>
|
||||
<Notifications position="top-right" />
|
||||
<div className="layout-wrapper">
|
||||
<SideNavbar navList={navList} />
|
||||
<div className="content">
|
||||
<CodeGen />
|
||||
</div>
|
||||
</div>
|
||||
</MantineProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
14
CodeGen/ui/react/src/__tests__/util.test.ts
Normal file
14
CodeGen/ui/react/src/__tests__/util.test.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { describe, expect, test } from "vitest";
|
||||
import { getCurrentTimeStamp, uuidv4 } from "../common/util";
|
||||
|
||||
describe("unit tests", () => {
|
||||
test("check UUID is of length 36", () => {
|
||||
expect(uuidv4()).toHaveLength(36);
|
||||
});
|
||||
test("check TimeStamp generated is of unix", () => {
|
||||
expect(getCurrentTimeStamp()).toBe(Math.floor(Date.now() / 1000));
|
||||
});
|
||||
});
|
||||
39
CodeGen/ui/react/src/assets/opea-icon-black.svg
Normal file
39
CodeGen/ui/react/src/assets/opea-icon-black.svg
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 28.4.1, 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"
|
||||
width="800px" height="800px" viewBox="0 0 800 800" style="enable-background:new 0 0 800 800;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.Drop_x0020_Shadow{fill:none;}
|
||||
.Outer_x0020_Glow_x0020_5_x0020_pt{fill:none;}
|
||||
.Blue_x0020_Neon{fill:none;stroke:#8AACDA;stroke-width:7;stroke-linecap:round;stroke-linejoin:round;}
|
||||
.Chrome_x0020_Highlight{fill:url(#SVGID_1_);stroke:#FFFFFF;stroke-width:0.3629;stroke-miterlimit:1;}
|
||||
.Jive_GS{fill:#FFDD00;}
|
||||
.Alyssa_GS{fill:#A6D0E4;}
|
||||
.st0{fill:#FF6900;}
|
||||
.st1{fill:#FFB500;}
|
||||
.st2{fill:#FFFFFF;}
|
||||
</style>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-1640" y1="-1640" x2="-1640" y2="-1641">
|
||||
<stop offset="0" style="stop-color:#656565"/>
|
||||
<stop offset="0.618" style="stop-color:#1B1B1B"/>
|
||||
<stop offset="0.6292" style="stop-color:#545454"/>
|
||||
<stop offset="0.9831" style="stop-color:#3E3E3E"/>
|
||||
</linearGradient>
|
||||
<g>
|
||||
<polygon points="400,0 737.5,181.7 607.3,252.8 269.7,71.1 "/>
|
||||
<path d="M708.3,414.7l29.2,15.7l-130.3,71.1l-44.9-24.2v-31.7l40,21.5c1.5,0.8,3.2,1.2,4.9,1.2c1.7,0,3.4-0.4,5-1.3l0,0
|
||||
L708.3,414.7z"/>
|
||||
<path d="M557.3,532.1c-0.1-0.1-0.3-0.1-0.4-0.2l0,0l-34.2-18.4l31.2-17l42.9,23.1v169.9l-34.5-18.6V541
|
||||
C562.4,537.3,560.4,533.9,557.3,532.1z"/>
|
||||
<polygon points="410.4,381.3 541.6,309.7 541.6,479.5 410.4,551.2 "/>
|
||||
<path d="M258.4,88.5l338.6,182.3v169.9l-34.5-18.6V292.2c0-3.7-1.9-7-5.1-8.9c-0.1-0.1-0.3-0.1-0.4-0.2l0,0L258.4,122.3V88.5z"/>
|
||||
<polygon points="192.7,110.6 530.3,292.3 400,363.4 62.5,181.6 "/>
|
||||
<polygon points="51.1,369 51.1,199 389.6,381.3 389.6,551.3 96.6,393.5 "/>
|
||||
<path d="M91.7,414.4l303.4,163.3c1.5,0.8,3.2,1.2,4.9,1.2c1.7,0,3.4-0.4,5-1.3l0,0l96.1-52.4l29.2,15.7L400,612.1L62.5,430.4
|
||||
L91.7,414.4z"/>
|
||||
<polygon points="51.1,447.8 389.6,630.1 389.6,800 51.1,617.7 "/>
|
||||
<polygon points="541.6,728.3 410.4,799.9 410.4,630 541.6,558.4 "/>
|
||||
<polygon points="748.9,617.7 617.6,689.3 617.6,519.5 748.9,447.9 "/>
|
||||
<polygon points="748.9,369 617.6,440.6 617.6,270.7 748.9,199.1 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
40
CodeGen/ui/react/src/assets/opea-icon-color.svg
Normal file
40
CodeGen/ui/react/src/assets/opea-icon-color.svg
Normal file
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 28.4.1, 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"
|
||||
width="800px" height="800px" viewBox="0 0 800 800" style="enable-background:new 0 0 800 800;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.Drop_x0020_Shadow{fill:none;}
|
||||
.Outer_x0020_Glow_x0020_5_x0020_pt{fill:none;}
|
||||
.Blue_x0020_Neon{fill:none;stroke:#8AACDA;stroke-width:7;stroke-linecap:round;stroke-linejoin:round;}
|
||||
.Chrome_x0020_Highlight{fill:url(#SVGID_1_);stroke:#FFFFFF;stroke-width:0.3629;stroke-miterlimit:1;}
|
||||
.Jive_GS{fill:#FFDD00;}
|
||||
.Alyssa_GS{fill:#A6D0E4;}
|
||||
.st0{fill:#FF6900;}
|
||||
.st1{fill:#FFB500;}
|
||||
.st2{fill:#FFFFFF;}
|
||||
</style>
|
||||
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="-820" y1="-1640" x2="-820" y2="-1641">
|
||||
<stop offset="0" style="stop-color:#656565"/>
|
||||
<stop offset="0.618" style="stop-color:#1B1B1B"/>
|
||||
<stop offset="0.6292" style="stop-color:#545454"/>
|
||||
<stop offset="0.9831" style="stop-color:#3E3E3E"/>
|
||||
</linearGradient>
|
||||
<g>
|
||||
<polygon class="st0" points="400,0 737.5,181.7 607.3,252.8 269.7,71.1 "/>
|
||||
<path class="st1" d="M708.3,414.7l29.2,15.7l-130.3,71.1l-44.9-24.2v-31.7l40,21.5c1.5,0.8,3.2,1.2,4.9,1.2c1.7,0,3.4-0.4,5-1.3
|
||||
l0,0L708.3,414.7z"/>
|
||||
<path class="st1" d="M557.3,532.1c-0.1-0.1-0.3-0.1-0.4-0.2l0,0l-34.2-18.4l31.2-17l42.9,23.1v169.9l-34.5-18.6V541
|
||||
C562.4,537.3,560.4,533.9,557.3,532.1z"/>
|
||||
<polygon class="st1" points="410.4,381.3 541.6,309.7 541.6,479.5 410.4,551.2 "/>
|
||||
<path class="st0" d="M258.4,88.5l338.6,182.3v169.9l-34.5-18.6V292.2c0-3.7-1.9-7-5.1-8.9c-0.1-0.1-0.3-0.1-0.4-0.2l0,0
|
||||
L258.4,122.3V88.5z"/>
|
||||
<polygon class="st1" points="192.7,110.6 530.3,292.3 400,363.4 62.5,181.6 "/>
|
||||
<polygon class="st1" points="51.1,369 51.1,199 389.6,381.3 389.6,551.3 96.6,393.5 "/>
|
||||
<path class="st0" d="M91.7,414.4l303.4,163.3c1.5,0.8,3.2,1.2,4.9,1.2c1.7,0,3.4-0.4,5-1.3l0,0l96.1-52.4l29.2,15.7L400,612.1
|
||||
L62.5,430.4L91.7,414.4z"/>
|
||||
<polygon class="st0" points="51.1,447.8 389.6,630.1 389.6,800 51.1,617.7 "/>
|
||||
<polygon class="st0" points="541.6,728.3 410.4,799.9 410.4,630 541.6,558.4 "/>
|
||||
<polygon class="st1" points="748.9,617.7 617.6,689.3 617.6,519.5 748.9,447.9 "/>
|
||||
<polygon class="st0" points="748.9,369 617.6,440.6 617.6,270.7 748.9,199.1 "/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
8
CodeGen/ui/react/src/common/client.ts
Normal file
8
CodeGen/ui/react/src/common/client.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import axios from "axios";
|
||||
|
||||
//add iterceptors to add any request headers
|
||||
|
||||
export default axios;
|
||||
12
CodeGen/ui/react/src/common/util.ts
Normal file
12
CodeGen/ui/react/src/common/util.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export const getCurrentTimeStamp = () => {
|
||||
return Math.floor(Date.now() / 1000);
|
||||
};
|
||||
|
||||
export const uuidv4 = () => {
|
||||
return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (c) =>
|
||||
(+c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (+c / 4)))).toString(16),
|
||||
);
|
||||
};
|
||||
135
CodeGen/ui/react/src/components/CodeGen/CodeGen.tsx
Normal file
135
CodeGen/ui/react/src/components/CodeGen/CodeGen.tsx
Normal file
@@ -0,0 +1,135 @@
|
||||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { KeyboardEventHandler, SyntheticEvent, useEffect, useRef, useState } from 'react'
|
||||
import styleClasses from "./codeGen.module.scss"
|
||||
import { ActionIcon, Textarea, Title, rem } from '@mantine/core'
|
||||
import { IconArrowRight } from '@tabler/icons-react'
|
||||
import { ConversationMessage } from '../Message/conversationMessage'
|
||||
import { fetchEventSource } from '@microsoft/fetch-event-source'
|
||||
import { CODE_GEN_URL } from '../../config'
|
||||
|
||||
|
||||
|
||||
const CodeGen = () => {
|
||||
const [prompt, setPrompt] = useState<string>("")
|
||||
const [submittedPrompt, setSubmittedPrompt] = useState<string>("")
|
||||
const [response,setResponse] = useState<string>("");
|
||||
const promptInputRef = useRef<HTMLTextAreaElement>(null)
|
||||
const scrollViewport = useRef<HTMLDivElement>(null)
|
||||
|
||||
const toSend = "Enter"
|
||||
|
||||
const handleSubmit = async () => {
|
||||
setResponse("")
|
||||
setSubmittedPrompt(prompt)
|
||||
const body = {
|
||||
messages:prompt
|
||||
}
|
||||
fetchEventSource(CODE_GEN_URL, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Accept":"*/*"
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
openWhenHidden: true,
|
||||
async onopen(response) {
|
||||
if (response.ok) {
|
||||
return;
|
||||
} else if (response.status >= 400 && response.status < 500 && response.status !== 429) {
|
||||
const e = await response.json();
|
||||
console.log(e);
|
||||
throw Error(e.error.message);
|
||||
} else {
|
||||
console.log("error", response);
|
||||
}
|
||||
},
|
||||
onmessage(msg) {
|
||||
if (msg?.data != "[DONE]") {
|
||||
try {
|
||||
const match = msg.data.match(/b'([^']*)'/);
|
||||
if (match && match[1] != "</s>") {
|
||||
const extractedText = match[1].replace(/\\n/g, "\n");
|
||||
setResponse(prev=>prev+extractedText);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("something wrong in msg", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
},
|
||||
onerror(err) {
|
||||
console.log("error", err);
|
||||
setResponse("")
|
||||
throw err;
|
||||
},
|
||||
onclose() {
|
||||
setPrompt("")
|
||||
},
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
const scrollToBottom = () => {
|
||||
scrollViewport.current!.scrollTo({ top: scrollViewport.current!.scrollHeight })
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
scrollToBottom()
|
||||
}, [response])
|
||||
|
||||
const handleKeyDown: KeyboardEventHandler = (event) => {
|
||||
if (!event.shiftKey && event.key === toSend) {
|
||||
handleSubmit()
|
||||
setTimeout(() => {
|
||||
setPrompt("")
|
||||
}, 1)
|
||||
}
|
||||
}
|
||||
|
||||
const handleChange = (event: SyntheticEvent) => {
|
||||
event.preventDefault()
|
||||
setPrompt((event.target as HTMLTextAreaElement).value)
|
||||
}
|
||||
return (
|
||||
<div className={styleClasses.codeGenWrapper}>
|
||||
<div className={styleClasses.codeGenContent}>
|
||||
<div className={styleClasses.codeGenContentMessages}>
|
||||
<div className={styleClasses.codeGenTitle}>
|
||||
<Title order={3}>CodeGen</Title>
|
||||
</div>
|
||||
|
||||
<div className={styleClasses.historyContainer} ref={scrollViewport}>
|
||||
{submittedPrompt && (
|
||||
<ConversationMessage key={`_ai`} date={Date.now()} human={true} message={submittedPrompt} />
|
||||
)}
|
||||
{response && (
|
||||
<ConversationMessage key={`_ai`} date={Date.now()} human={false} message={response} />
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className={styleClasses.codeGenActions}>
|
||||
<Textarea
|
||||
radius="xl"
|
||||
size="md"
|
||||
placeholder="Ask a question"
|
||||
ref={promptInputRef}
|
||||
onKeyDown={handleKeyDown}
|
||||
onChange={handleChange}
|
||||
value={prompt}
|
||||
rightSectionWidth={42}
|
||||
rightSection={
|
||||
<ActionIcon onClick={handleSubmit} size={32} radius="xl" variant="filled">
|
||||
<IconArrowRight style={{ width: rem(18), height: rem(18) }} stroke={1.5} />
|
||||
</ActionIcon>
|
||||
}
|
||||
// {...props}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div >
|
||||
)
|
||||
}
|
||||
export default CodeGen;
|
||||
59
CodeGen/ui/react/src/components/CodeGen/codeGen.module.scss
Normal file
59
CodeGen/ui/react/src/components/CodeGen/codeGen.module.scss
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
@import "../../styles/styles";
|
||||
|
||||
.spacer {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.codeGenWrapper {
|
||||
@include flex(row, nowrap, flex-start, flex-start);
|
||||
flex: 1 1 auto;
|
||||
height: 100%;
|
||||
& > * {
|
||||
height: 100%;
|
||||
}
|
||||
.codeGenContent {
|
||||
flex: 1 1 auto;
|
||||
position: relative;
|
||||
.codeGenContentMessages {
|
||||
@include absolutes;
|
||||
// @include flex(column, nowrap, flex-start, flex-start);
|
||||
|
||||
display: grid;
|
||||
grid-template-areas:
|
||||
"header"
|
||||
"messages"
|
||||
"inputs";
|
||||
|
||||
grid-template-columns: auto;
|
||||
grid-template-rows: 60px auto 100px;
|
||||
|
||||
.codeGenTitle {
|
||||
grid-area: header;
|
||||
@include flex(row, nowrap, center, flex-start);
|
||||
height: 60px;
|
||||
padding: 8px 24px;
|
||||
border-bottom: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
||||
}
|
||||
|
||||
.historyContainer {
|
||||
grid-area: messages;
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
padding: 16px 32px;
|
||||
& > * {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.codeGenActions {
|
||||
// padding: --var()
|
||||
grid-area: inputs;
|
||||
padding: 18px;
|
||||
border-top: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
@import "../../styles/styles";
|
||||
|
||||
.conversationMessage {
|
||||
@include flex(column, nowrap, flex-start, flex-start);
|
||||
margin-top: 16px;
|
||||
padding: 0 32px;
|
||||
width: 100%;
|
||||
|
||||
& > * {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { IconAi, IconUser } from "@tabler/icons-react"
|
||||
import style from "./conversationMessage.module.scss"
|
||||
import { Group, Text } from "@mantine/core"
|
||||
import { DateTime } from "luxon"
|
||||
import Markdown from "../Shared/Markdown/Markdown"
|
||||
|
||||
export interface ConversationMessageProps {
|
||||
message: string
|
||||
human: boolean
|
||||
date: number
|
||||
}
|
||||
|
||||
export function ConversationMessage({ human, message, date }: ConversationMessageProps) {
|
||||
const dateFormat = () => {
|
||||
return DateTime.fromJSDate(new Date(date)).toLocaleString(DateTime.DATETIME_MED)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={style.conversationMessage}>
|
||||
<Group>
|
||||
{human && <IconUser />}
|
||||
{!human && <IconAi />}
|
||||
|
||||
<div>
|
||||
<Text size="sm">
|
||||
{human && "You"} {!human && "Assistant"}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed">
|
||||
{dateFormat()}
|
||||
</Text>
|
||||
</div>
|
||||
</Group>
|
||||
<Text pl={54} pt="sm" size="sm">
|
||||
{human? message : (<Markdown content={message}/>)}
|
||||
</Text>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import styles from './codeRender.module.scss'
|
||||
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
|
||||
import { tomorrow } from "react-syntax-highlighter/dist/esm/styles/prism";
|
||||
import { IconCopy } from '@tabler/icons-react';
|
||||
import { Button, CopyButton } from '@mantine/core';
|
||||
|
||||
type CodeRenderProps = {
|
||||
cleanCode: React.ReactNode,
|
||||
language: string,
|
||||
inline: boolean
|
||||
}
|
||||
const CodeRender = ({ cleanCode, language, inline }:CodeRenderProps) => {
|
||||
cleanCode = String(cleanCode).replace(/\n$/, '').replace(/^\s*[\r\n]/gm, '') //right trim and remove empty lines from the input
|
||||
console.log(styles)
|
||||
try {
|
||||
return inline ? (<code className='inline-code'><i>{cleanCode}</i></code>) : (
|
||||
<div className={styles.code}>
|
||||
<div className={styles.codeHead}>
|
||||
<div className='code-title'>
|
||||
{language || "language not detected"}
|
||||
</div>
|
||||
<div className={styles.codeActionGroup} >
|
||||
<CopyButton value={cleanCode.toString()}>
|
||||
{({ copied, copy }) => (
|
||||
<Button color={copied ? 'teal' : 'blue'} styles={{root:{border:"none"}}} leftSection={<IconCopy size={12} />} onClick={copy}>
|
||||
{copied ? 'Copied' : 'Copy'}
|
||||
</Button>
|
||||
)}
|
||||
</CopyButton>
|
||||
</div>
|
||||
</div>
|
||||
<SyntaxHighlighter
|
||||
className={styles.codeHighlighterDiv}
|
||||
children={cleanCode.toString()}
|
||||
wrapLongLines={true}
|
||||
style={tomorrow}
|
||||
language={language}
|
||||
PreTag="div"
|
||||
/>
|
||||
</div>)
|
||||
} catch (err) {
|
||||
return (
|
||||
<pre>
|
||||
{cleanCode}
|
||||
</pre>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default CodeRender;
|
||||
@@ -0,0 +1,23 @@
|
||||
@import "../../../styles/styles";
|
||||
|
||||
.code {
|
||||
margin: 7px 0px;
|
||||
.codeHead {
|
||||
background: #379af1;
|
||||
|
||||
padding: 0px 10px !important;
|
||||
@include flex(row, nowrap, center, space-between);
|
||||
.codeTitle {
|
||||
}
|
||||
.codeActionGroup {
|
||||
@include flex(row, nowrap, center, flex-start);
|
||||
}
|
||||
}
|
||||
.codeHighlighterDiv {
|
||||
margin: 0px !important;
|
||||
}
|
||||
}
|
||||
|
||||
.inlineCode {
|
||||
background: #d7d7d7;
|
||||
}
|
||||
62
CodeGen/ui/react/src/components/Shared/Markdown/Markdown.tsx
Normal file
62
CodeGen/ui/react/src/components/Shared/Markdown/Markdown.tsx
Normal file
@@ -0,0 +1,62 @@
|
||||
import markdownStyles from './markdown.module.scss'
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import remarkFrontmatter from 'remark-frontmatter';
|
||||
// import Mermaid from '../../shared/Mermaid/Mermaid';
|
||||
import CodeRender from '../CodeRender/CodeRender';
|
||||
|
||||
type MarkdownProps = {
|
||||
content: string
|
||||
}
|
||||
const Markdown = ({ content }: MarkdownProps) => {
|
||||
return (
|
||||
<ReactMarkdown
|
||||
children={content}
|
||||
className={markdownStyles.md}
|
||||
remarkPlugins={[remarkGfm, remarkFrontmatter]}
|
||||
components={{
|
||||
p: ({ children, ...props }) => {
|
||||
return (
|
||||
<p {...props} style={{ whiteSpace: "pre-wrap" }}>
|
||||
{children}
|
||||
</p>
|
||||
);
|
||||
},
|
||||
a: ({ children, ...props }) => {
|
||||
return (
|
||||
<a
|
||||
href={props.href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
},
|
||||
table: ({ children, ...props }) => {
|
||||
return (
|
||||
<div
|
||||
className={markdownStyles.tableDiv}
|
||||
style={{
|
||||
overflowX: "auto",
|
||||
padding: "10px",
|
||||
}}
|
||||
>
|
||||
<table {...props}>{children}</table>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
//@ts-expect-error inline can undefined sometimes
|
||||
code({ inline, className, children, }) {
|
||||
const lang = /language-(\w+)/.exec(className || '')
|
||||
// if (lang && lang[1] === "mermaid") {
|
||||
// return <Mermaid content={String(children).replace(/\n$/, '')} key={"id"} />
|
||||
// }
|
||||
return <CodeRender cleanCode={children} inline={inline} language={(lang && lang[1]) || ""} />
|
||||
}
|
||||
}}
|
||||
/>)
|
||||
}
|
||||
|
||||
export default Markdown;
|
||||
@@ -0,0 +1,14 @@
|
||||
.tableDiv {
|
||||
table,
|
||||
th,
|
||||
td {
|
||||
border: 1px solid black;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
}
|
||||
|
||||
.md {
|
||||
li {
|
||||
margin-left: 35px; /* Adjust the value based on your preference */
|
||||
}
|
||||
}
|
||||
84
CodeGen/ui/react/src/components/sidebar/sidebar.module.scss
Normal file
84
CodeGen/ui/react/src/components/sidebar/sidebar.module.scss
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
Copyright (c) 2024 Intel Corporation
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
**/
|
||||
|
||||
@import "../../styles/styles";
|
||||
|
||||
.navbar {
|
||||
width: 100%;
|
||||
@include flex(column, nowrap, center, flex-start);
|
||||
padding: var(--mantine-spacing-md);
|
||||
background-color: var(--mantine-color-blue-filled);
|
||||
// background-color: light-dark(var(--mantine-color-white), var(--mantine-color-dark-6));
|
||||
// border-right: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
||||
}
|
||||
|
||||
.navbarMain {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.navbarLogo {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding-top: var(--mantine-spacing-md);
|
||||
margin-bottom: var(--mantine-spacing-xl);
|
||||
}
|
||||
|
||||
.link {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: var(--mantine-radius-md);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--mantine-color-white);
|
||||
|
||||
&:hover {
|
||||
background-color: var(--mantine-color-blue-7);
|
||||
}
|
||||
|
||||
&[data-active] {
|
||||
&,
|
||||
&:hover {
|
||||
box-shadow: var(--mantine-shadow-sm);
|
||||
background-color: var(--mantine-color-white);
|
||||
color: var(--mantine-color-blue-6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.aside {
|
||||
flex: 0 0 60px;
|
||||
background-color: var(--mantine-color-body);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
border-right: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-7));
|
||||
}
|
||||
|
||||
.logo {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
height: 60px;
|
||||
padding-top: var(--mantine-spacing-s);
|
||||
border-bottom: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-7));
|
||||
margin-bottom: var(--mantine-spacing-xl);
|
||||
}
|
||||
.logoImg {
|
||||
width: 30px;
|
||||
}
|
||||
58
CodeGen/ui/react/src/components/sidebar/sidebar.tsx
Normal file
58
CodeGen/ui/react/src/components/sidebar/sidebar.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { useState } from "react"
|
||||
import { Tooltip, UnstyledButton, Stack, rem } from "@mantine/core"
|
||||
import { IconHome2 } from "@tabler/icons-react"
|
||||
import classes from "./sidebar.module.scss"
|
||||
import OpeaLogo from "../../assets/opea-icon-color.svg"
|
||||
interface NavbarLinkProps {
|
||||
icon: typeof IconHome2
|
||||
label: string
|
||||
active?: boolean
|
||||
onClick?(): void
|
||||
}
|
||||
|
||||
function NavbarLink({ icon: Icon, label, active, onClick }: NavbarLinkProps) {
|
||||
return (
|
||||
<Tooltip label={label} position="right" transitionProps={{ duration: 0 }}>
|
||||
<UnstyledButton onClick={onClick} className={classes.link} data-active={active || undefined}>
|
||||
<Icon style={{ width: rem(20), height: rem(20) }} stroke={1.5} />
|
||||
</UnstyledButton>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
|
||||
export interface SidebarNavItem {
|
||||
icon: typeof IconHome2
|
||||
label: string
|
||||
}
|
||||
|
||||
export type SidebarNavList = SidebarNavItem[]
|
||||
|
||||
export interface SideNavbarProps {
|
||||
navList: SidebarNavList
|
||||
}
|
||||
|
||||
export function SideNavbar({ navList }: SideNavbarProps) {
|
||||
const [active, setActive] = useState(0)
|
||||
|
||||
|
||||
const links = navList.map((link, index) => (
|
||||
<NavbarLink {...link} key={link.label} active={index === active} onClick={() => setActive(index)} />
|
||||
))
|
||||
|
||||
return (
|
||||
<nav className={classes.navbar}>
|
||||
<div className={classes.navbarLogo}>
|
||||
<img className={classes.logoImg} src={OpeaLogo} alt="opea logo" />
|
||||
</div>
|
||||
|
||||
<div className={classes.navbarMain}>
|
||||
<Stack justify="center" gap={0}>
|
||||
{links}
|
||||
</Stack>
|
||||
</div>
|
||||
</nav>
|
||||
)
|
||||
}
|
||||
4
CodeGen/ui/react/src/config.ts
Normal file
4
CodeGen/ui/react/src/config.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
export const CODE_GEN_URL = import.meta.env.VITE_CODE_GEN_URL;
|
||||
20
CodeGen/ui/react/src/index.scss
Normal file
20
CodeGen/ui/react/src/index.scss
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
@import "@mantine/core/styles.css";
|
||||
|
||||
:root {
|
||||
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
13
CodeGen/ui/react/src/main.tsx
Normal file
13
CodeGen/ui/react/src/main.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import React from "react"
|
||||
import ReactDOM from "react-dom/client"
|
||||
import App from "./App.tsx"
|
||||
import "./index.scss"
|
||||
|
||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
)
|
||||
8
CodeGen/ui/react/src/styles/components/_sidebar.scss
Normal file
8
CodeGen/ui/react/src/styles/components/_sidebar.scss
Normal file
@@ -0,0 +1,8 @@
|
||||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
@import "../layout/flex";
|
||||
|
||||
@mixin sidebar {
|
||||
@include flex(column, nowrap, flex-start, flex-start);
|
||||
}
|
||||
5
CodeGen/ui/react/src/styles/components/content.scss
Normal file
5
CodeGen/ui/react/src/styles/components/content.scss
Normal file
@@ -0,0 +1,5 @@
|
||||
@mixin textWrapEllipsis {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
66
CodeGen/ui/react/src/styles/components/context.module.scss
Normal file
66
CodeGen/ui/react/src/styles/components/context.module.scss
Normal file
@@ -0,0 +1,66 @@
|
||||
@import "../layout/flex";
|
||||
@import "../components/content.scss";
|
||||
|
||||
.contextWrapper {
|
||||
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-6));
|
||||
border-right: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-4));
|
||||
width: 180px;
|
||||
overflow-y: hidden;
|
||||
overflow-x: hidden;
|
||||
// overflow-y: auto;
|
||||
.contextTitle {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
font-family:
|
||||
Greycliff CF,
|
||||
var(--mantine-font-family);
|
||||
margin-bottom: var(--mantine-spacing-xl);
|
||||
background-color: var(--mantine-color-body);
|
||||
padding: var(--mantine-spacing-md);
|
||||
padding-top: 18px;
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
border-bottom: 1px solid light-dark(var(--mantine-color-gray-3), var(--mantine-color-dark-7));
|
||||
}
|
||||
|
||||
.contextList {
|
||||
height: 70vh;
|
||||
// display: flex();
|
||||
|
||||
.contextListItem {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
border-top-right-radius: var(--mantine-radius-md);
|
||||
border-bottom-right-radius: var(--mantine-radius-md);
|
||||
color: light-dark(var(--mantine-color-gray-7), var(--mantine-color-dark-0));
|
||||
padding: 0 var(--mantine-spacing-md);
|
||||
font-size: var(--mantine-font-size-sm);
|
||||
margin-right: var(--mantine-spacing-md);
|
||||
font-weight: 500;
|
||||
height: 44px;
|
||||
width: 100%;
|
||||
line-height: 44px;
|
||||
cursor: pointer;
|
||||
|
||||
.contextItemName {
|
||||
flex: 1 1 auto;
|
||||
width: 130px;
|
||||
@include textWrapEllipsis;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: light-dark(var(--mantine-color-gray-1), var(--mantine-color-dark-5));
|
||||
color: light-dark(var(--mantine-color-dark), var(--mantine-color-light));
|
||||
}
|
||||
|
||||
&[data-active] {
|
||||
&,
|
||||
&:hover {
|
||||
border-left-color: var(--mantine-color-blue-filled);
|
||||
background-color: var(--mantine-color-blue-filled);
|
||||
color: var(--mantine-color-white);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
CodeGen/ui/react/src/styles/layout/_basics.scss
Normal file
7
CodeGen/ui/react/src/styles/layout/_basics.scss
Normal file
@@ -0,0 +1,7 @@
|
||||
@mixin absolutes {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
6
CodeGen/ui/react/src/styles/layout/_flex.scss
Normal file
6
CodeGen/ui/react/src/styles/layout/_flex.scss
Normal file
@@ -0,0 +1,6 @@
|
||||
@mixin flex($direction: row, $wrap: nowrap, $alignItems: center, $justifyContent: center) {
|
||||
display: flex;
|
||||
flex-flow: $direction $wrap;
|
||||
align-items: $alignItems;
|
||||
justify-content: $justifyContent;
|
||||
}
|
||||
5
CodeGen/ui/react/src/styles/styles.scss
Normal file
5
CodeGen/ui/react/src/styles/styles.scss
Normal file
@@ -0,0 +1,5 @@
|
||||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
@import "layout/flex";
|
||||
@import "layout/basics";
|
||||
4
CodeGen/ui/react/src/vite-env.d.ts
vendored
Normal file
4
CodeGen/ui/react/src/vite-env.d.ts
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
/// <reference types="vite/client" />
|
||||
Reference in New Issue
Block a user