@otto-code/highlight 0.5.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,4 @@
1
+ import type { DiffBackgroundColors, HighlightStyle } from "./types.js";
2
+ export declare const darkHighlightColors: Record<HighlightStyle, string> & DiffBackgroundColors;
3
+ export declare const lightHighlightColors: Record<HighlightStyle, string> & DiffBackgroundColors;
4
+ //# sourceMappingURL=colors.d.ts.map
package/dist/colors.js ADDED
@@ -0,0 +1,53 @@
1
+ export const darkHighlightColors = {
2
+ keyword: "#ff7b72",
3
+ comment: "#8b949e",
4
+ string: "#a5d6ff",
5
+ number: "#79c0ff",
6
+ literal: "#79c0ff",
7
+ function: "#d2a8ff",
8
+ definition: "#d2a8ff",
9
+ class: "#ffa657",
10
+ type: "#ff7b72",
11
+ tag: "#7ee787",
12
+ attribute: "#79c0ff",
13
+ property: "#79c0ff",
14
+ variable: "#c9d1d9",
15
+ operator: "#79c0ff",
16
+ punctuation: "#c9d1d9",
17
+ regexp: "#a5d6ff",
18
+ escape: "#79c0ff",
19
+ meta: "#8b949e",
20
+ heading: "#79c0ff",
21
+ link: "#a5d6ff",
22
+ diffAdded: "rgba(46, 160, 67, 0.15)",
23
+ diffRemoved: "rgba(248, 81, 73, 0.1)",
24
+ diffAddedEmphasis: "rgba(46, 160, 67, 0.4)",
25
+ diffRemovedEmphasis: "rgba(248, 81, 73, 0.35)",
26
+ };
27
+ export const lightHighlightColors = {
28
+ keyword: "#cf222e",
29
+ comment: "#6e7781",
30
+ string: "#0a3069",
31
+ number: "#0550ae",
32
+ literal: "#0550ae",
33
+ function: "#8250df",
34
+ definition: "#8250df",
35
+ class: "#953800",
36
+ type: "#cf222e",
37
+ tag: "#116329",
38
+ attribute: "#0550ae",
39
+ property: "#0550ae",
40
+ variable: "#24292f",
41
+ operator: "#0550ae",
42
+ punctuation: "#24292f",
43
+ regexp: "#0a3069",
44
+ escape: "#0550ae",
45
+ meta: "#6e7781",
46
+ heading: "#0550ae",
47
+ link: "#0a3069",
48
+ diffAdded: "rgba(46, 160, 67, 0.15)",
49
+ diffRemoved: "rgba(248, 81, 73, 0.1)",
50
+ diffAddedEmphasis: "rgba(46, 160, 67, 0.4)",
51
+ diffRemovedEmphasis: "rgba(248, 81, 73, 0.35)",
52
+ };
53
+ //# sourceMappingURL=colors.js.map
@@ -0,0 +1,4 @@
1
+ import type { HighlightToken } from "./types.js";
2
+ export declare function highlightCode(code: string, filename: string): HighlightToken[][];
3
+ export declare function highlightLine(line: string, filename: string): HighlightToken[];
4
+ //# sourceMappingURL=highlighter.d.ts.map
@@ -0,0 +1,97 @@
1
+ import { highlightTree, tagHighlighter, tags } from "@lezer/highlight";
2
+ import { getParserForFile } from "./parsers.js";
3
+ const highlighter = tagHighlighter([
4
+ { tag: tags.keyword, class: "keyword" },
5
+ { tag: tags.controlKeyword, class: "keyword" },
6
+ { tag: tags.operatorKeyword, class: "keyword" },
7
+ { tag: tags.definitionKeyword, class: "keyword" },
8
+ { tag: tags.moduleKeyword, class: "keyword" },
9
+ { tag: tags.comment, class: "comment" },
10
+ { tag: tags.lineComment, class: "comment" },
11
+ { tag: tags.blockComment, class: "comment" },
12
+ { tag: tags.docComment, class: "comment" },
13
+ { tag: tags.string, class: "string" },
14
+ { tag: tags.special(tags.string), class: "string" },
15
+ { tag: tags.number, class: "number" },
16
+ { tag: tags.integer, class: "number" },
17
+ { tag: tags.float, class: "number" },
18
+ { tag: tags.bool, class: "literal" },
19
+ { tag: tags.null, class: "literal" },
20
+ { tag: tags.function(tags.variableName), class: "function" },
21
+ { tag: tags.function(tags.propertyName), class: "function" },
22
+ { tag: tags.definition(tags.variableName), class: "definition" },
23
+ { tag: tags.definition(tags.propertyName), class: "definition" },
24
+ { tag: tags.definition(tags.function(tags.variableName)), class: "definition" },
25
+ { tag: tags.className, class: "class" },
26
+ { tag: tags.definition(tags.className), class: "class" },
27
+ { tag: tags.typeName, class: "type" },
28
+ { tag: tags.tagName, class: "tag" },
29
+ { tag: tags.attributeName, class: "attribute" },
30
+ { tag: tags.attributeValue, class: "string" },
31
+ { tag: tags.propertyName, class: "property" },
32
+ { tag: tags.variableName, class: "variable" },
33
+ { tag: tags.local(tags.variableName), class: "variable" },
34
+ { tag: tags.special(tags.variableName), class: "variable" },
35
+ { tag: tags.operator, class: "operator" },
36
+ { tag: tags.punctuation, class: "punctuation" },
37
+ { tag: tags.bracket, class: "punctuation" },
38
+ { tag: tags.separator, class: "punctuation" },
39
+ { tag: tags.regexp, class: "regexp" },
40
+ { tag: tags.escape, class: "escape" },
41
+ { tag: tags.meta, class: "meta" },
42
+ { tag: tags.heading, class: "heading" },
43
+ { tag: tags.link, class: "link" },
44
+ { tag: tags.url, class: "link" },
45
+ ]);
46
+ export function highlightCode(code, filename) {
47
+ const parser = getParserForFile(filename);
48
+ if (!parser) {
49
+ return code.split("\n").map((line) => [{ text: line, style: null }]);
50
+ }
51
+ const tree = parser.parse(code);
52
+ const lines = code.split("\n");
53
+ const result = [];
54
+ for (let i = 0; i < lines.length; i++) {
55
+ result.push([]);
56
+ }
57
+ // Build a map of character positions to styles
58
+ const styleMap = Array.from({ length: code.length }, () => null);
59
+ highlightTree(tree, highlighter, (from, to, classes) => {
60
+ for (let i = from; i < to && i < styleMap.length; i++) {
61
+ styleMap[i] = classes;
62
+ }
63
+ });
64
+ // Convert style map to tokens per line
65
+ let pos = 0;
66
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
67
+ const line = lines[lineIndex];
68
+ if (line.length === 0) {
69
+ result[lineIndex].push({ text: "", style: null });
70
+ pos++; // skip newline
71
+ continue;
72
+ }
73
+ let currentToken = { text: "", style: styleMap[pos] };
74
+ for (let i = 0; i < line.length; i++) {
75
+ const charStyle = styleMap[pos + i];
76
+ if (charStyle === currentToken.style) {
77
+ currentToken.text += line[i];
78
+ }
79
+ else {
80
+ if (currentToken.text) {
81
+ result[lineIndex].push(currentToken);
82
+ }
83
+ currentToken = { text: line[i], style: charStyle };
84
+ }
85
+ }
86
+ if (currentToken.text) {
87
+ result[lineIndex].push(currentToken);
88
+ }
89
+ pos += line.length + 1; // +1 for newline
90
+ }
91
+ return result;
92
+ }
93
+ export function highlightLine(line, filename) {
94
+ const result = highlightCode(line, filename);
95
+ return result[0] ?? [{ text: line, style: null }];
96
+ }
97
+ //# sourceMappingURL=highlighter.js.map
@@ -0,0 +1,9 @@
1
+ export type { HighlightStyle, HighlightToken } from "./types.js";
2
+ export { getParserForFile, isLanguageSupported, getSupportedExtensions } from "./parsers.js";
3
+ export { highlightCode, highlightLine } from "./highlighter.js";
4
+ export { extractSymbols } from "./symbols.js";
5
+ export type { CodeSymbol, SymbolKind } from "./symbols.js";
6
+ export { darkHighlightColors, lightHighlightColors } from "./colors.js";
7
+ export type { SyntaxThemeId, SyntaxThemeOption, SyntaxColors } from "./themes.js";
8
+ export { SYNTAX_THEME_IDS, SYNTAX_THEME_OPTIONS, isSyntaxThemeId, resolveSyntaxColors, } from "./themes.js";
9
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export { getParserForFile, isLanguageSupported, getSupportedExtensions } from "./parsers.js";
2
+ export { highlightCode, highlightLine } from "./highlighter.js";
3
+ export { extractSymbols } from "./symbols.js";
4
+ export { darkHighlightColors, lightHighlightColors } from "./colors.js";
5
+ export { SYNTAX_THEME_IDS, SYNTAX_THEME_OPTIONS, isSyntaxThemeId, resolveSyntaxColors, } from "./themes.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,5 @@
1
+ import type { Parser } from "@lezer/common";
2
+ export declare function getParserForFile(filename: string): Parser | null;
3
+ export declare function isLanguageSupported(filename: string): boolean;
4
+ export declare function getSupportedExtensions(): string[];
5
+ //# sourceMappingURL=parsers.d.ts.map
@@ -0,0 +1,85 @@
1
+ import { StreamLanguage } from "@codemirror/language";
2
+ import { dart } from "@codemirror/legacy-modes/mode/clike";
3
+ import { swift } from "@codemirror/legacy-modes/mode/swift";
4
+ import { parser as jsParser } from "@lezer/javascript";
5
+ import { parser as jsonParser } from "@lezer/json";
6
+ import { parser as cssParser } from "@lezer/css";
7
+ import { parser as cppParser } from "@lezer/cpp";
8
+ import { parser as goParser } from "@lezer/go";
9
+ import { parser as htmlParser } from "@lezer/html";
10
+ import { parser as javaParser } from "@lezer/java";
11
+ import { parser as pythonParser } from "@lezer/python";
12
+ import { parser as markdownParser } from "@lezer/markdown";
13
+ import { parser as phpParser } from "@lezer/php";
14
+ import { parser as rustParser } from "@lezer/rust";
15
+ import { parser as xmlParser } from "@lezer/xml";
16
+ import { parser as yamlParser } from "@lezer/yaml";
17
+ import { csharpLanguage } from "@replit/codemirror-lang-csharp";
18
+ import { parser as elixirParser } from "lezer-elixir";
19
+ const parsersByExtension = {
20
+ // JavaScript/TypeScript
21
+ js: jsParser,
22
+ jsx: jsParser.configure({ dialect: "jsx" }),
23
+ ts: jsParser.configure({ dialect: "ts" }),
24
+ tsx: jsParser.configure({ dialect: "ts jsx" }),
25
+ mjs: jsParser,
26
+ cjs: jsParser,
27
+ // C / C++ / Objective-C
28
+ c: cppParser,
29
+ h: cppParser,
30
+ cc: cppParser,
31
+ cpp: cppParser,
32
+ cxx: cppParser,
33
+ hpp: cppParser,
34
+ hxx: cppParser,
35
+ m: cppParser,
36
+ mm: cppParser,
37
+ // JSON
38
+ json: jsonParser,
39
+ // CSS
40
+ css: cssParser,
41
+ scss: cssParser,
42
+ // HTML
43
+ html: htmlParser,
44
+ htm: htmlParser,
45
+ // XML
46
+ xml: xmlParser,
47
+ // Java
48
+ java: javaParser,
49
+ // Python
50
+ py: pythonParser,
51
+ // Go
52
+ go: goParser,
53
+ // PHP
54
+ php: phpParser,
55
+ // YAML
56
+ yaml: yamlParser,
57
+ yml: yamlParser,
58
+ // Rust
59
+ rs: rustParser,
60
+ // Swift
61
+ swift: StreamLanguage.define(swift).parser,
62
+ // Dart
63
+ dart: StreamLanguage.define(dart).parser,
64
+ // C#
65
+ cs: csharpLanguage.parser,
66
+ // Elixir
67
+ ex: elixirParser,
68
+ exs: elixirParser,
69
+ // Markdown
70
+ md: markdownParser,
71
+ mdx: markdownParser,
72
+ };
73
+ export function getParserForFile(filename) {
74
+ const ext = filename.split(".").pop()?.toLowerCase();
75
+ if (!ext)
76
+ return null;
77
+ return parsersByExtension[ext] ?? null;
78
+ }
79
+ export function isLanguageSupported(filename) {
80
+ return getParserForFile(filename) !== null;
81
+ }
82
+ export function getSupportedExtensions() {
83
+ return Object.keys(parsersByExtension);
84
+ }
85
+ //# sourceMappingURL=parsers.js.map
@@ -0,0 +1,15 @@
1
+ export type SymbolKind = "function" | "class" | "type" | "variable" | "property";
2
+ export interface CodeSymbol {
3
+ name: string;
4
+ kind: SymbolKind;
5
+ /** 1-based line of the identifier. */
6
+ line: number;
7
+ /** 1-based column of the identifier. */
8
+ column: number;
9
+ }
10
+ /**
11
+ * Extract definition symbols from source. Returns [] for unsupported
12
+ * languages (there is no parser) rather than throwing.
13
+ */
14
+ export declare function extractSymbols(code: string, filename: string): CodeSymbol[];
15
+ //# sourceMappingURL=symbols.d.ts.map
@@ -0,0 +1,87 @@
1
+ import { highlightTree, tagHighlighter, tags } from "@lezer/highlight";
2
+ import { getParserForFile } from "./parsers.js";
3
+ // Order matters: more specific tags are listed first so a function definition
4
+ // is classified as "function", not the broader "variable".
5
+ const definitionHighlighter = tagHighlighter([
6
+ { tag: tags.definition(tags.function(tags.variableName)), class: "function" },
7
+ { tag: tags.function(tags.definition(tags.variableName)), class: "function" },
8
+ { tag: tags.definition(tags.className), class: "class" },
9
+ { tag: tags.definition(tags.propertyName), class: "property" },
10
+ { tag: tags.definition(tags.variableName), class: "variable" },
11
+ { tag: tags.definition(tags.typeName), class: "type" },
12
+ { tag: tags.className, class: "class" },
13
+ { tag: tags.typeName, class: "type" },
14
+ ]);
15
+ const CLASS_TO_KIND = {
16
+ function: "function",
17
+ class: "class",
18
+ type: "type",
19
+ variable: "variable",
20
+ property: "property",
21
+ };
22
+ function buildLineStarts(code) {
23
+ const starts = [0];
24
+ for (let index = 0; index < code.length; index += 1) {
25
+ if (code.charCodeAt(index) === 10) {
26
+ starts.push(index + 1);
27
+ }
28
+ }
29
+ return starts;
30
+ }
31
+ // Binary search for the 0-based line index whose start is <= offset.
32
+ function lineIndexForOffset(lineStarts, offset) {
33
+ let low = 0;
34
+ let high = lineStarts.length - 1;
35
+ while (low < high) {
36
+ const mid = (low + high + 1) >> 1;
37
+ if (lineStarts[mid] <= offset) {
38
+ low = mid;
39
+ }
40
+ else {
41
+ high = mid - 1;
42
+ }
43
+ }
44
+ return low;
45
+ }
46
+ const IDENTIFIER = /^[A-Za-z_$][\w$]*$/;
47
+ /**
48
+ * Extract definition symbols from source. Returns [] for unsupported
49
+ * languages (there is no parser) rather than throwing.
50
+ */
51
+ export function extractSymbols(code, filename) {
52
+ const parser = getParserForFile(filename);
53
+ if (!parser) {
54
+ return [];
55
+ }
56
+ const tree = parser.parse(code);
57
+ const lineStarts = buildLineStarts(code);
58
+ const symbols = [];
59
+ const seen = new Set();
60
+ highlightTree(tree, definitionHighlighter, (from, to, classes) => {
61
+ const kind = CLASS_TO_KIND[classes];
62
+ if (!kind) {
63
+ return;
64
+ }
65
+ const name = code.slice(from, to);
66
+ // Highlight ranges can cover punctuation or multi-token spans; keep only
67
+ // clean identifiers so the index stays name-addressable.
68
+ if (!IDENTIFIER.test(name)) {
69
+ return;
70
+ }
71
+ const lineIndex = lineIndexForOffset(lineStarts, from);
72
+ const dedupeKey = `${name} ${lineIndex} ${kind}`;
73
+ if (seen.has(dedupeKey)) {
74
+ return;
75
+ }
76
+ seen.add(dedupeKey);
77
+ symbols.push({
78
+ name,
79
+ kind,
80
+ line: lineIndex + 1,
81
+ column: from - lineStarts[lineIndex] + 1,
82
+ });
83
+ });
84
+ symbols.sort((a, b) => a.line - b.line || a.column - b.column);
85
+ return symbols;
86
+ }
87
+ //# sourceMappingURL=symbols.js.map
@@ -0,0 +1,12 @@
1
+ import type { DiffBackgroundColors, HighlightStyle } from "./types.js";
2
+ export type SyntaxThemeId = "default" | "github" | "vscode" | "jetbrains" | "monokai" | "nightshade" | "neotokyo";
3
+ export declare const SYNTAX_THEME_IDS: readonly SyntaxThemeId[];
4
+ export interface SyntaxThemeOption {
5
+ id: SyntaxThemeId;
6
+ label: string;
7
+ }
8
+ export declare const SYNTAX_THEME_OPTIONS: readonly SyntaxThemeOption[];
9
+ export type SyntaxColors = Record<HighlightStyle, string> & DiffBackgroundColors;
10
+ export declare function isSyntaxThemeId(value: string): value is SyntaxThemeId;
11
+ export declare function resolveSyntaxColors(id: SyntaxThemeId, colorScheme: "light" | "dark"): SyntaxColors;
12
+ //# sourceMappingURL=themes.d.ts.map
package/dist/themes.js ADDED
@@ -0,0 +1,257 @@
1
+ import { darkHighlightColors, lightHighlightColors } from "./colors.js";
2
+ export const SYNTAX_THEME_IDS = [
3
+ "default",
4
+ "github",
5
+ "vscode",
6
+ "jetbrains",
7
+ "monokai",
8
+ "nightshade",
9
+ "neotokyo",
10
+ ];
11
+ export const SYNTAX_THEME_OPTIONS = [
12
+ { id: "default", label: "Default" },
13
+ { id: "github", label: "GitHub" },
14
+ { id: "vscode", label: "VS Code" },
15
+ { id: "jetbrains", label: "JetBrains" },
16
+ { id: "monokai", label: "Monokai" },
17
+ { id: "nightshade", label: "Nightshade" },
18
+ { id: "neotokyo", label: "Neotokyo" },
19
+ ];
20
+ // Re-derives an rgba() string at a different alpha. Boosts each theme's diff
21
+ // row tints into their intraline emphasis pair without every theme authoring
22
+ // a second color for the same hue.
23
+ function withAlpha(rgbaColor, alpha) {
24
+ const match = rgbaColor.match(/^rgba\((\d+),\s*(\d+),\s*(\d+),/);
25
+ if (!match)
26
+ return rgbaColor;
27
+ const [, r, g, b] = match;
28
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
29
+ }
30
+ function expandRolePalette(r) {
31
+ return {
32
+ keyword: r.keyword,
33
+ comment: r.comment,
34
+ string: r.string,
35
+ number: r.number,
36
+ literal: r.number,
37
+ function: r.function,
38
+ definition: r.function,
39
+ class: r.type,
40
+ type: r.type,
41
+ tag: r.tag,
42
+ attribute: r.attribute,
43
+ property: r.attribute,
44
+ variable: r.base,
45
+ operator: r.operator,
46
+ punctuation: r.base,
47
+ regexp: r.string,
48
+ escape: r.number,
49
+ meta: r.comment,
50
+ heading: r.function,
51
+ link: r.string,
52
+ diffAdded: r.diffAdded,
53
+ diffRemoved: r.diffRemoved,
54
+ diffAddedEmphasis: withAlpha(r.diffAdded, 0.4),
55
+ diffRemovedEmphasis: withAlpha(r.diffRemoved, 0.35),
56
+ };
57
+ }
58
+ // --- Default (high-contrast primary hues — the CGA-basic baseline) -------
59
+ const defaultLight = {
60
+ base: "#000000",
61
+ keyword: "#0000aa",
62
+ comment: "#666666",
63
+ string: "#aa0000",
64
+ number: "#aa00aa",
65
+ function: "#aa5500",
66
+ type: "#00aaaa",
67
+ tag: "#0000aa",
68
+ attribute: "#00aa00",
69
+ operator: "#000000",
70
+ diffAdded: "rgba(0, 128, 0, 0.12)",
71
+ diffRemoved: "rgba(170, 0, 0, 0.12)",
72
+ };
73
+ const defaultDark = {
74
+ base: "#ffffff",
75
+ keyword: "#5555ff",
76
+ comment: "#999999",
77
+ string: "#55ff55",
78
+ number: "#55ffff",
79
+ function: "#ffff55",
80
+ type: "#ff55ff",
81
+ tag: "#5555ff",
82
+ attribute: "#55ffff",
83
+ operator: "#ffffff",
84
+ diffAdded: "rgba(85, 255, 85, 0.18)",
85
+ diffRemoved: "rgba(255, 85, 85, 0.18)",
86
+ };
87
+ // --- VS Code (Light+ / Dark+) ---------------------------------------------
88
+ const vscodeLight = {
89
+ base: "#000000",
90
+ keyword: "#0000ff",
91
+ comment: "#008000",
92
+ string: "#a31515",
93
+ number: "#098658",
94
+ function: "#795e26",
95
+ type: "#267f99",
96
+ tag: "#800000",
97
+ attribute: "#e50000",
98
+ operator: "#000000",
99
+ diffAdded: "rgba(46, 125, 50, 0.14)",
100
+ diffRemoved: "rgba(198, 40, 40, 0.14)",
101
+ };
102
+ const vscodeDark = {
103
+ base: "#d4d4d4",
104
+ keyword: "#569cd6",
105
+ comment: "#6a9955",
106
+ string: "#ce9178",
107
+ number: "#b5cea8",
108
+ function: "#dcdcaa",
109
+ type: "#4ec9b0",
110
+ tag: "#569cd6",
111
+ attribute: "#9cdcfe",
112
+ operator: "#d4d4d4",
113
+ diffAdded: "rgba(75, 139, 31, 0.22)",
114
+ diffRemoved: "rgba(190, 17, 0, 0.22)",
115
+ };
116
+ // --- JetBrains (IntelliJ Light / Darcula) ---------------------------------
117
+ const jetbrainsLight = {
118
+ base: "#000000",
119
+ keyword: "#0033b3",
120
+ comment: "#8c8c8c",
121
+ string: "#067d17",
122
+ number: "#1750eb",
123
+ function: "#00627a",
124
+ type: "#20999d",
125
+ tag: "#3f7f7f",
126
+ attribute: "#660000",
127
+ operator: "#000000",
128
+ diffAdded: "rgba(56, 142, 60, 0.15)",
129
+ diffRemoved: "rgba(198, 40, 40, 0.15)",
130
+ };
131
+ const jetbrainsDark = {
132
+ base: "#a9b7c6",
133
+ keyword: "#cc7832",
134
+ comment: "#808080",
135
+ string: "#6a8759",
136
+ number: "#6897bb",
137
+ function: "#ffc66d",
138
+ type: "#b3ae60",
139
+ tag: "#e8bf6a",
140
+ attribute: "#a9b7c6",
141
+ operator: "#a9b7c6",
142
+ diffAdded: "rgba(46, 107, 62, 0.3)",
143
+ diffRemoved: "rgba(107, 46, 46, 0.3)",
144
+ };
145
+ // --- Monokai (Light / Dark) ------------------------------------------------
146
+ const monokaiLight = {
147
+ base: "#272822",
148
+ keyword: "#c4133b",
149
+ comment: "#75715e",
150
+ string: "#a68a0d",
151
+ number: "#7c3fb5",
152
+ function: "#4b8b1f",
153
+ type: "#0f83a3",
154
+ tag: "#c4133b",
155
+ attribute: "#4b8b1f",
156
+ operator: "#c4133b",
157
+ diffAdded: "rgba(75, 139, 31, 0.14)",
158
+ diffRemoved: "rgba(179, 37, 31, 0.14)",
159
+ };
160
+ const monokaiDark = {
161
+ base: "#f8f8f2",
162
+ keyword: "#f92672",
163
+ comment: "#75715e",
164
+ string: "#e6db74",
165
+ number: "#ae81ff",
166
+ function: "#a6e22e",
167
+ type: "#66d9ef",
168
+ tag: "#f92672",
169
+ attribute: "#a6e22e",
170
+ operator: "#f92672",
171
+ diffAdded: "rgba(166, 226, 46, 0.18)",
172
+ diffRemoved: "rgba(248, 53, 53, 0.18)",
173
+ };
174
+ // --- Nightshade (Light / Dark — gothic pink/purple/cyan, formerly "Dracula";
175
+ // renamed once it grew a light variant the original theme never had) --------
176
+ const nightshadeLight = {
177
+ base: "#282a36",
178
+ keyword: "#bd2f7a",
179
+ comment: "#6272a4",
180
+ string: "#8a7a1f",
181
+ number: "#7c4fd1",
182
+ function: "#1f9c4a",
183
+ type: "#0e93a8",
184
+ tag: "#bd2f7a",
185
+ attribute: "#1f9c4a",
186
+ operator: "#bd2f7a",
187
+ diffAdded: "rgba(31, 156, 74, 0.14)",
188
+ diffRemoved: "rgba(179, 54, 54, 0.14)",
189
+ };
190
+ const nightshadeDark = {
191
+ base: "#f8f8f2",
192
+ keyword: "#ff79c6",
193
+ comment: "#6272a4",
194
+ string: "#f1fa8c",
195
+ number: "#bd93f9",
196
+ function: "#50fa7b",
197
+ type: "#8be9fd",
198
+ tag: "#ff79c6",
199
+ attribute: "#50fa7b",
200
+ operator: "#ff79c6",
201
+ diffAdded: "rgba(80, 250, 123, 0.18)",
202
+ diffRemoved: "rgba(255, 85, 85, 0.18)",
203
+ };
204
+ // --- Neotokyo (Light / Dark — cyber yellow, hot pink, neon cyan) ----------
205
+ const neotokyoLight = {
206
+ base: "#1a1025",
207
+ keyword: "#c2188f",
208
+ comment: "#7a7a9c",
209
+ string: "#8a7600",
210
+ number: "#0089a3",
211
+ function: "#a3157a",
212
+ type: "#7b2fd4",
213
+ tag: "#c2188f",
214
+ attribute: "#0089a3",
215
+ operator: "#0089a3",
216
+ diffAdded: "rgba(31, 156, 26, 0.14)",
217
+ diffRemoved: "rgba(194, 31, 61, 0.14)",
218
+ };
219
+ const neotokyoDark = {
220
+ base: "#e4e4f4",
221
+ keyword: "#ff2ec4",
222
+ comment: "#6f6f94",
223
+ string: "#fcee0a",
224
+ number: "#0ff0fc",
225
+ function: "#ff6ec7",
226
+ type: "#b967ff",
227
+ tag: "#ff2ec4",
228
+ attribute: "#0ff0fc",
229
+ operator: "#0ff0fc",
230
+ diffAdded: "rgba(57, 255, 20, 0.18)",
231
+ diffRemoved: "rgba(255, 43, 78, 0.18)",
232
+ };
233
+ export function isSyntaxThemeId(value) {
234
+ return SYNTAX_THEME_IDS.includes(value);
235
+ }
236
+ // Resolve a theme id + the app's color scheme to a full token palette. Only the
237
+ // light/dark axis is coupled to the app; the theme brand is the user's choice.
238
+ export function resolveSyntaxColors(id, colorScheme) {
239
+ const dark = colorScheme === "dark";
240
+ switch (id) {
241
+ case "default":
242
+ return expandRolePalette(dark ? defaultDark : defaultLight);
243
+ case "github":
244
+ return dark ? darkHighlightColors : lightHighlightColors;
245
+ case "vscode":
246
+ return expandRolePalette(dark ? vscodeDark : vscodeLight);
247
+ case "jetbrains":
248
+ return expandRolePalette(dark ? jetbrainsDark : jetbrainsLight);
249
+ case "monokai":
250
+ return expandRolePalette(dark ? monokaiDark : monokaiLight);
251
+ case "nightshade":
252
+ return expandRolePalette(dark ? nightshadeDark : nightshadeLight);
253
+ case "neotokyo":
254
+ return expandRolePalette(dark ? neotokyoDark : neotokyoLight);
255
+ }
256
+ }
257
+ //# sourceMappingURL=themes.js.map
@@ -0,0 +1,12 @@
1
+ export type HighlightStyle = "keyword" | "comment" | "string" | "number" | "literal" | "function" | "definition" | "class" | "type" | "tag" | "attribute" | "property" | "variable" | "operator" | "punctuation" | "regexp" | "escape" | "meta" | "heading" | "link";
2
+ export interface HighlightToken {
3
+ text: string;
4
+ style: HighlightStyle | null;
5
+ }
6
+ export interface DiffBackgroundColors {
7
+ diffAdded: string;
8
+ diffRemoved: string;
9
+ diffAddedEmphasis: string;
10
+ diffRemovedEmphasis: string;
11
+ }
12
+ //# sourceMappingURL=types.d.ts.map
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@otto-code/highlight",
3
+ "version": "0.5.0",
4
+ "files": [
5
+ "dist",
6
+ "!dist/**/*.map"
7
+ ],
8
+ "type": "module",
9
+ "main": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "source": "./src/index.ts",
15
+ "default": "./dist/index.js"
16
+ }
17
+ },
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "scripts": {
22
+ "clean": "node ../../scripts/clean-package-dist.mjs",
23
+ "build": "tsc -p tsconfig.json --incremental false",
24
+ "build:clean": "npm run clean && npm run build",
25
+ "prepack": "npm run build:clean",
26
+ "test": "vitest run",
27
+ "typecheck": "tsgo --noEmit"
28
+ },
29
+ "dependencies": {
30
+ "@codemirror/language": "^6.12.3",
31
+ "@codemirror/legacy-modes": "^6.5.3",
32
+ "@lezer/common": "^1.5.0",
33
+ "@lezer/cpp": "^1.1.5",
34
+ "@lezer/css": "^1.3.0",
35
+ "@lezer/go": "^1.0.1",
36
+ "@lezer/highlight": "^1.2.3",
37
+ "@lezer/html": "^1.3.13",
38
+ "@lezer/java": "^1.1.3",
39
+ "@lezer/javascript": "^1.5.4",
40
+ "@lezer/json": "^1.0.3",
41
+ "@lezer/markdown": "^1.6.2",
42
+ "@lezer/php": "^1.0.5",
43
+ "@lezer/python": "^1.1.18",
44
+ "@lezer/rust": "^1.0.2",
45
+ "@lezer/xml": "^1.0.6",
46
+ "@lezer/yaml": "^1.0.4",
47
+ "@replit/codemirror-lang-csharp": "^6.2.0",
48
+ "lezer-elixir": "^1.1.2"
49
+ },
50
+ "devDependencies": {
51
+ "typescript": "^5.9.2",
52
+ "vitest": "^4.1.6"
53
+ }
54
+ }