| ... | ... | @@ -22,7 +22,7 @@ import ChatMarkdown from "~/components/ChatMarkdown"; |
| 22 | 22 | import { OpenInPicker } from "~/components/chat/OpenInPicker"; |
| 23 | 23 | import { useClientSettings } from "~/hooks/useSettings"; |
| 24 | 24 | import { useTheme } from "~/hooks/useTheme"; |
| 25 | | import { getLocalStorageItem, setLocalStorageItem } from "~/hooks/useLocalStorage"; |
| 25 | import { getLocalStorageItem, setLocalStorageItem, useLocalStorage } from "~/hooks/useLocalStorage"; |
| 26 | 26 | import { resolveDiffThemeName } from "~/lib/diffRendering"; |
| 27 | 27 | import { cn } from "~/lib/utils"; |
| 28 | 28 | import { isPreviewSupportedInRuntime } from "~/previewStateStore"; |
| ... | ... | @@ -79,6 +79,7 @@ interface FilePreviewPanelProps { |
| 79 | 79 | } |
| 80 | 80 | |
| 81 | 81 | const FILE_EXPLORER_STORAGE_KEY = "t3code.fileExplorerOpen"; |
| 82 | const RENDER_MARKDOWN_STORAGE_KEY = "t3code.renderMarkdown"; |
| 82 | 83 | const FILE_SAVE_DEBOUNCE_MS = 500; |
| 83 | 84 | const FILE_LINK_REVEAL_ATTRIBUTE = "data-file-link-reveal"; |
| 84 | 85 | const FILE_LINK_REVEAL_UNSAFE_CSS = ` |
| ... | ... | @@ -680,16 +681,27 @@ export default function FilePreviewPanel({ |
| 680 | 681 | const isImage = relativePath !== null && isWorkspaceImagePreviewPath(relativePath); |
| 681 | 682 | const file = useProjectFileQuery(environmentId, cwd, relativePath, !isImage); |
| 682 | 683 | const [explorerOpen, setExplorerOpen] = useState(initialExplorerOpen); |
| 683 | | const [markdownView, setMarkdownView] = useState<{ |
| 684 | | path: string | null; |
| 685 | | revealRequestId: number | null; |
| 686 | | }>({ path: null, revealRequestId: null }); |
| 684 | // Reading markdown rendered is a preference, not a property of one file. Keeping |
| 685 | // it on the panel meant a thread switch dropped it and forced source back. |
| 686 | const [renderMarkdownPreferred, setRenderMarkdownPreferred] = useLocalStorage( |
| 687 | RENDER_MARKDOWN_STORAGE_KEY, |
| 688 | false, |
| 689 | Schema.Boolean, |
| 690 | ); |
| 691 | // Paired with the path on purpose: each file surface counts its reveals from |
| 692 | // one, so a bare id would let a dismissed reveal on one file swallow the first |
| 693 | // reveal on the next. |
| 694 | const [handledReveal, setHandledReveal] = useState<{ path: string; requestId: number } | null>( |
| 695 | null, |
| 696 | ); |
| 687 | 697 | const breadcrumbRef = useRef<HTMLDivElement>(null); |
| 688 | 698 | const isMarkdown = relativePath ? isMarkdownPreviewFile(relativePath) : false; |
| 699 | // A reveal still wins over the preference: the line only exists in the source. |
| 689 | 700 | const renderMarkdown = |
| 690 | 701 | isMarkdown && |
| 691 | | markdownView.path === relativePath && |
| 692 | | (revealLine === null || markdownView.revealRequestId === revealRequestId); |
| 702 | renderMarkdownPreferred && |
| 703 | (revealLine === null || |
| 704 | (handledReveal?.path === relativePath && handledReveal.requestId === revealRequestId)); |
| 693 | 705 | const canOpenInBrowser = |
| 694 | 706 | relativePath !== null && isPreviewSupportedInRuntime() && isBrowserPreviewFile(relativePath); |
| 695 | 707 | const absolutePath = relativePath ? resolvePathLinkTarget(relativePath, cwd) : null; |
| ... | ... | @@ -796,10 +808,12 @@ export default function FilePreviewPanel({ |
| 796 | 808 | className="shrink-0" |
| 797 | 809 | pressed={renderMarkdown} |
| 798 | 810 | onPressedChange={(pressed) => { |
| 799 | | setMarkdownView({ |
| 800 | | path: pressed ? relativePath : null, |
| 801 | | revealRequestId: pressed ? revealRequestId : null, |
| 802 | | }); |
| 811 | setRenderMarkdownPreferred(pressed); |
| 812 | setHandledReveal( |
| 813 | pressed && relativePath !== null |
| 814 | ? { path: relativePath, requestId: revealRequestId } |
| 815 | : null, |
| 816 | ); |
| 803 | 817 | }} |
| 804 | 818 | aria-label={renderMarkdown ? "Show markdown source" : "Show rendered markdown"} |
| 805 | 819 | variant="ghost" |