@rcarls/rc-textarea-adapters 0.0.0-next-20260921045401

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/CHANGELOG.md ADDED
@@ -0,0 +1,77 @@
1
+ # @rcarls/rc-textarea-adapters
2
+
3
+ ## 0.0.0-next-20260921045401
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [079e025]
8
+ - Updated dependencies [4617b45]
9
+ - @rcarls/rc-textarea@0.0.0-next-20260921045401
10
+
11
+ ## 0.6.0
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies [985067e]
16
+ - Updated dependencies [57370e4]
17
+ - @rcarls/rc-textarea@0.6.0
18
+
19
+ ## 0.5.0
20
+
21
+ ### Patch Changes
22
+
23
+ - Updated dependencies [689340c]
24
+ - @rcarls/rc-textarea@0.5.0
25
+
26
+ ## 0.4.2
27
+
28
+ ### Patch Changes
29
+
30
+ - @rcarls/rc-textarea@0.4.2
31
+
32
+ ## 0.4.1
33
+
34
+ ### Patch Changes
35
+
36
+ - @rcarls/rc-textarea@0.4.1
37
+
38
+ ## 0.4.0
39
+
40
+ ### Patch Changes
41
+
42
+ - Updated dependencies [46810f7]
43
+ - Updated dependencies [32b1dc4]
44
+ - Updated dependencies [c50d9e2]
45
+ - Updated dependencies [be4f0ae]
46
+ - Updated dependencies [c40a1be]
47
+ - Updated dependencies [aecba33]
48
+ - @rcarls/rc-textarea@0.4.0
49
+
50
+ ## 0.3.2
51
+
52
+ ### Patch Changes
53
+
54
+ - @rcarls/rc-textarea@0.3.2
55
+
56
+ ## 0.3.1
57
+
58
+ ### Patch Changes
59
+
60
+ - Updated dependencies [808afe4]
61
+ - @rcarls/rc-textarea@0.3.1
62
+
63
+ ## 0.3.0
64
+
65
+ ### Changed
66
+
67
+ - Update package metadata, README intro, and docs links.
68
+
69
+ ### Dependencies
70
+
71
+ - Sync internal dependencies to 0.3.0.
72
+
73
+ ## 0.2.0
74
+
75
+ ### Patch Changes
76
+
77
+ - @rcarls/rc-textarea@0.2.0
package/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # @rcarls/rc-textarea-adapters
2
+
3
+ Adapter factories that connect Lezer, unified, and Shiki tokenizers to `rc-textarea`.
4
+
5
+ Docs: [https://richardcarls.github.io/rc-webcomponents/components/rc-textarea](https://richardcarls.github.io/rc-webcomponents/components/rc-textarea).
6
+
7
+ Part of the [rc-webcomponents](https://github.com/richardcarls/rc-webcomponents) library.
8
+
9
+ ## Documentation
10
+
11
+ Full API, usage examples, and keyboard/accessibility notes are on the
12
+ [rc-webcomponents docs site](https://richardcarls.github.io/rc-webcomponents/).
13
+
14
+ ## Peer dependencies
15
+
16
+ All adapters are optional. Install only the peer(s) you use:
17
+
18
+ | Adapter | Peer dependency |
19
+ | ------- | --------------- |
20
+ | Lezer | `@lezer/common` |
21
+ | Unified | `unified` |
22
+ | Shiki | `shiki` |
23
+
24
+ ## License
25
+
26
+ MIT
@@ -0,0 +1,57 @@
1
+ function p(o, s) {
2
+ return {
3
+ update(r, i) {
4
+ const e = o.parse(r).cursor(), n = [];
5
+ do {
6
+ const t = s[e.type.name];
7
+ t && n.push({ type: "mark", from: e.from, to: e.to, ...t });
8
+ } while (e.next());
9
+ i.setDecorations(n);
10
+ }
11
+ };
12
+ }
13
+ function l(o, s) {
14
+ if (s(o), o.children)
15
+ for (const r of o.children) l(r, s);
16
+ }
17
+ function d(o, s) {
18
+ return {
19
+ update(r, i) {
20
+ const f = o.parse(r), e = [];
21
+ l(f, (n) => {
22
+ const t = s[n.type], a = n.position?.start.offset, c = n.position?.end.offset;
23
+ t && a !== void 0 && c !== void 0 && e.push({ type: "mark", from: a, to: c, ...t });
24
+ }), i.setDecorations(e);
25
+ }
26
+ };
27
+ }
28
+ function h(o, s, r) {
29
+ return {
30
+ async update(i, f) {
31
+ const e = await o.codeToTokens(i, {
32
+ lang: s,
33
+ ...r ? { theme: r } : {}
34
+ }), n = [];
35
+ let t = 0;
36
+ for (const a of e.tokens) {
37
+ for (const c of a) {
38
+ const u = c.content.length;
39
+ c.color && n.push({
40
+ type: "mark",
41
+ from: t,
42
+ to: t + u,
43
+ color: c.color
44
+ }), t += u;
45
+ }
46
+ t += 1;
47
+ }
48
+ f.setDecorations(n);
49
+ }
50
+ };
51
+ }
52
+ export {
53
+ p as createLezerPlugin,
54
+ h as createShikiPlugin,
55
+ d as createUnifiedPlugin
56
+ };
57
+ //# sourceMappingURL=rc-textarea-adapters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rc-textarea-adapters.js","sources":["../src/lezer.ts","../src/unified.ts","../src/shiki.ts"],"sourcesContent":["import type { RCTextareaPlugin, MarkDecoration, DecorationInput } from '@rcarls/rc-textarea';\n\n/** Minimal structural shape of a lezer `Parser` / language's `.parser`. */\ninterface LezerParser {\n parse(input: string): {\n cursor(): LezerCursor;\n };\n}\n\ninterface LezerCursor {\n type: { name: string };\n from: number;\n to: number;\n next(): boolean;\n}\n\n/**\n * Create an `rc-textarea` plugin that uses a lezer parser (e.g. any `@codemirror/lang-*` package)\n * to produce syntax decorations.\n *\n * Lezer node offsets (`.from` / `.to`) are already absolute character offsets, identical to\n * rc-textarea's coordinate space — no conversion is needed.\n *\n * ```ts\n * import { javascript } from '@codemirror/lang-javascript';\n * import { createLezerPlugin } from '@rcarls/rc-textarea-adapters';\n *\n * editor.usePlugin(createLezerPlugin(javascript().language.parser, {\n * VariableDeclaration: { color: 'var(--color-keyword)' },\n * String: { color: 'var(--color-string)' },\n * LineComment: { italic: true, color: 'var(--color-comment)' },\n * }));\n * ```\n */\nexport function createLezerPlugin(\n parser: LezerParser,\n nodeTypeToStyle: Record<string, Omit<MarkDecoration, 'id' | 'type' | 'from' | 'to'>>,\n): RCTextareaPlugin {\n return {\n update(value, api) {\n const tree = parser.parse(value);\n const cursor = tree.cursor();\n const decorations: DecorationInput[] = [];\n do {\n const style = nodeTypeToStyle[cursor.type.name];\n if (style) {\n decorations.push({ type: 'mark', from: cursor.from, to: cursor.to, ...style });\n }\n } while (cursor.next());\n api.setDecorations(decorations);\n },\n };\n}\n","import type { RCTextareaPlugin, MarkDecoration, DecorationInput } from '@rcarls/rc-textarea';\n\n/** Minimal structural shape of a unist `Node` (shared by mdast, hast, etc.). */\ninterface UnistNode {\n type: string;\n position?: {\n start: { offset?: number };\n end: { offset?: number };\n };\n children?: UnistNode[];\n}\n\n/** Minimal structural shape of a unified `Processor`. */\ninterface UnifiedProcessor {\n parse(value: string): UnistNode;\n}\n\nfunction visit(node: UnistNode, visitor: (n: UnistNode) => void): void {\n visitor(node);\n if (node.children) {\n for (const child of node.children) visit(child, visitor);\n }\n}\n\n/**\n * Create an `rc-textarea` plugin that uses a unified processor (e.g. `remark-parse` for\n * Markdown, `rehype-parse` for HTML) to produce syntax decorations.\n *\n * Node `position.start.offset` / `position.end.offset` values are absolute character offsets,\n * identical to rc-textarea's coordinate space — no conversion is needed.\n *\n * ```ts\n * import { unified } from 'unified';\n * import remarkParse from 'remark-parse';\n * import { createUnifiedPlugin } from '@rcarls/rc-textarea-adapters';\n *\n * const processor = unified().use(remarkParse);\n *\n * editor.usePlugin(createUnifiedPlugin(processor, {\n * strong: { bold: true },\n * emphasis: { italic: true },\n * inlineCode: { className: 'md-inline-code' },\n * heading: { bold: true, color: 'var(--color-heading)' },\n * }));\n * ```\n */\nexport function createUnifiedPlugin(\n processor: UnifiedProcessor,\n nodeTypeToStyle: Record<string, Omit<MarkDecoration, 'id' | 'type' | 'from' | 'to'>>,\n): RCTextareaPlugin {\n return {\n update(value, api) {\n const tree = processor.parse(value);\n const decorations: DecorationInput[] = [];\n visit(tree, (node) => {\n const style = nodeTypeToStyle[node.type];\n const start = node.position?.start.offset;\n const end = node.position?.end.offset;\n if (style && start !== undefined && end !== undefined) {\n decorations.push({ type: 'mark', from: start, to: end, ...style });\n }\n });\n api.setDecorations(decorations);\n },\n };\n}\n","import type { RCTextareaPlugin, DecorationInput } from '@rcarls/rc-textarea';\n\ninterface ShikiToken {\n content: string;\n color?: string;\n}\n\n/** Minimal structural shape of a shiki `Highlighter`. */\ninterface ShikiHighlighter {\n codeToTokens(\n code: string,\n opts: { lang: string; theme?: string },\n ): Promise<{ tokens: ShikiToken[][] }>;\n}\n\n/**\n * Create an `rc-textarea` plugin that uses a [shiki](https://shiki.style/) `Highlighter`\n * to produce syntax decorations.\n *\n * Shiki tokens carry line-relative offsets; this adapter converts them to the absolute\n * character offsets that rc-textarea expects by tracking position across lines.\n *\n * ```ts\n * import { createHighlighter } from 'shiki';\n * import { createShikiPlugin } from '@rcarls/rc-textarea-adapters';\n *\n * const highlighter = await createHighlighter({\n * themes: ['github-dark'],\n * langs: ['typescript'],\n * });\n *\n * editor.usePlugin(createShikiPlugin(highlighter, 'typescript', 'github-dark'));\n * ```\n */\nexport function createShikiPlugin(\n highlighter: ShikiHighlighter,\n lang: string,\n theme?: string,\n): RCTextareaPlugin {\n return {\n async update(value, api) {\n const result = await highlighter.codeToTokens(value, {\n lang,\n ...(theme ? { theme } : {}),\n });\n\n const decorations: DecorationInput[] = [];\n let offset = 0;\n\n for (const line of result.tokens) {\n for (const token of line) {\n const len = token.content.length;\n if (token.color) {\n decorations.push({\n type: 'mark',\n from: offset,\n to: offset + len,\n color: token.color,\n });\n }\n offset += len;\n }\n offset += 1; // '\\n'\n }\n\n api.setDecorations(decorations);\n },\n };\n}\n"],"names":["createLezerPlugin","parser","nodeTypeToStyle","value","api","cursor","decorations","style","visit","node","visitor","child","createUnifiedPlugin","processor","tree","start","end","createShikiPlugin","highlighter","lang","theme","result","offset","line","token","len"],"mappings":"AAkCO,SAASA,EACdC,GACAC,GACkB;AAClB,SAAO;AAAA,IACL,OAAOC,GAAOC,GAAK;AAEjB,YAAMC,IADOJ,EAAO,MAAME,CAAK,EACX,OAAA,GACdG,IAAiC,CAAA;AACvC,SAAG;AACD,cAAMC,IAAQL,EAAgBG,EAAO,KAAK,IAAI;AAC9C,QAAIE,KACFD,EAAY,KAAK,EAAE,MAAM,QAAQ,MAAMD,EAAO,MAAM,IAAIA,EAAO,IAAI,GAAGE,EAAA,CAAO;AAAA,MAEjF,SAASF,EAAO,KAAA;AAChB,MAAAD,EAAI,eAAeE,CAAW;AAAA,IAChC;AAAA,EAAA;AAEJ;ACnCA,SAASE,EAAMC,GAAiBC,GAAuC;AAErE,MADAA,EAAQD,CAAI,GACRA,EAAK;AACP,eAAWE,KAASF,EAAK,SAAU,CAAAD,EAAMG,GAAOD,CAAO;AAE3D;AAwBO,SAASE,EACdC,GACAX,GACkB;AAClB,SAAO;AAAA,IACL,OAAOC,GAAOC,GAAK;AACjB,YAAMU,IAAOD,EAAU,MAAMV,CAAK,GAC5BG,IAAiC,CAAA;AACvC,MAAAE,EAAMM,GAAM,CAACL,MAAS;AACpB,cAAMF,IAAQL,EAAgBO,EAAK,IAAI,GACjCM,IAAQN,EAAK,UAAU,MAAM,QAC7BO,IAAMP,EAAK,UAAU,IAAI;AAC/B,QAAIF,KAASQ,MAAU,UAAaC,MAAQ,UAC1CV,EAAY,KAAK,EAAE,MAAM,QAAQ,MAAMS,GAAO,IAAIC,GAAK,GAAGT,GAAO;AAAA,MAErE,CAAC,GACDH,EAAI,eAAeE,CAAW;AAAA,IAChC;AAAA,EAAA;AAEJ;AC/BO,SAASW,EACdC,GACAC,GACAC,GACkB;AAClB,SAAO;AAAA,IACL,MAAM,OAAOjB,GAAOC,GAAK;AACvB,YAAMiB,IAAS,MAAMH,EAAY,aAAaf,GAAO;AAAA,QACnD,MAAAgB;AAAA,QACA,GAAIC,IAAQ,EAAE,OAAAA,MAAU,CAAA;AAAA,MAAC,CAC1B,GAEKd,IAAiC,CAAA;AACvC,UAAIgB,IAAS;AAEb,iBAAWC,KAAQF,EAAO,QAAQ;AAChC,mBAAWG,KAASD,GAAM;AACxB,gBAAME,IAAMD,EAAM,QAAQ;AAC1B,UAAIA,EAAM,SACRlB,EAAY,KAAK;AAAA,YACf,MAAM;AAAA,YACN,MAAMgB;AAAA,YACN,IAAIA,IAASG;AAAA,YACb,OAAOD,EAAM;AAAA,UAAA,CACd,GAEHF,KAAUG;AAAA,QACZ;AACA,QAAAH,KAAU;AAAA,MACZ;AAEA,MAAAlB,EAAI,eAAeE,CAAW;AAAA,IAChC;AAAA,EAAA;AAEJ;"}
@@ -0,0 +1,3 @@
1
+ export { createLezerPlugin } from './lezer.js';
2
+ export { createUnifiedPlugin } from './unified.js';
3
+ export { createShikiPlugin } from './shiki.js';
@@ -0,0 +1,35 @@
1
+ import { RCTextareaPlugin, MarkDecoration } from '@rcarls/rc-textarea';
2
+ /** Minimal structural shape of a lezer `Parser` / language's `.parser`. */
3
+ interface LezerParser {
4
+ parse(input: string): {
5
+ cursor(): LezerCursor;
6
+ };
7
+ }
8
+ interface LezerCursor {
9
+ type: {
10
+ name: string;
11
+ };
12
+ from: number;
13
+ to: number;
14
+ next(): boolean;
15
+ }
16
+ /**
17
+ * Create an `rc-textarea` plugin that uses a lezer parser (e.g. any `@codemirror/lang-*` package)
18
+ * to produce syntax decorations.
19
+ *
20
+ * Lezer node offsets (`.from` / `.to`) are already absolute character offsets, identical to
21
+ * rc-textarea's coordinate space — no conversion is needed.
22
+ *
23
+ * ```ts
24
+ * import { javascript } from '@codemirror/lang-javascript';
25
+ * import { createLezerPlugin } from '@rcarls/rc-textarea-adapters';
26
+ *
27
+ * editor.usePlugin(createLezerPlugin(javascript().language.parser, {
28
+ * VariableDeclaration: { color: 'var(--color-keyword)' },
29
+ * String: { color: 'var(--color-string)' },
30
+ * LineComment: { italic: true, color: 'var(--color-comment)' },
31
+ * }));
32
+ * ```
33
+ */
34
+ export declare function createLezerPlugin(parser: LezerParser, nodeTypeToStyle: Record<string, Omit<MarkDecoration, 'id' | 'type' | 'from' | 'to'>>): RCTextareaPlugin;
35
+ export {};
@@ -0,0 +1,35 @@
1
+ import { RCTextareaPlugin } from '@rcarls/rc-textarea';
2
+ interface ShikiToken {
3
+ content: string;
4
+ color?: string;
5
+ }
6
+ /** Minimal structural shape of a shiki `Highlighter`. */
7
+ interface ShikiHighlighter {
8
+ codeToTokens(code: string, opts: {
9
+ lang: string;
10
+ theme?: string;
11
+ }): Promise<{
12
+ tokens: ShikiToken[][];
13
+ }>;
14
+ }
15
+ /**
16
+ * Create an `rc-textarea` plugin that uses a [shiki](https://shiki.style/) `Highlighter`
17
+ * to produce syntax decorations.
18
+ *
19
+ * Shiki tokens carry line-relative offsets; this adapter converts them to the absolute
20
+ * character offsets that rc-textarea expects by tracking position across lines.
21
+ *
22
+ * ```ts
23
+ * import { createHighlighter } from 'shiki';
24
+ * import { createShikiPlugin } from '@rcarls/rc-textarea-adapters';
25
+ *
26
+ * const highlighter = await createHighlighter({
27
+ * themes: ['github-dark'],
28
+ * langs: ['typescript'],
29
+ * });
30
+ *
31
+ * editor.usePlugin(createShikiPlugin(highlighter, 'typescript', 'github-dark'));
32
+ * ```
33
+ */
34
+ export declare function createShikiPlugin(highlighter: ShikiHighlighter, lang: string, theme?: string): RCTextareaPlugin;
35
+ export {};
@@ -0,0 +1,42 @@
1
+ import { RCTextareaPlugin, MarkDecoration } from '@rcarls/rc-textarea';
2
+ /** Minimal structural shape of a unist `Node` (shared by mdast, hast, etc.). */
3
+ interface UnistNode {
4
+ type: string;
5
+ position?: {
6
+ start: {
7
+ offset?: number;
8
+ };
9
+ end: {
10
+ offset?: number;
11
+ };
12
+ };
13
+ children?: UnistNode[];
14
+ }
15
+ /** Minimal structural shape of a unified `Processor`. */
16
+ interface UnifiedProcessor {
17
+ parse(value: string): UnistNode;
18
+ }
19
+ /**
20
+ * Create an `rc-textarea` plugin that uses a unified processor (e.g. `remark-parse` for
21
+ * Markdown, `rehype-parse` for HTML) to produce syntax decorations.
22
+ *
23
+ * Node `position.start.offset` / `position.end.offset` values are absolute character offsets,
24
+ * identical to rc-textarea's coordinate space — no conversion is needed.
25
+ *
26
+ * ```ts
27
+ * import { unified } from 'unified';
28
+ * import remarkParse from 'remark-parse';
29
+ * import { createUnifiedPlugin } from '@rcarls/rc-textarea-adapters';
30
+ *
31
+ * const processor = unified().use(remarkParse);
32
+ *
33
+ * editor.usePlugin(createUnifiedPlugin(processor, {
34
+ * strong: { bold: true },
35
+ * emphasis: { italic: true },
36
+ * inlineCode: { className: 'md-inline-code' },
37
+ * heading: { bold: true, color: 'var(--color-heading)' },
38
+ * }));
39
+ * ```
40
+ */
41
+ export declare function createUnifiedPlugin(processor: UnifiedProcessor, nodeTypeToStyle: Record<string, Omit<MarkDecoration, 'id' | 'type' | 'from' | 'to'>>): RCTextareaPlugin;
42
+ export {};
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@rcarls/rc-textarea-adapters",
3
+ "publishConfig": {
4
+ "access": "public"
5
+ },
6
+ "version": "0.0.0-next-20260921045401",
7
+ "description": "Adapter factories that connect Lezer, unified, and Shiki tokenizers to rc-textarea.",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/richardcarls/rc-webcomponents.git",
11
+ "directory": "packages/rc-textarea-adapters"
12
+ },
13
+ "homepage": "https://richardcarls.github.io/rc-webcomponents/components/rc-textarea",
14
+ "license": "MIT",
15
+ "type": "module",
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "module": "./dist/rc-textarea-adapters.js",
20
+ "exports": {
21
+ ".": {
22
+ "import": {
23
+ "types": "./dist/types/index.d.ts",
24
+ "default": "./dist/rc-textarea-adapters.js"
25
+ }
26
+ }
27
+ },
28
+ "types": "./dist/types/index.d.ts",
29
+ "sideEffects": false,
30
+ "scripts": {
31
+ "build": "tsc && vite build"
32
+ },
33
+ "dependencies": {
34
+ "@rcarls/rc-textarea": "0.0.0-next-20260921045401"
35
+ },
36
+ "peerDependencies": {
37
+ "@lezer/common": ">=1.0.0",
38
+ "shiki": ">=1.0.0",
39
+ "unified": ">=11.0.0"
40
+ },
41
+ "peerDependenciesMeta": {
42
+ "@lezer/common": {
43
+ "optional": true
44
+ },
45
+ "shiki": {
46
+ "optional": true
47
+ },
48
+ "unified": {
49
+ "optional": true
50
+ }
51
+ },
52
+ "devDependencies": {
53
+ "typescript": "~5.9.3",
54
+ "vite": "^7.1.7",
55
+ "vite-plugin-dts": "^4.5.4"
56
+ }
57
+ }