mirror of
https://github.com/Nezumi-2711/next-gdrive-index.git
synced 2026-09-22 20:01:36 +00:00
Add switch between markdown or raw files on rich preview and readme
This commit is contained in:
+51
-32
@@ -10,6 +10,7 @@ import remarkGfm from "remark-gfm";
|
||||
import remarkMath from "remark-math";
|
||||
import remarkSlug from "remark-slug";
|
||||
import remarkToc from "remark-toc";
|
||||
import { cn } from "~/utils";
|
||||
|
||||
import Icon from "~/components/Icon";
|
||||
|
||||
@@ -17,41 +18,59 @@ import "./highlight.css";
|
||||
|
||||
type Props = {
|
||||
content: string;
|
||||
view: "markdown" | "raw";
|
||||
};
|
||||
export default function Markdown({ content }: Props) {
|
||||
export default function Markdown({ content, view }: Props) {
|
||||
return (
|
||||
<div className='markdown w-full rounded-[var(--radius)] p-3'>
|
||||
<ReactMarkdown
|
||||
className='w-full'
|
||||
disallowedElements={["script"]}
|
||||
remarkPlugins={[remarkGfm, remarkMath, remarkSlug, remarkToc]}
|
||||
rehypePlugins={[
|
||||
rehypeKatex,
|
||||
rehypeRaw,
|
||||
[rehypePrism, { ignoreMissing: true }],
|
||||
]}
|
||||
components={{
|
||||
p: ({ node, children, ...props }) => (
|
||||
<p
|
||||
{...props}
|
||||
className='paragraph text-balance'
|
||||
>
|
||||
{children}
|
||||
</p>
|
||||
),
|
||||
pre: PreComponent,
|
||||
code: ({ node, inline, className, children, ...props }) => (
|
||||
<code
|
||||
className={`${className} font-mono !text-sm`}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</code>
|
||||
),
|
||||
}}
|
||||
<div className='flex flex-col p-3'>
|
||||
<div
|
||||
className={cn(
|
||||
"markdown w-full rounded-[var(--radius)]",
|
||||
view !== "raw" && "hidden",
|
||||
)}
|
||||
>
|
||||
{content}
|
||||
</ReactMarkdown>
|
||||
<pre className='w-full whitespace-pre-wrap break-words bg-transparent !p-0 shadow shadow-background'>
|
||||
{content}
|
||||
</pre>
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
"markdown w-full rounded-[var(--radius)]",
|
||||
view !== "markdown" && "hidden",
|
||||
)}
|
||||
>
|
||||
<ReactMarkdown
|
||||
className='w-full'
|
||||
disallowedElements={["script"]}
|
||||
remarkPlugins={[remarkGfm, remarkMath, remarkSlug, remarkToc]}
|
||||
rehypePlugins={[
|
||||
rehypeKatex,
|
||||
rehypeRaw,
|
||||
[rehypePrism, { ignoreMissing: true }],
|
||||
]}
|
||||
components={{
|
||||
p: ({ node, children, ...props }) => (
|
||||
<p
|
||||
{...props}
|
||||
className='paragraph text-balance'
|
||||
>
|
||||
{children}
|
||||
</p>
|
||||
),
|
||||
pre: PreComponent,
|
||||
code: ({ node, inline, className, children, ...props }) => (
|
||||
<code
|
||||
className={`${className} font-mono !text-sm`}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</code>
|
||||
),
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { z } from "zod";
|
||||
import { Schema_File } from "~/schema";
|
||||
|
||||
import { Card, CardContent } from "~/components/ui/card";
|
||||
|
||||
import { getFileType } from "~/utils/previewHelper";
|
||||
|
||||
import PreviewAction from "./@preview.action";
|
||||
import PreviewAudio from "./@preview.audio";
|
||||
import PreviewDoc from "./@preview.doc";
|
||||
import PreviewImage from "./@preview.image";
|
||||
import PreviewManga from "./@preview.manga";
|
||||
import PreviewRich from "./@preview.rich";
|
||||
import PreviewUnknown from "./@preview.unknown";
|
||||
import PreviewVideo from "./@preview.video";
|
||||
import RichHeader from "./@rich-header";
|
||||
|
||||
type Props = {
|
||||
data: z.infer<typeof Schema_File>;
|
||||
fileType: "unknown" | ReturnType<typeof getFileType>;
|
||||
};
|
||||
export default function FilePreviewLayout({ data, fileType }: Props) {
|
||||
const [view, setView] = useState<"markdown" | "raw">("markdown");
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card>
|
||||
<RichHeader
|
||||
title={data.name}
|
||||
view={view}
|
||||
onViewChange={setView}
|
||||
/>
|
||||
<CardContent className='p-1.5 pt-0 tablet:p-3 tablet:pt-0'>
|
||||
<div className='px-3'>
|
||||
{fileType === "image" ? (
|
||||
<PreviewImage file={data} />
|
||||
) : fileType === "audio" ? (
|
||||
<PreviewAudio file={data} />
|
||||
) : fileType === "video" ? (
|
||||
<PreviewVideo file={data} />
|
||||
) : fileType === "code" ? (
|
||||
<PreviewRich
|
||||
file={data}
|
||||
code
|
||||
view={view}
|
||||
/>
|
||||
) : fileType === "text" ? (
|
||||
<PreviewRich
|
||||
file={data}
|
||||
view={view}
|
||||
/>
|
||||
) : fileType === "markdown" ? (
|
||||
<PreviewRich
|
||||
file={data}
|
||||
view={view}
|
||||
/>
|
||||
) : fileType === "document" ? (
|
||||
<PreviewDoc file={data} />
|
||||
) : fileType === "pdf" ? (
|
||||
<PreviewDoc file={data} />
|
||||
) : fileType === "manga" ? (
|
||||
<PreviewManga file={data} />
|
||||
) : (
|
||||
<PreviewUnknown />
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<PreviewAction file={data} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
+25
-12
@@ -13,9 +13,11 @@ import { GetContent } from "./actions";
|
||||
|
||||
type Props = {
|
||||
file: z.infer<typeof Schema_File>;
|
||||
view: "markdown" | "raw";
|
||||
code?: boolean;
|
||||
};
|
||||
export default function PreviewRich({ file, code }: Props) {
|
||||
export default function PreviewRich({ file, code, view }: Props) {
|
||||
const [fetchedContent, setFetchedContent] = useState<string>("");
|
||||
const [content, setContent] = useState<string>("");
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [error, setError] = useState<string>("");
|
||||
@@ -30,11 +32,12 @@ export default function PreviewRich({ file, code }: Props) {
|
||||
setError("Looks like there is no content to preview");
|
||||
return;
|
||||
}
|
||||
if (code) {
|
||||
setContent(`\`\`\`${file.fileExtension}\n${text}\`\`\``);
|
||||
} else {
|
||||
setContent(text);
|
||||
}
|
||||
// setFetchedContent(text);
|
||||
// if (code) {
|
||||
// setContent(`\`\`\`${file.fileExtension}\n${text}\`\`\``);
|
||||
// } else {
|
||||
setContent(text);
|
||||
// }
|
||||
} catch (error) {
|
||||
const e = error as Error;
|
||||
console.error(e);
|
||||
@@ -46,7 +49,7 @@ export default function PreviewRich({ file, code }: Props) {
|
||||
}, [file, code]);
|
||||
|
||||
return (
|
||||
<div className='flex min-h-[33dvh] w-full items-center justify-center py-3'>
|
||||
<div className='flex h-fit min-h-[33dvh] w-full items-center justify-center py-3'>
|
||||
{loading ? (
|
||||
<div
|
||||
className={cn(
|
||||
@@ -72,17 +75,27 @@ export default function PreviewRich({ file, code }: Props) {
|
||||
</div>
|
||||
) : (
|
||||
<div
|
||||
className={cn("relative w-full", expand ? "h-full" : "max-h-[50dvh]")}
|
||||
className={cn(
|
||||
"relative w-full overflow-hidden",
|
||||
expand ? "h-full" : "max-h-[50dvh]",
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"h-full w-full",
|
||||
"w-full overflow-hidden",
|
||||
expand
|
||||
? " [mask-image:linear-gradient(180deg,white_65%,white]"
|
||||
: "max-h-[50dvh] [mask-image:linear-gradient(180deg,white_65%,rgba(255,255,255,0))]",
|
||||
? "[mask-image:linear-gradient(180deg,white_65%,white] h-full"
|
||||
: "h-[50dvh] max-h-[50dvh] [mask-image:linear-gradient(180deg,white_65%,rgba(255,255,255,0))]",
|
||||
)}
|
||||
>
|
||||
<Markdown content={content} />
|
||||
<Markdown
|
||||
content={
|
||||
code && view === "markdown"
|
||||
? `\`\`\`${file.fileExtension}\n${content}\`\`\``
|
||||
: content
|
||||
}
|
||||
view={view}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={cn(
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
|
||||
import { Card, CardContent } from "~/components/ui/card";
|
||||
|
||||
import Markdown from "./@markdown";
|
||||
import RichHeader from "./@rich-header";
|
||||
|
||||
type Props = {
|
||||
content: string;
|
||||
title: string;
|
||||
};
|
||||
export default function Readme({ content, title }: Props) {
|
||||
const [view, setView] = useState<"markdown" | "raw">("markdown");
|
||||
|
||||
return (
|
||||
<div
|
||||
slot='readme'
|
||||
className='w-full'
|
||||
>
|
||||
<Card>
|
||||
<RichHeader
|
||||
title={title}
|
||||
view={view}
|
||||
onViewChange={setView}
|
||||
/>
|
||||
<CardContent className='p-1.5 pt-0 tablet:p-3 tablet:pt-0'>
|
||||
<Markdown
|
||||
content={content}
|
||||
view={view}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "~/components/ui/button";
|
||||
import { CardHeader, CardTitle } from "~/components/ui/card";
|
||||
import { Separator } from "~/components/ui/separator";
|
||||
|
||||
type Props = {
|
||||
title: string;
|
||||
view: "markdown" | "raw";
|
||||
onViewChange: (value: "markdown" | "raw") => void;
|
||||
};
|
||||
export default function RichHeader({ title, view, onViewChange }: Props) {
|
||||
return (
|
||||
<CardHeader className='pb-0'>
|
||||
<div className='flex flex-col gap-3 mobile:flex-row mobile:items-center mobile:justify-between'>
|
||||
<CardTitle>{title}</CardTitle>
|
||||
<div className='flex w-full items-center mobile:w-fit'>
|
||||
<Button
|
||||
size={"sm"}
|
||||
variant={view === "markdown" ? "default" : "secondary"}
|
||||
onClick={() => onViewChange("markdown")}
|
||||
className='w-full rounded-r-none mobile:w-fit'
|
||||
>
|
||||
Markdown
|
||||
</Button>
|
||||
<Button
|
||||
size={"sm"}
|
||||
variant={view === "raw" ? "default" : "secondary"}
|
||||
onClick={() => onViewChange("raw")}
|
||||
className='w-full rounded-l-none mobile:w-fit'
|
||||
>
|
||||
Raw
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<Separator />
|
||||
</CardHeader>
|
||||
);
|
||||
}
|
||||
+44
-76
@@ -14,16 +14,9 @@ import { getFileType } from "~/utils/previewHelper";
|
||||
import FileBrowser from "../@explorer";
|
||||
import Header from "../@header";
|
||||
import HeaderButton from "../@header.button";
|
||||
import Markdown from "../@markdown";
|
||||
import Password from "../@password";
|
||||
import PreviewAction from "../@preview.action";
|
||||
import PreviewAudio from "../@preview.audio";
|
||||
import PreviewDoc from "../@preview.doc";
|
||||
import PreviewImage from "../@preview.image";
|
||||
import PreviewManga from "../@preview.manga";
|
||||
import PreviewRich from "../@preview.rich";
|
||||
import PreviewUnknown from "../@preview.unknown";
|
||||
import PreviewVideo from "../@preview.video";
|
||||
import FilePreviewLayout from "../@preview.layout";
|
||||
import Readme from "../@readme";
|
||||
import {
|
||||
CheckPassword,
|
||||
CheckPaths,
|
||||
@@ -141,76 +134,51 @@ export default async function RestPage({ params: { rest } }: Props) {
|
||||
slot='content'
|
||||
className='w-full'
|
||||
>
|
||||
<Card>
|
||||
<CardHeader className='pb-0'>
|
||||
{isFile ? (
|
||||
<div className='flex w-full gap-3'>
|
||||
<CardTitle className='line-clamp-2 flex-grow whitespace-pre-wrap break-all'>
|
||||
{data.name}
|
||||
</CardTitle>
|
||||
</div>
|
||||
) : (
|
||||
<div className='flex w-full items-center justify-between gap-3'>
|
||||
<CardTitle className='flex-grow'>Browse files</CardTitle>
|
||||
<HeaderButton />
|
||||
</div>
|
||||
)}
|
||||
<Separator />
|
||||
</CardHeader>
|
||||
<CardContent className='p-1.5 pt-0 tablet:p-3 tablet:pt-0'>
|
||||
{isFile ? (
|
||||
<div className='px-3'>
|
||||
{fileType === "image" ? (
|
||||
<PreviewImage file={data} />
|
||||
) : fileType === "audio" ? (
|
||||
<PreviewAudio file={data} />
|
||||
) : fileType === "video" ? (
|
||||
<PreviewVideo file={data} />
|
||||
) : fileType === "code" ? (
|
||||
<PreviewRich
|
||||
file={data}
|
||||
code
|
||||
/>
|
||||
) : fileType === "text" ? (
|
||||
<PreviewRich file={data} />
|
||||
) : fileType === "markdown" ? (
|
||||
<PreviewRich file={data} />
|
||||
) : fileType === "document" ? (
|
||||
<PreviewDoc file={data} />
|
||||
) : fileType === "pdf" ? (
|
||||
<PreviewDoc file={data} />
|
||||
) : fileType === "manga" ? (
|
||||
<PreviewManga file={data} />
|
||||
) : (
|
||||
<PreviewUnknown />
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<FileBrowser
|
||||
files={data.files}
|
||||
nextPageToken={data.nextPageToken}
|
||||
{isFile ? (
|
||||
<FilePreviewLayout
|
||||
data={data}
|
||||
fileType={fileType || "unknown"}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<Card>
|
||||
<CardHeader className='pb-0'>
|
||||
<div className='flex w-full items-center justify-between gap-3'>
|
||||
<CardTitle className='flex-grow'>Browse files</CardTitle>
|
||||
<HeaderButton />
|
||||
</div>
|
||||
<Separator />
|
||||
</CardHeader>
|
||||
<CardContent className='p-1.5 pt-0 tablet:p-3 tablet:pt-0'>
|
||||
<FileBrowser
|
||||
files={data.files}
|
||||
nextPageToken={data.nextPageToken}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
{readme && (
|
||||
<Readme
|
||||
content={readme}
|
||||
title={"README.md"}
|
||||
/>
|
||||
// <div
|
||||
// slot='readme'
|
||||
// className='w-full'
|
||||
// >
|
||||
// <Card>
|
||||
// <CardHeader className='pb-0'>
|
||||
// <CardTitle>README.md</CardTitle>
|
||||
// <Separator />
|
||||
// </CardHeader>
|
||||
// <CardContent className='p-1.5 pt-0 tablet:p-3 tablet:pt-0'>
|
||||
// <Markdown content={readme} />
|
||||
// </CardContent>
|
||||
// </Card>
|
||||
// </div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{readme && (
|
||||
<div
|
||||
slot='readme'
|
||||
className='w-full'
|
||||
>
|
||||
<Card>
|
||||
<CardHeader className='pb-0'>
|
||||
<CardTitle>README.md</CardTitle>
|
||||
<Separator />
|
||||
</CardHeader>
|
||||
<CardContent className='p-1.5 pt-0 tablet:p-3 tablet:pt-0'>
|
||||
<Markdown content={readme} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
{isFile && <PreviewAction file={data} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+19
-15
@@ -6,7 +6,7 @@ import { Separator } from "~/components/ui/separator";
|
||||
import FileBrowser from "./@explorer";
|
||||
import Header from "./@header";
|
||||
import HeaderButton from "./@header.button";
|
||||
import Markdown from "./@markdown";
|
||||
import Readme from "./@readme";
|
||||
import { GetFiles, GetReadme } from "./actions";
|
||||
|
||||
export const revalidate = 300;
|
||||
@@ -43,20 +43,24 @@ export default async function RootPage() {
|
||||
</Card>
|
||||
</div>
|
||||
{readme && (
|
||||
<div
|
||||
slot='readme'
|
||||
className='w-full'
|
||||
>
|
||||
<Card>
|
||||
<CardHeader className='pb-0'>
|
||||
<CardTitle>README.md</CardTitle>
|
||||
<Separator />
|
||||
</CardHeader>
|
||||
<CardContent className='p-1.5 pt-0 tablet:p-3 tablet:pt-0'>
|
||||
<Markdown content={readme} />
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
<Readme
|
||||
content={readme}
|
||||
title={"README.md"}
|
||||
/>
|
||||
// <div
|
||||
// slot='readme'
|
||||
// className='w-full'
|
||||
// >
|
||||
// <Card>
|
||||
// <CardHeader className='pb-0'>
|
||||
// <CardTitle>README.md</CardTitle>
|
||||
// <Separator />
|
||||
// </CardHeader>
|
||||
// <CardContent className='p-1.5 pt-0 tablet:p-3 tablet:pt-0'>
|
||||
// <Markdown content={readme} />
|
||||
// </CardContent>
|
||||
// </Card>
|
||||
// </div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user