fix: client side refresh for component
This commit is contained in:
parent
4c715b815a
commit
ea675fcd4c
@ -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>
|
||||||
);
|
);
|
||||||
|
35
web/src/components/CodeBlockClient.tsx
Normal file
35
web/src/components/CodeBlockClient.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
@ -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;
|
||||||
|
|
||||||
|
@ -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} />;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -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];
|
||||||
|
@ -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"
|
||||||
/>
|
/>
|
||||||
|
@ -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>
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
18
web/src/server/highlight.tsx
Normal file
18
web/src/server/highlight.tsx
Normal 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",
|
||||||
|
});
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user