critique 0.1.122 → 0.1.127
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/dist/cli.js +1 -1
- package/dist/kv-codec.d.ts +16 -0
- package/dist/kv-codec.d.ts.map +1 -0
- package/dist/kv-codec.js +36 -0
- package/dist/kv-codec.test.d.ts +2 -0
- package/dist/kv-codec.test.d.ts.map +1 -0
- package/dist/kv-codec.test.js +48 -0
- package/dist/web-utils.d.ts +8 -1
- package/dist/web-utils.d.ts.map +1 -1
- package/dist/web-utils.js +105 -85
- package/dist/web-utils.test.d.ts.map +1 -1
- package/dist/web-utils.test.js +55 -6
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +73 -8
- package/dist/worker.preview-upload.e2e.test.d.ts +2 -0
- package/dist/worker.preview-upload.e2e.test.d.ts.map +1 -0
- package/dist/worker.preview-upload.e2e.test.js +106 -0
- package/package.json +3 -2
- package/src/cli.tsx +1 -1
- package/src/kv-codec.test.ts +67 -0
- package/src/kv-codec.ts +59 -0
- package/src/{web-utils.test.ts → web-utils.test.tsx} +71 -9
- package/src/web-utils.tsx +168 -102
- package/src/worker.preview-upload.e2e.test.ts +121 -0
- package/src/worker.tsx +111 -9
package/dist/cli.js
CHANGED
|
@@ -909,7 +909,7 @@ async function runWebMode(diffContent, options) {
|
|
|
909
909
|
// after the URL is printed, so the user sees the URL faster.
|
|
910
910
|
const { htmlDesktop, htmlMobile, ogImage } = await captureResponsiveHtml(diffContent, { desktopCols, mobileCols, baseRows, themeName, title: options.title, skipOgImage: true });
|
|
911
911
|
log("Uploading...");
|
|
912
|
-
const result = await uploadHtml(htmlDesktop, htmlMobile);
|
|
912
|
+
const result = await uploadHtml(htmlDesktop, htmlMobile, undefined, diffContent);
|
|
913
913
|
log(`\nPreview URL: ${result.url}`);
|
|
914
914
|
log(formatPreviewExpiry(result.expiresInDays));
|
|
915
915
|
if (typeof result.expiresInDays === "number") {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type KvValueMetadata = {
|
|
2
|
+
contentType?: string;
|
|
3
|
+
contentEncoding?: "gzip";
|
|
4
|
+
schemaVersion?: 1;
|
|
5
|
+
};
|
|
6
|
+
export declare const KV_SCHEMA_VERSION: 1;
|
|
7
|
+
export declare function buildKvPutOptions(ttlSeconds?: number, metadata?: KvValueMetadata): {
|
|
8
|
+
expirationTtl?: number;
|
|
9
|
+
metadata?: KvValueMetadata;
|
|
10
|
+
} | undefined;
|
|
11
|
+
export declare function gzipArrayBuffer(buffer: ArrayBuffer): Promise<ArrayBuffer>;
|
|
12
|
+
export declare function gunzipArrayBuffer(buffer: ArrayBuffer): Promise<ArrayBuffer>;
|
|
13
|
+
export declare function gzipText(value: string): Promise<ArrayBuffer>;
|
|
14
|
+
export declare function decodeTextFromKv(value: ArrayBuffer, metadata: KvValueMetadata | null): Promise<string>;
|
|
15
|
+
export declare function decodeBinaryFromKv(value: ArrayBuffer, metadata: KvValueMetadata | null): Promise<ArrayBuffer>;
|
|
16
|
+
//# sourceMappingURL=kv-codec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kv-codec.d.ts","sourceRoot":"","sources":["../src/kv-codec.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,eAAe,GAAG;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,aAAa,CAAC,EAAE,CAAC,CAAA;CAClB,CAAA;AAED,eAAO,MAAM,iBAAiB,EAAG,CAAU,CAAA;AAE3C,wBAAgB,iBAAiB,CAC/B,UAAU,CAAC,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,eAAe,GACzB;IAAE,aAAa,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,eAAe,CAAA;CAAE,GAAG,SAAS,CASpE;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAG/E;AAED,wBAAsB,iBAAiB,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAGjF;AAED,wBAAsB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAGlE;AAED,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,eAAe,GAAG,IAAI,GAC/B,OAAO,CAAC,MAAM,CAAC,CAKjB;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,eAAe,GAAG,IAAI,GAC/B,OAAO,CAAC,WAAW,CAAC,CAKtB"}
|
package/dist/kv-codec.js
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// KV value compression/decompression helpers for Cloudflare Worker storage.
|
|
2
|
+
// Provides metadata-aware gzip encoding and decoding for text and binary payloads.
|
|
3
|
+
export const KV_SCHEMA_VERSION = 1;
|
|
4
|
+
export function buildKvPutOptions(ttlSeconds, metadata) {
|
|
5
|
+
if (ttlSeconds === undefined && metadata === undefined) {
|
|
6
|
+
return undefined;
|
|
7
|
+
}
|
|
8
|
+
return {
|
|
9
|
+
...(ttlSeconds !== undefined ? { expirationTtl: ttlSeconds } : {}),
|
|
10
|
+
...(metadata !== undefined ? { metadata } : {}),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export async function gzipArrayBuffer(buffer) {
|
|
14
|
+
const stream = new Blob([buffer]).stream().pipeThrough(new CompressionStream("gzip"));
|
|
15
|
+
return await new Response(stream).arrayBuffer();
|
|
16
|
+
}
|
|
17
|
+
export async function gunzipArrayBuffer(buffer) {
|
|
18
|
+
const stream = new Blob([buffer]).stream().pipeThrough(new DecompressionStream("gzip"));
|
|
19
|
+
return await new Response(stream).arrayBuffer();
|
|
20
|
+
}
|
|
21
|
+
export async function gzipText(value) {
|
|
22
|
+
const bytes = new TextEncoder().encode(value);
|
|
23
|
+
return gzipArrayBuffer(bytes.buffer);
|
|
24
|
+
}
|
|
25
|
+
export async function decodeTextFromKv(value, metadata) {
|
|
26
|
+
const buffer = metadata?.contentEncoding === "gzip"
|
|
27
|
+
? await gunzipArrayBuffer(value)
|
|
28
|
+
: value;
|
|
29
|
+
return new TextDecoder().decode(buffer);
|
|
30
|
+
}
|
|
31
|
+
export async function decodeBinaryFromKv(value, metadata) {
|
|
32
|
+
if (metadata?.contentEncoding === "gzip") {
|
|
33
|
+
return await gunzipArrayBuffer(value);
|
|
34
|
+
}
|
|
35
|
+
return value;
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kv-codec.test.d.ts","sourceRoot":"","sources":["../src/kv-codec.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Tests for KV gzip codec helpers used by the Cloudflare worker.
|
|
2
|
+
import { describe, expect, test } from "bun:test";
|
|
3
|
+
import { buildKvPutOptions, decodeBinaryFromKv, decodeTextFromKv, gzipArrayBuffer, gzipText, } from "./kv-codec.js";
|
|
4
|
+
describe("kv-codec", () => {
|
|
5
|
+
test("round-trips gzip text payloads", async () => {
|
|
6
|
+
const input = "<html><body>diff line +1 -0</body></html>\n".repeat(200);
|
|
7
|
+
const compressed = await gzipText(input);
|
|
8
|
+
const decoded = await decodeTextFromKv(compressed, {
|
|
9
|
+
contentType: "text/html; charset=utf-8",
|
|
10
|
+
contentEncoding: "gzip",
|
|
11
|
+
schemaVersion: 1,
|
|
12
|
+
});
|
|
13
|
+
expect(decoded).toBe(input);
|
|
14
|
+
});
|
|
15
|
+
test("decodes legacy uncompressed text payloads", async () => {
|
|
16
|
+
const input = "plain html without kv metadata";
|
|
17
|
+
const bytes = new TextEncoder().encode(input);
|
|
18
|
+
const decoded = await decodeTextFromKv(bytes.buffer, null);
|
|
19
|
+
expect(decoded).toBe(input);
|
|
20
|
+
});
|
|
21
|
+
test("round-trips gzip binary payloads", async () => {
|
|
22
|
+
const bytes = Uint8Array.from({ length: 1024 }, (_, index) => index % 256);
|
|
23
|
+
const compressed = await gzipArrayBuffer(bytes.buffer);
|
|
24
|
+
const decoded = await decodeBinaryFromKv(compressed, {
|
|
25
|
+
contentType: "image/png",
|
|
26
|
+
contentEncoding: "gzip",
|
|
27
|
+
schemaVersion: 1,
|
|
28
|
+
});
|
|
29
|
+
expect(new Uint8Array(decoded)).toEqual(bytes);
|
|
30
|
+
});
|
|
31
|
+
test("returns undefined put options when empty", () => {
|
|
32
|
+
expect(buildKvPutOptions()).toBeUndefined();
|
|
33
|
+
});
|
|
34
|
+
test("builds put options with ttl and metadata", () => {
|
|
35
|
+
expect(buildKvPutOptions(3600, {
|
|
36
|
+
contentType: "text/html; charset=utf-8",
|
|
37
|
+
contentEncoding: "gzip",
|
|
38
|
+
schemaVersion: 1,
|
|
39
|
+
})).toEqual({
|
|
40
|
+
expirationTtl: 3600,
|
|
41
|
+
metadata: {
|
|
42
|
+
contentType: "text/html; charset=utf-8",
|
|
43
|
+
contentEncoding: "gzip",
|
|
44
|
+
schemaVersion: 1,
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
});
|
package/dist/web-utils.d.ts
CHANGED
|
@@ -39,6 +39,13 @@ export declare function slugifyFileName(name: string): string;
|
|
|
39
39
|
* Returns null for non-diff lines (headers, hunk markers, etc.).
|
|
40
40
|
*/
|
|
41
41
|
export declare function extractLineNumber(line: CapturedLine): string | null;
|
|
42
|
+
/**
|
|
43
|
+
* Match a rendered tree file row and extract the file path label.
|
|
44
|
+
* Examples:
|
|
45
|
+
* "│ ├── index.ts (+5,-2)"
|
|
46
|
+
* "└── README.md (-15)"
|
|
47
|
+
*/
|
|
48
|
+
export declare function extractTreeFilePath(lineText: string): string | null;
|
|
42
49
|
/**
|
|
43
50
|
* Build line-indexed anchors from file section layout positions.
|
|
44
51
|
* This avoids regex detection on rendered text, which can produce
|
|
@@ -110,7 +117,7 @@ export declare function captureReviewResponsiveHtml(options: {
|
|
|
110
117
|
/**
|
|
111
118
|
* Upload HTML to the critique.work worker
|
|
112
119
|
*/
|
|
113
|
-
export declare function uploadHtml(htmlDesktop: string, htmlMobile: string, ogImage?: Buffer | null): Promise<UploadResult>;
|
|
120
|
+
export declare function uploadHtml(htmlDesktop: string, htmlMobile: string, ogImage?: Buffer | null, patch?: string): Promise<UploadResult>;
|
|
114
121
|
/**
|
|
115
122
|
* Upload OG image to an existing diff via PATCH.
|
|
116
123
|
* Called in the background after the initial upload returns the URL.
|
package/dist/web-utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web-utils.d.ts","sourceRoot":"","sources":["../src/web-utils.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"web-utils.d.ts","sourceRoot":"","sources":["../src/web-utils.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAiB,aAAa,EAAE,YAAY,EAA+B,MAAM,gBAAgB,CAAA;AAE7G,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAMhE,eAAO,MAAM,UAAU,QAA6D,CAAA;AAEpF,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,iDAAiD;IACjD,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;IACnC,iEAAiE;IACjE,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC9B,wFAAwF;IACxF,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;4EACwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAC9B;AA8WD,wBAAsB,iBAAiB,CACrC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC,CAGxB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAMpD;AAYD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI,CAwBnE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAInE;AAUD;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,KAAK,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,GACvD,GAAG,CAAC,MAAM,EAAE;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAgB5C;AAiCD;;GAEG;AACH,wBAAsB,aAAa,CACjC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,MAAM,CAAC,CA2GjB;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE;IACP,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,kFAAkF;IAClF,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,uDAAuD;IACvD,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,GACA,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CA4C9E;AAED,MAAM,WAAW,mBAAoB,SAAQ,cAAc;IACzD,KAAK,EAAE,WAAW,EAAE,CAAA;IACpB,UAAU,EAAE,UAAU,GAAG,IAAI,CAAA;CAC9B;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,aAAa,CAAC,CAuGxB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,MAAM,CAAC,CAqBjB;AAED;;GAEG;AACH,wBAAsB,2BAA2B,CAC/C,OAAO,EAAE;IACP,KAAK,EAAE,WAAW,EAAE,CAAA;IACpB,UAAU,EAAE,UAAU,GAAG,IAAI,CAAA;IAC7B,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,kFAAkF;IAClF,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,uDAAuD;IACvD,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB,GACA,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAkD9E;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,EACvB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,YAAY,CAAC,CA4CvB;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB9E;AAED;;GAEG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAa9D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAIlF;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAMtD;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAe5D;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CA8BzE"}
|
package/dist/web-utils.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "@opentuah/react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "@opentuah/react/jsx-runtime";
|
|
2
2
|
// Web preview generation utilities for uploading diffs to critique.work.
|
|
3
3
|
// Renders diff components using opentui test renderer, converts to HTML with responsive layout,
|
|
4
4
|
// and uploads desktop/mobile versions for shareable diff viewing.
|
|
@@ -8,6 +8,7 @@ import fs from "fs";
|
|
|
8
8
|
import { tmpdir } from "os";
|
|
9
9
|
import { join } from "path";
|
|
10
10
|
import { getResolvedTheme, rgbaToHex } from "./themes.js";
|
|
11
|
+
import { buildDirectoryTree } from "./directory-tree.js";
|
|
11
12
|
import { DiffRenderable } from "@opentuah/core";
|
|
12
13
|
import { loadStoredLicenseKey, loadOrCreateOwnerSecret } from "./license.js";
|
|
13
14
|
const execAsync = promisify(exec);
|
|
@@ -119,13 +120,12 @@ async function renderDiffToFrameWithSectionPositions(diffContent, options) {
|
|
|
119
120
|
const { createTestRenderer } = await import("@opentuah/core/testing");
|
|
120
121
|
const { createRoot } = await import("@opentuah/react");
|
|
121
122
|
const { getTreeSitterClient } = await import("@opentuah/core");
|
|
122
|
-
const React = await import("react");
|
|
123
123
|
const { parsePatch, formatPatch } = await import("diff");
|
|
124
124
|
// Pre-initialize TreeSitter client to ensure syntax highlighting works
|
|
125
125
|
const tsClient = getTreeSitterClient();
|
|
126
126
|
await tsClient.initialize();
|
|
127
|
-
const { DiffView } = await import("./components/index.js");
|
|
128
|
-
const { getFileName, getOldFileName, countChanges, getViewMode, processFiles, detectFiletype, stripSubmoduleHeaders, parseGitDiffFiles, } = await import("./diff-utils.js");
|
|
127
|
+
const { DiffView, DirectoryTreeView } = await import("./components/index.js");
|
|
128
|
+
const { getFileName, getOldFileName, countChanges, getFileStatus, getViewMode, processFiles, detectFiletype, stripSubmoduleHeaders, parseGitDiffFiles, } = await import("./diff-utils.js");
|
|
129
129
|
const { themeNames, defaultThemeName } = await import("./themes.js");
|
|
130
130
|
const themeName = options.themeName && themeNames.includes(options.themeName)
|
|
131
131
|
? options.themeName
|
|
@@ -134,6 +134,19 @@ async function renderDiffToFrameWithSectionPositions(diffContent, options) {
|
|
|
134
134
|
const files = parseGitDiffFiles(stripSubmoduleHeaders(diffContent), parsePatch);
|
|
135
135
|
const filesWithRawDiff = processFiles(files, formatPatch);
|
|
136
136
|
const fileNames = filesWithRawDiff.map((file) => getFileName(file));
|
|
137
|
+
const treeFiles = filesWithRawDiff.map((file, idx) => {
|
|
138
|
+
const { additions, deletions } = countChanges(file.hunks);
|
|
139
|
+
return {
|
|
140
|
+
path: getFileName(file),
|
|
141
|
+
status: getFileStatus(file),
|
|
142
|
+
additions,
|
|
143
|
+
deletions,
|
|
144
|
+
fileIndex: idx,
|
|
145
|
+
};
|
|
146
|
+
});
|
|
147
|
+
const treeFileOrder = buildDirectoryTree(treeFiles)
|
|
148
|
+
.filter((node) => node.isFile && node.fileIndex !== undefined)
|
|
149
|
+
.map((node) => node.fileIndex);
|
|
137
150
|
if (filesWithRawDiff.length === 0) {
|
|
138
151
|
throw new Error("No files to display");
|
|
139
152
|
}
|
|
@@ -150,57 +163,35 @@ async function renderDiffToFrameWithSectionPositions(diffContent, options) {
|
|
|
150
163
|
// Create the diff view component
|
|
151
164
|
// NOTE: No height: "100%" - let content determine its natural height
|
|
152
165
|
function WebApp() {
|
|
153
|
-
return
|
|
154
|
-
style: {
|
|
166
|
+
return (_jsxs("box", { style: {
|
|
155
167
|
flexDirection: "column",
|
|
156
168
|
backgroundColor: webBg,
|
|
157
|
-
},
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
else
|
|
184
|
-
fileSectionRefs.delete(idx);
|
|
185
|
-
},
|
|
186
|
-
style: { flexDirection: "column", marginBottom: 2 },
|
|
187
|
-
}, React.createElement("box", {
|
|
188
|
-
style: {
|
|
189
|
-
paddingBottom: 1,
|
|
190
|
-
paddingLeft: 1,
|
|
191
|
-
paddingRight: 1,
|
|
192
|
-
flexShrink: 0,
|
|
193
|
-
flexDirection: "row",
|
|
194
|
-
alignItems: "center",
|
|
195
|
-
},
|
|
196
|
-
}, ...fileHeaderChildren, React.createElement("text", { fg: "#2d8a47" }, ` +${additions}`), React.createElement("text", { fg: "#c53b53" }, `-${deletions}`)), React.createElement(DiffView, {
|
|
197
|
-
diff: file.rawDiff || "",
|
|
198
|
-
view: viewMode,
|
|
199
|
-
filetype,
|
|
200
|
-
themeName,
|
|
201
|
-
...(options.wrapMode && { wrapMode: options.wrapMode }),
|
|
202
|
-
}));
|
|
203
|
-
}));
|
|
169
|
+
}, children: [showNotice
|
|
170
|
+
? renderNoticeBlock({
|
|
171
|
+
mutedColor: webMuted,
|
|
172
|
+
showExpiry: showExpiryNotice,
|
|
173
|
+
})
|
|
174
|
+
: null, _jsx("box", { style: { marginBottom: 2 }, children: _jsx(DirectoryTreeView, { files: treeFiles, themeName: themeName }) }), filesWithRawDiff.map((file, idx) => {
|
|
175
|
+
const fileName = getFileName(file);
|
|
176
|
+
const oldFileName = getOldFileName(file);
|
|
177
|
+
const filetype = detectFiletype(fileName);
|
|
178
|
+
const { additions, deletions } = countChanges(file.hunks);
|
|
179
|
+
// Use forced viewMode if set, otherwise auto-detect (higher threshold 150 for web vs TUI 100)
|
|
180
|
+
const viewMode = options.viewMode || getViewMode(additions, deletions, options.cols, 150);
|
|
181
|
+
return (_jsxs("box", { ref: (r) => {
|
|
182
|
+
if (r)
|
|
183
|
+
fileSectionRefs.set(idx, r);
|
|
184
|
+
else
|
|
185
|
+
fileSectionRefs.delete(idx);
|
|
186
|
+
}, style: { flexDirection: "column", marginBottom: 2 }, children: [_jsxs("box", { style: {
|
|
187
|
+
paddingBottom: 1,
|
|
188
|
+
paddingLeft: 1,
|
|
189
|
+
paddingRight: 1,
|
|
190
|
+
flexShrink: 0,
|
|
191
|
+
flexDirection: "row",
|
|
192
|
+
alignItems: "center",
|
|
193
|
+
}, children: [oldFileName ? (_jsxs(_Fragment, { children: [_jsx("text", { fg: webMuted, children: oldFileName.trim() }), _jsx("text", { fg: webMuted, children: " \u2192 " }), _jsx("text", { fg: webText, children: fileName.trim() })] })) : (_jsx("text", { fg: webText, children: fileName.trim() })), _jsxs("text", { fg: "#2d8a47", children: [" +", additions] }), _jsxs("text", { fg: "#c53b53", children: ["-", deletions] })] }), _jsx(DiffView, { diff: file.rawDiff || "", view: viewMode, filetype: filetype, themeName: themeName, wrapMode: options.wrapMode })] }, idx));
|
|
194
|
+
})] }));
|
|
204
195
|
}
|
|
205
196
|
// Content-fitting rendering:
|
|
206
197
|
// 1. Start with small initial height
|
|
@@ -213,7 +204,7 @@ async function renderDiffToFrameWithSectionPositions(diffContent, options) {
|
|
|
213
204
|
height: currentHeight,
|
|
214
205
|
});
|
|
215
206
|
// Mount and do initial render
|
|
216
|
-
createRoot(renderer).render(
|
|
207
|
+
createRoot(renderer).render(_jsx(WebApp, {}));
|
|
217
208
|
await renderOnce();
|
|
218
209
|
// Wait for React to mount components (may take a few render cycles)
|
|
219
210
|
let contentHeight = getContentHeight(renderer.root);
|
|
@@ -246,6 +237,7 @@ async function renderDiffToFrameWithSectionPositions(diffContent, options) {
|
|
|
246
237
|
sectionPositions.push({
|
|
247
238
|
lineIndex: Math.max(0, Math.round(layout.top)),
|
|
248
239
|
fileName: fileNames[idx],
|
|
240
|
+
fileIndex: idx,
|
|
249
241
|
});
|
|
250
242
|
}
|
|
251
243
|
// Capture the final frame
|
|
@@ -261,6 +253,7 @@ async function renderDiffToFrameWithSectionPositions(diffContent, options) {
|
|
|
261
253
|
return {
|
|
262
254
|
frame,
|
|
263
255
|
sectionPositions,
|
|
256
|
+
treeFileOrder,
|
|
264
257
|
};
|
|
265
258
|
}
|
|
266
259
|
export async function renderDiffToFrame(diffContent, options) {
|
|
@@ -324,6 +317,25 @@ export function extractLineNumber(line) {
|
|
|
324
317
|
// Prefer new-file (right/second) number; fall back to old-file (left/first)
|
|
325
318
|
return secondNum ?? firstNum;
|
|
326
319
|
}
|
|
320
|
+
/**
|
|
321
|
+
* Match a rendered tree file row and extract the file path label.
|
|
322
|
+
* Examples:
|
|
323
|
+
* "│ ├── index.ts (+5,-2)"
|
|
324
|
+
* "└── README.md (-15)"
|
|
325
|
+
*/
|
|
326
|
+
export function extractTreeFilePath(lineText) {
|
|
327
|
+
const match = lineText.match(/^\s*[│ ]*[├└]──\s+(.+?)\s+\([^)]*\)\s*$/);
|
|
328
|
+
if (!match || !match[1])
|
|
329
|
+
return null;
|
|
330
|
+
return match[1].trim();
|
|
331
|
+
}
|
|
332
|
+
function escapeHtmlAttribute(value) {
|
|
333
|
+
return value
|
|
334
|
+
.replace(/&/g, "&")
|
|
335
|
+
.replace(/"/g, """)
|
|
336
|
+
.replace(/</g, "<")
|
|
337
|
+
.replace(/>/g, ">");
|
|
338
|
+
}
|
|
327
339
|
/**
|
|
328
340
|
* Build line-indexed anchors from file section layout positions.
|
|
329
341
|
* This avoids regex detection on rendered text, which can produce
|
|
@@ -351,6 +363,8 @@ const SECTION_ANCHOR_CSS = `
|
|
|
351
363
|
.file-section { scroll-margin-top: 16px; }
|
|
352
364
|
.file-link { color: inherit; text-decoration: none; cursor: copy; }
|
|
353
365
|
.file-link:hover { text-decoration: underline; }
|
|
366
|
+
.tree-file-link { color: inherit; text-decoration: none; }
|
|
367
|
+
.tree-file-link:hover { text-decoration: underline; }
|
|
354
368
|
`;
|
|
355
369
|
// JS: scroll to hash fragment on page load + click-to-copy filename on .file-link click.
|
|
356
370
|
// On click: copies the filename text to clipboard and updates the URL hash.
|
|
@@ -379,8 +393,16 @@ export async function captureToHtml(diffContent, options) {
|
|
|
379
393
|
const { frameToHtmlDocument } = await import("./ansi-html.js");
|
|
380
394
|
// Render diff to captured frame (with notice for web uploads)
|
|
381
395
|
// and collect exact section line positions from layout metadata.
|
|
382
|
-
const { frame, sectionPositions } = await renderDiffToFrameWithSectionPositions(diffContent, { ...options, showNotice: true });
|
|
396
|
+
const { frame, sectionPositions, treeFileOrder } = await renderDiffToFrameWithSectionPositions(diffContent, { ...options, showNotice: true });
|
|
383
397
|
const anchors = buildAnchorMap(sectionPositions);
|
|
398
|
+
const anchorIdByFileIndex = new Map();
|
|
399
|
+
for (const section of sectionPositions) {
|
|
400
|
+
const anchor = anchors.get(section.lineIndex);
|
|
401
|
+
if (anchor) {
|
|
402
|
+
anchorIdByFileIndex.set(section.fileIndex, anchor.id);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
const treeAnchorOrder = treeFileOrder.map((fileIndex) => anchorIdByFileIndex.get(fileIndex) ?? null);
|
|
384
406
|
// Get theme colors for HTML output
|
|
385
407
|
const theme = getResolvedTheme(options.themeName);
|
|
386
408
|
const backgroundColor = rgbaToHex(theme.background);
|
|
@@ -398,6 +420,7 @@ export async function captureToHtml(diffContent, options) {
|
|
|
398
420
|
const sortedSections = [...sectionPositions].sort((a, b) => a.lineIndex - b.lineIndex);
|
|
399
421
|
let sectionPtr = 0;
|
|
400
422
|
let currentFile = null;
|
|
423
|
+
let treeLinkPtr = 0;
|
|
401
424
|
const renderLineCallback = (defaultHtml, line, lineIndex) => {
|
|
402
425
|
// Advance current file when we pass a section boundary
|
|
403
426
|
while (sectionPtr < sortedSections.length && lineIndex >= sortedSections[sectionPtr].lineIndex) {
|
|
@@ -405,15 +428,23 @@ export async function captureToHtml(diffContent, options) {
|
|
|
405
428
|
sectionPtr++;
|
|
406
429
|
}
|
|
407
430
|
let html = defaultHtml;
|
|
431
|
+
if (currentFile === null && treeLinkPtr < treeAnchorOrder.length) {
|
|
432
|
+
const lineText = line.spans.map((span) => span.text).join("");
|
|
433
|
+
const treePath = extractTreeFilePath(lineText);
|
|
434
|
+
if (treePath) {
|
|
435
|
+
const targetAnchor = treeAnchorOrder[treeLinkPtr];
|
|
436
|
+
treeLinkPtr++;
|
|
437
|
+
if (targetAnchor) {
|
|
438
|
+
const escapedPath = escapeHtmlAttribute(treePath);
|
|
439
|
+
html = html.replace(`>${escapedPath}</span>`, `><a href="#${targetAnchor}" class="tree-file-link">${escapedPath}</a></span>`);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
408
443
|
// File-section header: add id + clickable link
|
|
409
444
|
const anchor = anchors.get(lineIndex);
|
|
410
445
|
if (anchor) {
|
|
411
446
|
html = html.replace('<div class="line">', `<div id="${anchor.id}" class="line file-section">`);
|
|
412
|
-
const escapedLabel = anchor.label
|
|
413
|
-
.replace(/&/g, "&")
|
|
414
|
-
.replace(/</g, "<")
|
|
415
|
-
.replace(/>/g, ">")
|
|
416
|
-
.replace(/"/g, """);
|
|
447
|
+
const escapedLabel = escapeHtmlAttribute(anchor.label);
|
|
417
448
|
html = html.replace(`>${escapedLabel}</span>`, `><a href="#${anchor.id}" class="file-link">${escapedLabel}</a></span>`);
|
|
418
449
|
}
|
|
419
450
|
// Diff line: extract line number from spans and add data-anchor.
|
|
@@ -423,11 +454,7 @@ export async function captureToHtml(diffContent, options) {
|
|
|
423
454
|
const lineNum = extractLineNumber(line);
|
|
424
455
|
if (lineNum) {
|
|
425
456
|
const anchorValue = `${currentFile}:${lineNum}`;
|
|
426
|
-
const safeAnchor = anchorValue
|
|
427
|
-
.replace(/&/g, "&")
|
|
428
|
-
.replace(/"/g, """)
|
|
429
|
-
.replace(/</g, "<")
|
|
430
|
-
.replace(/>/g, ">");
|
|
457
|
+
const safeAnchor = escapeHtmlAttribute(anchorValue);
|
|
431
458
|
html = html.replace('<div class="line">', `<div class="line" data-anchor="${safeAnchor}">`);
|
|
432
459
|
}
|
|
433
460
|
}
|
|
@@ -498,7 +525,6 @@ export async function renderReviewToFrame(options) {
|
|
|
498
525
|
const { createTestRenderer } = await import("@opentuah/core/testing");
|
|
499
526
|
const { createRoot } = await import("@opentuah/react");
|
|
500
527
|
const { getTreeSitterClient } = await import("@opentuah/core");
|
|
501
|
-
const React = await import("react");
|
|
502
528
|
// Pre-initialize TreeSitter client to ensure syntax highlighting works
|
|
503
529
|
const tsClient = getTreeSitterClient();
|
|
504
530
|
await tsClient.initialize();
|
|
@@ -523,28 +549,18 @@ export async function renderReviewToFrame(options) {
|
|
|
523
549
|
// Pass renderer to enable custom renderNode (wrapMode: "none" for diagrams)
|
|
524
550
|
// NOTE: No height: "100%" - let content determine its natural height
|
|
525
551
|
function ReviewWebApp() {
|
|
526
|
-
return
|
|
527
|
-
style: {
|
|
552
|
+
return (_jsxs("box", { style: {
|
|
528
553
|
flexDirection: "column",
|
|
529
554
|
backgroundColor: webBg,
|
|
530
|
-
},
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
: null, React.createElement(ReviewAppView, {
|
|
537
|
-
hunks: options.hunks,
|
|
538
|
-
reviewData: options.reviewData,
|
|
539
|
-
isGenerating: false,
|
|
540
|
-
themeName,
|
|
541
|
-
width: options.cols,
|
|
542
|
-
showFooter: false,
|
|
543
|
-
renderer: renderer,
|
|
544
|
-
}));
|
|
555
|
+
}, children: [showNotice
|
|
556
|
+
? renderNoticeBlock({
|
|
557
|
+
mutedColor: webMuted,
|
|
558
|
+
showExpiry: showExpiryNotice,
|
|
559
|
+
})
|
|
560
|
+
: null, _jsx(ReviewAppView, { hunks: options.hunks, reviewData: options.reviewData, isGenerating: false, themeName: themeName, width: options.cols, showFooter: false, renderer: renderer })] }));
|
|
545
561
|
}
|
|
546
562
|
// Mount and do initial render
|
|
547
|
-
createRoot(renderer).render(
|
|
563
|
+
createRoot(renderer).render(_jsx(ReviewWebApp, {}));
|
|
548
564
|
await renderOnce();
|
|
549
565
|
// Wait for React to mount components (may take a few render cycles)
|
|
550
566
|
let contentHeight = getContentHeight(renderer.root);
|
|
@@ -657,7 +673,7 @@ export async function captureReviewResponsiveHtml(options) {
|
|
|
657
673
|
/**
|
|
658
674
|
* Upload HTML to the critique.work worker
|
|
659
675
|
*/
|
|
660
|
-
export async function uploadHtml(htmlDesktop, htmlMobile, ogImage) {
|
|
676
|
+
export async function uploadHtml(htmlDesktop, htmlMobile, ogImage, patch) {
|
|
661
677
|
const body = {
|
|
662
678
|
html: htmlDesktop,
|
|
663
679
|
htmlMobile,
|
|
@@ -666,6 +682,10 @@ export async function uploadHtml(htmlDesktop, htmlMobile, ogImage) {
|
|
|
666
682
|
if (ogImage) {
|
|
667
683
|
body.ogImage = ogImage.toString("base64");
|
|
668
684
|
}
|
|
685
|
+
// Include raw unified diff (patch) for programmatic access
|
|
686
|
+
if (patch) {
|
|
687
|
+
body.patch = patch;
|
|
688
|
+
}
|
|
669
689
|
const licenseKey = loadStoredLicenseKey();
|
|
670
690
|
const ownerSecret = loadOrCreateOwnerSecret();
|
|
671
691
|
const headers = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web-utils.test.d.ts","sourceRoot":"","sources":["../src/web-utils.test.
|
|
1
|
+
{"version":3,"file":"web-utils.test.d.ts","sourceRoot":"","sources":["../src/web-utils.test.tsx"],"names":[],"mappings":""}
|
package/dist/web-utils.test.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "@opentuah/react/jsx-runtime";
|
|
1
2
|
import { describe, test, expect, afterEach } from "bun:test";
|
|
2
3
|
import { createTestRenderer } from "@opentuah/core/testing";
|
|
3
4
|
import { createRoot } from "@opentuah/react";
|
|
4
|
-
import React from "react";
|
|
5
5
|
import { RGBA } from "@opentuah/core";
|
|
6
|
-
import { slugifyFileName, buildAnchorMap, extractLineNumber } from "./web-utils.js";
|
|
6
|
+
import { slugifyFileName, buildAnchorMap, extractLineNumber, extractTreeFilePath } from "./web-utils.js";
|
|
7
7
|
import { frameToHtml, frameToHtmlDocument } from "./ansi-html.js";
|
|
8
8
|
describe("getSpanLines rendering", () => {
|
|
9
9
|
let renderer = null;
|
|
@@ -16,9 +16,9 @@ describe("getSpanLines rendering", () => {
|
|
|
16
16
|
renderer = setup.renderer;
|
|
17
17
|
const { renderOnce } = setup;
|
|
18
18
|
function App() {
|
|
19
|
-
return
|
|
19
|
+
return (_jsxs("box", { style: { flexDirection: "column" }, children: [_jsx("text", { content: "\u251C\u2500\u2500 src/errors" }), _jsx("text", { content: "\u2502 \u2514\u2500\u2500 index.ts" }), _jsx("text", { content: "\u2514\u2500\u2500 api" })] }));
|
|
20
20
|
}
|
|
21
|
-
createRoot(renderer).render(
|
|
21
|
+
createRoot(renderer).render(_jsx(App, {}));
|
|
22
22
|
for (let i = 0; i < 5; i++) {
|
|
23
23
|
await renderOnce();
|
|
24
24
|
await new Promise(r => setTimeout(r, 50));
|
|
@@ -36,9 +36,9 @@ describe("getSpanLines rendering", () => {
|
|
|
36
36
|
renderer = setup.renderer;
|
|
37
37
|
const { renderOnce } = setup;
|
|
38
38
|
function App() {
|
|
39
|
-
return
|
|
39
|
+
return _jsx("text", { content: "src/errors/index.ts" });
|
|
40
40
|
}
|
|
41
|
-
createRoot(renderer).render(
|
|
41
|
+
createRoot(renderer).render(_jsx(App, {}));
|
|
42
42
|
for (let i = 0; i < 5; i++) {
|
|
43
43
|
await renderOnce();
|
|
44
44
|
await new Promise(r => setTimeout(r, 50));
|
|
@@ -227,6 +227,17 @@ describe("extractLineNumber", () => {
|
|
|
227
227
|
expect(extractLineNumber(line)).toBe("42");
|
|
228
228
|
});
|
|
229
229
|
});
|
|
230
|
+
describe("extractTreeFilePath", () => {
|
|
231
|
+
test("matches tree file rows with change stats", () => {
|
|
232
|
+
expect(extractTreeFilePath("│ ├── index.ts (+5,-2)")).toBe("index.ts");
|
|
233
|
+
expect(extractTreeFilePath("└── README.md (-15)")).toBe("README.md");
|
|
234
|
+
expect(extractTreeFilePath("│ └── utils.ts (+30)")).toBe("utils.ts");
|
|
235
|
+
});
|
|
236
|
+
test("does not match directory rows", () => {
|
|
237
|
+
expect(extractTreeFilePath("└── src")).toBe(null);
|
|
238
|
+
expect(extractTreeFilePath("│ ├── components")).toBe(null);
|
|
239
|
+
});
|
|
240
|
+
});
|
|
230
241
|
describe("data-anchor in rendered HTML", () => {
|
|
231
242
|
test("injects data-anchor with file and line number", () => {
|
|
232
243
|
// Simulate a frame with a file header line and diff lines
|
|
@@ -300,6 +311,44 @@ describe("data-anchor in rendered HTML", () => {
|
|
|
300
311
|
expect(html).toContain('data-anchor="src/"quoted".ts:5"');
|
|
301
312
|
});
|
|
302
313
|
});
|
|
314
|
+
describe("tree links in rendered HTML", () => {
|
|
315
|
+
test("adds href links to tree file rows without ids", () => {
|
|
316
|
+
const frame = {
|
|
317
|
+
cols: 80,
|
|
318
|
+
rows: 3,
|
|
319
|
+
cursor: [0, 0],
|
|
320
|
+
lines: [
|
|
321
|
+
{ spans: [mockSpan("└── src")] },
|
|
322
|
+
{ spans: [mockSpan(" └── "), mockSpan("foo.ts"), mockSpan(" (+2,-1)")] },
|
|
323
|
+
{ spans: [mockSpan("src/foo.ts")] },
|
|
324
|
+
],
|
|
325
|
+
};
|
|
326
|
+
let treeLinkPtr = 0;
|
|
327
|
+
const treeAnchorOrder = ["src-foo-ts"];
|
|
328
|
+
const { html } = frameToHtml(frame, {
|
|
329
|
+
renderLine: (defaultHtml, line, lineIndex) => {
|
|
330
|
+
let out = defaultHtml;
|
|
331
|
+
if (lineIndex < 2 && treeLinkPtr < treeAnchorOrder.length) {
|
|
332
|
+
const treePath = extractTreeFilePath(line.spans.map((span) => span.text).join(""));
|
|
333
|
+
if (treePath) {
|
|
334
|
+
const targetAnchor = treeAnchorOrder[treeLinkPtr];
|
|
335
|
+
treeLinkPtr++;
|
|
336
|
+
out = out.replace(`>${treePath}</span>`, `><a href="#${targetAnchor}" class="tree-file-link">${treePath}</a></span>`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
if (lineIndex === 2) {
|
|
340
|
+
out = out.replace('<div class="line">', '<div id="src-foo-ts" class="line file-section">');
|
|
341
|
+
}
|
|
342
|
+
return out;
|
|
343
|
+
},
|
|
344
|
+
});
|
|
345
|
+
const htmlLines = html.split("\n");
|
|
346
|
+
expect(htmlLines[1]).toContain('href="#src-foo-ts"');
|
|
347
|
+
expect(htmlLines[1]).toContain('class="tree-file-link"');
|
|
348
|
+
expect(htmlLines[1]).not.toContain('id="src-foo-ts"');
|
|
349
|
+
expect(htmlLines[2]).toContain('id="src-foo-ts"');
|
|
350
|
+
});
|
|
351
|
+
});
|
|
303
352
|
describe("ansi-html extraCss and extraJs", () => {
|
|
304
353
|
test("extraCss is injected into document style block", () => {
|
|
305
354
|
const frame = mockFrame(["test"]);
|
package/dist/worker.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.tsx"],"names":[],"mappings":"AAAA,gCAAgC;AAMhC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAG3B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.tsx"],"names":[],"mappings":"AAAA,gCAAgC;AAMhC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AAG3B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AAiB5D,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AAEnD,KAAK,QAAQ,GAAG;IACd,WAAW,EAAE,WAAW,CAAA;IACxB,iBAAiB,EAAE,MAAM,CAAA;IACzB,qBAAqB,EAAE,MAAM,CAAA;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,EAAE;QAAE,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG;YAAE,QAAQ,IAAI,MAAM,CAAA;SAAE,CAAC;QAAC,GAAG,CAAC,EAAE,EAAE,GAAG,GAAG;YAAE,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;SAAE,CAAA;KAAE,CAAA;CAChI,CAAA;AAUD,QAAA,MAAM,GAAG;cAAwB,QAAQ;eAAa;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE;yCAAK,CAAA;AAi8B7E,eAAe,GAAG,CAAA"}
|