Markdown rendering for skills, refresh button on replay
- react-markdown + remark-gfm for proper skill SKILL.md rendering - Prose styles matching dark theme (tables, code blocks, blockquotes) - Strips YAML frontmatter before rendering - Refresh button in session replay header
This commit is contained in:
@@ -68,6 +68,41 @@ body {
|
||||
.btn-accent:active { opacity: 0.7; }
|
||||
.btn-accent:disabled { opacity: 0.3; }
|
||||
|
||||
/* Markdown prose */
|
||||
.prose-hermes {
|
||||
font-size: 13px;
|
||||
line-height: 1.7;
|
||||
color: var(--text-1);
|
||||
}
|
||||
.prose-hermes h1 { font-size: 18px; font-weight: 700; margin: 20px 0 8px; color: var(--text-1); }
|
||||
.prose-hermes h2 { font-size: 16px; font-weight: 600; margin: 16px 0 6px; color: var(--text-1); }
|
||||
.prose-hermes h3 { font-size: 14px; font-weight: 600; margin: 12px 0 4px; color: var(--text-1); }
|
||||
.prose-hermes p { margin: 6px 0; }
|
||||
.prose-hermes ul, .prose-hermes ol { margin: 6px 0; padding-left: 20px; }
|
||||
.prose-hermes li { margin: 2px 0; }
|
||||
.prose-hermes code {
|
||||
font-size: 12px;
|
||||
background: rgba(255,255,255,0.06);
|
||||
padding: 1px 5px;
|
||||
border-radius: 4px;
|
||||
font-family: ui-monospace, "SF Mono", monospace;
|
||||
}
|
||||
.prose-hermes pre {
|
||||
background: rgba(0,0,0,0.3);
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
overflow-x: auto;
|
||||
margin: 8px 0;
|
||||
}
|
||||
.prose-hermes pre code { background: none; padding: 0; }
|
||||
.prose-hermes a { color: var(--accent); text-decoration: none; }
|
||||
.prose-hermes table { width: 100%; border-collapse: collapse; margin: 8px 0; font-size: 12px; }
|
||||
.prose-hermes th { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--border); color: var(--text-2); font-weight: 600; }
|
||||
.prose-hermes td { padding: 6px 8px; border-bottom: 1px solid var(--border); }
|
||||
.prose-hermes blockquote { border-left: 3px solid var(--accent); padding-left: 12px; margin: 8px 0; color: var(--text-2); }
|
||||
.prose-hermes hr { border: none; border-top: 1px solid var(--border); margin: 16px 0; }
|
||||
.prose-hermes strong { color: var(--text-1); }
|
||||
|
||||
/* Divider */
|
||||
.divider {
|
||||
height: 1px;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { use, useState, useRef } from "react";
|
||||
import { ArrowLeft, Send, Loader2, Eye } from "lucide-react";
|
||||
import { ArrowLeft, Send, Loader2, Eye, RefreshCw } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useSession, useMessages } from "@/lib/hooks";
|
||||
import { timeAgo, formatTokens, platformBadgeClass } from "@/lib/utils";
|
||||
@@ -96,6 +96,14 @@ export default function SessionReplayPage({ params }: { params: Promise<{ id: st
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* Refresh */}
|
||||
<button
|
||||
onClick={() => { qc.invalidateQueries({ queryKey: ["messages", id] }); qc.invalidateQueries({ queryKey: ["session", id] }); }}
|
||||
className="p-1.5 rounded-lg active:bg-white/[0.04]"
|
||||
style={{ color: "var(--text-2)" }}
|
||||
>
|
||||
<RefreshCw size={16} />
|
||||
</button>
|
||||
{/* Verbosity toggle */}
|
||||
<div className="relative">
|
||||
<button
|
||||
|
||||
@@ -4,11 +4,19 @@ import { use } from "react";
|
||||
import { ArrowLeft, FileText, Folder } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useSkill } from "@/lib/hooks";
|
||||
import { Markdown } from "@/components/Markdown";
|
||||
|
||||
export default function SkillDetailPage({ params }: { params: Promise<{ name: string }> }) {
|
||||
const { name } = use(params);
|
||||
const { data, isLoading } = useSkill(name);
|
||||
|
||||
// Strip YAML frontmatter for rendering
|
||||
let content = data?.content || "";
|
||||
if (content.startsWith("---")) {
|
||||
const end = content.indexOf("---", 3);
|
||||
if (end > 0) content = content.slice(end + 3).trim();
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-3">
|
||||
@@ -40,10 +48,8 @@ export default function SkillDetailPage({ params }: { params: Promise<{ name: st
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="card p-4">
|
||||
<pre className="text-[13px] whitespace-pre-wrap leading-relaxed font-sans overflow-x-auto" style={{ color: "var(--text-1)" }}>
|
||||
{data.content}
|
||||
</pre>
|
||||
<div className="card p-4 overflow-x-hidden">
|
||||
<Markdown content={content} />
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import ReactMarkdown from "react-markdown";
|
||||
import remarkGfm from "remark-gfm";
|
||||
|
||||
export function Markdown({ content }: { content: string }) {
|
||||
return (
|
||||
<div className="prose-hermes">
|
||||
<ReactMarkdown remarkPlugins={[remarkGfm]}>{content}</ReactMarkdown>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Generated
+1470
-6
File diff suppressed because it is too large
Load Diff
+3
-1
@@ -13,7 +13,9 @@
|
||||
"lucide-react": "^1.8.0",
|
||||
"next": "16.2.3",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4"
|
||||
"react-dom": "19.2.4",
|
||||
"react-markdown": "^10.1.0",
|
||||
"remark-gfm": "^4.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4",
|
||||
|
||||
Reference in New Issue
Block a user