mirror of
https://github.com/Kaelio/ktx.git
synced 2026-06-13 08:15:14 +02:00
feat(docs): simplify quickstart code-block styling
Remove the fake terminal chrome (traffic lights + zsh header) and language pill from bash blocks, and the teal left-accent from output blocks. Bash fences now render as minimal cards; text fences route to a muted "output" preview. Make detectLanguage recursive and enable addLanguageClass in source.config.ts so Shiki tokens carry through to the renderer. Switch Shiki themes to min-light / github-dark and disable monospace ligatures so flag pairs like --agents keep a visible space.
This commit is contained in:
parent
e875cbab54
commit
a39ce02e55
4 changed files with 44 additions and 109 deletions
|
|
@ -231,57 +231,10 @@ figure[data-rehype-pretty-code-figure]:has(.ktx-code) {
|
|||
margin: 0;
|
||||
}
|
||||
|
||||
/* ── Mode A: Terminal ─────────────────────── */
|
||||
.ktx-code-terminal {
|
||||
background: #0c1417;
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
color: #c8c3bc;
|
||||
box-shadow:
|
||||
0 1px 2px rgba(0, 0, 0, 0.1),
|
||||
0 12px 32px -16px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.ktx-code-terminal:hover {
|
||||
border-color: rgba(34, 211, 238, 0.2);
|
||||
box-shadow:
|
||||
0 1px 2px rgba(0, 0, 0, 0.1),
|
||||
0 14px 32px -12px rgba(34, 211, 238, 0.18);
|
||||
}
|
||||
|
||||
.ktx-code-terminal-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 10px 12px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.06);
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.03), transparent);
|
||||
}
|
||||
|
||||
.ktx-tl-dot {
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
border-radius: 999px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.ktx-code-terminal-label {
|
||||
margin-left: 8px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.02em;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
|
||||
.ktx-code-body-terminal {
|
||||
background: transparent !important;
|
||||
color: #c8c3bc !important;
|
||||
}
|
||||
|
||||
/* ── Mode D: Output preview (wizard prompts, status output) ── */
|
||||
.ktx-code-output {
|
||||
background: var(--color-fd-muted);
|
||||
border: 1px solid var(--color-fd-border);
|
||||
border-left: 3px solid color-mix(in oklch, var(--color-fd-primary) 50%, var(--color-fd-border));
|
||||
position: relative;
|
||||
box-shadow: 0 1px 2px rgba(27, 27, 24, 0.02);
|
||||
}
|
||||
|
|
@ -289,17 +242,14 @@ figure[data-rehype-pretty-code-figure]:has(.ktx-code) {
|
|||
.dark .ktx-code-output {
|
||||
background: #111a1e;
|
||||
border-color: rgba(255, 255, 255, 0.05);
|
||||
border-left-color: rgba(34, 211, 238, 0.25);
|
||||
}
|
||||
|
||||
.ktx-code-output:hover {
|
||||
border-color: color-mix(in oklch, var(--color-fd-primary) 25%, var(--color-fd-border));
|
||||
border-left-color: var(--color-fd-primary);
|
||||
}
|
||||
|
||||
.dark .ktx-code-output:hover {
|
||||
border-color: rgba(255, 255, 255, 0.08);
|
||||
border-left-color: rgba(34, 211, 238, 0.45);
|
||||
}
|
||||
|
||||
.ktx-code-output-label {
|
||||
|
|
@ -460,26 +410,6 @@ figure[data-rehype-pretty-code-figure]:has(.ktx-code) {
|
|||
border-color: rgba(34, 211, 238, 0.2);
|
||||
}
|
||||
|
||||
.ktx-code-minimal-lang {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
left: 14px;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--color-fd-muted-foreground);
|
||||
font-family: var(--font-display), var(--font-sans), sans-serif;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s var(--ktx-ease);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.ktx-code-minimal:hover .ktx-code-minimal-lang {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.ktx-code-minimal-copy {
|
||||
position: absolute !important;
|
||||
top: 7px !important;
|
||||
|
|
@ -1073,8 +1003,7 @@ body::after {
|
|||
.pill-badge .pill-dot { animation: none; }
|
||||
.card-lift { transition: none; }
|
||||
.ktx-code,
|
||||
.ktx-code-minimal-copy,
|
||||
.ktx-code-minimal-lang {
|
||||
.ktx-code-minimal-copy {
|
||||
transition: none;
|
||||
}
|
||||
#nd-sidebar div[data-state]:not([class]) > button[data-state] svg {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ type Props = ComponentPropsWithoutRef<"pre"> & {
|
|||
"data-language"?: string;
|
||||
};
|
||||
|
||||
const TERMINAL_LANGS = new Set(["bash", "sh", "shell", "zsh"]);
|
||||
const OUTPUT_LANGS = new Set(["text", "plain", "plaintext", "console", "output"]);
|
||||
const WIZARD_GLYPHS = /^\s*[◆◇◯◐○●]/;
|
||||
|
||||
function extractText(node: ReactNode): string {
|
||||
|
|
@ -27,6 +27,33 @@ function extractText(node: ReactNode): string {
|
|||
return "";
|
||||
}
|
||||
|
||||
function findLanguageInNode(node: ReactNode): string | null {
|
||||
if (!isValidElement(node)) return null;
|
||||
const props = (node as ReactElement<{
|
||||
className?: string;
|
||||
"data-language"?: string;
|
||||
children?: ReactNode;
|
||||
}>).props;
|
||||
|
||||
const dataLang = props["data-language"];
|
||||
if (typeof dataLang === "string" && dataLang) return dataLang;
|
||||
|
||||
const className = typeof props.className === "string" ? props.className : "";
|
||||
const m = className.match(/language-([\w-]+)/);
|
||||
if (m) return m[1];
|
||||
|
||||
const children = props.children;
|
||||
if (Array.isArray(children)) {
|
||||
for (const child of children) {
|
||||
const found = findLanguageInNode(child);
|
||||
if (found) return found;
|
||||
}
|
||||
} else if (children) {
|
||||
return findLanguageInNode(children);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function detectLanguage(props: Props, children: ReactNode): string | null {
|
||||
const dataLang = props["data-language"];
|
||||
if (typeof dataLang === "string" && dataLang) return dataLang;
|
||||
|
|
@ -35,14 +62,7 @@ function detectLanguage(props: Props, children: ReactNode): string | null {
|
|||
const m = className.match(/language-([\w-]+)/);
|
||||
if (m) return m[1];
|
||||
|
||||
if (isValidElement(children)) {
|
||||
const childProps = (children as ReactElement<{ className?: string }>).props;
|
||||
const childClass = typeof childProps.className === "string" ? childProps.className : "";
|
||||
const cm = childClass.match(/language-([\w-]+)/);
|
||||
if (cm) return cm[1];
|
||||
}
|
||||
|
||||
return null;
|
||||
return findLanguageInNode(children);
|
||||
}
|
||||
|
||||
export function CodeBlock(props: Props) {
|
||||
|
|
@ -50,32 +70,11 @@ export function CodeBlock(props: Props) {
|
|||
const language = detectLanguage(props, children);
|
||||
const codeText = extractText(children);
|
||||
|
||||
const isTerminal = language !== null && TERMINAL_LANGS.has(language);
|
||||
const isOutput = !isTerminal && WIZARD_GLYPHS.test(codeText);
|
||||
const hasTitle = typeof title === "string" && title.length > 0;
|
||||
|
||||
// Mode A - Terminal (commands the user types)
|
||||
if (isTerminal) {
|
||||
return (
|
||||
<div className="not-prose ktx-code ktx-code-terminal group">
|
||||
<div className="ktx-code-terminal-head">
|
||||
<span className="ktx-tl-dot" style={{ background: "#ff5f57" }} />
|
||||
<span className="ktx-tl-dot" style={{ background: "#febc2e" }} />
|
||||
<span className="ktx-tl-dot" style={{ background: "#28c840" }} />
|
||||
<span className="ktx-code-terminal-label">
|
||||
{hasTitle ? title : "zsh"}
|
||||
</span>
|
||||
<CopyButton
|
||||
text={codeText}
|
||||
className="ml-auto text-white/80"
|
||||
/>
|
||||
</div>
|
||||
<pre {...rest} className="ktx-code-body ktx-code-body-terminal">
|
||||
{children}
|
||||
</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const isOutput =
|
||||
!hasTitle &&
|
||||
(WIZARD_GLYPHS.test(codeText) ||
|
||||
(language !== null && OUTPUT_LANGS.has(language)));
|
||||
|
||||
// Mode D - Output preview (wizard prompts, terminal output)
|
||||
if (isOutput) {
|
||||
|
|
@ -110,7 +109,6 @@ export function CodeBlock(props: Props) {
|
|||
// Mode C - Minimal default
|
||||
return (
|
||||
<div className="not-prose ktx-code ktx-code-minimal group relative">
|
||||
{language && <span className="ktx-code-minimal-lang">{language}</span>}
|
||||
<CopyButton text={codeText} className="ktx-code-minimal-copy" />
|
||||
<pre {...rest} className="ktx-code-body ktx-code-body-minimal">
|
||||
{children}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export function CopyButton({ text, className = "" }: Props) {
|
|||
type="button"
|
||||
onClick={onClick}
|
||||
aria-label={copied ? "Copied" : "Copy code"}
|
||||
className={`inline-flex items-center justify-center w-9 h-9 rounded-md transition-all hover:bg-white/5 ${className}`}
|
||||
className={`inline-flex items-center justify-center w-9 h-9 rounded-md transition-all hover:bg-fd-muted ${className}`}
|
||||
>
|
||||
{copied ? (
|
||||
<svg
|
||||
|
|
|
|||
|
|
@ -5,5 +5,13 @@ export const docs = defineDocs({
|
|||
});
|
||||
|
||||
export default defineConfig({
|
||||
mdxOptions: {},
|
||||
mdxOptions: {
|
||||
rehypeCodeOptions: {
|
||||
addLanguageClass: true,
|
||||
themes: {
|
||||
light: "min-light",
|
||||
dark: "github-dark",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue