colorfulcode 0.0.1 → 0.1.0

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.
@@ -0,0 +1,25 @@
1
+ .cc{background:#002b36;color:#839496}
2
+ .cc .cc-comment{color:#586e75;font-style:italic}
3
+ .cc .cc-keyword{color:#859900}
4
+ .cc .cc-string{color:#2aa198}
5
+ .cc .cc-escape{color:#dc322f}
6
+ .cc .cc-number{color:#d33682}
7
+ .cc .cc-constant{color:#cb4b16}
8
+ .cc .cc-function{color:#268bd2}
9
+ .cc .cc-type{color:#b58900}
10
+ .cc .cc-variable{color:#268bd2}
11
+ .cc .cc-property{color:#268bd2}
12
+ .cc .cc-builtin{color:#6c71c4}
13
+ .cc .cc-operator{color:#859900}
14
+ .cc .cc-punctuation{color:#839496}
15
+ .cc .cc-tag{color:#268bd2}
16
+ .cc .cc-attribute{color:#93a1a1}
17
+ .cc .cc-meta{color:#cb4b16}
18
+ .cc .cc-regex{color:#dc322f}
19
+ .cc .cc-heading{color:#cb4b16;font-weight:bold}
20
+ .cc .cc-emphasis{color:#839496;font-style:italic}
21
+ .cc .cc-strong{color:#839496;font-weight:bold}
22
+ .cc .cc-link{color:#6c71c4;text-decoration:underline}
23
+ .cc .cc-inserted{color:#859900}
24
+ .cc .cc-deleted{color:#dc322f}
25
+ .cc .cc-parameter{color:#268bd2}
@@ -0,0 +1,25 @@
1
+ .cc{background:#fdf6e3;color:#657b83}
2
+ .cc .cc-comment{color:#93a1a1;font-style:italic}
3
+ .cc .cc-keyword{color:#859900}
4
+ .cc .cc-string{color:#2aa198}
5
+ .cc .cc-escape{color:#dc322f}
6
+ .cc .cc-number{color:#d33682}
7
+ .cc .cc-constant{color:#cb4b16}
8
+ .cc .cc-function{color:#268bd2}
9
+ .cc .cc-type{color:#b58900}
10
+ .cc .cc-variable{color:#268bd2}
11
+ .cc .cc-property{color:#268bd2}
12
+ .cc .cc-builtin{color:#6c71c4}
13
+ .cc .cc-operator{color:#859900}
14
+ .cc .cc-punctuation{color:#657b83}
15
+ .cc .cc-tag{color:#268bd2}
16
+ .cc .cc-attribute{color:#93a1a1}
17
+ .cc .cc-meta{color:#cb4b16}
18
+ .cc .cc-regex{color:#dc322f}
19
+ .cc .cc-heading{color:#cb4b16;font-weight:bold}
20
+ .cc .cc-emphasis{color:#657b83;font-style:italic}
21
+ .cc .cc-strong{color:#657b83;font-weight:bold}
22
+ .cc .cc-link{color:#6c71c4;text-decoration:underline}
23
+ .cc .cc-inserted{color:#859900}
24
+ .cc .cc-deleted{color:#dc322f}
25
+ .cc .cc-parameter{color:#268bd2}
@@ -0,0 +1,25 @@
1
+ .cc{background:#1a1b26;color:#c0caf5}
2
+ .cc .cc-comment{color:#565f89;font-style:italic}
3
+ .cc .cc-keyword{color:#bb9af7}
4
+ .cc .cc-string{color:#9ece6a}
5
+ .cc .cc-escape{color:#89ddff}
6
+ .cc .cc-number{color:#ff9e64}
7
+ .cc .cc-constant{color:#ff9e64}
8
+ .cc .cc-function{color:#7aa2f7}
9
+ .cc .cc-type{color:#2ac3de}
10
+ .cc .cc-variable{color:#e0af68}
11
+ .cc .cc-property{color:#73daca}
12
+ .cc .cc-builtin{color:#7dcfff}
13
+ .cc .cc-operator{color:#89ddff}
14
+ .cc .cc-punctuation{color:#9abdf5}
15
+ .cc .cc-tag{color:#f7768e}
16
+ .cc .cc-attribute{color:#bb9af7}
17
+ .cc .cc-meta{color:#e0af68}
18
+ .cc .cc-regex{color:#b4f9f8}
19
+ .cc .cc-heading{color:#89ddff;font-weight:bold}
20
+ .cc .cc-emphasis{color:#c0caf5;font-style:italic}
21
+ .cc .cc-strong{color:#c0caf5;font-weight:bold}
22
+ .cc .cc-link{color:#7aa2f7;text-decoration:underline}
23
+ .cc .cc-inserted{color:#9ece6a}
24
+ .cc .cc-deleted{color:#f7768e}
25
+ .cc .cc-parameter{color:#e0af68;font-style:italic}
@@ -0,0 +1,108 @@
1
+ /** The category of a highlighted token. */
2
+ export type Kind =
3
+ | "text" | "comment" | "keyword" | "string" | "escape" | "number" | "constant" | "function" | "type"
4
+ | "variable" | "property" | "builtin" | "operator" | "punctuation" | "tag" | "attribute" | "meta"
5
+ | "regex" | "heading" | "emphasis" | "strong" | "link" | "inserted" | "deleted"
6
+ | "parameter";
7
+
8
+ export type ThemeName =
9
+ | "colorful" | "colorful-light" | "github-dark" | "github-light" | "dracula" | "nord" | "one-dark"
10
+ | "monokai" | "catppuccin-mocha" | "catppuccin-latte" | "tokyo-night" | "gruvbox-dark"
11
+ | "solarized-dark" | "solarized-light";
12
+
13
+ export interface Token {
14
+ /** Start offset in the string (UTF-16 code units, like `String.prototype.slice`). */
15
+ start: number;
16
+ end: number;
17
+ kind: Kind;
18
+ text: string;
19
+ }
20
+
21
+ export interface LineToken {
22
+ text: string;
23
+ kind: Kind;
24
+ }
25
+
26
+ export interface LanguageInfo {
27
+ id: string;
28
+ name: string;
29
+ aliases: string[];
30
+ extensions: string[];
31
+ filenames: string[];
32
+ }
33
+
34
+ export interface Style {
35
+ color: string;
36
+ bold: boolean;
37
+ italic: boolean;
38
+ underline: boolean;
39
+ }
40
+
41
+ export interface ThemeInfo {
42
+ name: string;
43
+ dark: boolean;
44
+ background: string;
45
+ foreground: string;
46
+ styles: Record<Kind, Style>;
47
+ }
48
+
49
+ /** A custom theme. Kinds you leave out inherit a sensible style. */
50
+ export interface ThemeDefinition {
51
+ name: string;
52
+ background: string;
53
+ foreground: string;
54
+ /** Defaults to a guess from the background brightness. */
55
+ dark?: boolean;
56
+ /** `"#rrggbb"` or a full style per kind. */
57
+ styles?: Partial<Record<Kind, string | { color: string; bold?: boolean; italic?: boolean; underline?: boolean }>>;
58
+ }
59
+
60
+ export interface HighlightOptions {
61
+ /**
62
+ * Theme for inline styles: a built in name, a name registered with
63
+ * `defineTheme`, or a theme object. `false` gives class names. Defaults to "colorful".
64
+ */
65
+ theme?: ThemeName | (string & {}) | ThemeDefinition | false;
66
+ /** Wrap the output in `<pre class="cc"><code>`. Defaults to true. */
67
+ pre?: boolean;
68
+ /** Wrap every line in `<span class="cc-line">`. */
69
+ lines?: boolean;
70
+ /** Prefix every line with its number. */
71
+ lineNumbers?: boolean;
72
+ /** One based line numbers to highlight. */
73
+ highlightLines?: number[];
74
+ }
75
+
76
+ /**
77
+ * A language id, alias, name or extension, or `"auto"` to guess the language
78
+ * from the code (plain text when unsure). Unknown names mean plain text.
79
+ */
80
+ export type LanguageName = "auto" | (string & {});
81
+
82
+ /** Highlights code and returns HTML. Unknown languages render as plain text. */
83
+ export function highlight(code: string, language: LanguageName, options?: HighlightOptions): string;
84
+ /** Tokens covering the whole input. */
85
+ export function tokenize(code: string, language: LanguageName): Token[];
86
+ /** Tokens split into lines, without line breaks. */
87
+ export function tokenizeLines(code: string, language: LanguageName): LineToken[][];
88
+ export function listLanguages(): LanguageInfo[];
89
+ export function listThemes(): ThemeInfo[];
90
+ export function getLanguage(name: string): LanguageInfo | undefined;
91
+ export function getTheme(name: string | ThemeDefinition): ThemeInfo | undefined;
92
+ /** Registers a custom theme and returns its name. */
93
+ export function defineTheme(theme: ThemeDefinition): string;
94
+ /**
95
+ * Guesses a language id from a file path, then from a shebang or doctype in
96
+ * the code. Undefined when unsure.
97
+ */
98
+ export function detectLanguage(input: {
99
+ path?: string;
100
+ code?: string;
101
+ /** Also guess from the code itself, scoring it against every language. */
102
+ guess?: boolean;
103
+ }): string | undefined;
104
+ /** CSS for the class based output, scoped to `selector` (default ".cc"). */
105
+ export function themeCss(theme?: ThemeName | (string & {}) | ThemeDefinition, selector?: string): string;
106
+ /** Highlights every `code.language-xyz` element inside `root`. */
107
+ export function highlightAll(options?: HighlightOptions, root?: ParentNode): void;
108
+ export function highlightElement(element: Element, options?: HighlightOptions & { language?: LanguageName }): void;
package/dist/index.js ADDED
@@ -0,0 +1,262 @@
1
+ import wasmBase64 from "./wasm.js";
2
+
3
+ const encoder = new TextEncoder();
4
+ const decoder = new TextDecoder();
5
+
6
+ let wasm;
7
+ let metadata;
8
+ const languageCache = new Map();
9
+
10
+ function decodeBase64(text) {
11
+ if (typeof Buffer === "function") return Buffer.from(text, "base64");
12
+ const binary = atob(text);
13
+ const bytes = new Uint8Array(binary.length);
14
+ for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
15
+ return bytes;
16
+ }
17
+
18
+ /** Instantiates the module on first use. Synchronous, so it works during SSR too. */
19
+ function instance() {
20
+ if (!wasm) {
21
+ const module = new WebAssembly.Module(decodeBase64(wasmBase64));
22
+ wasm = new WebAssembly.Instance(module, {}).exports;
23
+ }
24
+ return wasm;
25
+ }
26
+
27
+ /** Writes a string into the input buffer and returns its length in bytes. */
28
+ function writeInput(text) {
29
+ const w = instance();
30
+ const capacity = text.length * 3;
31
+ const ptr = w.cc_input(capacity);
32
+ return encoder.encodeInto(text, new Uint8Array(w.memory.buffer, ptr, capacity)).written;
33
+ }
34
+
35
+ // Node's Buffer decodes UTF-8 several times faster than its TextDecoder.
36
+ const NodeBuffer = typeof Buffer === "function" && typeof Buffer.from === "function" ? Buffer : undefined;
37
+
38
+ function readOutput(length) {
39
+ const w = instance();
40
+ const ptr = w.cc_output_ptr();
41
+ if (NodeBuffer) return NodeBuffer.from(w.memory.buffer, ptr, length).toString("utf8");
42
+ return decoder.decode(new Uint8Array(w.memory.buffer, ptr, length));
43
+ }
44
+
45
+ function meta() {
46
+ if (!metadata) {
47
+ metadata = JSON.parse(readOutput(instance().cc_metadata()));
48
+ metadata.themeIndex = new Map(metadata.themes.map((t, i) => [t.name, i]));
49
+ }
50
+ return metadata;
51
+ }
52
+
53
+ /** Detection only reads the first few KB, so there is no point copying more. */
54
+ const DETECT_CHARS = 4096;
55
+
56
+ /** Guesses a language index from the code alone, or -1. */
57
+ function guessLanguage(code) {
58
+ return instance().cc_language_detect(writeInput(code.slice(0, DETECT_CHARS)));
59
+ }
60
+
61
+ /** The index of a language name, or of the guess for `code` when the name is "auto". */
62
+ function languageFor(name, code) {
63
+ return name === "auto" ? guessLanguage(code) : resolveLanguage(name);
64
+ }
65
+
66
+ function resolveLanguage(name) {
67
+ if (typeof name !== "string" || name === "") return -1;
68
+ let index = languageCache.get(name);
69
+ if (index === undefined) {
70
+ index = instance().cc_language_find(writeInput(name));
71
+ languageCache.set(name, index);
72
+ }
73
+ return index;
74
+ }
75
+
76
+ const definedThemes = new WeakMap();
77
+
78
+ /** The name of a theme given by name or as an object (registered on first use). */
79
+ function themeName(theme) {
80
+ if (typeof theme !== "object") return theme;
81
+ let name = definedThemes.get(theme);
82
+ if (name === undefined) {
83
+ name = defineTheme(theme);
84
+ definedThemes.set(theme, name);
85
+ }
86
+ return name;
87
+ }
88
+
89
+ function resolveTheme(theme) {
90
+ if (theme === false || theme === null) return -1;
91
+ theme = themeName(theme);
92
+ const index = meta().themeIndex.get(theme ?? "colorful");
93
+ if (index === undefined) throw new Error(`colorfulcode: unknown theme "${theme}"`);
94
+ return index;
95
+ }
96
+
97
+ const WRAP = 1;
98
+ const LINES = 2;
99
+ const LINE_NUMBERS = 4;
100
+
101
+ /**
102
+ * Highlights code and returns HTML.
103
+ *
104
+ * With a theme (the default is "colorful") the HTML uses inline styles and
105
+ * needs no CSS. Pass `theme: false` to get class names instead and style them
106
+ * with `themeCss()` or `colorfulcode/css/<name>.css`.
107
+ *
108
+ * Pass `"auto"` as the language to guess it from the code first.
109
+ */
110
+ export function highlight(code, language, options = {}) {
111
+ const w = instance();
112
+ const { theme, pre = true, lines = false, lineNumbers = false, highlightLines } = options;
113
+ const themeIndex = resolveTheme(theme);
114
+ const languageIndex = languageFor(language, code);
115
+ let lineCount = 0;
116
+ if (highlightLines && highlightLines.length) {
117
+ lineCount = highlightLines.length;
118
+ new Uint32Array(w.memory.buffer, w.cc_lines(lineCount), lineCount).set(highlightLines);
119
+ }
120
+ const flags = (pre ? WRAP : 0) | (lines ? LINES : 0) | (lineNumbers ? LINE_NUMBERS : 0);
121
+ const length = writeInput(code);
122
+ return readOutput(w.cc_highlight(length, languageIndex, themeIndex, flags, lineCount));
123
+ }
124
+
125
+ /**
126
+ * Tokenizes code. Every token has `start` and `end` offsets into the string
127
+ * (UTF-16, like `String.prototype.slice`), its `kind` and its `text`. The
128
+ * tokens cover the whole input. The language can be `"auto"`, as in `highlight()`.
129
+ */
130
+ export function tokenize(code, language) {
131
+ const w = instance();
132
+ // Resolve names first: lookups reuse the input and output buffers.
133
+ const kinds = meta().kinds;
134
+ const languageIndex = languageFor(language, code);
135
+ const count = w.cc_tokenize(writeInput(code), languageIndex);
136
+ const raw = new Uint32Array(w.memory.buffer, w.cc_output_ptr(), count * 3);
137
+ const tokens = new Array(count);
138
+ for (let i = 0, j = 0; i < count; i++, j += 3) {
139
+ const start = raw[j];
140
+ const end = raw[j + 1];
141
+ tokens[i] = { start, end, kind: kinds[raw[j + 2]], text: code.slice(start, end) };
142
+ }
143
+ return tokens;
144
+ }
145
+
146
+ /** Like `tokenize`, but split into lines of `{ text, kind }` without line breaks. */
147
+ export function tokenizeLines(code, language) {
148
+ const lines = [[]];
149
+ for (const { text, kind } of tokenize(code, language)) {
150
+ const parts = text.split("\n");
151
+ for (let i = 0; i < parts.length; i++) {
152
+ if (i > 0) lines.push([]);
153
+ const part = parts[i].endsWith("\r") ? parts[i].slice(0, -1) : parts[i];
154
+ if (part) lines[lines.length - 1].push({ text: part, kind });
155
+ }
156
+ }
157
+ return lines;
158
+ }
159
+
160
+ /** Every supported language. */
161
+ export function listLanguages() {
162
+ return meta().languages;
163
+ }
164
+
165
+ /** Every built in theme with its colors. */
166
+ export function listThemes() {
167
+ return meta().themes;
168
+ }
169
+
170
+ /** Finds a language by id, alias, name or extension. */
171
+ export function getLanguage(name) {
172
+ const index = resolveLanguage(name);
173
+ return index < 0 ? undefined : meta().languages[index];
174
+ }
175
+
176
+ /**
177
+ * Registers a custom theme and returns its name, which then works anywhere a
178
+ * built in theme name does. Kinds you leave out inherit a sensible style, so
179
+ * a handful of colors is enough:
180
+ *
181
+ * defineTheme({
182
+ * name: "discord",
183
+ * background: "#2b2d31",
184
+ * foreground: "#dbdee1",
185
+ * styles: { keyword: "#ff7ab2", comment: { color: "#80848e", italic: true } },
186
+ * });
187
+ */
188
+ export function defineTheme(theme) {
189
+ const { name, dark, background, foreground, styles = {} } = theme;
190
+ if (!name || !background || !foreground) {
191
+ throw new Error("colorfulcode: a theme needs a name, a background and a foreground");
192
+ }
193
+ const lines = [name, (dark ?? isDark(background)) ? "dark" : "light", `${background} ${foreground}`];
194
+ for (const [kind, value] of Object.entries(styles)) {
195
+ const style = typeof value === "string" ? { color: value } : value;
196
+ const flags = (style.bold ? "b" : "") + (style.italic ? "i" : "") + (style.underline ? "u" : "");
197
+ lines.push(`${kind} ${style.color} ${flags}`);
198
+ }
199
+ if (instance().cc_theme_define(writeInput(lines.join("\n"))) < 0) {
200
+ throw new Error(`colorfulcode: invalid theme "${name}" (colors must be #rrggbb)`);
201
+ }
202
+ metadata = undefined; // Reload so listThemes() and getTheme() include it.
203
+ return name;
204
+ }
205
+
206
+ function isDark(hex) {
207
+ const n = parseInt(hex.replace("#", ""), 16);
208
+ const luma = 0.299 * (n >> 16) + 0.587 * ((n >> 8) & 255) + 0.114 * (n & 255);
209
+ return luma < 128;
210
+ }
211
+
212
+ /** Finds a theme by name, or returns the colors of a theme object. */
213
+ export function getTheme(name) {
214
+ if (name && typeof name === "object") name = themeName(name);
215
+ const index = meta().themeIndex.get(name);
216
+ return index === undefined ? undefined : meta().themes[index];
217
+ }
218
+
219
+ /**
220
+ * Guesses a language id from a file path, then from a shebang or doctype in
221
+ * the code. With `guess: true` the code itself is scored against every
222
+ * language too. Returns undefined when unsure.
223
+ */
224
+ export function detectLanguage({ path, code, guess = false } = {}) {
225
+ const w = instance();
226
+ let index = -1;
227
+ if (path) index = w.cc_language_from_path(writeInput(path));
228
+ if (index < 0 && code) {
229
+ index = guess ? guessLanguage(code) : w.cc_language_from_content(writeInput(code.slice(0, 256)));
230
+ }
231
+ return index < 0 ? undefined : meta().languages[index].id;
232
+ }
233
+
234
+ /** CSS for the class based output (`theme: false`), scoped to `selector`. */
235
+ export function themeCss(theme = "colorful", selector = ".cc") {
236
+ const themeIndex = resolveTheme(theme);
237
+ return readOutput(instance().cc_theme_css(themeIndex, writeInput(selector)));
238
+ }
239
+
240
+ /**
241
+ * Highlights every `<code class="language-xyz">` inside `root` in place.
242
+ * Handy for static pages and Markdown output.
243
+ */
244
+ export function highlightAll(options = {}, root = globalThis.document) {
245
+ for (const el of root.querySelectorAll('code[class*="language-"], code[class*="lang-"]')) {
246
+ highlightElement(el, options);
247
+ }
248
+ }
249
+
250
+ /** Highlights a single `<code>` element in place using its `language-xyz` class. */
251
+ export function highlightElement(el, options = {}) {
252
+ const match = /(?:^|\s)lang(?:uage)?-([\w+#.-]+)/.exec(el.className);
253
+ const language = options.language ?? (match ? match[1] : "text");
254
+ el.innerHTML = highlight(el.textContent ?? "", language, { ...options, pre: false });
255
+ const theme = options.theme === false ? undefined : getTheme(options.theme ?? "colorful");
256
+ const target = el.parentElement && el.parentElement.tagName === "PRE" ? el.parentElement : el;
257
+ target.classList.add("cc");
258
+ if (theme) {
259
+ target.style.background = theme.background;
260
+ target.style.color = theme.foreground;
261
+ }
262
+ }
@@ -0,0 +1,32 @@
1
+ import type { HTMLAttributes, ReactElement } from "react";
2
+ import type { HighlightOptions, LanguageName, LineToken } from "./index.js";
3
+
4
+ export * from "./index.js";
5
+
6
+ export interface CodeProps extends Omit<HTMLAttributes<HTMLElement>, "children"> {
7
+ /** The source code. A string child works too. */
8
+ code?: string;
9
+ children?: string;
10
+ /** Language id, alias or extension, or "auto" to guess it. Defaults to plain text. */
11
+ language?: LanguageName;
12
+ /** Theme for inline styles, or `false` for class names. Defaults to "colorful". */
13
+ theme?: HighlightOptions["theme"];
14
+ lineNumbers?: boolean;
15
+ /** One based line numbers to highlight. */
16
+ highlightLines?: number[];
17
+ /** Render a `<code>` element for inline snippets instead of a `<pre>` block. */
18
+ inline?: boolean;
19
+ }
20
+
21
+ export function Code(props: CodeProps): ReactElement;
22
+ export default Code;
23
+
24
+ /** Memoized `highlight()` without the `<pre>` wrapper. */
25
+ export function useHighlight(
26
+ code: string,
27
+ language: LanguageName,
28
+ options?: Omit<HighlightOptions, "pre">,
29
+ ): string;
30
+
31
+ /** Memoized `tokenizeLines()`, for custom rendering. */
32
+ export function useTokens(code: string, language: LanguageName): LineToken[][];
package/dist/react.js ADDED
@@ -0,0 +1,76 @@
1
+ import { createElement, useMemo } from "react";
2
+ import { detectLanguage, getLanguage, getTheme, highlight, tokenizeLines } from "./index.js";
3
+
4
+ export * from "./index.js";
5
+
6
+ /** Memoized `highlight()`: returns the HTML for `code`. */
7
+ export function useHighlight(code, language, options = {}) {
8
+ const { theme, lines, lineNumbers, highlightLines } = options;
9
+ const marked = highlightLines ? highlightLines.join(",") : "";
10
+ return useMemo(
11
+ () => highlight(code, language, { theme, lines, lineNumbers, highlightLines, pre: false }),
12
+ // eslint-disable-next-line react-hooks/exhaustive-deps
13
+ [code, language, theme, lines, lineNumbers, marked],
14
+ );
15
+ }
16
+
17
+ /** Memoized `tokenizeLines()`, for rendering tokens with your own components. */
18
+ export function useTokens(code, language) {
19
+ return useMemo(() => tokenizeLines(code, language), [code, language]);
20
+ }
21
+
22
+ /**
23
+ * Renders highlighted code.
24
+ *
25
+ * `<Code code={source} language="tsx" theme="dracula" lineNumbers />`
26
+ *
27
+ * The code can also be passed as a string child. With a theme (default
28
+ * "colorful") everything is styled inline and no CSS is required. Pass
29
+ * `theme={false}` to get class names for your own stylesheet, and
30
+ * `language="auto"` to guess the language from the code.
31
+ */
32
+ export function Code({
33
+ code,
34
+ children,
35
+ language = "text",
36
+ theme = "colorful",
37
+ lineNumbers = false,
38
+ highlightLines,
39
+ inline = false,
40
+ className,
41
+ style,
42
+ ...rest
43
+ }) {
44
+ const source = code ?? (typeof children === "string" ? children : "");
45
+ // Guess once here, so the highlighting and `data-language` agree.
46
+ const resolved = useMemo(
47
+ () => (language === "auto" ? (detectLanguage({ code: source, guess: true }) ?? "text") : language),
48
+ [source, language],
49
+ );
50
+ const html = useHighlight(source, resolved, { theme, lineNumbers, highlightLines });
51
+ const colors = theme === false ? undefined : getTheme(theme);
52
+ const lang = getLanguage(resolved);
53
+ const classes = className ? `cc ${className}` : "cc";
54
+ const themed = colors ? { background: colors.background, color: colors.foreground } : undefined;
55
+ if (inline) {
56
+ return createElement("code", {
57
+ ...rest,
58
+ className: classes,
59
+ "data-language": lang?.id,
60
+ style: themed ? { padding: "0.15em 0.35em", borderRadius: "0.3em", ...themed, ...style } : style,
61
+ dangerouslySetInnerHTML: { __html: html },
62
+ });
63
+ }
64
+ return createElement(
65
+ "pre",
66
+ {
67
+ ...rest,
68
+ className: classes,
69
+ "data-language": lang?.id,
70
+ style: themed ? { padding: "1em", overflowX: "auto", borderRadius: "0.5em", ...themed, ...style } : style,
71
+ },
72
+ createElement("code", { dangerouslySetInnerHTML: { __html: html } }),
73
+ );
74
+ }
75
+
76
+ export default Code;
@@ -0,0 +1,12 @@
1
+ /* Base styles for the class based output (theme: false). Pair with a theme from colorfulcode/themes. */
2
+ .cc { padding: 1em; overflow-x: auto; border-radius: 0.5em; tab-size: 4; }
3
+ .cc .cc-line { display: inline-block; min-width: 100%; }
4
+ .cc .cc-hl { background: rgb(127 127 127 / 15%); }
5
+ .cc .cc-ln {
6
+ display: inline-block;
7
+ min-width: 3ch;
8
+ margin-right: 2ch;
9
+ text-align: right;
10
+ opacity: 0.45;
11
+ user-select: none;
12
+ }
@@ -0,0 +1,31 @@
1
+ // Optional themes. Each one is a plain theme object you can pass as `theme`.
2
+ import type { ThemeDefinition } from "./index.js";
3
+
4
+ export declare const rosePine: ThemeDefinition;
5
+ export declare const rosePineMoon: ThemeDefinition;
6
+ export declare const rosePineDawn: ThemeDefinition;
7
+ export declare const ayuDark: ThemeDefinition;
8
+ export declare const ayuMirage: ThemeDefinition;
9
+ export declare const ayuLight: ThemeDefinition;
10
+ export declare const nightOwl: ThemeDefinition;
11
+ export declare const lightOwl: ThemeDefinition;
12
+ export declare const palenight: ThemeDefinition;
13
+ export declare const materialDarker: ThemeDefinition;
14
+ export declare const synthwave84: ThemeDefinition;
15
+ export declare const kanagawa: ThemeDefinition;
16
+ export declare const everforestDark: ThemeDefinition;
17
+ export declare const everforestLight: ThemeDefinition;
18
+ export declare const vitesseDark: ThemeDefinition;
19
+ export declare const vitesseLight: ThemeDefinition;
20
+ export declare const poimandres: ThemeDefinition;
21
+ export declare const githubDarkDimmed: ThemeDefinition;
22
+ export declare const oneLight: ThemeDefinition;
23
+ export declare const gruvboxLight: ThemeDefinition;
24
+ export declare const catppuccinFrappe: ThemeDefinition;
25
+ export declare const catppuccinMacchiato: ThemeDefinition;
26
+ export declare const tokyoNightStorm: ThemeDefinition;
27
+ export declare const tokyoNightLight: ThemeDefinition;
28
+ export declare const monokaiPro: ThemeDefinition;
29
+ export declare const horizon: ThemeDefinition;
30
+ export declare const panda: ThemeDefinition;
31
+ export declare const cobalt2: ThemeDefinition;