Skip to content

Commit

Permalink
feat: add raw markdown view for text
Browse files Browse the repository at this point in the history
  • Loading branch information
listlessbird committed Oct 29, 2024
1 parent 788639e commit 7779dd9
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 39 deletions.
15 changes: 15 additions & 0 deletions src/components/artifacts/ArtifactRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
Copy,
LoaderCircle,
CircleCheck,
Eye,
} from "lucide-react";
import {
Dispatch,
Expand Down Expand Up @@ -86,6 +87,7 @@ export function ArtifactRenderer(props: ArtifactRendererProps) {
const [isInputVisible, setIsInputVisible] = useState(false);
const [isSelectionActive, setIsSelectionActive] = useState(false);
const [inputValue, setInputValue] = useState("");
const [isRawView, setIsRawView] = useState(false);

const handleMouseUp = useCallback(() => {
const selection = window.getSelection();
Expand Down Expand Up @@ -377,6 +379,18 @@ export function ArtifactRenderer(props: ArtifactRendererProps) {
/>
</div>
<div className="pr-4 pt-3">
<TooltipIconButton
tooltip={"Toggle Markdown View"}
variant="ghost"
delayDuration={400}
onClick={() => setIsRawView((p) => !p)}
className={cn(
"transition-colors w-fit h-fit p-2",
isRawView && "bg-gray-100 text-gray-900"
)}
>
<Eye className="w-6 h-6 text-gray-600" />
</TooltipIconButton>
<TooltipIconButton
tooltip="Copy"
variant="ghost"
Expand Down Expand Up @@ -437,6 +451,7 @@ export function ArtifactRenderer(props: ArtifactRendererProps) {
setUpdateRenderedArtifactRequired={
props.setUpdateRenderedArtifactRequired
}
isRawView={isRawView}
/>
) : null}
{currentArtifactContent.type === "code" ? (
Expand Down
95 changes: 56 additions & 39 deletions src/components/artifacts/TextRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ export interface TextRendererProps {
updateRenderedArtifactRequired: boolean;
setUpdateRenderedArtifactRequired: Dispatch<SetStateAction<boolean>>;
firstTokenReceived: boolean;
isRawView: boolean;
}

export function TextRenderer(props: TextRendererProps) {
const editor = useCreateBlockNote({});

const [rawMarkdown, setRawMarkdown] = useState("");

const [manuallyUpdatingArtifact, setManuallyUpdatingArtifact] =
useState(false);

Expand Down Expand Up @@ -110,6 +113,12 @@ export function TextRenderer(props: TextRendererProps) {
}
}, [props.artifact, props.updateRenderedArtifactRequired]);

useEffect(() => {
if (props.isRawView) {
editor.blocksToMarkdownLossy(editor.document).then(setRawMarkdown);
}
}, [props.isRawView, editor]);

const isComposition = useRef(false);

const onChange = async () => {
Expand Down Expand Up @@ -153,45 +162,53 @@ export function TextRenderer(props: TextRendererProps) {

return (
<div className="w-full h-full mt-2 flex flex-col border-t-[1px] border-gray-200 overflow-y-auto py-5">
<style jsx global>{`
.pulse-text .bn-block-group {
animation: pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.3;
}
}
`}</style>
<BlockNoteView
theme="light"
formattingToolbar={false}
slashMenu={false}
onCompositionStartCapture={() => (isComposition.current = true)}
onCompositionEndCapture={() => (isComposition.current = false)}
onChange={onChange}
editable={
!props.isStreaming || props.isEditing || !manuallyUpdatingArtifact
}
editor={editor}
className={
props.isStreaming && !props.firstTokenReceived ? "pulse-text" : ""
}
>
<SuggestionMenuController
getItems={async () =>
getDefaultReactSlashMenuItems(editor).filter(
(z) => z.group !== "Media"
)
}
triggerCharacter={"/"}
/>
</BlockNoteView>
{props.isRawView ? (
<pre className="whitespace-pre-wrap font-mono text-sm p-4 bg-gray-50 rounded-md border border-gray-200 hover:bg-gray-100 transition-colors">
{rawMarkdown}
</pre>
) : (
<>
<style jsx global>{`
.pulse-text .bn-block-group {
animation: pulse 1.5s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes pulse {
0%,
100% {
opacity: 1;
}
50% {
opacity: 0.3;
}
}
`}</style>
<BlockNoteView
theme="light"
formattingToolbar={false}
slashMenu={false}
onCompositionStartCapture={() => (isComposition.current = true)}
onCompositionEndCapture={() => (isComposition.current = false)}
onChange={onChange}
editable={
!props.isStreaming || props.isEditing || !manuallyUpdatingArtifact
}
editor={editor}
className={
props.isStreaming && !props.firstTokenReceived ? "pulse-text" : ""
}
>
<SuggestionMenuController
getItems={async () =>
getDefaultReactSlashMenuItems(editor).filter(
(z) => z.group !== "Media"
)
}
triggerCharacter={"/"}
/>
</BlockNoteView>
</>
)}
</div>
);
}

0 comments on commit 7779dd9

Please sign in to comment.