Skip to content

Commit

Permalink
Merge pull request #5 from ConnectAI-E/feature/artifacts-style
Browse files Browse the repository at this point in the history
Feature/artifacts style
  • Loading branch information
lloydzhou authored Jul 25, 2024
2 parents 5ec0311 + c27ef6f commit a0f0b4f
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 53 deletions.
30 changes: 30 additions & 0 deletions app/components/artifact.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
.artifact {
display: flex;
width: 100%;
height: 100%;
flex-direction: column;
&-header {
display: flex;
align-items: center;
height: 36px;
padding: 20px;
background: var(--second);
}
&-title {
flex: 1;
text-align: center;
font-weight: bold;
font-size: 24px;
}
&-content {
flex-grow: 1;
padding: 0 20px 20px 20px;
background-color: var(--second);
}
}

.artifact-iframe {
width: 100%;
border: var(--border-in-light);
border-radius: 6px;
}
65 changes: 29 additions & 36 deletions app/components/artifact.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import { Modal, showToast } from "./ui-lib";
import { copyToClipboard, downloadAs } from "../utils";
import { Path, ApiPath, REPO_URL } from "@/app/constant";
import { Loading } from "./home";
import styles from "./artifact.module.scss";

export function HTMLPreview(props: {
code: string;
autoHeight?: boolean;
height?: number;
height?: number | string;
onLoad?: (title?: string) => void;
}) {
const ref = useRef<HTMLIFrameElement>(null);
Expand Down Expand Up @@ -65,17 +66,22 @@ export function HTMLPreview(props: {
return props.code + script;
}, [props.code]);

const handleOnLoad = () => {
if (props?.onLoad) {
props.onLoad(title);
}
};

return (
<iframe
className={styles["artifact-iframe"]}
id={frameId.current}
ref={ref}
frameBorder={0}
sandbox="allow-forms allow-modals allow-scripts"
style={{ width: "100%", height }}
// src={`data:text/html,${encodeURIComponent(srcDoc)}`}
style={{ height }}
srcDoc={srcDoc}
onLoad={(e) => props?.onLoad && props?.onLoad(title)}
></iframe>
onLoad={handleOnLoad}
/>
);
}

Expand Down Expand Up @@ -179,7 +185,6 @@ export function Artifact() {
const [code, setCode] = useState("");
const [loading, setLoading] = useState(true);
const [fileName, setFileName] = useState("");
const { height } = useWindowSize();

useEffect(() => {
if (id) {
Expand All @@ -199,40 +204,28 @@ export function Artifact() {
}, [id]);

return (
<div
style={{
display: "block",
width: "100%",
height: "100%",
position: "relative",
}}
>
<div
style={{
height: 36,
display: "flex",
alignItems: "center",
padding: 12,
}}
>
<div className={styles["artifact"]}>
<div className={styles["artifact-header"]}>
<a href={REPO_URL} target="_blank" rel="noopener noreferrer">
<IconButton bordered icon={<GithubIcon />} shadow />
</a>
<div style={{ flex: 1, textAlign: "center" }}>NextChat Artifact</div>
<div className={styles["artifact-title"]}>NextChat Artifact</div>
<ArtifactShareButton id={id} getCode={() => code} fileName={fileName} />
</div>
{loading && <Loading />}
{code && (
<HTMLPreview
code={code}
autoHeight={false}
height={height - 36}
onLoad={(title) => {
setFileName(title as string);
setLoading(false);
}}
/>
)}
<div className={styles["artifact-content"]}>
{loading && <Loading />}
{code && (
<HTMLPreview
code={code}
autoHeight={false}
height={"100%"}
onLoad={(title) => {
setFileName(title as string);
setLoading(false);
}}
/>
)}
</div>
</div>
);
}
5 changes: 3 additions & 2 deletions app/components/chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -641,12 +641,13 @@ export function ChatActions(props: {
]}
onClose={() => setShowPluginSelector(false)}
onSelection={(s) => {
if (s.length === 0) return;
const plugin = s[0];
chatStore.updateCurrentSession((session) => {
session.mask.plugin = s;
});
showToast(plugin);
if (plugin) {
showToast(plugin);
}
}}
/>
)}
Expand Down
17 changes: 13 additions & 4 deletions app/components/markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import React from "react";
import { useDebouncedCallback } from "use-debounce";
import { showImageModal, FullScreen } from "./ui-lib";
import { ArtifactShareButton, HTMLPreview } from "./artifact";

import { Plugin } from "../constant";
import { useChatStore } from "../store";
export function Mermaid(props: { code: string }) {
const ref = useRef<HTMLDivElement>(null);
const [hasError, setHasError] = useState(false);
Expand Down Expand Up @@ -67,6 +68,9 @@ export function PreCode(props: { children: any }) {
const [mermaidCode, setMermaidCode] = useState("");
const [htmlCode, setHtmlCode] = useState("");
const { height } = useWindowSize();
const chatStore = useChatStore();
const session = chatStore.currentSession();
const plugins = session.mask?.plugin;

const renderArtifacts = useDebouncedCallback(() => {
if (!ref.current) return;
Expand All @@ -87,6 +91,11 @@ export function PreCode(props: { children: any }) {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [refText]);

const enableArtifacts = useMemo(
() => plugins?.includes(Plugin.Artifact),
[plugins],
);

return (
<>
<pre ref={ref}>
Expand All @@ -104,10 +113,10 @@ export function PreCode(props: { children: any }) {
{mermaidCode.length > 0 && (
<Mermaid code={mermaidCode} key={mermaidCode} />
)}
{htmlCode.length > 0 && (
<FullScreen className="no-dark html" right={60}>
{htmlCode.length > 0 && enableArtifacts && (
<FullScreen className="no-dark html" right={70}>
<ArtifactShareButton
style={{ position: "absolute", right: 10, top: 10 }}
style={{ position: "absolute", right: 20, top: 10 }}
getCode={() => htmlCode}
/>
<HTMLPreview
Expand Down
1 change: 1 addition & 0 deletions app/components/ui-lib.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@
}

&-content {
min-width: 300px;
.list {
max-height: 90vh;
overflow-x: hidden;
Expand Down
41 changes: 30 additions & 11 deletions app/components/ui-lib.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export function ListItem(props: {
children?: JSX.Element | JSX.Element[];
icon?: JSX.Element;
className?: string;
onClick?: (event: MouseEvent) => void;
onClick?: (e: MouseEvent) => void;
vertical?: boolean;
}) {
return (
Expand Down Expand Up @@ -470,15 +470,35 @@ export function Selector<T>(props: {
onClose?: () => void;
multiple?: boolean;
}) {
const [selectedValues, setSelectedValues] = useState<T[]>(
Array.isArray(props.defaultSelectedValue)
? props.defaultSelectedValue
: props.defaultSelectedValue !== undefined
? [props.defaultSelectedValue]
: [],
);

const handleSelection = (e: MouseEvent, value: T) => {
if (props.multiple) {
e.stopPropagation();
const newSelectedValues = selectedValues.includes(value)
? selectedValues.filter((v) => v !== value)
: [...selectedValues, value];
setSelectedValues(newSelectedValues);
props.onSelection?.(newSelectedValues);
} else {
setSelectedValues([value]);
props.onSelection?.([value]);
props.onClose?.();
}
};

return (
<div className={styles["selector"]} onClick={() => props.onClose?.()}>
<div className={styles["selector-content"]}>
<List>
{props.items.map((item, i) => {
const selected = props.multiple
? // @ts-ignore
props.defaultSelectedValue?.includes(item.value)
: props.defaultSelectedValue === item.value;
const selected = selectedValues.includes(item.value);
return (
<ListItem
className={`${styles["selector-item"]} ${
Expand All @@ -487,11 +507,11 @@ export function Selector<T>(props: {
key={i}
title={item.title}
subTitle={item.subTitle}
onClick={(event) => {
event.stopPropagation();
if (!item.disable) {
props.onSelection?.([item.value]);
props.onClose?.();
onClick={(e) => {
if (item.disable) {
e.stopPropagation();
} else {
handleSelection(e, item.value);
}
}}
>
Expand All @@ -515,7 +535,6 @@ export function Selector<T>(props: {
</div>
);
}

export function FullScreen(props: any) {
const { children, right = 10, top = 10, ...rest } = props;
const ref = useRef<HTMLDivElement>();
Expand Down

0 comments on commit a0f0b4f

Please sign in to comment.