Compare commits

..

1 Commits

Author SHA1 Message Date
Dane Urban
0604d9ca83 md formatting 2026-02-22 18:17:06 -08:00
2 changed files with 35 additions and 28 deletions

View File

@@ -7,15 +7,27 @@ interface LinguistLanguage {
filenames?: string[];
}
const allLanguages = Object.values(languages) as LinguistLanguage[];
// Collect extensions that linguist-languages assigns to "Markdown" so we can
// exclude them from the code-language map (some programming languages also
// claim `.md`).
const markdownExtensions = new Set(
allLanguages
.find((lang) => lang.name === "Markdown")
?.extensions?.map((ext) => ext.toLowerCase()) ?? []
);
// Build extension → language name and filename → language name maps at module load
const extensionMap = new Map<string, string>();
const filenameMap = new Map<string, string>();
for (const lang of Object.values(languages) as LinguistLanguage[]) {
for (const lang of allLanguages) {
if (lang.type !== "programming") continue;
const name = lang.name.toLowerCase();
for (const ext of lang.extensions ?? []) {
if (markdownExtensions.has(ext)) continue;
// First language to claim an extension wins
if (!extensionMap.has(ext)) {
extensionMap.set(ext, name);
@@ -38,3 +50,12 @@ export function getCodeLanguage(name: string): string | null {
const ext = lower.match(/\.[^.]+$/)?.[0];
return (ext && extensionMap.get(ext)) ?? filenameMap.get(lower) ?? null;
}
/**
* Returns true if the file name has a Markdown extension (as defined by
* linguist-languages) and should be rendered as rich text rather than code.
*/
export function isMarkdownFile(name: string): boolean {
const ext = name.toLowerCase().match(/\.[^.]+$/)?.[0];
return !!ext && markdownExtensions.has(ext);
}

View File

@@ -13,10 +13,9 @@ import Text from "@/refresh-components/texts/Text";
import { SvgDownload, SvgZoomIn, SvgZoomOut } from "@opal/icons";
import ScrollIndicatorDiv from "@/refresh-components/ScrollIndicatorDiv";
import CopyIconButton from "@/refresh-components/buttons/CopyIconButton";
import TextSeparator from "@/refresh-components/TextSeparator";
import { cn } from "@/lib/utils";
import { Section } from "@/layouts/general-layouts";
import { getCodeLanguage } from "@/lib/languages";
import { getCodeLanguage, isMarkdownFile } from "@/lib/languages";
import { CodeBlock } from "@/app/app/message/CodeBlock";
import { extractCodeText } from "@/app/app/message/codeUtils";
@@ -272,10 +271,15 @@ const csvVariant: PreviewVariant = {
<Section justifyContent="start" alignItems="start" padding={1}>
<Table>
<TableHeader className="sticky top-0 z-sticky">
<TableRow noHover>
<TableRow className="bg-background-tint-02">
{headers.map((h: string, i: number) => (
<TableHead key={i}>
<Text as="p" className="line-clamp-2" text04 secondaryAction>
<Text
as="p"
className="line-clamp-2 font-medium"
text03
mainUiBody
>
{h}
</Text>
</TableHead>
@@ -284,33 +288,22 @@ const csvVariant: PreviewVariant = {
</TableHeader>
<TableBody>
{rows.map((row: string[], rIdx: number) => (
<TableRow key={rIdx} noHover>
<TableRow key={rIdx}>
{headers.map((_: string, cIdx: number) => (
<TableCell
key={cIdx}
className={cn(
cIdx === 0 && "sticky left-0",
"py-3 px-4 whitespace-normal break-words"
cIdx === 0 && "sticky left-0 bg-background-tint-01",
"py-0 px-4 whitespace-normal break-words"
)}
>
<Text
as="p"
{...(cIdx === 0
? { text04: true, secondaryAction: true }
: { text03: true, secondaryBody: true })}
>
{row?.[cIdx] ?? ""}
</Text>
{row?.[cIdx] ?? ""}
</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
<TextSeparator
count={rows.length}
text={rows.length === 1 ? "row" : "rows"}
/>
</Section>
);
},
@@ -336,14 +329,7 @@ const csvVariant: PreviewVariant = {
const markdownVariant: PreviewVariant = {
matches: (name, mime) => {
if (MARKDOWN_MIMES.some((m) => mime.startsWith(m))) return true;
const lower = (name || "").toLowerCase();
return (
lower.endsWith(".md") ||
lower.endsWith(".markdown") ||
lower.endsWith(".txt") ||
lower.endsWith(".rst") ||
lower.endsWith(".org")
);
return isMarkdownFile(name || "");
},
width: "lg",
height: "full",