@kushagradhawan/kookie-blocks 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components.css +67 -1
- package/dist/cjs/components/code/CodeBlock.d.ts +2 -1
- package/dist/cjs/components/code/CodeBlock.d.ts.map +1 -1
- package/dist/cjs/components/code/CodeBlock.js +1 -1
- package/dist/cjs/components/code/CodeBlock.js.map +3 -3
- package/dist/cjs/components/code/LanguageBadge.d.ts +9 -0
- package/dist/cjs/components/code/LanguageBadge.d.ts.map +1 -1
- package/dist/cjs/components/code/LanguageBadge.js +1 -1
- package/dist/cjs/components/code/LanguageBadge.js.map +3 -3
- package/dist/cjs/components/code/index.d.ts +5 -2
- package/dist/cjs/components/code/index.d.ts.map +1 -1
- package/dist/cjs/components/code/index.js +1 -1
- package/dist/cjs/components/code/index.js.map +3 -3
- package/dist/cjs/components/code/types.d.ts +132 -13
- package/dist/cjs/components/code/types.d.ts.map +1 -1
- package/dist/cjs/components/code/types.js +1 -1
- package/dist/cjs/components/code/types.js.map +3 -3
- package/dist/cjs/components/code/useCodeCard.d.ts +20 -0
- package/dist/cjs/components/code/useCodeCard.d.ts.map +1 -0
- package/dist/cjs/components/code/useCodeCard.js +2 -0
- package/dist/cjs/components/code/useCodeCard.js.map +7 -0
- package/dist/esm/components/code/CodeBlock.d.ts +2 -1
- package/dist/esm/components/code/CodeBlock.d.ts.map +1 -1
- package/dist/esm/components/code/CodeBlock.js +1 -1
- package/dist/esm/components/code/CodeBlock.js.map +3 -3
- package/dist/esm/components/code/LanguageBadge.d.ts +9 -0
- package/dist/esm/components/code/LanguageBadge.d.ts.map +1 -1
- package/dist/esm/components/code/LanguageBadge.js +1 -1
- package/dist/esm/components/code/LanguageBadge.js.map +3 -3
- package/dist/esm/components/code/index.d.ts +5 -2
- package/dist/esm/components/code/index.d.ts.map +1 -1
- package/dist/esm/components/code/index.js +1 -1
- package/dist/esm/components/code/index.js.map +3 -3
- package/dist/esm/components/code/types.d.ts +132 -13
- package/dist/esm/components/code/types.d.ts.map +1 -1
- package/dist/esm/components/code/types.js +1 -0
- package/dist/esm/components/code/types.js.map +4 -4
- package/dist/esm/components/code/useCodeCard.d.ts +20 -0
- package/dist/esm/components/code/useCodeCard.d.ts.map +1 -0
- package/dist/esm/components/code/useCodeCard.js +2 -0
- package/dist/esm/components/code/useCodeCard.js.map +7 -0
- package/package.json +1 -1
- package/src/components/code/CodeBlock.tsx +248 -341
- package/src/components/code/LanguageBadge.tsx +67 -20
- package/src/components/code/index.ts +6 -3
- package/src/components/code/types.ts +219 -27
- package/src/components/code/useCodeCard.ts +82 -0
- package/src/components/index.css +62 -1
- package/styles.css +57 -1
|
@@ -1,33 +1,80 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Code } from "@kushagradhawan/kookie-ui";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
const LANGUAGE_DISPLAY_MAP: Record<string, string> = {
|
|
5
|
+
js: "JS",
|
|
6
|
+
javascript: "JS",
|
|
7
|
+
jsx: "JSX",
|
|
8
|
+
ts: "TS",
|
|
9
|
+
typescript: "TS",
|
|
10
|
+
tsx: "TSX",
|
|
11
|
+
py: "Python",
|
|
12
|
+
python: "Python",
|
|
13
|
+
rb: "Ruby",
|
|
14
|
+
ruby: "Ruby",
|
|
15
|
+
sh: "Shell",
|
|
16
|
+
bash: "Shell",
|
|
17
|
+
shell: "Shell",
|
|
18
|
+
zsh: "Shell",
|
|
19
|
+
css: "CSS",
|
|
20
|
+
scss: "SCSS",
|
|
21
|
+
sass: "Sass",
|
|
22
|
+
less: "Less",
|
|
23
|
+
html: "HTML",
|
|
24
|
+
xml: "XML",
|
|
25
|
+
json: "JSON",
|
|
26
|
+
yaml: "YAML",
|
|
27
|
+
yml: "YAML",
|
|
28
|
+
md: "Markdown",
|
|
29
|
+
markdown: "Markdown",
|
|
30
|
+
mdx: "MDX",
|
|
31
|
+
sql: "SQL",
|
|
32
|
+
graphql: "GraphQL",
|
|
33
|
+
gql: "GraphQL",
|
|
34
|
+
go: "Go",
|
|
35
|
+
rust: "Rust",
|
|
36
|
+
rs: "Rust",
|
|
37
|
+
swift: "Swift",
|
|
38
|
+
kotlin: "Kotlin",
|
|
39
|
+
java: "Java",
|
|
40
|
+
cpp: "C++",
|
|
41
|
+
c: "C",
|
|
42
|
+
cs: "C#",
|
|
43
|
+
csharp: "C#",
|
|
44
|
+
php: "PHP",
|
|
45
|
+
vue: "Vue",
|
|
46
|
+
svelte: "Svelte",
|
|
47
|
+
astro: "Astro",
|
|
48
|
+
dockerfile: "Docker",
|
|
49
|
+
docker: "Docker",
|
|
50
|
+
text: "Text",
|
|
51
|
+
plaintext: "Text",
|
|
52
|
+
txt: "Text",
|
|
53
|
+
};
|
|
7
54
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
sh: "Shell",
|
|
17
|
-
bash: "Bash",
|
|
18
|
-
text: "plaintext",
|
|
19
|
-
};
|
|
55
|
+
/**
|
|
56
|
+
* Formats a language identifier for display in the badge.
|
|
57
|
+
* Converts technical identifiers to human-readable labels.
|
|
58
|
+
*/
|
|
59
|
+
export function formatLanguageForDisplay(lang: string): string {
|
|
60
|
+
const normalized = lang.toLowerCase().trim();
|
|
61
|
+
return LANGUAGE_DISPLAY_MAP[normalized] || lang.toUpperCase();
|
|
62
|
+
}
|
|
20
63
|
|
|
21
|
-
|
|
64
|
+
interface LanguageBadgeProps {
|
|
65
|
+
language: string;
|
|
22
66
|
}
|
|
23
67
|
|
|
68
|
+
/**
|
|
69
|
+
* Displays the programming language as a small badge.
|
|
70
|
+
* Uses Kookie UI's Code component with consistent styling.
|
|
71
|
+
*/
|
|
24
72
|
export function LanguageBadge({ language }: LanguageBadgeProps) {
|
|
25
|
-
const displayLanguage =
|
|
73
|
+
const displayLanguage = formatLanguageForDisplay(language);
|
|
26
74
|
|
|
27
75
|
return (
|
|
28
|
-
<Code size="1" color="gray"
|
|
29
|
-
{displayLanguage}
|
|
76
|
+
<Code size="1" color="gray" variant="ghost">
|
|
77
|
+
{displayLanguage.toLowerCase()}
|
|
30
78
|
</Code>
|
|
31
79
|
);
|
|
32
80
|
}
|
|
33
|
-
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
// Public API
|
|
2
|
-
export { CodeBlock } from "./CodeBlock";
|
|
3
|
-
export
|
|
1
|
+
// Public API
|
|
2
|
+
export { CodeBlock, useCodeBlockContext } from "./CodeBlock";
|
|
3
|
+
export { useCodeCard } from "./useCodeCard";
|
|
4
|
+
export { LanguageBadge, formatLanguageForDisplay } from "./LanguageBadge";
|
|
5
|
+
export type { CodeBlockProps, ShikiConfig, PreviewBackgroundProps } from "./types";
|
|
6
|
+
export { extractTextFromChildren, extractLanguageFromChildren, isReactElement } from "./types";
|
|
@@ -1,32 +1,224 @@
|
|
|
1
|
-
import type { ReactNode } from "react";
|
|
1
|
+
import type { ReactNode, ReactElement } from "react";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
// Preview styling (for kookie-ui docs)
|
|
18
|
-
background?: "none" | "dots" | string; // Background type
|
|
19
|
-
backgroundProps?: {
|
|
20
|
-
dotSize?: number;
|
|
21
|
-
color?: string;
|
|
22
|
-
backgroundColor?: string;
|
|
23
|
-
height?: string;
|
|
24
|
-
width?: string;
|
|
25
|
-
radius?: string;
|
|
3
|
+
/**
|
|
4
|
+
* Shiki syntax highlighting configuration.
|
|
5
|
+
* Controls themes and advanced highlighting options.
|
|
6
|
+
*/
|
|
7
|
+
export interface ShikiConfig {
|
|
8
|
+
/**
|
|
9
|
+
* Theme names for light and dark modes.
|
|
10
|
+
* Uses Shiki's dual-theme mode for automatic theme switching.
|
|
11
|
+
* @default { light: 'one-light', dark: 'one-dark-pro' }
|
|
12
|
+
*/
|
|
13
|
+
themes?: {
|
|
14
|
+
light?: string;
|
|
15
|
+
dark?: string;
|
|
26
16
|
};
|
|
27
17
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Language aliases for custom language mapping.
|
|
20
|
+
* @example { js: 'javascript', ts: 'typescript' }
|
|
21
|
+
*/
|
|
22
|
+
langAlias?: Record<string, string>;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Shiki transformers for custom code transformations.
|
|
26
|
+
* Useful for line highlighting, diff syntax, etc.
|
|
27
|
+
* @see https://shiki.style/guide/transformers
|
|
28
|
+
*/
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
30
|
+
transformers?: any[];
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Metadata string passed to transformers.
|
|
34
|
+
* Often used for line highlighting syntax like `{1,3-5}`.
|
|
35
|
+
*/
|
|
36
|
+
meta?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Background configuration for preview section.
|
|
41
|
+
*/
|
|
42
|
+
export interface PreviewBackgroundProps {
|
|
43
|
+
/** Dot size in pixels (for dots background) */
|
|
44
|
+
dotSize?: number;
|
|
45
|
+
/** Dot color (CSS value) */
|
|
46
|
+
color?: string;
|
|
47
|
+
/** Background color (CSS value) */
|
|
48
|
+
backgroundColor?: string;
|
|
49
|
+
/** Preview section height */
|
|
50
|
+
height?: string;
|
|
51
|
+
/** Preview section width */
|
|
52
|
+
width?: string;
|
|
53
|
+
/** Border radius token (e.g., "3" for var(--radius-3)) */
|
|
54
|
+
radius?: string;
|
|
31
55
|
}
|
|
32
56
|
|
|
57
|
+
/**
|
|
58
|
+
* CodeBlock Component Props
|
|
59
|
+
*
|
|
60
|
+
* A unified code block component supporting:
|
|
61
|
+
* - Runtime syntax highlighting via Shiki (`code` + `language`)
|
|
62
|
+
* - Build-time highlighted content from MDX/rehype-pretty-code (`children`)
|
|
63
|
+
* - Optional preview section for documentation
|
|
64
|
+
*/
|
|
65
|
+
export interface CodeBlockProps {
|
|
66
|
+
/**
|
|
67
|
+
* Raw code string for runtime Shiki highlighting.
|
|
68
|
+
* Mutually exclusive with `children`.
|
|
69
|
+
*/
|
|
70
|
+
code?: string;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Language identifier for syntax highlighting.
|
|
74
|
+
* Required when using `code` prop.
|
|
75
|
+
* @example "typescript", "tsx", "python", "bash"
|
|
76
|
+
*/
|
|
77
|
+
language?: string;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Pre-highlighted content from MDX/rehype-pretty-code.
|
|
81
|
+
* Mutually exclusive with `code` prop.
|
|
82
|
+
* Language is auto-detected from `data-language` or `className="language-xxx"`.
|
|
83
|
+
*/
|
|
84
|
+
children?: ReactNode;
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Shiki syntax highlighting configuration.
|
|
88
|
+
* Only applies when using `code` prop (runtime highlighting).
|
|
89
|
+
*/
|
|
90
|
+
shikiConfig?: ShikiConfig;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Show copy-to-clipboard button.
|
|
94
|
+
* @default true
|
|
95
|
+
*/
|
|
96
|
+
showCopy?: boolean;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Show language badge.
|
|
100
|
+
* @default true
|
|
101
|
+
*/
|
|
102
|
+
showLanguage?: boolean;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Show line numbers.
|
|
106
|
+
* @default true
|
|
107
|
+
*/
|
|
108
|
+
showLineNumbers?: boolean;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* File path displayed above the code block.
|
|
112
|
+
* @example "src/components/Button.tsx"
|
|
113
|
+
*/
|
|
114
|
+
file?: string;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Enable expand/collapse for long code blocks.
|
|
118
|
+
* @default true
|
|
119
|
+
*/
|
|
120
|
+
collapsible?: boolean;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Height threshold (in pixels) before code is collapsed.
|
|
124
|
+
* @default 360
|
|
125
|
+
*/
|
|
126
|
+
collapsedHeight?: number;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Preview component displayed above the code block.
|
|
130
|
+
* Useful for showcasing component examples in documentation.
|
|
131
|
+
*/
|
|
132
|
+
preview?: ReactNode;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Background style for preview section.
|
|
136
|
+
* - `"none"`: Plain card background
|
|
137
|
+
* - `"dots"`: Dotted pattern background
|
|
138
|
+
* - `string`: Image URL for custom background
|
|
139
|
+
* @default "none"
|
|
140
|
+
*/
|
|
141
|
+
background?: "none" | "dots" | string;
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Customization for preview background.
|
|
145
|
+
*/
|
|
146
|
+
backgroundProps?: PreviewBackgroundProps;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Type guard for React elements with props.
|
|
151
|
+
*/
|
|
152
|
+
export function isReactElement(node: unknown): node is ReactElement {
|
|
153
|
+
return typeof node === "object" && node !== null && "props" in node && typeof (node as ReactElement).props === "object";
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Extracts plain text content from React children.
|
|
158
|
+
* Used to get copyable code from pre-highlighted MDX content.
|
|
159
|
+
*/
|
|
160
|
+
export function extractTextFromChildren(children: ReactNode): string {
|
|
161
|
+
if (typeof children === "string") return children;
|
|
162
|
+
if (typeof children === "number") return String(children);
|
|
163
|
+
if (!children) return "";
|
|
164
|
+
|
|
165
|
+
if (Array.isArray(children)) {
|
|
166
|
+
return children.map(extractTextFromChildren).join("");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (isReactElement(children)) {
|
|
170
|
+
const props = children.props as { children?: ReactNode };
|
|
171
|
+
if (props.children) {
|
|
172
|
+
return extractTextFromChildren(props.children);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return "";
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Extracts language identifier from pre-highlighted MDX children.
|
|
181
|
+
* Checks `data-language` attribute and `className="language-xxx"` pattern.
|
|
182
|
+
*/
|
|
183
|
+
export function extractLanguageFromChildren(children: ReactNode): string {
|
|
184
|
+
const findLanguage = (node: ReactNode): string | null => {
|
|
185
|
+
if (!node) return null;
|
|
186
|
+
|
|
187
|
+
if (isReactElement(node)) {
|
|
188
|
+
const props = node.props as {
|
|
189
|
+
"data-language"?: string;
|
|
190
|
+
className?: string;
|
|
191
|
+
class?: string;
|
|
192
|
+
children?: ReactNode;
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// Check data-language attribute (rehype-pretty-code)
|
|
196
|
+
if (props["data-language"]) {
|
|
197
|
+
return props["data-language"];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Check className for language-xxx pattern
|
|
201
|
+
const className = props.className || props.class || "";
|
|
202
|
+
if (typeof className === "string") {
|
|
203
|
+
const match = className.match(/language-([\w-]+)/i);
|
|
204
|
+
if (match) return match[1];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Recursively check children
|
|
208
|
+
if (props.children) {
|
|
209
|
+
if (Array.isArray(props.children)) {
|
|
210
|
+
for (const child of props.children) {
|
|
211
|
+
const lang = findLanguage(child);
|
|
212
|
+
if (lang) return lang;
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
return findLanguage(props.children);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return null;
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
return findLanguage(children) || "text";
|
|
224
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { useState, useRef, useEffect, useCallback } from "react";
|
|
2
|
+
|
|
3
|
+
const COPY_FEEDBACK_DURATION_MS = 2000;
|
|
4
|
+
|
|
5
|
+
interface UseCodeCardOptions {
|
|
6
|
+
code: string;
|
|
7
|
+
collapsedHeight: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface UseCodeCardReturn {
|
|
11
|
+
isExpanded: boolean;
|
|
12
|
+
shouldShowToggle: boolean;
|
|
13
|
+
copied: boolean;
|
|
14
|
+
contentRef: React.RefObject<HTMLDivElement | null>;
|
|
15
|
+
contentMaxHeight: number;
|
|
16
|
+
handleToggle: () => void;
|
|
17
|
+
handleCopy: () => Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Shared hook for code card UI behavior.
|
|
22
|
+
* Handles expand/collapse state and copy functionality.
|
|
23
|
+
*/
|
|
24
|
+
export function useCodeCard({ code, collapsedHeight }: UseCodeCardOptions): UseCodeCardReturn {
|
|
25
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
26
|
+
const [contentHeight, setContentHeight] = useState(collapsedHeight);
|
|
27
|
+
const [copied, setCopied] = useState(false);
|
|
28
|
+
const contentRef = useRef<HTMLDivElement | null>(null);
|
|
29
|
+
const resetTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
30
|
+
|
|
31
|
+
const shouldShowToggle = contentHeight > collapsedHeight;
|
|
32
|
+
const contentMaxHeight = isExpanded ? contentHeight : collapsedHeight;
|
|
33
|
+
|
|
34
|
+
// Measure content height on mount and when content changes
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (contentRef.current) {
|
|
37
|
+
setContentHeight(contentRef.current.scrollHeight);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Cleanup timeout on unmount
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
return () => {
|
|
44
|
+
if (resetTimeoutRef.current) {
|
|
45
|
+
clearTimeout(resetTimeoutRef.current);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}, []);
|
|
49
|
+
|
|
50
|
+
const handleToggle = useCallback(() => {
|
|
51
|
+
setIsExpanded((prev) => !prev);
|
|
52
|
+
}, []);
|
|
53
|
+
|
|
54
|
+
const handleCopy = useCallback(async () => {
|
|
55
|
+
if (!code.trim()) return;
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
await navigator.clipboard.writeText(code);
|
|
59
|
+
setCopied(true);
|
|
60
|
+
|
|
61
|
+
if (resetTimeoutRef.current) {
|
|
62
|
+
clearTimeout(resetTimeoutRef.current);
|
|
63
|
+
}
|
|
64
|
+
resetTimeoutRef.current = setTimeout(() => setCopied(false), COPY_FEEDBACK_DURATION_MS);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
// Log error in development, fail gracefully in production
|
|
67
|
+
if (process.env.NODE_ENV === "development") {
|
|
68
|
+
console.error("[CodeBlock] Failed to copy to clipboard:", error);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}, [code]);
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
isExpanded,
|
|
75
|
+
shouldShowToggle,
|
|
76
|
+
copied,
|
|
77
|
+
contentRef,
|
|
78
|
+
contentMaxHeight,
|
|
79
|
+
handleToggle,
|
|
80
|
+
handleCopy,
|
|
81
|
+
};
|
|
82
|
+
}
|
package/src/components/index.css
CHANGED
|
@@ -21,6 +21,32 @@
|
|
|
21
21
|
margin: 0;
|
|
22
22
|
width: 100%;
|
|
23
23
|
box-sizing: border-box;
|
|
24
|
+
font-family: var(--font-mono);
|
|
25
|
+
font-size: var(--font-size-2);
|
|
26
|
+
line-height: 1.75;
|
|
27
|
+
padding: var(--space-3);
|
|
28
|
+
overflow-x: auto;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/* Rehype-pretty-code theme support */
|
|
32
|
+
pre[data-theme] code {
|
|
33
|
+
background: none !important;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
pre[data-theme] code span {
|
|
37
|
+
color: var(--shiki-light);
|
|
38
|
+
font-style: var(--shiki-light-font-style);
|
|
39
|
+
font-weight: var(--shiki-light-font-weight);
|
|
40
|
+
text-decoration: var(--shiki-light-text-decoration);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
.dark pre[data-theme] code span,
|
|
44
|
+
.dark-theme pre[data-theme] code span,
|
|
45
|
+
[data-appearance="dark"] pre[data-theme] code span {
|
|
46
|
+
color: var(--shiki-dark) !important;
|
|
47
|
+
font-style: var(--shiki-dark-font-style) !important;
|
|
48
|
+
font-weight: var(--shiki-dark-font-weight) !important;
|
|
49
|
+
text-decoration: var(--shiki-dark-text-decoration) !important;
|
|
24
50
|
}
|
|
25
51
|
|
|
26
52
|
/* Code elements inside pre */
|
|
@@ -42,7 +68,7 @@
|
|
|
42
68
|
gap: 0;
|
|
43
69
|
}
|
|
44
70
|
|
|
45
|
-
/* Line numbers */
|
|
71
|
+
/* Line numbers (shown by default) */
|
|
46
72
|
.code-content pre code .line::before {
|
|
47
73
|
counter-increment: line;
|
|
48
74
|
content: counter(line);
|
|
@@ -56,6 +82,41 @@
|
|
|
56
82
|
margin-right: var(--space-4);
|
|
57
83
|
}
|
|
58
84
|
|
|
85
|
+
/* Hide line numbers when disabled */
|
|
86
|
+
.code-content.hide-line-numbers pre code .line::before {
|
|
87
|
+
display: none;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/* Loading skeleton */
|
|
91
|
+
.code-skeleton {
|
|
92
|
+
display: flex;
|
|
93
|
+
flex-direction: column;
|
|
94
|
+
gap: var(--space-2);
|
|
95
|
+
padding: var(--space-3);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.code-skeleton-line {
|
|
99
|
+
height: 1em;
|
|
100
|
+
background: linear-gradient(
|
|
101
|
+
90deg,
|
|
102
|
+
var(--gray-a3) 0%,
|
|
103
|
+
var(--gray-a4) 50%,
|
|
104
|
+
var(--gray-a3) 100%
|
|
105
|
+
);
|
|
106
|
+
background-size: 200% 100%;
|
|
107
|
+
animation: code-skeleton-shimmer 1.5s ease-in-out infinite;
|
|
108
|
+
border-radius: var(--radius-1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@keyframes code-skeleton-shimmer {
|
|
112
|
+
0% {
|
|
113
|
+
background-position: 200% 0;
|
|
114
|
+
}
|
|
115
|
+
100% {
|
|
116
|
+
background-position: -200% 0;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
59
120
|
/* Scroll shadow overlay for collapsed state */
|
|
60
121
|
.code-scroll-shadow {
|
|
61
122
|
position: absolute;
|
package/styles.css
CHANGED
|
@@ -18,6 +18,31 @@
|
|
|
18
18
|
margin: 0;
|
|
19
19
|
width: 100%;
|
|
20
20
|
box-sizing: border-box;
|
|
21
|
+
font-family: var(--font-mono);
|
|
22
|
+
font-size: var(--font-size-2);
|
|
23
|
+
line-height: 1.75;
|
|
24
|
+
padding: var(--space-3);
|
|
25
|
+
overflow-x: auto;
|
|
26
|
+
}
|
|
27
|
+
/* Rehype-pretty-code theme support */
|
|
28
|
+
pre[data-theme] code {
|
|
29
|
+
background: none !important;
|
|
30
|
+
}
|
|
31
|
+
pre[data-theme] code span {
|
|
32
|
+
color: var(--shiki-light);
|
|
33
|
+
font-style: var(--shiki-light-font-style);
|
|
34
|
+
font-weight: var(--shiki-light-font-weight);
|
|
35
|
+
-webkit-text-decoration: var(--shiki-light-text-decoration);
|
|
36
|
+
text-decoration: var(--shiki-light-text-decoration);
|
|
37
|
+
}
|
|
38
|
+
.dark pre[data-theme] code span,
|
|
39
|
+
.dark-theme pre[data-theme] code span,
|
|
40
|
+
[data-appearance="dark"] pre[data-theme] code span {
|
|
41
|
+
color: var(--shiki-dark) !important;
|
|
42
|
+
font-style: var(--shiki-dark-font-style) !important;
|
|
43
|
+
font-weight: var(--shiki-dark-font-weight) !important;
|
|
44
|
+
-webkit-text-decoration: var(--shiki-dark-text-decoration) !important;
|
|
45
|
+
text-decoration: var(--shiki-dark-text-decoration) !important;
|
|
21
46
|
}
|
|
22
47
|
/* Code elements inside pre */
|
|
23
48
|
.code-content pre code {
|
|
@@ -36,7 +61,7 @@
|
|
|
36
61
|
align-items: center;
|
|
37
62
|
gap: 0;
|
|
38
63
|
}
|
|
39
|
-
/* Line numbers */
|
|
64
|
+
/* Line numbers (shown by default) */
|
|
40
65
|
.code-content pre code .line::before {
|
|
41
66
|
counter-increment: line;
|
|
42
67
|
content: counter(line);
|
|
@@ -51,6 +76,37 @@
|
|
|
51
76
|
flex-shrink: 0;
|
|
52
77
|
margin-right: var(--space-4);
|
|
53
78
|
}
|
|
79
|
+
/* Hide line numbers when disabled */
|
|
80
|
+
.code-content.hide-line-numbers pre code .line::before {
|
|
81
|
+
display: none;
|
|
82
|
+
}
|
|
83
|
+
/* Loading skeleton */
|
|
84
|
+
.code-skeleton {
|
|
85
|
+
display: flex;
|
|
86
|
+
flex-direction: column;
|
|
87
|
+
gap: var(--space-2);
|
|
88
|
+
padding: var(--space-3);
|
|
89
|
+
}
|
|
90
|
+
.code-skeleton-line {
|
|
91
|
+
height: 1em;
|
|
92
|
+
background: linear-gradient(
|
|
93
|
+
90deg,
|
|
94
|
+
var(--gray-a3) 0%,
|
|
95
|
+
var(--gray-a4) 50%,
|
|
96
|
+
var(--gray-a3) 100%
|
|
97
|
+
);
|
|
98
|
+
background-size: 200% 100%;
|
|
99
|
+
animation: code-skeleton-shimmer 1.5s ease-in-out infinite;
|
|
100
|
+
border-radius: var(--radius-1);
|
|
101
|
+
}
|
|
102
|
+
@keyframes code-skeleton-shimmer {
|
|
103
|
+
0% {
|
|
104
|
+
background-position: 200% 0;
|
|
105
|
+
}
|
|
106
|
+
100% {
|
|
107
|
+
background-position: -200% 0;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
54
110
|
/* Scroll shadow overlay for collapsed state */
|
|
55
111
|
.code-scroll-shadow {
|
|
56
112
|
position: absolute;
|