fix: client side refresh for component

This commit is contained in:
bennykok 2024-02-01 17:01:54 +08:00
parent 4c715b815a
commit ea675fcd4c
10 changed files with 106 additions and 43 deletions

View File

@ -13,10 +13,7 @@ export async function CodeBlock(props: {
return ( return (
<div className="relative w-full text-sm"> <div className="relative w-full text-sm">
{/* max-w-[calc(32rem-1.5rem-1.5rem)] */}
{/* <div className=""> */}
<p <p
// tabIndex={1}
className="[&>pre]:p-4 rounded-lg max-h-96 overflow-auto w-full" className="[&>pre]:p-4 rounded-lg max-h-96 overflow-auto w-full"
style={{ style={{
overflowWrap: "break-word", overflowWrap: "break-word",
@ -28,7 +25,6 @@ export async function CodeBlock(props: {
}), }),
}} }}
/> />
{/* </div> */}
<CopyButton className="absolute right-2 top-2" text={props.code} /> <CopyButton className="absolute right-2 top-2" text={props.code} />
</div> </div>
); );

View File

@ -0,0 +1,35 @@
"use client";
import { CopyButton } from "@/components/CopyButton";
import type { StringLiteralUnion } from "shikiji";
import useSWR from "swr";
import { highlight } from "../server/highlight";
export function CodeBlockClient({
code,
lang,
}: {
code: string;
lang: StringLiteralUnion<string>;
}) {
const { data } = useSWR(code, async () => {
return highlight(code.trim(), lang);
});
return (
<div className="relative w-full text-sm">
{data && (
<p
className="[&>pre]:p-4 rounded-lg max-h-96 overflow-auto w-full"
style={{
overflowWrap: "break-word",
}}
dangerouslySetInnerHTML={{
__html: data,
}}
/>
)}
<CopyButton className="absolute right-2 top-2" text={code} />
</div>
);
}

View File

@ -14,10 +14,18 @@ export function LogsViewer({
logs, logs,
hideTimestamp, hideTimestamp,
className, className,
}: { logs: LogsType; hideTimestamp?: boolean; className?: string }) { stickToBottom = true,
}: {
logs: LogsType;
hideTimestamp?: boolean;
className?: string;
stickToBottom?: boolean;
}) {
const container = useRef<HTMLDivElement | null>(null); const container = useRef<HTMLDivElement | null>(null);
useEffect(() => { useEffect(() => {
if (!stickToBottom) return;
// console.log(logs.length, container.current); // console.log(logs.length, container.current);
if (container.current) { if (container.current) {
const scrollHeight = container.current.scrollHeight; const scrollHeight = container.current.scrollHeight;
@ -27,11 +35,12 @@ export function LogsViewer({
behavior: "smooth", behavior: "smooth",
}); });
} }
}, [logs.length]); }, [logs.length, stickToBottom]);
return ( return (
<div <div
ref={(ref) => { ref={(ref) => {
if (!stickToBottom) return;
if (!container.current && ref) { if (!container.current && ref) {
const scrollHeight = ref.scrollHeight; const scrollHeight = ref.scrollHeight;

View File

@ -1,15 +1,25 @@
"use client";
import useSWR from "swr";
import { DownloadButton } from "./DownloadButton"; import { DownloadButton } from "./DownloadButton";
import { getFileDownloadUrl } from "@/server/getFileDownloadUrl"; import { getFileDownloadUrl } from "@/server/getFileDownloadUrl";
export async function OutputRender(props: { export function OutputRender(props: {
run_id: string; run_id: string;
filename: string; filename: string;
}) { }) {
if (props.filename.endsWith(".mp4") || props.filename.endsWith(".webm")) { const { data: url } = useSWR(
const url = await getFileDownloadUrl( "run-outputs+" + props.run_id + props.filename,
async () => {
return await getFileDownloadUrl(
`outputs/runs/${props.run_id}/${props.filename}`, `outputs/runs/${props.run_id}/${props.filename}`,
); );
},
);
if (!url) return <></>;
if (props.filename.endsWith(".mp4") || props.filename.endsWith(".webm")) {
return ( return (
<video controls autoPlay className="w-[400px]"> <video controls autoPlay className="w-[400px]">
<source src={url} type="video/mp4" /> <source src={url} type="video/mp4" />
@ -25,17 +35,8 @@ export async function OutputRender(props: {
props.filename.endsWith(".jpg") || props.filename.endsWith(".jpg") ||
props.filename.endsWith(".jpeg") props.filename.endsWith(".jpeg")
) { ) {
const url = await getFileDownloadUrl(
`outputs/runs/${props.run_id}/${props.filename}`,
);
return <img className="max-w-[200px]" alt={props.filename} src={url} />; return <img className="max-w-[200px]" alt={props.filename} src={url} />;
} else { } else {
const url = await getFileDownloadUrl(
`outputs/runs/${props.run_id}/${props.filename}`,
);
// console.log(url);
return <DownloadButton filename={props.filename} href={url} />; return <DownloadButton filename={props.filename} href={url} />;
} }
} }

View File

@ -1,12 +1,12 @@
"use client";
import { RunInputs } from "@/components/RunInputs"; import { RunInputs } from "@/components/RunInputs";
import { RunOutputs } from "@/components/RunOutputs"; import { RunOutputs } from "@/components/RunOutputs";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { import {
Dialog, Dialog,
DialogClose,
DialogContent, DialogContent,
DialogDescription, DialogDescription,
DialogFooter,
DialogHeader, DialogHeader,
DialogTitle, DialogTitle,
DialogTrigger, DialogTrigger,
@ -19,11 +19,8 @@ import {
} from "@/components/ui/tooltip"; } from "@/components/ui/tooltip";
import { getDuration, getRelativeTime } from "@/lib/getRelativeTime"; import { getDuration, getRelativeTime } from "@/lib/getRelativeTime";
import { type findAllRuns } from "@/server/findAllRuns"; import { type findAllRuns } from "@/server/findAllRuns";
import { Suspense } from "react";
import { LiveStatus } from "./LiveStatus"; import { LiveStatus } from "./LiveStatus";
import { LogsType, LogsViewer } from "@/components/LogsViewer"; import { LoadingWrapper } from "@/components/LoadingWrapper";
import { Button } from "@/components/ui/button";
import { Edit, ExternalLink } from "lucide-react";
export function RunDisplay({ export function RunDisplay({
run, run,
@ -78,9 +75,9 @@ export function RunDisplay({
</DialogHeader> </DialogHeader>
<div className="max-h-96 overflow-y-scroll"> <div className="max-h-96 overflow-y-scroll">
<RunInputs run={run} /> <RunInputs run={run} />
<Suspense> <LoadingWrapper tag="output">
<RunOutputs run={run} /> <RunOutputs run={run} />
</Suspense> </LoadingWrapper>
</div> </div>
{/* <div className="max-h-96 overflow-y-scroll">{view}</div> */} {/* <div className="max-h-96 overflow-y-scroll">{view}</div> */}
</DialogContent> </DialogContent>

View File

@ -8,7 +8,7 @@ import {
} from "@/components/ui/table"; } from "@/components/ui/table";
import type { findAllRuns } from "@/server/findAllRuns"; import type { findAllRuns } from "@/server/findAllRuns";
export async function RunInputs({ export function RunInputs({
run, run,
}: { }: {
run: Awaited<ReturnType<typeof findAllRuns>>[0]; run: Awaited<ReturnType<typeof findAllRuns>>[0];

View File

@ -1,3 +1,5 @@
"use client";
import { import {
Dialog, Dialog,
DialogClose, DialogClose,
@ -9,7 +11,6 @@ import {
DialogTrigger, DialogTrigger,
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { OutputRender } from "./OutputRender"; import { OutputRender } from "./OutputRender";
import { CodeBlock } from "@/components/CodeBlock";
import { import {
Table, Table,
TableBody, TableBody,
@ -24,11 +25,19 @@ import { Button } from "@/components/ui/button";
import { ExternalLink } from "lucide-react"; import { ExternalLink } from "lucide-react";
import { LogsViewer } from "@/components/LogsViewer"; import { LogsViewer } from "@/components/LogsViewer";
import { CopyButton } from "@/components/CopyButton"; import { CopyButton } from "@/components/CopyButton";
import useSWR from "swr";
import { CodeBlockClient } from "@/components/CodeBlockClient";
export async function RunOutputs({ export function RunOutputs({
run, run,
}: { run: Awaited<ReturnType<typeof findAllRuns>>[0] }) { }: { run: Awaited<ReturnType<typeof findAllRuns>>[0] }) {
const outputs = await getRunsOutput(run.id); const { data, isValidating, error } = useSWR(
"run-outputs+" + run.id,
async () => {
return await getRunsOutput(run.id);
},
);
return ( return (
<Table className="table-fixed"> <Table className="table-fixed">
<TableHeader className="bg-background top-0 sticky"> <TableHeader className="bg-background top-0 sticky">
@ -52,7 +61,7 @@ export async function RunOutputs({
<DialogHeader> <DialogHeader>
<DialogTitle>Run Log</DialogTitle> <DialogTitle>Run Log</DialogTitle>
</DialogHeader> </DialogHeader>
<LogsViewer logs={run.run_log} /> <LogsViewer logs={run.run_log} stickToBottom={false} />
<DialogFooter> <DialogFooter>
<CopyButton <CopyButton
className="w-fit aspect-auto p-4" className="w-fit aspect-auto p-4"
@ -74,7 +83,7 @@ export async function RunOutputs({
</TableCell> </TableCell>
</TableRow> </TableRow>
{outputs?.map((run) => { {data?.map((run) => {
const fileName = const fileName =
run.data.images?.[0].filename || run.data.images?.[0].filename ||
run.data.files?.[0].filename || run.data.files?.[0].filename ||
@ -85,7 +94,7 @@ export async function RunOutputs({
<TableRow key={run.id}> <TableRow key={run.id}>
<TableCell>Output</TableCell> <TableCell>Output</TableCell>
<TableCell className=""> <TableCell className="">
<CodeBlock <CodeBlockClient
code={JSON.stringify(run.data, null, 2)} code={JSON.stringify(run.data, null, 2)}
lang="json" lang="json"
/> />

View File

@ -29,7 +29,7 @@ export function RunsTable(props: {
const { data, error, isLoading, isValidating } = useSWR( const { data, error, isLoading, isValidating } = useSWR(
"runs+" + page, "runs+" + page,
async () => { async () => {
const data = await getAllRunstableContent({ const data = await findAllRunsWithCounts({
workflow_id: props.workflow_id, workflow_id: props.workflow_id,
limit: itemPerPage, limit: itemPerPage,
offset: (page - 1) * itemPerPage, offset: (page - 1) * itemPerPage,
@ -68,7 +68,11 @@ export function RunsTable(props: {
<TableHead className="text-right">Status</TableHead> <TableHead className="text-right">Status</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody>{data?.table}</TableBody> <TableBody>
{data?.allRuns.map((run) => (
<RunDisplay run={run} key={run.id} />
))}
</TableBody>
</Table> </Table>
</div> </div>

View File

@ -1,16 +1,10 @@
"use server"; "use server";
import { RunOutputs } from "@/components/RunOutputs";
import { db } from "@/db/db"; import { db } from "@/db/db";
import { workflowRunOutputs } from "@/db/schema"; import { workflowRunOutputs } from "@/db/schema";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
// export async function getRunsOutputDisplay(run_id: string) {
// return <RunOutputs run_id={run_id} />;
// }
export async function getRunsOutput(run_id: string) { export async function getRunsOutput(run_id: string) {
// throw new Error("Not implemented");
return await db return await db
.select() .select()
.from(workflowRunOutputs) .from(workflowRunOutputs)

View File

@ -0,0 +1,18 @@
"use server";
import type { StringLiteralUnion } from "shikiji";
import { getHighlighter } from "shikiji";
export async function highlight(
code: string,
lang: StringLiteralUnion<string>,
) {
const highlighter = await getHighlighter({
themes: ["one-dark-pro"],
langs: [lang],
});
return highlighter.codeToHtml(code.trim(), {
lang: lang,
theme: "one-dark-pro",
});
}