@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.
Files changed (44) hide show
  1. package/LICENSE +29 -0
  2. package/README.md +216 -2
  3. package/dist/capped-code-block.d.ts +21 -0
  4. package/dist/capped-code-block.d.ts.map +1 -0
  5. package/dist/capped-code-block.js +41 -0
  6. package/dist/capped-code-block.js.map +1 -0
  7. package/dist/code-block.d.ts +147 -0
  8. package/dist/code-block.d.ts.map +1 -0
  9. package/dist/code-block.js +494 -0
  10. package/dist/code-block.js.map +1 -0
  11. package/dist/code-worker.d.ts +28 -0
  12. package/dist/code-worker.d.ts.map +1 -0
  13. package/dist/code-worker.js +29 -0
  14. package/dist/code-worker.js.map +1 -0
  15. package/dist/content.d.ts +12 -0
  16. package/dist/content.d.ts.map +1 -0
  17. package/dist/content.js +35 -0
  18. package/dist/content.js.map +1 -0
  19. package/dist/file-text.d.ts +16 -0
  20. package/dist/file-text.d.ts.map +1 -0
  21. package/dist/file-text.js +37 -0
  22. package/dist/file-text.js.map +1 -0
  23. package/dist/index.d.ts +6 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +6 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/patch-commit.d.ts +27 -0
  28. package/dist/patch-commit.d.ts.map +1 -0
  29. package/dist/patch-commit.js +109 -0
  30. package/dist/patch-commit.js.map +1 -0
  31. package/dist/patch-content.d.ts +13 -0
  32. package/dist/patch-content.d.ts.map +1 -0
  33. package/dist/patch-content.js +43 -0
  34. package/dist/patch-content.js.map +1 -0
  35. package/dist/patch-view.d.ts +49 -0
  36. package/dist/patch-view.d.ts.map +1 -0
  37. package/dist/patch-view.js +167 -0
  38. package/dist/patch-view.js.map +1 -0
  39. package/dist/word-diff.d.ts +82 -0
  40. package/dist/word-diff.d.ts.map +1 -0
  41. package/dist/word-diff.js +209 -0
  42. package/dist/word-diff.js.map +1 -0
  43. package/package.json +63 -3
  44. 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
- # Temporary Holding Version
1
+ # @orz/ui-code
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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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"}