@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.
Files changed (53) hide show
  1. package/README.md +69 -61
  2. package/index.html +18 -0
  3. package/package.json +7 -5
  4. package/src/annotations.ts +36 -29
  5. package/src/changes.ts +229 -0
  6. package/src/cli.test.ts +50 -0
  7. package/src/cli.ts +41 -5
  8. package/src/errors.ts +10 -0
  9. package/src/gist/GistApp.tsx +39 -20
  10. package/src/gist/GistStatus.tsx +2 -51
  11. package/src/gist/fetchGist.ts +23 -0
  12. package/src/git.test.ts +402 -0
  13. package/src/git.ts +498 -0
  14. package/src/github/GitHubApp.tsx +39 -22
  15. package/src/github/fetchRepo.test.ts +59 -1
  16. package/src/github/fetchRepo.ts +99 -43
  17. package/src/github/patch.test.ts +18 -9
  18. package/src/github/patch.ts +30 -19
  19. package/src/host/HostHome.tsx +86 -0
  20. package/src/host/files.ts +10 -0
  21. package/src/host/home.md +156 -0
  22. package/src/host/home.test.ts +83 -0
  23. package/src/index.ts +1 -0
  24. package/src/packageVersion.ts +7 -0
  25. package/src/parseFence.ts +6 -6
  26. package/src/parseViewer.test.ts +87 -0
  27. package/src/parseViewer.ts +103 -77
  28. package/src/serve.test.ts +306 -0
  29. package/src/serve.ts +48 -5
  30. package/src/share.ts +18 -0
  31. package/src/update.test.ts +19 -0
  32. package/src/update.ts +285 -0
  33. package/src/viewer/CallStackDiff.tsx +15 -3
  34. package/src/viewer/ChangesCard.tsx +267 -0
  35. package/src/viewer/CloseServerButton.tsx +16 -0
  36. package/src/viewer/ComarkView.tsx +83 -15
  37. package/src/viewer/Fence.tsx +9 -1
  38. package/src/viewer/FileExcerpt.tsx +25 -3
  39. package/src/viewer/HtmlPlaceholder.tsx +6 -1
  40. package/src/viewer/MermaidBlock.tsx +99 -11
  41. package/src/viewer/RangePicker.tsx +526 -0
  42. package/src/viewer/SourceDiffPanel.tsx +277 -121
  43. package/src/viewer/TableOfContents.tsx +313 -39
  44. package/src/viewer/ViewerApp.tsx +120 -68
  45. package/src/viewer/changesSource.ts +91 -0
  46. package/src/viewer/sourceHeader.test.ts +2 -87
  47. package/src/viewer/sourceHeader.ts +4 -63
  48. package/src/viewer/sourceNavigation.ts +33 -42
  49. package/src/viewer/viewerMode.ts +1 -1
  50. package/vite.config.ts +2 -1
  51. package/vite.gist.config.ts +57 -0
  52. package/src/viewer/Check.tsx +0 -24
  53. 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 (mode === "gist") return;
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
- export function matchingSourceDiff(
109
- source: SourceDefinition,
110
- items: CodeViewDiffItem[],
111
- ): CodeViewLineSelection | undefined {
112
- const lines = source.contents.split(/\r?\n/);
113
- for (const item of items) {
114
- const diff = item.fileDiff;
115
- if (diff.name !== source.path) continue;
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
- return undefined;
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
  }
@@ -1,6 +1,6 @@
1
1
  import { createContext, useContext } from "react";
2
2
 
3
- export type ViewerMode = "local" | "gist" | "github";
3
+ export type ViewerMode = "local" | "gist" | "github" | "host";
4
4
 
5
5
  export const ViewerModeContext = createContext<ViewerMode>("local");
6
6
 
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: 4177,
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());
@@ -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
- );