@orz/ui-code 0.0.0-stage → 0.0.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/LICENSE +29 -0
- package/README.md +216 -2
- package/dist/capped-code-block.d.ts +21 -0
- package/dist/capped-code-block.d.ts.map +1 -0
- package/dist/capped-code-block.js +41 -0
- package/dist/capped-code-block.js.map +1 -0
- package/dist/code-block.d.ts +147 -0
- package/dist/code-block.d.ts.map +1 -0
- package/dist/code-block.js +494 -0
- package/dist/code-block.js.map +1 -0
- package/dist/code-worker.d.ts +28 -0
- package/dist/code-worker.d.ts.map +1 -0
- package/dist/code-worker.js +29 -0
- package/dist/code-worker.js.map +1 -0
- package/dist/content.d.ts +12 -0
- package/dist/content.d.ts.map +1 -0
- package/dist/content.js +35 -0
- package/dist/content.js.map +1 -0
- package/dist/file-text.d.ts +16 -0
- package/dist/file-text.d.ts.map +1 -0
- package/dist/file-text.js +37 -0
- package/dist/file-text.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/patch-commit.d.ts +27 -0
- package/dist/patch-commit.d.ts.map +1 -0
- package/dist/patch-commit.js +109 -0
- package/dist/patch-commit.js.map +1 -0
- package/dist/patch-content.d.ts +13 -0
- package/dist/patch-content.d.ts.map +1 -0
- package/dist/patch-content.js +43 -0
- package/dist/patch-content.js.map +1 -0
- package/dist/patch-view.d.ts +49 -0
- package/dist/patch-view.d.ts.map +1 -0
- package/dist/patch-view.js +167 -0
- package/dist/patch-view.js.map +1 -0
- package/dist/word-diff.d.ts +82 -0
- package/dist/word-diff.d.ts.map +1 -0
- package/dist/word-diff.js +209 -0
- package/dist/word-diff.js.map +1 -0
- package/package.json +63 -3
- package/styles.css +34 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright 2026 the orzui team
|
|
4
|
+
Copyright 2026 the todou team
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,217 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @orz/ui-code
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Read-only code and diff blocks, syntax-highlighted by [pierre](https://www.npmjs.com/package/@pierre/diffs)
|
|
4
|
+
on shiki and themed by `@orz/ui`, plus a word differ that segments CJK as well as English. The
|
|
5
|
+
package contributes two renderers to `@orz/ui-content`'s registry: `codeContent` for code and
|
|
6
|
+
`patchContent` for unified diffs.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pnpm add @orz/ui-code @orz/ui @orz/ui-content @pierre/diffs lucide-react react react-dom
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`@orz/ui-content` is a peer dependency so that the app and every content package share one registry.
|
|
15
|
+
|
|
16
|
+
`@pierre/diffs` is a peer dependency. pierre's highlighter is a module singleton, and its engine is
|
|
17
|
+
fixed by whichever surface creates it first. An app that renders pierre surfaces of its own has to
|
|
18
|
+
share that one copy with these blocks: a second copy would bring its own singleton, its own shiki
|
|
19
|
+
and its own wasm, and two highlighters that disagree about the engine.
|
|
20
|
+
|
|
21
|
+
## CSS
|
|
22
|
+
|
|
23
|
+
```css
|
|
24
|
+
@import "tailwindcss";
|
|
25
|
+
@import "@orz/ui/styles.css";
|
|
26
|
+
@import "@orz/ui-code/styles.css";
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Import it after `@orz/ui/styles.css`. It pins every `diffs-container` to the app theme's
|
|
30
|
+
`color-scheme` and paints pierre's background and foreground from `--background` and
|
|
31
|
+
`--foreground`, so a block matches the page under every theme, including themes with no syntax
|
|
32
|
+
counterpart. It also colours a patch's `+`/`−` counts from the theme's `--diff-addition` and
|
|
33
|
+
`--diff-deletion`. Like `@orz/ui`'s stylesheet, it registers `@source "./dist"` on its own.
|
|
34
|
+
|
|
35
|
+
## Theme
|
|
36
|
+
|
|
37
|
+
Blocks follow `@orz/ui`'s `ThemeProvider`. A palette's `syntax` (or its variant's) names the shiki
|
|
38
|
+
theme for its token colors, and a palette without one uses that slot of pierre's own light/dark
|
|
39
|
+
pair. Under `system` each slot comes from its own palette, so Solarized highlights as
|
|
40
|
+
solarized-light by day and solarized-dark at night. A pinned mode or a one-palette theme fills both
|
|
41
|
+
slots from the side on screen. Outside a provider, blocks use pierre's pair.
|
|
42
|
+
|
|
43
|
+
## Entry points
|
|
44
|
+
|
|
45
|
+
| Import | Contents |
|
|
46
|
+
|---|---|
|
|
47
|
+
| `@orz/ui-code` | `CodeBlock`, `CodeDiffBlock`, `CodeWorkerProvider`, `useCodeWorkerFactory`, `fenceFilename`, `codeContent`, `patchContent`, `PatchView`, `useSyntaxTheme`, `PIERRE_THEME`, `PIERRE_THEME_TYPE`, `PIERRE_HIGHLIGHTER`, `LazyMultiFileDiff`, `type SyntaxTheme` |
|
|
48
|
+
| `@orz/ui-code/word-diff` | `wordDiff`, `coalescedWordDiff`, `wordBag`, `bagWith`, types |
|
|
49
|
+
| `@orz/ui-code/styles.css` | the `diffs-container` rules |
|
|
50
|
+
|
|
51
|
+
pierre is only reached through `lazy()`, so a bundler splits it and shiki into their own chunks,
|
|
52
|
+
fetched when the first block renders.
|
|
53
|
+
|
|
54
|
+
### `@orz/ui-code`
|
|
55
|
+
|
|
56
|
+
`CodeBlock` takes a filename and infers the language from its extension. `fenceFilename` turns a
|
|
57
|
+
markdown fence's language tag into such a filename, falling back to plain text for a tag pierre
|
|
58
|
+
does not know:
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import { CodeBlock, CodeDiffBlock, fenceFilename } from "@orz/ui-code";
|
|
62
|
+
|
|
63
|
+
<CodeBlock filename={fenceFilename(lang)} contents={code} />
|
|
64
|
+
<CodeBlock filename="main.ts" contents={file} lineNumbers />
|
|
65
|
+
<CodeDiffBlock filename="main.ts" before={previous} after={current} />
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
`CodeBlock` takes `plain` to skip highlighting, and a `className` that caps a long file's height:
|
|
69
|
+
the code view virtualizes against its own box.
|
|
70
|
+
|
|
71
|
+
### Highlighting off the main thread
|
|
72
|
+
|
|
73
|
+
pierre tokenizes a whole file even when it draws a screenful: 1 MiB of log held the tab for 9.3 s
|
|
74
|
+
on the main thread, 0.2 s in pierre's worker. `CodeWorkerProvider` lends that worker to every
|
|
75
|
+
`CodeBlock` below it that sets `offThread`, which `codeContent` does for files. The lines show as
|
|
76
|
+
plain text at once and their colours follow. The worker script has to come from the app's bundler,
|
|
77
|
+
so the app starts it:
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
import { CodeWorkerProvider } from "@orz/ui-code";
|
|
81
|
+
import PierreWorker from "@pierre/diffs/worker/worker.js?worker"; // Vite
|
|
82
|
+
|
|
83
|
+
<CodeWorkerProvider workerFactory={() => new PierreWorker()}>{app}</CodeWorkerProvider>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Without a provider those blocks tokenize on the main thread, as every other block does. The pool
|
|
87
|
+
is a page-wide singleton of two workers, created from the first factory and terminated when the
|
|
88
|
+
last block using it unmounts. `offThread` only pays off with a height cap in `className`:
|
|
89
|
+
uncapped, laying out every line of 1 MiB still held the main thread for 2.2 s.
|
|
90
|
+
|
|
91
|
+
`CodeDiffBlock` is a unified line diff of one block; with identical sides it renders a `CodeBlock`.
|
|
92
|
+
|
|
93
|
+
### `codeContent`
|
|
94
|
+
|
|
95
|
+
The renderer this package contributes to `@orz/ui-content`'s registry. It
|
|
96
|
+
accepts every inline source, whatever its `lang`, and every file `isTextFile` calls text, in either
|
|
97
|
+
placement:
|
|
98
|
+
|
|
99
|
+
- An inline source, such as a markdown fence, is a `CodeBlock` named `fenceFilename(lang)`.
|
|
100
|
+
- A file is loaded through `useSourceText` and drawn as a `CodeBlock` under its own name, with
|
|
101
|
+
line numbers and without its final newline, tokenized off the main thread when a
|
|
102
|
+
`CodeWorkerProvider` is mounted, and capped to the viewer's pane or a card's collapsed body. While it loads, it is the registry's fallback: a
|
|
103
|
+
download link, or the bare name when there is no `url`. A failed load shows that fallback, the
|
|
104
|
+
error and a Retry button.
|
|
105
|
+
|
|
106
|
+
It takes everything a narrower renderer would, so it goes after them in the list:
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
import { codeContent } from "@orz/ui-code";
|
|
110
|
+
import { ContentRenderers } from "@orz/ui-content";
|
|
111
|
+
import { markdownContent } from "@orz/ui-markdown";
|
|
112
|
+
import { mermaidContent } from "@orz/ui-mermaid";
|
|
113
|
+
|
|
114
|
+
const RENDERERS = [mermaidContent, markdownContent(), codeContent];
|
|
115
|
+
|
|
116
|
+
<ContentRenderers renderers={RENDERERS}>{app}</ContentRenderers>;
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
It highlights at most `TEXT_PREVIEW_MAX_BYTES` (1 MiB, from `@orz/ui-content`), so a path that
|
|
120
|
+
reaches it with a large file cannot freeze the tab:
|
|
121
|
+
|
|
122
|
+
- A file whose `size` is over `TEXT_PREVIEW_HARD_MAX_BYTES` (10 MiB) is not loaded: it draws the
|
|
123
|
+
registry's fallback and "This file is too large to preview."
|
|
124
|
+
- Text over 1 MiB, declared or found once loaded, is drawn plain: still a virtualized code view
|
|
125
|
+
with line numbers for a file, but with no highlighting, which is what costs. A file's plain view
|
|
126
|
+
is capped at the viewer's pane height, or at a card's collapsed height in a document. The viewer
|
|
127
|
+
and `attachmentCard` ask before they draw a file that large.
|
|
128
|
+
|
|
129
|
+
### `patchContent`
|
|
130
|
+
|
|
131
|
+
Draws a unified diff as one diff per file, each commit's subject, author, date and hash in a row
|
|
132
|
+
above its message and files, with a file list (with `+`/`−` counts, jumping to a file) and a *raw*
|
|
133
|
+
toggle back to the text as written. It accepts a source tagged `diff` or `patch` (a fenced block)
|
|
134
|
+
and a file `isPatchFile` calls a patch, in either placement, so it goes before `codeContent`:
|
|
135
|
+
|
|
136
|
+
```tsx
|
|
137
|
+
const RENDERERS = [attachmentCard(), markdownContent(), patchContent, codeContent];
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
- `format-patch` mail, `show` / `log -p` output and a plain `diff -u` all read. The mail headers
|
|
141
|
+
and the `--stat` are left to *raw*; a `diff` command line or an `Index:` banner above a plain diff
|
|
142
|
+
names no commit.
|
|
143
|
+
- Parsing is strict: a hunk whose line counts do not add up, or text with no file in it, shows the
|
|
144
|
+
text instead, rather than a patch with a change silently missing. A fence that is not a patch,
|
|
145
|
+
such as a bare `+`/`-` snippet, stays highlighted text; a file that is not one says so above it.
|
|
146
|
+
- The file list and *raw* go into the title bar a surrounding surface lends (`useContentTitleBar`;
|
|
147
|
+
`attachmentCard` lends its card's). With none, the patch draws a framed bar of its own, which is
|
|
148
|
+
what a fence gets, so a fence inside a markdown card never takes over the card's bar.
|
|
149
|
+
- The diffs scroll inside a height-capped box, which is what lets pierre virtualize them: 70vh in a
|
|
150
|
+
document, the viewer's 75vh pane less its bar (maximized too, for now). Inside a card, whose
|
|
151
|
+
collapsed body hides its overflow, they do not scroll and the file list does not jump; the card
|
|
152
|
+
expands instead. That assumes the card's `collapsedClassName` caps it below one screen: past it,
|
|
153
|
+
the patch is cut at one screen.
|
|
154
|
+
- A file diff over `LARGE_DIFF_LINES` (2,000) changed lines is drawn as its header row with a
|
|
155
|
+
*Load diff* button, unless it is the patch's only file: not for speed (a 40,000-line file drew in
|
|
156
|
+
0.1 s) but so a generated file does not bury the files around it.
|
|
157
|
+
- A file is loaded and capped as `codeContent` caps it, except that a patch up to 10 MiB is never
|
|
158
|
+
asked about: its view is virtualized per file, and a 20 MiB one held the tab for 0.6 s at most.
|
|
159
|
+
|
|
160
|
+
`PatchView` is the same view on its own, for a surface outside the registry:
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
<PatchView filename="fix.patch" text={text} lineNumbers invalidNote titleBar={bar} />
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### `@orz/ui-code/word-diff`
|
|
167
|
+
|
|
168
|
+
Importing it pulls in neither React nor pierre.
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
import { coalescedWordDiff, wordDiff } from "@orz/ui-code/word-diff";
|
|
172
|
+
|
|
173
|
+
const { ins, del } = coalescedWordDiff(oldText, newText);
|
|
174
|
+
// ins: [start, end) ranges in newText
|
|
175
|
+
// del: { at, from, to }, a caret in newText and the deleted range in oldText
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`wordDiff` is the bare word-level diff, for measuring how much changed. `coalescedWordDiff` folds
|
|
179
|
+
anchors lighter than the changes around them into those changes, which is what a reader should see
|
|
180
|
+
for a heavily rewritten line.
|
|
181
|
+
|
|
182
|
+
## Your own pierre surfaces
|
|
183
|
+
|
|
184
|
+
To render a pierre surface of your own that matches these blocks, build its options from the same
|
|
185
|
+
pieces. Naming the same `preferredHighlighter` is what keeps it on the shared singleton:
|
|
186
|
+
|
|
187
|
+
```tsx
|
|
188
|
+
import {
|
|
189
|
+
LazyMultiFileDiff,
|
|
190
|
+
PIERRE_HIGHLIGHTER,
|
|
191
|
+
PIERRE_THEME_TYPE,
|
|
192
|
+
useSyntaxTheme,
|
|
193
|
+
} from "@orz/ui-code";
|
|
194
|
+
|
|
195
|
+
const theme = useSyntaxTheme();
|
|
196
|
+
const options = useMemo(
|
|
197
|
+
() => ({
|
|
198
|
+
theme,
|
|
199
|
+
themeType: PIERRE_THEME_TYPE,
|
|
200
|
+
preferredHighlighter: PIERRE_HIGHLIGHTER,
|
|
201
|
+
}),
|
|
202
|
+
[theme],
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
<Suspense>
|
|
206
|
+
<LazyMultiFileDiff oldFile={oldFile} newFile={newFile} options={options} />
|
|
207
|
+
</Suspense>;
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Failures
|
|
211
|
+
|
|
212
|
+
- While pierre's chunk loads and until it renders its first line, the block shows its text as a
|
|
213
|
+
plain `<pre>`.
|
|
214
|
+
- When highlighting fails, the block falls back to the same plain text and logs one `console.warn`
|
|
215
|
+
naming the file.
|
|
216
|
+
- A highlight that fails in the background leaves the plain text up, with no warning: pierre 1.6
|
|
217
|
+
only logs those itself.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Whether `text` takes more than `max` bytes as UTF-8, counted without encoding it, and stopping
|
|
3
|
+
* as soon as it is over.
|
|
4
|
+
*/
|
|
5
|
+
export declare function exceedsBytes(text: string, max: number): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Highlights `contents` when it is within the cap, and draws it as plain text otherwise: pierre
|
|
8
|
+
* over a multi-megabyte text would freeze the tab (T-31), where plain text costs next to nothing.
|
|
9
|
+
* Plain text is still a virtualized code view, so it needs `capClassName` to bound its height:
|
|
10
|
+
* laying out every line of a large file is a cost of its own. With `offThread`, highlighted text
|
|
11
|
+
* tokenizes in the worker a `CodeWorkerProvider` lends, inside that same cap.
|
|
12
|
+
*/
|
|
13
|
+
export declare function CappedCodeBlock({ filename, contents, lineNumbers, capClassName, offThread, }: {
|
|
14
|
+
filename: string;
|
|
15
|
+
contents: string;
|
|
16
|
+
lineNumbers?: boolean;
|
|
17
|
+
/** The box a text over the cap is drawn in; a height cap with its own overflow. */
|
|
18
|
+
capClassName?: string;
|
|
19
|
+
offThread?: boolean;
|
|
20
|
+
}): import("react").JSX.Element;
|
|
21
|
+
//# sourceMappingURL=capped-code-block.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capped-code-block.d.ts","sourceRoot":"","sources":["../src/capped-code-block.tsx"],"names":[],"mappings":"AAIA;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAiB/D;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,SAAiB,GAClB,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,mFAAmF;IACnF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,+BAeA"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { TEXT_PREVIEW_MAX_BYTES } from "@orz/ui-content";
|
|
3
|
+
import { useMemo } from "react";
|
|
4
|
+
import { CodeBlock } from "./code-block.js";
|
|
5
|
+
/**
|
|
6
|
+
* Whether `text` takes more than `max` bytes as UTF-8, counted without encoding it, and stopping
|
|
7
|
+
* as soon as it is over.
|
|
8
|
+
*/
|
|
9
|
+
export function exceedsBytes(text, max) {
|
|
10
|
+
// Every UTF-16 unit is at least one byte and at most three.
|
|
11
|
+
if (text.length > max)
|
|
12
|
+
return true;
|
|
13
|
+
if (text.length * 3 <= max)
|
|
14
|
+
return false;
|
|
15
|
+
let bytes = 0;
|
|
16
|
+
for (let i = 0; i < text.length; i++) {
|
|
17
|
+
const unit = text.charCodeAt(i);
|
|
18
|
+
// A surrogate is half of a four-byte character.
|
|
19
|
+
bytes +=
|
|
20
|
+
unit < 0x80
|
|
21
|
+
? 1
|
|
22
|
+
: unit < 0x800 || (unit >= 0xd800 && unit < 0xe000)
|
|
23
|
+
? 2
|
|
24
|
+
: 3;
|
|
25
|
+
if (bytes > max)
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Highlights `contents` when it is within the cap, and draws it as plain text otherwise: pierre
|
|
32
|
+
* over a multi-megabyte text would freeze the tab (T-31), where plain text costs next to nothing.
|
|
33
|
+
* Plain text is still a virtualized code view, so it needs `capClassName` to bound its height:
|
|
34
|
+
* laying out every line of a large file is a cost of its own. With `offThread`, highlighted text
|
|
35
|
+
* tokenizes in the worker a `CodeWorkerProvider` lends, inside that same cap.
|
|
36
|
+
*/
|
|
37
|
+
export function CappedCodeBlock({ filename, contents, lineNumbers, capClassName, offThread = false, }) {
|
|
38
|
+
const tooLarge = useMemo(() => exceedsBytes(contents, TEXT_PREVIEW_MAX_BYTES), [contents]);
|
|
39
|
+
return (_jsx(CodeBlock, { filename: filename, contents: contents, lineNumbers: lineNumbers, plain: tooLarge, offThread: offThread, className: tooLarge || offThread ? capClassName : undefined }));
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=capped-code-block.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capped-code-block.js","sourceRoot":"","sources":["../src/capped-code-block.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAkB,CAAC;AAE7C;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,GAAW;IACpD,4DAA4D;IAC5D,IAAI,IAAI,CAAC,MAAM,GAAG,GAAG;QAAE,OAAO,IAAI,CAAC;IACnC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,GAAG;QAAE,OAAO,KAAK,CAAC;IACzC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAChC,gDAAgD;QAChD,KAAK;YACH,IAAI,GAAG,IAAI;gBACT,CAAC,CAAC,CAAC;gBACH,CAAC,CAAC,IAAI,GAAG,KAAK,IAAI,CAAC,IAAI,IAAI,MAAM,IAAI,IAAI,GAAG,MAAM,CAAC;oBACjD,CAAC,CAAC,CAAC;oBACH,CAAC,CAAC,CAAC,CAAC;QACV,IAAI,KAAK,GAAG,GAAG;YAAE,OAAO,IAAI,CAAC;IAC/B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,EAC9B,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,SAAS,GAAG,KAAK,GAQlB;IACC,MAAM,QAAQ,GAAG,OAAO,CACtB,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,sBAAsB,CAAC,EACpD,CAAC,QAAQ,CAAC,CACX,CAAC;IACF,OAAO,CACL,KAAC,SAAS,IACR,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,QAAQ,EAClB,WAAW,EAAE,WAAW,EACxB,KAAK,EAAE,QAAQ,EACf,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,QAAQ,IAAI,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,SAAS,GAC3D,CACH,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import type { CodeViewItem, ParsedPatch } from "@pierre/diffs";
|
|
2
|
+
import type { CodeViewHandle, MultiFileDiffProps } from "@pierre/diffs/react";
|
|
3
|
+
import { type ComponentType, type ReactNode, type Ref } from "react";
|
|
4
|
+
export declare const LazyMultiFileDiff: import("react").LazyExoticComponent<ComponentType<MultiFileDiffProps<undefined, undefined>>>;
|
|
5
|
+
/**
|
|
6
|
+
* A unified diff split into commits and their files, or null when the text is
|
|
7
|
+
* not one. Strict on purpose: pierre's lenient mode skips a hunk whose counts
|
|
8
|
+
* do not add up and carries on, so the reader would be shown a patch with a
|
|
9
|
+
* change silently missing. Text before the first file — a commit message — is
|
|
10
|
+
* no reason to refuse; a patch with no file in it is.
|
|
11
|
+
*/
|
|
12
|
+
export declare function parsePatch(text: string): Promise<ParsedPatch[] | null>;
|
|
13
|
+
export declare const PIERRE_THEME: {
|
|
14
|
+
readonly dark: "pierre-dark";
|
|
15
|
+
readonly light: "pierre-light";
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* "system" makes pierre inject no color-scheme pin of its own; the actual
|
|
19
|
+
* scheme is forced from the outer tree by the `diffs-container` rules in
|
|
20
|
+
* this package's styles.css, which follow the app theme (T-36). Options objects that
|
|
21
|
+
* captured this value at module scope stay correct for the same reason.
|
|
22
|
+
*/
|
|
23
|
+
export declare const PIERRE_THEME_TYPE: "system";
|
|
24
|
+
export type SyntaxTheme = {
|
|
25
|
+
light: string;
|
|
26
|
+
dark: string;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Syntax token colors for the active theme (T-144). Under "system" each slot
|
|
30
|
+
* takes its own palette's shiki theme, because the light/dark choice is then
|
|
31
|
+
* made by CSS light-dark() inside the shadow root rather than by a React
|
|
32
|
+
* render. A pinned mode or a one-palette theme fills both slots from the side
|
|
33
|
+
* on screen: the slot the browser will not paint is irrelevant. A palette
|
|
34
|
+
* with no shiki theme keeps that slot of pierre's own pair.
|
|
35
|
+
*/
|
|
36
|
+
export declare function useSyntaxTheme(): SyntaxTheme;
|
|
37
|
+
/** The shape every code surface falls back to: the code, unhighlighted. */
|
|
38
|
+
export declare function PlainCodeFallback({ contents }: {
|
|
39
|
+
contents: string;
|
|
40
|
+
}): import("react").JSX.Element;
|
|
41
|
+
/**
|
|
42
|
+
* pierre defaults to "shiki-js", which transpiles the TextMate grammars'
|
|
43
|
+
* Oniguruma patterns into native RegExp through oniguruma-to-es. That
|
|
44
|
+
* transpiler is a recursive-descent pass over patterns, and the TypeScript
|
|
45
|
+
* grammar's giant lookaheads overflow SpiderMonkey's much shallower stack:
|
|
46
|
+
* Firefox rendered `too much recursion` in place of every `ts` fence
|
|
47
|
+
* (T-177). The wasm build is real Oniguruma and never runs that pass.
|
|
48
|
+
*
|
|
49
|
+
* pierre's highlighter is a module singleton whose engine is fixed by
|
|
50
|
+
* whichever surface creates it first, so every options object in the app has
|
|
51
|
+
* to name the same one — snippets, files, and the revision diff alike.
|
|
52
|
+
*/
|
|
53
|
+
export declare const PIERRE_HIGHLIGHTER: "shiki-wasm";
|
|
54
|
+
/** px; the header rows `PatchFiles` callers draw must be exactly this tall. */
|
|
55
|
+
export declare const PATCH_HEADER_HEIGHT = 36;
|
|
56
|
+
/**
|
|
57
|
+
* A synthetic filename whose extension carries the fence's language.
|
|
58
|
+
* Passing the tag as `lang` instead would reject on unknown languages;
|
|
59
|
+
* filename inference degrades to plain text.
|
|
60
|
+
*/
|
|
61
|
+
export declare function fenceFilename(tag: string | undefined): string;
|
|
62
|
+
/**
|
|
63
|
+
* Read-only code rendering through pierre CodeView: syntax highlighting
|
|
64
|
+
* from the filename, dark/light theme in step with the revision diffs.
|
|
65
|
+
* Until pierre renders its first line a plain `<pre>` shows the same text, so
|
|
66
|
+
* content stays readable through both chunk loading and highlighter startup.
|
|
67
|
+
*
|
|
68
|
+
* pierre's virtualization scaffold declares `contain: inline-size`, so this
|
|
69
|
+
* block contributes 0px to an ancestor's intrinsic width: in a content-sized
|
|
70
|
+
* column (horizontal flex item, table cell, `max-content`) the code wraps at
|
|
71
|
+
* the width of the prose beside it — 30 lines came out 5416px tall in a 109px
|
|
72
|
+
* column, and a fence with no prose left the column 0px wide (T-287). Any such
|
|
73
|
+
* column has to be made to fill its row.
|
|
74
|
+
*/
|
|
75
|
+
export declare function CodeBlock({ filename, contents, lineNumbers, plain, offThread, className, }: {
|
|
76
|
+
filename: string;
|
|
77
|
+
contents: string;
|
|
78
|
+
/** Line numbers suit whole files; snippets read better without. */
|
|
79
|
+
lineNumbers?: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* No highlighting. Tokenizing is what costs: virtualized lines spare the
|
|
82
|
+
* DOM, but pierre tokenizes the whole file regardless, 11 s for 1 MiB of
|
|
83
|
+
* log where plain text took 0.16 s.
|
|
84
|
+
*/
|
|
85
|
+
plain?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Tokenize in pierre's worker pool, when a `CodeWorkerProvider` lends one.
|
|
88
|
+
* Only pays off with `className` capping the height: uncapped, laying out
|
|
89
|
+
* every line of 1 MiB still held the main thread for 2.2 s.
|
|
90
|
+
*/
|
|
91
|
+
offThread?: boolean;
|
|
92
|
+
/** Caps the height for a long file: CodeView virtualizes against its box. */
|
|
93
|
+
className?: string;
|
|
94
|
+
}): import("react").JSX.Element;
|
|
95
|
+
/**
|
|
96
|
+
* One code block shown as the line-level diff between two versions of it:
|
|
97
|
+
* red for the lines the baseline had, green for the ones that replaced them
|
|
98
|
+
* (T-343).
|
|
99
|
+
*
|
|
100
|
+
* Nothing is injected into the code. pierre is handed both bodies as plain
|
|
101
|
+
* strings and does its own diffing, highlighting and theming, so T-31's
|
|
102
|
+
* constraint — a mark reaching inside a fence deletes the code it wraps — is
|
|
103
|
+
* never engaged.
|
|
104
|
+
*/
|
|
105
|
+
export declare function CodeDiffBlock({ filename, before, after, }: {
|
|
106
|
+
filename: string;
|
|
107
|
+
before: string;
|
|
108
|
+
after: string;
|
|
109
|
+
}): import("react").JSX.Element;
|
|
110
|
+
/** The CodeView item id of a patch's `index`-th file, counted across commits. */
|
|
111
|
+
export declare function patchFileId(index: number): string;
|
|
112
|
+
/** The CodeView item id of a commit's message, counted across the patch. */
|
|
113
|
+
export declare function patchCommitId(index: number): string;
|
|
114
|
+
/**
|
|
115
|
+
* A parsed patch in one virtualized CodeView: per commit, its message, then
|
|
116
|
+
* one diff per file.
|
|
117
|
+
*
|
|
118
|
+
* `className` must cap the height. CodeView virtualizes against its own box,
|
|
119
|
+
* so an uncapped one grows to the whole patch and lays every line out: a
|
|
120
|
+
* 1 MB, 116-file patch froze the tab for 15 s in one task that way, against
|
|
121
|
+
* under 0.3 s capped.
|
|
122
|
+
*
|
|
123
|
+
* A highlighter failure takes the whole patch back to its plain text rather
|
|
124
|
+
* than one file: the grammar that failed is likely shared by its neighbours.
|
|
125
|
+
*/
|
|
126
|
+
export declare function PatchFiles({ patches, messages, text, className, viewRef, collapsedFiles, renderHeader, }: {
|
|
127
|
+
patches: ParsedPatch[];
|
|
128
|
+
/**
|
|
129
|
+
* Each patch's commit message, index for index with `patches`, drawn as an
|
|
130
|
+
* item of its own above its files: an empty one is only its header row, and
|
|
131
|
+
* null gets no item at all.
|
|
132
|
+
*/
|
|
133
|
+
messages: (string | null)[];
|
|
134
|
+
/** The patch as written, for the plain-text fallback. */
|
|
135
|
+
text: string;
|
|
136
|
+
className: string;
|
|
137
|
+
/** For scrolling to an item by `patchFileId`. */
|
|
138
|
+
viewRef?: Ref<CodeViewHandle<undefined, undefined>>;
|
|
139
|
+
/** Files, by position across the patch, drawn as their header row only. */
|
|
140
|
+
collapsedFiles?: ReadonlySet<number>;
|
|
141
|
+
/**
|
|
142
|
+
* Every item's header row, `PATCH_HEADER_HEIGHT` tall: pierre reserves that
|
|
143
|
+
* height for it without measuring.
|
|
144
|
+
*/
|
|
145
|
+
renderHeader: (item: CodeViewItem<undefined>) => ReactNode;
|
|
146
|
+
}): import("react").JSX.Element;
|
|
147
|
+
//# sourceMappingURL=code-block.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-block.d.ts","sourceRoot":"","sources":["../src/code-block.tsx"],"names":[],"mappings":"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"}
|