@tanishqkancharla/diffmap 0.1.4 → 0.2.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/README.md +69 -61
- package/index.html +18 -0
- package/package.json +7 -5
- package/src/annotations.ts +36 -29
- package/src/changes.ts +229 -0
- package/src/cli.test.ts +50 -0
- package/src/cli.ts +41 -5
- package/src/errors.ts +10 -0
- package/src/gist/GistApp.tsx +39 -20
- package/src/gist/GistStatus.tsx +2 -51
- package/src/gist/fetchGist.ts +23 -0
- package/src/git.test.ts +402 -0
- package/src/git.ts +498 -0
- package/src/github/GitHubApp.tsx +39 -22
- package/src/github/fetchRepo.test.ts +59 -1
- package/src/github/fetchRepo.ts +99 -43
- package/src/github/patch.test.ts +18 -9
- package/src/github/patch.ts +30 -19
- package/src/host/HostHome.tsx +86 -0
- package/src/host/files.ts +10 -0
- package/src/host/home.md +156 -0
- package/src/host/home.test.ts +83 -0
- package/src/index.ts +1 -0
- package/src/packageVersion.ts +7 -0
- package/src/parseFence.ts +6 -6
- package/src/parseViewer.test.ts +87 -0
- package/src/parseViewer.ts +103 -77
- package/src/serve.test.ts +306 -0
- package/src/serve.ts +48 -5
- package/src/share.ts +18 -0
- package/src/update.test.ts +19 -0
- package/src/update.ts +285 -0
- package/src/viewer/CallStackDiff.tsx +15 -3
- package/src/viewer/ChangesCard.tsx +267 -0
- package/src/viewer/CloseServerButton.tsx +16 -0
- package/src/viewer/ComarkView.tsx +83 -15
- package/src/viewer/Fence.tsx +9 -1
- package/src/viewer/FileExcerpt.tsx +25 -3
- package/src/viewer/HtmlPlaceholder.tsx +6 -1
- package/src/viewer/MermaidBlock.tsx +99 -11
- package/src/viewer/RangePicker.tsx +526 -0
- package/src/viewer/SourceDiffPanel.tsx +277 -121
- package/src/viewer/TableOfContents.tsx +313 -39
- package/src/viewer/ViewerApp.tsx +120 -68
- package/src/viewer/changesSource.ts +91 -0
- package/src/viewer/sourceHeader.test.ts +2 -87
- package/src/viewer/sourceHeader.ts +4 -63
- package/src/viewer/sourceNavigation.ts +33 -42
- package/src/viewer/viewerMode.ts +1 -1
- package/vite.config.ts +2 -1
- package/vite.gist.config.ts +57 -0
- package/src/viewer/Check.tsx +0 -24
- package/src/viewer/DoneButton.tsx +0 -32
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { useEffect, useState } from "react";
|
|
2
|
-
import type { CodeViewDiffItem, CodeViewLineSelection } from "@pierre/diffs";
|
|
1
|
+
import { useEffect, useMemo, useState } from "react";
|
|
3
2
|
import type { SourceReference } from "../annotations.js";
|
|
4
3
|
import type { SourceDefinition, DefinitionResponse } from "../definitions.js";
|
|
5
4
|
import { DiffmapDefinitionError } from "../errors.js";
|
|
6
5
|
import { useGitHubPin } from "../github/pin.ts";
|
|
7
6
|
import { fetchPinnedBlob, findSymbolLineRange } from "../github/fetchRepo.ts";
|
|
7
|
+
import { useHostFiles } from "../host/files.ts";
|
|
8
8
|
import { useViewerMode } from "./viewerMode.ts";
|
|
9
9
|
|
|
10
10
|
export async function requestSource(
|
|
@@ -46,10 +46,15 @@ export function useSourceReference(reference: SourceReference | undefined) {
|
|
|
46
46
|
result: SourceDefinition | Error;
|
|
47
47
|
}>();
|
|
48
48
|
const pin = useGitHubPin();
|
|
49
|
+
const hostFiles = useHostFiles();
|
|
49
50
|
const mode = useViewerMode();
|
|
51
|
+
const hostResult = useMemo(() => {
|
|
52
|
+
if (reference?.kind !== "file" || hostFiles === undefined) return undefined;
|
|
53
|
+
return readHostFileReference(hostFiles, reference);
|
|
54
|
+
}, [reference, hostFiles]);
|
|
50
55
|
useEffect(() => {
|
|
51
56
|
if (reference?.kind !== "file") return;
|
|
52
|
-
if (
|
|
57
|
+
if (hostFiles !== undefined) return;
|
|
53
58
|
let active = true;
|
|
54
59
|
if (pin !== undefined) {
|
|
55
60
|
// Pinned GitHub spec: load this path at the spec commit SHA, never main.
|
|
@@ -89,6 +94,7 @@ export function useSourceReference(reference: SourceReference | undefined) {
|
|
|
89
94
|
active = false;
|
|
90
95
|
};
|
|
91
96
|
}
|
|
97
|
+
if (mode !== "local") return;
|
|
92
98
|
const params = new URLSearchParams({ path: reference.path });
|
|
93
99
|
if (reference.symbol !== undefined) params.set("symbol", reference.symbol);
|
|
94
100
|
if (reference.start !== undefined)
|
|
@@ -101,48 +107,33 @@ export function useSourceReference(reference: SourceReference | undefined) {
|
|
|
101
107
|
return () => {
|
|
102
108
|
active = false;
|
|
103
109
|
};
|
|
104
|
-
}, [reference, pin, mode]);
|
|
110
|
+
}, [reference, pin, mode, hostFiles]);
|
|
111
|
+
if (hostResult !== undefined) return hostResult;
|
|
105
112
|
return resolution?.reference === reference ? resolution?.result : undefined;
|
|
106
113
|
}
|
|
107
114
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
):
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const ranges = diff.hunks.flatMap((hunk) => {
|
|
117
|
-
const start = Math.max(source.start, hunk.additionStart);
|
|
118
|
-
const end = Math.min(
|
|
119
|
-
source.end,
|
|
120
|
-
hunk.additionStart + hunk.additionCount - 1,
|
|
121
|
-
);
|
|
122
|
-
return start <= end ? [{ start, end, hunk }] : [];
|
|
115
|
+
function readHostFileReference(
|
|
116
|
+
files: Record<string, string>,
|
|
117
|
+
reference: Extract<SourceReference, { kind: "file" }>,
|
|
118
|
+
): SourceDefinition | Error {
|
|
119
|
+
const contents = files[reference.path];
|
|
120
|
+
if (contents === undefined) {
|
|
121
|
+
return new DiffmapDefinitionError({
|
|
122
|
+
reason: `No hosted file for ${reference.path}.`,
|
|
123
123
|
});
|
|
124
|
-
if (ranges.length === 0) continue;
|
|
125
|
-
const matches = ranges.every(({ start, end, hunk }) => {
|
|
126
|
-
for (let line = start; line <= end; line++) {
|
|
127
|
-
const index = diff.isPartial
|
|
128
|
-
? hunk.additionLineIndex + line - hunk.additionStart
|
|
129
|
-
: line - 1;
|
|
130
|
-
if (
|
|
131
|
-
diff.additionLines[index]?.replace(/\r?\n$/, "") !== lines[line - 1]
|
|
132
|
-
)
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
135
|
-
return true;
|
|
136
|
-
});
|
|
137
|
-
if (!matches) continue;
|
|
138
|
-
return {
|
|
139
|
-
id: item.id,
|
|
140
|
-
range: {
|
|
141
|
-
start: Math.min(...ranges.map((range) => range.start)),
|
|
142
|
-
end: Math.max(...ranges.map((range) => range.end)),
|
|
143
|
-
side: "additions",
|
|
144
|
-
},
|
|
145
|
-
};
|
|
146
124
|
}
|
|
147
|
-
|
|
125
|
+
const lineCount = Math.max(1, contents.split(/\r?\n/).length);
|
|
126
|
+
const range =
|
|
127
|
+
reference.symbol !== undefined
|
|
128
|
+
? findSymbolLineRange(contents, reference.symbol)
|
|
129
|
+
: {
|
|
130
|
+
start: reference.start ?? 1,
|
|
131
|
+
end: reference.end ?? lineCount,
|
|
132
|
+
};
|
|
133
|
+
return {
|
|
134
|
+
path: reference.path,
|
|
135
|
+
contents,
|
|
136
|
+
start: range.start,
|
|
137
|
+
end: range.end,
|
|
138
|
+
};
|
|
148
139
|
}
|
package/src/viewer/viewerMode.ts
CHANGED
package/vite.config.ts
CHANGED
|
@@ -22,6 +22,7 @@ export default defineConfig(({ command }) => {
|
|
|
22
22
|
"react-dom",
|
|
23
23
|
"react/jsx-dev-runtime",
|
|
24
24
|
"maui",
|
|
25
|
+
"motion/react",
|
|
25
26
|
"purse-styles",
|
|
26
27
|
"@pierre/diffs/react",
|
|
27
28
|
"beautiful-mermaid",
|
|
@@ -34,7 +35,7 @@ export default defineConfig(({ command }) => {
|
|
|
34
35
|
},
|
|
35
36
|
clearScreen: false,
|
|
36
37
|
server: {
|
|
37
|
-
port:
|
|
38
|
+
port: 0,
|
|
38
39
|
strictPort: false,
|
|
39
40
|
},
|
|
40
41
|
};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import react from "@vitejs/plugin-react";
|
|
4
|
+
import { defineConfig, type UserConfig } from "vite";
|
|
5
|
+
|
|
6
|
+
const packageRoot = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const gistRoot = path.join(packageRoot, "gist");
|
|
8
|
+
|
|
9
|
+
/** Production / preview: static gist viewer. Never pulls `virtual:diffmap`. */
|
|
10
|
+
export function gistAppConfig(): UserConfig {
|
|
11
|
+
return {
|
|
12
|
+
root: gistRoot,
|
|
13
|
+
cacheDir: path.join(packageRoot, "node_modules/.vite-gist"),
|
|
14
|
+
publicDir: false,
|
|
15
|
+
optimizeDeps: {
|
|
16
|
+
include: [
|
|
17
|
+
"react",
|
|
18
|
+
"react-dom",
|
|
19
|
+
"react/jsx-dev-runtime",
|
|
20
|
+
"maui",
|
|
21
|
+
"motion/react",
|
|
22
|
+
"purse-styles",
|
|
23
|
+
"@pierre/diffs/react",
|
|
24
|
+
"beautiful-mermaid",
|
|
25
|
+
"errore",
|
|
26
|
+
"md4x/standalone",
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
plugins: [react()],
|
|
30
|
+
appType: "spa",
|
|
31
|
+
resolve: {
|
|
32
|
+
alias: {
|
|
33
|
+
"md4x/napi": path.join(
|
|
34
|
+
packageRoot,
|
|
35
|
+
"node_modules/md4x/lib/standalone.mjs",
|
|
36
|
+
),
|
|
37
|
+
},
|
|
38
|
+
dedupe: ["react", "react-dom", "purse-styles"],
|
|
39
|
+
},
|
|
40
|
+
server: {
|
|
41
|
+
fs: {
|
|
42
|
+
allow: [packageRoot],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
preview: {
|
|
46
|
+
host: "127.0.0.1",
|
|
47
|
+
port: 4178,
|
|
48
|
+
},
|
|
49
|
+
build: {
|
|
50
|
+
outDir: path.join(packageRoot, "dist"),
|
|
51
|
+
emptyOutDir: true,
|
|
52
|
+
},
|
|
53
|
+
clearScreen: false,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export default defineConfig(gistAppConfig());
|
package/src/viewer/Check.tsx
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { SVGProps } from "react";
|
|
2
|
-
|
|
3
|
-
export function Check(props: SVGProps<SVGSVGElement>) {
|
|
4
|
-
return (
|
|
5
|
-
<svg
|
|
6
|
-
focusable="false"
|
|
7
|
-
aria-hidden="true"
|
|
8
|
-
role="img"
|
|
9
|
-
width={24}
|
|
10
|
-
height={24}
|
|
11
|
-
fill="none"
|
|
12
|
-
viewBox="0 0 24 24"
|
|
13
|
-
{...props}
|
|
14
|
-
>
|
|
15
|
-
<path
|
|
16
|
-
stroke="currentColor"
|
|
17
|
-
strokeLinecap="round"
|
|
18
|
-
strokeLinejoin="round"
|
|
19
|
-
strokeWidth={1.5}
|
|
20
|
-
d="M5.75 12.5L10 16.75L18.25 7.25"
|
|
21
|
-
/>
|
|
22
|
-
</svg>
|
|
23
|
-
);
|
|
24
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { Button, background, colors, iconSizeValues, text } from "maui";
|
|
2
|
-
import { style, useStyles } from "purse-styles";
|
|
3
|
-
import { Check } from "./Check.tsx";
|
|
4
|
-
|
|
5
|
-
const checkIcon = style({
|
|
6
|
-
width: iconSizeValues.sm,
|
|
7
|
-
height: iconSizeValues.sm,
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
export function DoneButton(props: { onClick?: () => void }) {
|
|
11
|
-
const checkClass = useStyles(checkIcon);
|
|
12
|
-
const primaryClass = useStyles(primaryButtonClass);
|
|
13
|
-
return (
|
|
14
|
-
<Button className={primaryClass} onClick={props.onClick}>
|
|
15
|
-
<Check className={checkClass} />
|
|
16
|
-
Done
|
|
17
|
-
</Button>
|
|
18
|
-
);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
const primaryButtonClass = style(
|
|
22
|
-
background.accent,
|
|
23
|
-
text({ size: "xs", fontWeight: 400, color: "onAccent" }),
|
|
24
|
-
{
|
|
25
|
-
"&:hover": {
|
|
26
|
-
backgroundColor: colors.accent[10],
|
|
27
|
-
},
|
|
28
|
-
"&:active": {
|
|
29
|
-
backgroundColor: colors.accent[11],
|
|
30
|
-
},
|
|
31
|
-
},
|
|
32
|
-
);
|