@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
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Word-like tokens of `text`, weighted by how many characters each spends.
3
+ * This is what `alignGroups` scores a candidate pair with (T-211): the
4
+ * question there is only "are these two the same block", and a bag of words
5
+ * answers it without the quadratic cost of a real diff — which matters once
6
+ * the run being scored is a whole rewritten section rather than one line
7
+ * hunk. Spaces and punctuation stay out: T-180 already found those to be
8
+ * the anchors that mean nothing.
9
+ *
10
+ * What it weighs is prose. A candidate that can also hold pictures goes
11
+ * through `bagWith`, which counts those by identity instead (T-239).
12
+ */
13
+ export type WordBag = {
14
+ weights: Map<string, number>;
15
+ total: number;
16
+ };
17
+ export declare function wordBag(text: string): WordBag;
18
+ /** The bag of `text`, plus one entry for each of `urls`.
19
+ *
20
+ * An image used to reach the bag as its markdown source, and a word is weighed
21
+ * by its characters, so `/api/projects/<slug>/attachments/<id>/download/` —
22
+ * the path every attachment in one deployment shares — became the vocabulary
23
+ * two unrelated tables had most in common (T-239). Two tables sharing nothing
24
+ * but the fact of holding attachments scored 0.69 against each other while a
25
+ * table and its real counterpart scored 0.22, and both numbers get worse the
26
+ * longer the deployment spells that prefix.
27
+ *
28
+ * A url is an identity, not prose: worth counting that this picture is that
29
+ * picture, worth nothing to count how many characters the path to it spends.
30
+ * So each one lands as a single fixed-weight entry under a key no prose can
31
+ * collide with — `wordBag` keeps `isWordLike` segments only, which one
32
+ * non-word character in front is enough to stay clear of.
33
+ */
34
+ export declare function bagWith(text: string, urls: string[]): WordBag;
35
+ /** Half-open character range `[start, end)`. */
36
+ export type WordDiffRange = {
37
+ start: number;
38
+ end: number;
39
+ };
40
+ /**
41
+ * Text present in the old side and gone from the new one. `at` is where it
42
+ * sat in the new text (a caret, not a span — deleted text has no new-side
43
+ * extent); `from`/`to` locate it in the old text so the caller can quote it.
44
+ */
45
+ export type WordDiffDeletion = {
46
+ at: number;
47
+ from: number;
48
+ to: number;
49
+ };
50
+ export type WordDiffResult = {
51
+ ins: WordDiffRange[];
52
+ del: WordDiffDeletion[];
53
+ };
54
+ /**
55
+ * Word-level edits with the anchors that cost more than they earn folded into
56
+ * the change around them (T-180).
57
+ *
58
+ * A heavily rewritten line still shares spaces, punctuation and the odd single
59
+ * character with the line it replaced, and every one of those the word diff
60
+ * keeps cuts the rewrite apart: the reader is told `format.ts` became `引入`
61
+ * and `East` became `sindresorhus,纯`, pairings that carry no meaning, while
62
+ * the sentence's genuinely untouched tail drowns among the fragments. An
63
+ * anchor is only worth drawing around when it outweighs the change on *both*
64
+ * sides of it; a lighter one is folded in, and the two sides each run together
65
+ * into a single coherent before and after.
66
+ *
67
+ * No threshold is involved, which is the point: a small edit keeps every
68
+ * anchor it has and comes out word-level exactly as it did before, while a
69
+ * line rewritten past the last surviving anchor degrades into the whole-block
70
+ * before/after T-142 already renders for a pair that shares nothing. Weight is
71
+ * in characters and a tie leaves the anchor standing — the conservative of the
72
+ * two tiers T-180 weighed, and tightening it later is the one comparison
73
+ * below.
74
+ *
75
+ * `wordDiff` keeps its bare output beside this one: coalescing answers "what
76
+ * should the reader see", and anything measuring how much genuinely changed
77
+ * would read a folded-in anchor as change that is not there.
78
+ */
79
+ export declare function coalescedWordDiff(oldText: string, newText: string): WordDiffResult;
80
+ /** Word-level edits between two texts, as offsets into each side. */
81
+ export declare function wordDiff(oldText: string, newText: string): WordDiffResult;
82
+ //# sourceMappingURL=word-diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"word-diff.d.ts","sourceRoot":"","sources":["../src/word-diff.ts"],"names":[],"mappings":"AA0BA;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,OAAO,GAAG;IAAE,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtE,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAS7C;AAYD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAQ7D;AAED,gDAAgD;AAChD,MAAM,MAAM,aAAa,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAExE,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,EAAE,aAAa,EAAE,CAAC;IACrB,GAAG,EAAE,gBAAgB,EAAE,CAAC;CACzB,CAAC;AAsCF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,cAAc,CAuDhB;AAED,qEAAqE;AACrE,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,cAAc,CA4BzE"}
@@ -0,0 +1,209 @@
1
+ import { Diff } from "diff";
2
+ /**
3
+ * Word segmentation that works without spaces. jsdiff's own `diffWords`
4
+ * splits on whitespace, so an edited Chinese sentence is one giant token
5
+ * and the whole paragraph reads as changed — the exact complaint T-142
6
+ * exists to fix. `Intl.Segmenter` knows where words end in CJK as well as
7
+ * in English, and keeps whitespace and punctuation as their own segments,
8
+ * which is what makes "one word replaced" come out as one word replaced.
9
+ */
10
+ const segmenter = new Intl.Segmenter(undefined, { granularity: "word" });
11
+ class WordDiff extends Diff {
12
+ tokenize(value) {
13
+ const tokens = [];
14
+ for (const { segment } of segmenter.segment(value))
15
+ tokens.push(segment);
16
+ return tokens;
17
+ }
18
+ join(tokens) {
19
+ return tokens.join("");
20
+ }
21
+ }
22
+ const differ = new WordDiff();
23
+ export function wordBag(text) {
24
+ const weights = new Map();
25
+ let total = 0;
26
+ for (const { segment, isWordLike } of segmenter.segment(text)) {
27
+ if (isWordLike !== true)
28
+ continue;
29
+ weights.set(segment, (weights.get(segment) ?? 0) + segment.length);
30
+ total += segment.length;
31
+ }
32
+ return { weights, total };
33
+ }
34
+ /**
35
+ * What one picture is worth against one character of prose. It is a rate, not
36
+ * a threshold — it rules nothing out, it only gives an image enough weight to
37
+ * take part in the score at all. T-239 swept 1 through 20 and every fixture
38
+ * came out identical; at 40 one picture outweighs all the prose beside it and
39
+ * a table that lost an image stops resembling its own counterpart, so the
40
+ * bottom of the flat range is what this takes.
41
+ */
42
+ const IMAGE_WEIGHT = 1;
43
+ /** The bag of `text`, plus one entry for each of `urls`.
44
+ *
45
+ * An image used to reach the bag as its markdown source, and a word is weighed
46
+ * by its characters, so `/api/projects/<slug>/attachments/<id>/download/` —
47
+ * the path every attachment in one deployment shares — became the vocabulary
48
+ * two unrelated tables had most in common (T-239). Two tables sharing nothing
49
+ * but the fact of holding attachments scored 0.69 against each other while a
50
+ * table and its real counterpart scored 0.22, and both numbers get worse the
51
+ * longer the deployment spells that prefix.
52
+ *
53
+ * A url is an identity, not prose: worth counting that this picture is that
54
+ * picture, worth nothing to count how many characters the path to it spends.
55
+ * So each one lands as a single fixed-weight entry under a key no prose can
56
+ * collide with — `wordBag` keeps `isWordLike` segments only, which one
57
+ * non-word character in front is enough to stay clear of.
58
+ */
59
+ export function bagWith(text, urls) {
60
+ const bag = wordBag(text);
61
+ for (const url of urls) {
62
+ const key = `\u0000${url}`;
63
+ bag.weights.set(key, (bag.weights.get(key) ?? 0) + IMAGE_WEIGHT);
64
+ bag.total += IMAGE_WEIGHT;
65
+ }
66
+ return bag;
67
+ }
68
+ function diffRuns(oldText, newText) {
69
+ const runs = [];
70
+ let edit = null;
71
+ for (const part of differ.diff(oldText, newText)) {
72
+ if (part.added || part.removed) {
73
+ if (edit === null) {
74
+ edit = { kind: "edit", del: "", ins: "" };
75
+ runs.push(edit);
76
+ }
77
+ if (part.added)
78
+ edit.ins += part.value;
79
+ else
80
+ edit.del += part.value;
81
+ continue;
82
+ }
83
+ edit = null;
84
+ const last = runs.at(-1);
85
+ if (last !== undefined && last.kind === "eq")
86
+ last.text += part.value;
87
+ else
88
+ runs.push({ kind: "eq", text: part.value });
89
+ }
90
+ return runs;
91
+ }
92
+ /** How much a run of changed text weighs against a neighbouring anchor. */
93
+ function weightOf(run) {
94
+ return Math.max(run.del.length, run.ins.length);
95
+ }
96
+ /**
97
+ * Word-level edits with the anchors that cost more than they earn folded into
98
+ * the change around them (T-180).
99
+ *
100
+ * A heavily rewritten line still shares spaces, punctuation and the odd single
101
+ * character with the line it replaced, and every one of those the word diff
102
+ * keeps cuts the rewrite apart: the reader is told `format.ts` became `引入`
103
+ * and `East` became `sindresorhus,纯`, pairings that carry no meaning, while
104
+ * the sentence's genuinely untouched tail drowns among the fragments. An
105
+ * anchor is only worth drawing around when it outweighs the change on *both*
106
+ * sides of it; a lighter one is folded in, and the two sides each run together
107
+ * into a single coherent before and after.
108
+ *
109
+ * No threshold is involved, which is the point: a small edit keeps every
110
+ * anchor it has and comes out word-level exactly as it did before, while a
111
+ * line rewritten past the last surviving anchor degrades into the whole-block
112
+ * before/after T-142 already renders for a pair that shares nothing. Weight is
113
+ * in characters and a tie leaves the anchor standing — the conservative of the
114
+ * two tiers T-180 weighed, and tightening it later is the one comparison
115
+ * below.
116
+ *
117
+ * `wordDiff` keeps its bare output beside this one: coalescing answers "what
118
+ * should the reader see", and anything measuring how much genuinely changed
119
+ * would read a folded-in anchor as change that is not there.
120
+ */
121
+ export function coalescedWordDiff(oldText, newText) {
122
+ if (oldText === newText)
123
+ return { ins: [], del: [] };
124
+ const runs = diffRuns(oldText, newText);
125
+ // The first and last run are never anchors with a change on either side, so
126
+ // the text a line opens and closes with survives however heavy the rewrite
127
+ // between them — which is how the evidence line keeps its untouched tail.
128
+ for (let i = 1; i + 1 < runs.length;) {
129
+ const anchor = runs[i];
130
+ const before = runs[i - 1];
131
+ const after = runs[i + 1];
132
+ if (anchor?.kind !== "eq" ||
133
+ before?.kind !== "edit" ||
134
+ after?.kind !== "edit" ||
135
+ anchor.text.length >= weightOf(before) ||
136
+ anchor.text.length >= weightOf(after)) {
137
+ i++;
138
+ continue;
139
+ }
140
+ runs.splice(i - 1, 3, {
141
+ kind: "edit",
142
+ del: before.del + anchor.text + after.del,
143
+ ins: before.ins + anchor.text + after.ins,
144
+ });
145
+ // Leaving `i` where it is lands on the anchor after the merged run, which
146
+ // now weighs itself against the heavier thing folding produced — that is
147
+ // how a run of light anchors collapses together in one pass. An anchor
148
+ // already found heavy enough is never revisited: letting a growing
149
+ // neighbour come back for it would erode surviving anchors one at a time
150
+ // until only the first and last stood, which is the aggressive tier T-180
151
+ // ruled against.
152
+ }
153
+ const ins = [];
154
+ const del = [];
155
+ let oldPos = 0;
156
+ let newPos = 0;
157
+ for (const run of runs) {
158
+ if (run.kind === "eq") {
159
+ oldPos += run.text.length;
160
+ newPos += run.text.length;
161
+ continue;
162
+ }
163
+ if (run.del !== "") {
164
+ del.push({ at: newPos, from: oldPos, to: oldPos + run.del.length });
165
+ oldPos += run.del.length;
166
+ }
167
+ if (run.ins !== "") {
168
+ ins.push({ start: newPos, end: newPos + run.ins.length });
169
+ newPos += run.ins.length;
170
+ }
171
+ }
172
+ return { ins, del };
173
+ }
174
+ /** Word-level edits between two texts, as offsets into each side. */
175
+ export function wordDiff(oldText, newText) {
176
+ const ins = [];
177
+ const del = [];
178
+ if (oldText === newText)
179
+ return { ins, del };
180
+ let oldPos = 0;
181
+ let newPos = 0;
182
+ for (const part of differ.diff(oldText, newText)) {
183
+ const length = part.value.length;
184
+ if (part.added) {
185
+ const last = ins.at(-1);
186
+ if (last !== undefined && last.end === newPos)
187
+ last.end = newPos + length;
188
+ else
189
+ ins.push({ start: newPos, end: newPos + length });
190
+ newPos += length;
191
+ }
192
+ else if (part.removed) {
193
+ const last = del.at(-1);
194
+ if (last !== undefined && last.at === newPos && last.to === oldPos) {
195
+ last.to = oldPos + length;
196
+ }
197
+ else {
198
+ del.push({ at: newPos, from: oldPos, to: oldPos + length });
199
+ }
200
+ oldPos += length;
201
+ }
202
+ else {
203
+ oldPos += length;
204
+ newPos += length;
205
+ }
206
+ }
207
+ return { ins, del };
208
+ }
209
+ //# sourceMappingURL=word-diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"word-diff.js","sourceRoot":"","sources":["../src/word-diff.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,66 @@
1
1
  {
2
2
  "name": "@orz/ui-code",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "version": "0.0.0",
4
+ "description": "Syntax-highlighted code and diff blocks on pierre, a CJK-aware word differ, and the code renderer for the @orz/ui-content registry",
5
+ "license": "BSD-3-Clause",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/orzFly/ui.git",
9
+ "directory": "packages/ui-code"
10
+ },
11
+ "type": "module",
12
+ "sideEffects": [
13
+ "**/*.css"
14
+ ],
15
+ "exports": {
16
+ ".": {
17
+ "@orz/source": "./src/index.ts",
18
+ "types": "./dist/index.d.ts",
19
+ "default": "./dist/index.js"
20
+ },
21
+ "./word-diff": {
22
+ "@orz/source": "./src/word-diff.ts",
23
+ "types": "./dist/word-diff.d.ts",
24
+ "default": "./dist/word-diff.js"
25
+ },
26
+ "./styles.css": "./styles.css"
27
+ },
28
+ "files": [
29
+ "dist",
30
+ "styles.css"
31
+ ],
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "peerDependencies": {
36
+ "@pierre/diffs": "^1.6.1",
37
+ "lucide-react": "^1",
38
+ "react": "^19",
39
+ "react-dom": "^19",
40
+ "@orz/ui": "^0.0.0",
41
+ "@orz/ui-content": "^0.0.0"
42
+ },
43
+ "dependencies": {
44
+ "diff": "^9.0.0"
45
+ },
46
+ "devDependencies": {
47
+ "@pierre/diffs": "^1.6.1",
48
+ "@testing-library/react": "^16.3.2",
49
+ "@types/node": "^22.20.5",
50
+ "@types/react": "^19.2.18",
51
+ "@types/react-dom": "^19.2.4",
52
+ "happy-dom": "^21.0.3",
53
+ "lucide-react": "^1.31.0",
54
+ "react": "^19.2.8",
55
+ "react-dom": "^19.2.8",
56
+ "vitest": "^5.0.5",
57
+ "@orz/themes": "0.0.0",
58
+ "@orz/ui": "0.0.0",
59
+ "@orz/ui-content": "0.0.0"
60
+ },
61
+ "scripts": {
62
+ "build": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.build.json",
63
+ "typecheck": "tsc --noEmit",
64
+ "test": "vitest run"
65
+ }
6
66
  }
package/styles.css ADDED
@@ -0,0 +1,34 @@
1
+ /* Tailwind resolves an imported file's @source against that file, so a
2
+ consumer importing this stylesheet scans the classes in the package's
3
+ shipped JS without writing an @source of its own. */
4
+ @source "./dist";
5
+
6
+ /* Every pierre code surface renders in a shadow root and resolves its
7
+ palette with light-dark(); declarations from the outer tree win over
8
+ shadow :host rules, which is what lets these lines keep pierre in step
9
+ with the app theme.
10
+
11
+ The bg/fg pairs override what the syntax theme wrote into :host, and every
12
+ other chrome color pierre uses — line-number gutter, context rows, hunk
13
+ separators, the addition/deletion tints — is color-mix()'d off them, so
14
+ aligning these four aligns the whole surface, including themes with no
15
+ syntax counterpart to map to (T-144). */
16
+ diffs-container {
17
+ color-scheme: light;
18
+ --diffs-light-bg: var(--background);
19
+ --diffs-dark-bg: var(--background);
20
+ --diffs-light: var(--foreground);
21
+ --diffs-dark: var(--foreground);
22
+ }
23
+ .dark diffs-container {
24
+ color-scheme: dark;
25
+ }
26
+
27
+ /* A patch's +/- counts take the theme's diff colours, mixed toward the
28
+ foreground so small text stays legible. */
29
+ .diff-addition-text {
30
+ color: color-mix(in oklab, var(--foreground) 75%, var(--diff-addition));
31
+ }
32
+ .diff-deletion-text {
33
+ color: color-mix(in oklab, var(--foreground) 75%, var(--diff-deletion));
34
+ }