Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"next": "14.2.18",
"react": "^18",
"react-dom": "^18",
"react-syntax-highlighter": "^15.5.0",
"recharts": "^2.15.1",
"tailwind-merge": "^3.0.1",
"tailwindcss-animate": "^1.0.7"
Expand All @@ -40,6 +41,7 @@
"eslint-config-next": "14.2.18",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
"typescript": "^5",
"@types/react-syntax-highlighter": "^15.5.13"
}
}
5 changes: 2 additions & 3 deletions apps/web/src/components/(dashboard)/jobs/jobs-management.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { DataTable } from "../data-table"
import { createColumns } from "./columns"
import { CreateJobDialog } from "./create-job-dialog"
import { JobEditDialog } from "./job-edit-dialog"
import { YamlViewer } from "@/components/ui/yaml-viewer"

export type JobStatus = {
name: string;
Expand Down Expand Up @@ -354,9 +355,7 @@ export default function JobsManagement() {
{/* YAML Configuration */}
<div>
<h3 className="font-semibold mb-2">YAML Configuration</h3>
<pre className="bg-gray-900 text-green-400 p-4 rounded-lg overflow-x-auto text-sm">
<code>{selectedJob.yaml}</code>
</pre>
<YamlViewer yaml={selectedJob.yaml} />
</div>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { usePaginationClamp } from "@/hooks/use-pagination-clamp"
import { ListPagination } from "../list-pagination"
import { DataTable } from "../data-table"
import { createColumns } from "./columns"
import { YamlViewer } from "@/components/ui/yaml-viewer"

export type PodGroupStatus = {
name: string;
Expand Down Expand Up @@ -347,9 +348,7 @@ export default function PodGroupManagement() {

<div>
<h3 className="font-semibold mb-2">YAML Configuration</h3>
<pre className="bg-gray-900 text-green-400 p-4 rounded-lg overflow-x-auto text-sm">
<code>{selectedPodGroup.yaml}</code>
</pre>
<YamlViewer yaml={selectedPodGroup.yaml} />
</div>
</div>
)}
Expand Down
5 changes: 2 additions & 3 deletions apps/web/src/components/(dashboard)/pods/pod-management.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { DataTable } from "../data-table"
import { createColumns } from "./columns"
import { CreatePodDialog } from "./pod-create-dialog"
import { PodEditDialog } from "./pod-edit-dialog"
import { YamlViewer } from "@/components/ui/yaml-viewer"



Expand Down Expand Up @@ -349,9 +350,7 @@ export default function PodManagement() {

<div>
<h3 className="font-semibold mb-2">YAML Configuration</h3>
<pre className="bg-gray-900 text-green-400 p-4 rounded-lg overflow-x-auto text-sm">
<code>{selectedPod.yaml}</code>
</pre>
<YamlViewer yaml={selectedPod.yaml} />
</div>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { createColumns } from "./columns"
import { isProtectedQueue, protectedQueueDeleteMessage } from "@/lib/queue-constants"
import { CreateQueueDialog } from "./create-queue-dialog"
import { QueueEditDialog } from "./queue-edit-dialog"
import { YamlViewer } from "@/components/ui/yaml-viewer"

export type QueueStatus = {
name: string;
Expand Down Expand Up @@ -337,9 +338,7 @@ export default function QueueManagement() {

<div>
<h3 className="font-semibold mb-2">YAML Configuration</h3>
<pre className="bg-gray-900 text-green-400 p-4 rounded-lg overflow-x-auto text-sm">
<code>{selectedQueue.yaml}</code>
</pre>
<YamlViewer yaml={selectedQueue.yaml} />
</div>
</div>
)}
Expand Down
17 changes: 17 additions & 0 deletions apps/web/src/components/ui/yaml-viewer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Light as SyntaxHighlighter } from 'react-syntax-highlighter';
import yamlLang from 'react-syntax-highlighter/dist/esm/languages/hljs/yaml';
import { vs2015 } from 'react-syntax-highlighter/dist/esm/styles/hljs';

SyntaxHighlighter.registerLanguage('yaml', yamlLang);

export function YamlViewer({ yaml }: { yaml: string | undefined }) {
if (!yaml) return null;

return (
<div className="rounded-lg overflow-hidden text-sm border border-gray-800">
<SyntaxHighlighter language="yaml" style={vs2015} customStyle={{ margin: 0, padding: '1rem', backgroundColor: '#111827' }}>
{yaml}
</SyntaxHighlighter>
</div>
);
}
Comment on lines +1 to +17

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

In Next.js (App Router), using react-syntax-highlighter directly can lead to two major issues:

  1. SSR Import Errors: Importing from dist/esm/... can cause SyntaxError: Cannot use import statement outside a module during server-side rendering. Using the dist/cjs/... paths resolves this compatibility issue.
  2. Hydration Mismatches: The HTML generated by react-syntax-highlighter on the server may differ slightly from the client-side rendering, leading to React hydration warnings/errors.

To prevent these issues, we can:

  • Switch to CommonJS (dist/cjs/...) imports.
  • Use a mounted state with useEffect to render a simple <pre><code> block as a fallback during SSR/hydration, and swap in the syntax highlighter once mounted on the client. This avoids hydration mismatches and prevents layout shifts.
import { useEffect, useState } from 'react';
import { Light as SyntaxHighlighter } from 'react-syntax-highlighter';
import yamlLang from 'react-syntax-highlighter/dist/cjs/languages/hljs/yaml';
import { vs2015 } from 'react-syntax-highlighter/dist/cjs/styles/hljs';

SyntaxHighlighter.registerLanguage('yaml', yamlLang);

export function YamlViewer({ yaml }: { yaml: string | undefined }) {
    const [mounted, setMounted] = useState(false);

    useEffect(() => {
        setMounted(true);
    }, []);

    if (!yaml) return null;

    if (!mounted) {
        return (
            <div className="rounded-lg overflow-hidden text-sm border border-gray-800 bg-[#111827] p-4">
                <pre className="whitespace-pre overflow-x-auto text-gray-300">
                    <code>{yaml}</code>
                </pre>
            </div>
        );
    }

    return (
        <div className="rounded-lg overflow-hidden text-sm border border-gray-800">
            <SyntaxHighlighter language="yaml" style={vs2015} customStyle={{ margin: 0, padding: '1rem', backgroundColor: '#111827' }}>
                {yaml}
            </SyntaxHighlighter>
        </div>
    );
}