| ... | @@ -849,7 +849,7 @@ function MarkdownCodeBlock({ | ... | @@ -849,7 +849,7 @@ function MarkdownCodeBlock({ |
| 849 | theme, | 849 | theme, |
| 850 | onRunShellCommand, | 850 | onRunShellCommand, |
| 851 | isStreaming, | 851 | isStreaming, |
| 852 | leadingActions, | 852 | viewToggle, |
| 853 | canWrap = true, | 853 | canWrap = true, |
| 854 | diagram = false, | 854 | diagram = false, |
| 855 | children, | 855 | children, |
| ... | @@ -860,9 +860,10 @@ function MarkdownCodeBlock({ | ... | @@ -860,9 +860,10 @@ function MarkdownCodeBlock({ |
| 860 | theme: "light" | "dark"; | 860 | theme: "light" | "dark"; |
| 861 | onRunShellCommand?: ((command: string) => void) | undefined; | 861 | onRunShellCommand?: ((command: string) => void) | undefined; |
| 862 | isStreaming: boolean; | 862 | isStreaming: boolean; |
| 863 | leadingActions?: ReactNode; | 863 | /** Switches between source and rendered content; sits left of copy in both views. */ |
| | 864 | viewToggle?: ReactNode; |
| 864 | canWrap?: boolean; | 865 | canWrap?: boolean; |
| 865 | /** Renders content instead of code, with actions below it like tables. */ | 866 | /** Renders content frameless, with the code header's actions above it. */ |
| 866 | diagram?: boolean; | 867 | diagram?: boolean; |
| 867 | children: ReactNode; | 868 | children: ReactNode; |
| 868 | }) { | 869 | }) { |
| ... | @@ -942,12 +943,14 @@ function MarkdownCodeBlock({ | ... | @@ -942,12 +943,14 @@ function MarkdownCodeBlock({ |
| 942 | | 943 | |
| 943 | if (diagram) { | 944 | if (diagram) { |
| 944 | return ( | 945 | return ( |
| 945 | <div className="my-[0.65rem]" data-language={language}> | 946 | // The transparent border and header padding match the code frame, so the |
| 946 | {children} | 947 | // actions stay in place when switching views. |
| 947 | <div className="mt-0.5 flex items-center justify-between select-none"> | 948 | <div className="my-[0.65rem] border border-transparent" data-language={language}> |
| 948 | {leadingActions} | 949 | <div className="flex items-center justify-end gap-0.5 pt-1.5 pr-1.5 select-none"> |
| | 950 | {viewToggle} |
| 949 | {copyButton} | 951 | {copyButton} |
| 950 | </div> | 952 | </div> |
| | 953 | {children} |
| 951 | </div> | 954 | </div> |
| 952 | ); | 955 | ); |
| 953 | } | 956 | } |
| ... | @@ -962,7 +965,6 @@ function MarkdownCodeBlock({ | ... | @@ -962,7 +965,6 @@ function MarkdownCodeBlock({ |
| 962 | headerProps={THREAD_FIND_IGNORE_PROPS} | 965 | headerProps={THREAD_FIND_IGNORE_PROPS} |
| 963 | actions={ | 966 | actions={ |
| 964 | <span className="flex items-center gap-0.5" role="toolbar" aria-label="Code block actions"> | 967 | <span className="flex items-center gap-0.5" role="toolbar" aria-label="Code block actions"> |
| 965 | {leadingActions} | | |
| 966 | {canWrap ? ( | 968 | {canWrap ? ( |
| 967 | <Tooltip> | 969 | <Tooltip> |
| 968 | <TooltipTrigger | 970 | <TooltipTrigger |
| ... | @@ -1000,6 +1002,7 @@ function MarkdownCodeBlock({ | ... | @@ -1000,6 +1002,7 @@ function MarkdownCodeBlock({ |
| 1000 | <TooltipPopup side="top">Run in terminal</TooltipPopup> | 1002 | <TooltipPopup side="top">Run in terminal</TooltipPopup> |
| 1001 | </Tooltip> | 1003 | </Tooltip> |
| 1002 | ) : null} | 1004 | ) : null} |
| | 1005 | {viewToggle} |
| 1003 | {copyButton} | 1006 | {copyButton} |
| 1004 | </span> | 1007 | </span> |
| 1005 | } | 1008 | } |
| ... | @@ -1043,7 +1046,7 @@ function MarkdownMermaidCodeBlock({ | ... | @@ -1043,7 +1046,7 @@ function MarkdownMermaidCodeBlock({ |
| 1043 | isStreaming={isStreaming} | 1046 | isStreaming={isStreaming} |
| 1044 | canWrap={!showDiagram} | 1047 | canWrap={!showDiagram} |
| 1045 | diagram={showDiagram} | 1048 | diagram={showDiagram} |
| 1046 | leadingActions={ | 1049 | viewToggle={ |
| 1047 | isStreaming ? null : ( | 1050 | isStreaming ? null : ( |
| 1048 | <Tooltip> | 1051 | <Tooltip> |
| 1049 | <TooltipTrigger | 1052 | <TooltipTrigger |