@vortexlabs/kirari-react 1.0.1 → 1.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.
- package/CHANGELOG.md +8 -0
- package/README.md +4 -3
- package/dist/index.d.ts +3 -1
- package/dist/index.js +24 -6
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @vortexlabs/kirari-react
|
|
2
2
|
|
|
3
|
+
## 1.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 52ad674: Accept text children in `<Code>` the way React renders text. `<Code>let x = {count}</Code>` now highlights the joined source instead of misaligned tokens. Numbers and bigints are kept instead of dropped, and `null`, `undefined`, and booleans render nothing instead of throwing. Element children throw a `TypeError`. The `children` prop type widens from `string` to the new exported `CodeText` type.
|
|
8
|
+
|
|
9
|
+
Stop `lines` and `highlightLines` from adding an empty last line wrapper after a trailing newline. The newline still follows the last wrapper, so the source text is unchanged, and blank lines before it keep their wrappers.
|
|
10
|
+
|
|
3
11
|
## 1.0.1
|
|
4
12
|
|
|
5
13
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -31,8 +31,9 @@ export const CodeExample = () => (
|
|
|
31
31
|
|
|
32
32
|
Output is `<pre class="kirari"><code>…</code></pre>` with the standard
|
|
33
33
|
`kirari-*` classes, so every Kirari theme works. Code text is escaped by React;
|
|
34
|
-
the component does not use `dangerouslySetInnerHTML`.
|
|
35
|
-
|
|
34
|
+
the component does not use `dangerouslySetInnerHTML`. Text children are joined
|
|
35
|
+
as React joins text, so `{value}` interpolation works; elements throw. `language`
|
|
36
|
+
defaults to JavaScript. Other props go to the `<pre>`.
|
|
36
37
|
|
|
37
38
|
## Lines and customization
|
|
38
39
|
|
|
@@ -83,7 +84,7 @@ export const StreamExample = () => {
|
|
|
83
84
|
This example adds one chunk per click. In your app, call `write(chunk)` as text
|
|
84
85
|
arrives. The hook also returns `end()`, which closes the stream when it is complete.
|
|
85
86
|
|
|
86
|
-
- Built-in JavaScript, TypeScript,
|
|
87
|
+
- Built-in JavaScript, TypeScript, JSX, TSX, JSON, JSONC, and Python reuse completed scanning work. Other languages rescan the full source. Long unfinished tokens can still be expensive; see [token streaming](https://github.com/vortexkit/kirari/blob/main/docs/streaming.md).
|
|
87
88
|
- Changing `language` starts a new, empty stream.
|
|
88
89
|
- After `end()`, call `reset()` before writing again.
|
|
89
90
|
- `/stream` is a client module; `<Code>` is not.
|
package/dist/index.d.ts
CHANGED
|
@@ -7,9 +7,11 @@ export interface Line {
|
|
|
7
7
|
readonly start: number;
|
|
8
8
|
readonly end: number;
|
|
9
9
|
}
|
|
10
|
+
/** Text children, joined like React text: `null`, `undefined`, and booleans render nothing. */
|
|
11
|
+
export type CodeText = string | number | bigint | boolean | null | undefined | readonly CodeText[];
|
|
10
12
|
export interface CodeProps extends Omit<ComponentPropsWithoutRef<'pre'>, 'children'> {
|
|
11
13
|
/** Source to highlight. Ignored when `tokens` is set. */
|
|
12
|
-
readonly children?:
|
|
14
|
+
readonly children?: CodeText;
|
|
13
15
|
/** Pre-scanned tokens, such as the output of `useTokenStream`. */
|
|
14
16
|
readonly tokens?: readonly Token[];
|
|
15
17
|
readonly language?: Language;
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,15 @@ import { javascript } from '@vortexlabs/kirari/languages/javascript';
|
|
|
4
4
|
const join = (...names) => {
|
|
5
5
|
return names.filter(Boolean).join(' ');
|
|
6
6
|
};
|
|
7
|
+
const sourceText = (children) => {
|
|
8
|
+
if (children == null || typeof children === 'boolean')
|
|
9
|
+
return '';
|
|
10
|
+
if (typeof children === 'string' || typeof children === 'number' || typeof children === 'bigint')
|
|
11
|
+
return String(children);
|
|
12
|
+
if (Array.isArray(children))
|
|
13
|
+
return children.map(sourceText).join('');
|
|
14
|
+
throw new TypeError('Kirari: <Code> children must be text');
|
|
15
|
+
};
|
|
7
16
|
const splitsSurrogate = (source, offset) => {
|
|
8
17
|
const before = source.charCodeAt(offset - 1);
|
|
9
18
|
const after = source.charCodeAt(offset);
|
|
@@ -13,7 +22,7 @@ const highlighted = (ranges) => {
|
|
|
13
22
|
const pairs = ranges.map(range => typeof range === 'number' ? [range, range] : range);
|
|
14
23
|
return line => pairs.some(([first, last]) => first <= line && line <= last);
|
|
15
24
|
};
|
|
16
|
-
const
|
|
25
|
+
const assertContiguous = (tokens) => {
|
|
17
26
|
let offset = 0;
|
|
18
27
|
for (const token of tokens) {
|
|
19
28
|
if (token.start !== offset || token.end !== offset + token.value.length) {
|
|
@@ -21,8 +30,9 @@ const codeNodes = (tokens, options = {}) => {
|
|
|
21
30
|
}
|
|
22
31
|
offset = token.end;
|
|
23
32
|
}
|
|
24
|
-
|
|
25
|
-
|
|
33
|
+
};
|
|
34
|
+
/** Validated enter/leave points for each decoration, in source order. */
|
|
35
|
+
const decorationEvents = (decorations, source) => {
|
|
26
36
|
const events = [];
|
|
27
37
|
for (const [index, decoration] of decorations.entries()) {
|
|
28
38
|
if (!Number.isInteger(decoration.start) || !Number.isInteger(decoration.end)
|
|
@@ -34,7 +44,13 @@ const codeNodes = (tokens, options = {}) => {
|
|
|
34
44
|
}
|
|
35
45
|
events.push({ offset: decoration.start, index, entering: true }, { offset: decoration.end, index, entering: false });
|
|
36
46
|
}
|
|
37
|
-
events.sort((a, b) => a.offset - b.offset);
|
|
47
|
+
return events.sort((a, b) => a.offset - b.offset);
|
|
48
|
+
};
|
|
49
|
+
const codeNodes = (tokens, options = {}) => {
|
|
50
|
+
assertContiguous(tokens);
|
|
51
|
+
const source = tokens.map(token => token.value).join('');
|
|
52
|
+
const decorations = options.decorations ?? [];
|
|
53
|
+
const events = decorationEvents(decorations, source);
|
|
38
54
|
// Resolve callbacks once so tokens split by lines or decorations see them a single time.
|
|
39
55
|
const tokenClasses = tokens.map(token => join(options.classes && Object.hasOwn(options.classes, token.type) ? options.classes[token.type] : undefined, options.tokenClassName?.(token)));
|
|
40
56
|
const active = new Set();
|
|
@@ -85,13 +101,15 @@ const codeNodes = (tokens, options = {}) => {
|
|
|
85
101
|
break;
|
|
86
102
|
nodes.push(match[0]);
|
|
87
103
|
start = end + match[0].length;
|
|
104
|
+
if (start === source.length)
|
|
105
|
+
break;
|
|
88
106
|
number++;
|
|
89
107
|
} while (true);
|
|
90
108
|
return nodes;
|
|
91
109
|
};
|
|
92
110
|
/** Highlighted code as React elements. Uses no hooks, so it also renders as a Server Component. */
|
|
93
|
-
export const Code = ({ children
|
|
94
|
-
const nodes = codeNodes(tokens ?? tokenize(children, language), {
|
|
111
|
+
export const Code = ({ children, tokens, language = javascript, classes, tokenClassName, decorations, lines, lineClassName, highlightLines, className, ...props }) => {
|
|
112
|
+
const nodes = codeNodes(tokens ?? tokenize(sourceText(children), language), {
|
|
95
113
|
...(classes && { classes }),
|
|
96
114
|
...(tokenClassName && { tokenClassName }),
|
|
97
115
|
...(decorations && { decorations }),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vortexlabs/kirari-react",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Kirari syntax highlighting as React elements.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"@happy-dom/global-registrator": "^20.14.6",
|
|
56
56
|
"@types/react": "^19.3.0",
|
|
57
57
|
"@types/react-dom": "^19.3.0",
|
|
58
|
-
"@vortexlabs/kirari": "1.0.
|
|
58
|
+
"@vortexlabs/kirari": "1.0.5",
|
|
59
59
|
"react": "^19.3.1",
|
|
60
60
|
"react-dom": "^19.3.1"
|
|
61
61
|
},
|