@tanishqkancharla/diffmap 0.1.3 → 0.1.4
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 +13 -10
- package/package.json +2 -2
- package/src/cli.ts +94 -54
- package/src/contentPlugin.ts +5 -3
- package/src/errors.ts +1 -1
- package/src/gist/GistApp.tsx +31 -19
- package/src/gist/GistStatus.tsx +22 -8
- package/src/gist/main.tsx +2 -2
- package/src/gist/pin.ts +14 -0
- package/src/github/GitHubApp.tsx +235 -0
- package/src/github/fetchRepo.test.ts +17 -0
- package/src/github/fetchRepo.ts +300 -0
- package/src/github/patch.test.ts +30 -0
- package/src/github/patch.ts +40 -0
- package/src/github/pin.ts +14 -0
- package/src/github/route.test.ts +68 -0
- package/src/github/route.ts +175 -0
- package/src/host/HostApp.tsx +22 -0
- package/src/index.ts +1 -0
- package/src/mermaid.ts +29 -1
- package/src/parseViewer.test.ts +185 -0
- package/src/parseViewer.ts +27 -3
- package/src/serve.ts +9 -1
- package/src/share.ts +5 -1
- package/src/viewer/CallStackDiff.tsx +26 -6
- package/src/viewer/FileExcerpt.tsx +28 -1
- package/src/viewer/SourceDiffPanel.tsx +131 -54
- package/src/viewer/TableOfContents.tsx +1 -0
- package/src/viewer/ViewerApp.tsx +4 -1
- package/src/viewer/main.tsx +10 -3
- package/src/viewer/sourceHeader.test.ts +131 -0
- package/src/viewer/sourceHeader.ts +103 -0
- package/src/viewer/sourceNavigation.ts +46 -2
- package/src/viewer/viewerMode.ts +1 -1
- package/src/viewer/vite-env.d.ts +2 -1
package/README.md
CHANGED
|
@@ -5,27 +5,26 @@
|
|
|
5
5
|
diffmap is a set of personal skills as well as a web-viewer for Markdown files. It has a unique form of showing diffs using a mixture of callstack diffs and mermaid diagram links, which I've found to be personally extremely helpful in understanding massive diffs.
|
|
6
6
|
|
|
7
7
|
```sh
|
|
8
|
-
npx diffmap path/to/file.md
|
|
8
|
+
npx @tanishqkancharla/diffmap serve path/to/file.md
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
Bare `npx @tanishqkancharla/diffmap path/to/file.md` is the same as `serve`.
|
|
12
|
+
|
|
11
13
|
Share a secret gist and print a `diffmap.dev` link (requires [`gh`](https://cli.github.com/) logged in). Does not start a local server:
|
|
12
14
|
|
|
13
15
|
```sh
|
|
14
|
-
npx diffmap share path/to.md
|
|
16
|
+
npx @tanishqkancharla/diffmap share path/to.md
|
|
15
17
|
```
|
|
16
18
|
|
|
17
19
|
Hosted viewer: `https://diffmap.dev/g/<gistId>` (optional `/<file.md>`; `#heading` is the table of contents). The page fetches the gist in the browser from `api.github.com`. Gists are unlisted, not private.
|
|
18
20
|
|
|
19
21
|
## Agent skills
|
|
20
22
|
|
|
21
|
-
Install skills from this repository:
|
|
22
|
-
|
|
23
23
|
```sh
|
|
24
|
-
npx skills add tanishqkancharla/diffmap --skill code-walkthrough
|
|
25
24
|
npx skills add tanishqkancharla/diffmap --skill generate-spec
|
|
26
25
|
```
|
|
27
26
|
|
|
28
|
-
|
|
27
|
+
Keeps a spec in `specs/` that stays true to what's planned versus already in the tree. Serve it with diffmap. Share from a GitHub PR (`https://diffmap.dev/<owner>/<repo>/pull/<n>/specs/<name>.md`) or `npx @tanishqkancharla/diffmap share`.
|
|
29
28
|
|
|
30
29
|
Options:
|
|
31
30
|
|
|
@@ -41,7 +40,7 @@ Vite connections do not keep it alive.
|
|
|
41
40
|
List every running viewer, including viewers using custom ports:
|
|
42
41
|
|
|
43
42
|
```sh
|
|
44
|
-
npx diffmap list
|
|
43
|
+
npx @tanishqkancharla/diffmap list
|
|
45
44
|
```
|
|
46
45
|
|
|
47
46
|
Request the page with `Accept: text/markdown` to read the current source file
|
|
@@ -54,10 +53,14 @@ curl -H 'Accept: text/markdown' http://127.0.0.1:4177/
|
|
|
54
53
|
## Library
|
|
55
54
|
|
|
56
55
|
```ts
|
|
57
|
-
import {
|
|
56
|
+
import {
|
|
57
|
+
startServer,
|
|
58
|
+
parseFence,
|
|
59
|
+
parseViewerDocument,
|
|
60
|
+
} from "@tanishqkancharla/diffmap";
|
|
58
61
|
```
|
|
59
62
|
|
|
60
|
-
`parseViewerDocument` turns markdown into the page document with [md4x](https://github.com/unjs/md4x). `startServer` listens. The generate-spec
|
|
63
|
+
`parseViewerDocument` turns markdown into the page document with [md4x](https://github.com/unjs/md4x). `startServer` listens. The generate-spec skill owns document shape; the viewer does not.
|
|
61
64
|
|
|
62
65
|
## Fences
|
|
63
66
|
|
|
@@ -176,7 +179,7 @@ Resolution uses the workspace's TypeScript configuration and current files.
|
|
|
176
179
|
Deleted files and lines that no longer match the workspace show a message;
|
|
177
180
|
their call stack references still highlight the embedded old-side diff.
|
|
178
181
|
|
|
179
|
-
Run `npx diffmap fixtures/annotations.md` for an example
|
|
182
|
+
Run `npx @tanishqkancharla/diffmap fixtures/annotations.md` for an example
|
|
180
183
|
with old/new references, multiple files, and unchanged context.
|
|
181
184
|
|
|
182
185
|
## Link Mermaid nodes and edges
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanishqkancharla/diffmap",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Serve a spec or code walkthrough as a local page",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Tanishq Kancharla",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"lint:fix": "oxlint --fix src vite.config.ts vite.gist.config.ts bin.js",
|
|
34
34
|
"prepublishOnly": "npm run typecheck && npm run lint && npm run format:check",
|
|
35
35
|
"release": "npm publish",
|
|
36
|
-
"test": "tsx --test src/gist/route.test.ts",
|
|
36
|
+
"test": "tsx --test src/parseViewer.test.ts src/gist/route.test.ts src/github/route.test.ts src/github/patch.test.ts src/github/fetchRepo.test.ts src/viewer/sourceHeader.test.ts",
|
|
37
37
|
"typecheck": "tsc --noEmit",
|
|
38
38
|
"build": "vite build",
|
|
39
39
|
"build:gist": "vite build",
|
package/src/cli.ts
CHANGED
|
@@ -6,65 +6,76 @@ import { listRunningDiffmaps } from "./registry.js";
|
|
|
6
6
|
import { startServer } from "./serve.js";
|
|
7
7
|
import { shareMarkdownFile } from "./share.js";
|
|
8
8
|
|
|
9
|
+
const serveArgs = z.object({
|
|
10
|
+
file: z.string().describe("Path to the markdown file"),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const serveOptions = z.object({
|
|
14
|
+
port: z.coerce.number().optional().describe("Port (default 4177)"),
|
|
15
|
+
root: z.string().optional().describe("Workspace root for file excerpts"),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const serveOutput = z.object({
|
|
19
|
+
url: z.string(),
|
|
20
|
+
file: z.string(),
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
type ServeContext = {
|
|
24
|
+
args: { file: string };
|
|
25
|
+
options: { port?: number | undefined; root?: string | undefined };
|
|
26
|
+
error: (input: { code: string; message: string }) => never;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
async function* runServe(c: ServeContext) {
|
|
30
|
+
const started = await startServer({
|
|
31
|
+
filePath: resolveFromInvokeCwd(c.args.file),
|
|
32
|
+
workspaceRoot:
|
|
33
|
+
c.options.root === undefined
|
|
34
|
+
? resolveFromInvokeCwd(".")
|
|
35
|
+
: resolveFromInvokeCwd(c.options.root),
|
|
36
|
+
port: c.options.port === undefined ? 4177 : c.options.port,
|
|
37
|
+
});
|
|
38
|
+
if (started instanceof Error) {
|
|
39
|
+
return c.error({
|
|
40
|
+
code: "DIFFMAP",
|
|
41
|
+
message: started.message,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
yield { url: started.url, file: started.filePath };
|
|
45
|
+
await started.closed;
|
|
46
|
+
return { url: started.url, file: started.filePath };
|
|
47
|
+
}
|
|
48
|
+
|
|
9
49
|
const cli = Cli.create("diffmap", {
|
|
10
|
-
description: "Serve a spec or
|
|
50
|
+
description: "Serve a spec locally, or share it as a gist",
|
|
11
51
|
version: "0.1.3",
|
|
12
|
-
args:
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
url: z.string(),
|
|
21
|
-
file: z.string(),
|
|
22
|
-
}),
|
|
23
|
-
async *run(c) {
|
|
24
|
-
const started = await startServer({
|
|
25
|
-
filePath: resolveFromInvokeCwd(c.args.file),
|
|
26
|
-
workspaceRoot:
|
|
27
|
-
c.options.root === undefined
|
|
28
|
-
? resolveFromInvokeCwd(".")
|
|
29
|
-
: resolveFromInvokeCwd(c.options.root),
|
|
30
|
-
port: c.options.port === undefined ? 4177 : c.options.port,
|
|
31
|
-
});
|
|
32
|
-
if (started instanceof Error) {
|
|
33
|
-
return c.error({
|
|
34
|
-
code: "DIFFMAP",
|
|
35
|
-
message: started.message,
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
yield { url: started.url, file: started.filePath };
|
|
39
|
-
await started.closed;
|
|
40
|
-
return { url: started.url, file: started.filePath };
|
|
41
|
-
},
|
|
42
|
-
})
|
|
43
|
-
.command("list", {
|
|
44
|
-
description: "List running diffmap viewers",
|
|
45
|
-
output: z.object({
|
|
46
|
-
instances: z.array(
|
|
47
|
-
z.object({
|
|
48
|
-
pid: z.number(),
|
|
49
|
-
title: z.string(),
|
|
50
|
-
url: z.string(),
|
|
51
|
-
file: z.string(),
|
|
52
|
-
}),
|
|
53
|
-
),
|
|
54
|
-
}),
|
|
55
|
-
async run(c) {
|
|
56
|
-
const instances = await listRunningDiffmaps();
|
|
57
|
-
if (instances instanceof Error) {
|
|
58
|
-
return c.error({
|
|
59
|
-
code: "DIFFMAP",
|
|
60
|
-
message: instances.message,
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
return c.ok({ instances });
|
|
52
|
+
args: serveArgs,
|
|
53
|
+
options: serveOptions,
|
|
54
|
+
output: serveOutput,
|
|
55
|
+
hint: "`serve <spec.md>` runs the local viewer. `share <spec.md>` publishes a secret gist.",
|
|
56
|
+
examples: [
|
|
57
|
+
{
|
|
58
|
+
args: { file: "spec.md" },
|
|
59
|
+
description: "Same as `diffmap serve spec.md`",
|
|
64
60
|
},
|
|
61
|
+
],
|
|
62
|
+
run: runServe,
|
|
63
|
+
})
|
|
64
|
+
.command("serve", {
|
|
65
|
+
description: "Run the local viewer server",
|
|
66
|
+
args: serveArgs,
|
|
67
|
+
options: serveOptions,
|
|
68
|
+
output: serveOutput,
|
|
69
|
+
examples: [
|
|
70
|
+
{
|
|
71
|
+
args: { file: "spec.md" },
|
|
72
|
+
description: "Open the local viewer",
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
run: runServe,
|
|
65
76
|
})
|
|
66
77
|
.command("share", {
|
|
67
|
-
description: "
|
|
78
|
+
description: "Publish a secret gist and print https://diffmap.dev/g/<id>",
|
|
68
79
|
args: z.object({
|
|
69
80
|
file: z.string().describe("Path to the markdown file"),
|
|
70
81
|
}),
|
|
@@ -76,6 +87,12 @@ const cli = Cli.create("diffmap", {
|
|
|
76
87
|
gistUrl: z.string(),
|
|
77
88
|
viewerUrl: z.string(),
|
|
78
89
|
}),
|
|
90
|
+
examples: [
|
|
91
|
+
{
|
|
92
|
+
args: { file: "spec.md" },
|
|
93
|
+
description: "Secret gist → https://diffmap.dev/g/<id>",
|
|
94
|
+
},
|
|
95
|
+
],
|
|
79
96
|
async run(c) {
|
|
80
97
|
const shared = await shareMarkdownFile({
|
|
81
98
|
filePath: resolveFromInvokeCwd(c.args.file),
|
|
@@ -89,6 +106,29 @@ const cli = Cli.create("diffmap", {
|
|
|
89
106
|
}
|
|
90
107
|
return c.ok(shared);
|
|
91
108
|
},
|
|
109
|
+
})
|
|
110
|
+
.command("list", {
|
|
111
|
+
description: "List running diffmap viewers",
|
|
112
|
+
output: z.object({
|
|
113
|
+
instances: z.array(
|
|
114
|
+
z.object({
|
|
115
|
+
pid: z.number(),
|
|
116
|
+
title: z.string(),
|
|
117
|
+
url: z.string(),
|
|
118
|
+
file: z.string(),
|
|
119
|
+
}),
|
|
120
|
+
),
|
|
121
|
+
}),
|
|
122
|
+
async run(c) {
|
|
123
|
+
const instances = await listRunningDiffmaps();
|
|
124
|
+
if (instances instanceof Error) {
|
|
125
|
+
return c.error({
|
|
126
|
+
code: "DIFFMAP",
|
|
127
|
+
message: instances.message,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
return c.ok({ instances });
|
|
131
|
+
},
|
|
92
132
|
});
|
|
93
133
|
|
|
94
134
|
await cli.serve();
|
package/src/contentPlugin.ts
CHANGED
|
@@ -15,9 +15,11 @@ export function diffmapContentPlugin(input: {
|
|
|
15
15
|
async load(id) {
|
|
16
16
|
if (id !== virtualId) return;
|
|
17
17
|
const source = await fs.readFile(input.filePath, "utf8");
|
|
18
|
-
const document = parseViewerDocument(source);
|
|
19
|
-
if (document instanceof Error)
|
|
20
|
-
|
|
18
|
+
const document = parseViewerDocument(source, input.filePath);
|
|
19
|
+
if (document instanceof Error) {
|
|
20
|
+
return `export const viewerDocument = null; export const parseError = ${JSON.stringify(document.message)};`;
|
|
21
|
+
}
|
|
22
|
+
return `export const viewerDocument = ${JSON.stringify(document)}; export const parseError = null;`;
|
|
21
23
|
},
|
|
22
24
|
transformIndexHtml(html) {
|
|
23
25
|
return html.replaceAll(
|
package/src/errors.ts
CHANGED
|
@@ -17,7 +17,7 @@ export class DiffmapRegistryError extends errore.createTaggedError({
|
|
|
17
17
|
|
|
18
18
|
export class DiffmapMermaidError extends errore.createTaggedError({
|
|
19
19
|
name: "DiffmapMermaidError",
|
|
20
|
-
message: "diffmap could not
|
|
20
|
+
message: "diffmap could not parse mermaid in $path ($where): $reason",
|
|
21
21
|
}) {}
|
|
22
22
|
|
|
23
23
|
export class DiffmapParseError extends errore.createTaggedError({
|
package/src/gist/GistApp.tsx
CHANGED
|
@@ -6,6 +6,7 @@ import { parseViewerDocument } from "../parseViewer.ts";
|
|
|
6
6
|
import type { ViewerDocument } from "../parseViewer.ts";
|
|
7
7
|
import { ViewerApp } from "../viewer/ViewerApp.tsx";
|
|
8
8
|
import { loadGistMarkdown, type GistDocument } from "./fetchGist.ts";
|
|
9
|
+
import { GistPinContext } from "./pin.ts";
|
|
9
10
|
import { GistLanding, GistLoading, GistStatus } from "./GistStatus.tsx";
|
|
10
11
|
import {
|
|
11
12
|
gistPath,
|
|
@@ -151,24 +152,32 @@ export function GistApp() {
|
|
|
151
152
|
}
|
|
152
153
|
|
|
153
154
|
return (
|
|
154
|
-
<
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
155
|
+
<GistPinContext.Provider
|
|
156
|
+
value={{
|
|
157
|
+
gistId: view.gist.id,
|
|
158
|
+
file: view.file,
|
|
159
|
+
htmlUrl: view.gist.htmlUrl,
|
|
160
|
+
}}
|
|
161
|
+
>
|
|
162
|
+
<ViewerApp
|
|
163
|
+
key={`${view.gist.id}:${view.file}`}
|
|
164
|
+
document={view.document}
|
|
165
|
+
mode="gist"
|
|
166
|
+
headerActions={
|
|
167
|
+
<GistHeader
|
|
168
|
+
gistId={view.gist.id}
|
|
169
|
+
htmlUrl={view.gist.htmlUrl}
|
|
170
|
+
files={view.gist.files}
|
|
171
|
+
current={view.file}
|
|
172
|
+
onSelect={(file) => {
|
|
173
|
+
const next = gistPath(view.gist.id, file);
|
|
174
|
+
window.history.pushState({}, "", next);
|
|
175
|
+
setRoute(parseGistPath(next));
|
|
176
|
+
}}
|
|
177
|
+
/>
|
|
178
|
+
}
|
|
179
|
+
/>
|
|
180
|
+
</GistPinContext.Provider>
|
|
172
181
|
);
|
|
173
182
|
}
|
|
174
183
|
|
|
@@ -196,7 +205,10 @@ async function loadView(
|
|
|
196
205
|
file: file ?? "",
|
|
197
206
|
};
|
|
198
207
|
}
|
|
199
|
-
const parsed = parseViewerDocument(
|
|
208
|
+
const parsed = parseViewerDocument(
|
|
209
|
+
loaded.gist.chosen.content,
|
|
210
|
+
loaded.gist.chosen.name,
|
|
211
|
+
);
|
|
200
212
|
if (parsed instanceof Error) {
|
|
201
213
|
return {
|
|
202
214
|
kind: "parse-error",
|
package/src/gist/GistStatus.tsx
CHANGED
|
@@ -19,6 +19,7 @@ export function GistStatus(props: {
|
|
|
19
19
|
children: ReactNode;
|
|
20
20
|
gistId?: string;
|
|
21
21
|
gistUrl?: string;
|
|
22
|
+
linkLabel?: string;
|
|
22
23
|
}) {
|
|
23
24
|
const shell = useStyles(styles.shell);
|
|
24
25
|
const header = useStyles(styles.header);
|
|
@@ -39,7 +40,9 @@ export function GistStatus(props: {
|
|
|
39
40
|
{props.children}
|
|
40
41
|
{gistUrl !== undefined && (
|
|
41
42
|
<P>
|
|
42
|
-
<Link href={gistUrl}>
|
|
43
|
+
<Link href={gistUrl}>
|
|
44
|
+
{props.linkLabel ?? "Open gist on GitHub"}
|
|
45
|
+
</Link>
|
|
43
46
|
</P>
|
|
44
47
|
)}
|
|
45
48
|
</main>
|
|
@@ -47,10 +50,10 @@ export function GistStatus(props: {
|
|
|
47
50
|
);
|
|
48
51
|
}
|
|
49
52
|
|
|
50
|
-
export function GistLoading() {
|
|
53
|
+
export function GistLoading(props: { title?: string }) {
|
|
51
54
|
const row = useStyles(styles.loading);
|
|
52
55
|
return (
|
|
53
|
-
<GistStatus title="Loading gist…">
|
|
56
|
+
<GistStatus title={props.title ?? "Loading gist…"}>
|
|
54
57
|
<div className={row}>
|
|
55
58
|
<Thinking />
|
|
56
59
|
</div>
|
|
@@ -64,21 +67,32 @@ export function GistLanding() {
|
|
|
64
67
|
<GistStatus title="diffmap">
|
|
65
68
|
<P>
|
|
66
69
|
A hosted viewer for diffmap walkthroughs. This page fetches a GitHub
|
|
67
|
-
gist in your browser. There is no server holding
|
|
70
|
+
gist or a public repo file in your browser. There is no server holding
|
|
71
|
+
the markdown.
|
|
68
72
|
</P>
|
|
69
73
|
<P>
|
|
70
|
-
Canonical URL:{" "}
|
|
74
|
+
Canonical gist URL:{" "}
|
|
71
75
|
<code className={code}>https://diffmap.dev/g/<gistId></code>.
|
|
72
76
|
Optional file:{" "}
|
|
73
|
-
<code className={code}>/g/<gistId>/<file.md></code>.
|
|
74
|
-
|
|
77
|
+
<code className={code}>/g/<gistId>/<file.md></code>. GitHub
|
|
78
|
+
spec:{" "}
|
|
79
|
+
<code className={code}>
|
|
80
|
+
https://diffmap.dev/<owner>/<repo>/pull/<n>/<path.md>
|
|
81
|
+
</code>{" "}
|
|
82
|
+
or{" "}
|
|
83
|
+
<code className={code}>
|
|
84
|
+
/<owner>/<repo>/commit/<sha>/<path.md>
|
|
85
|
+
</code>
|
|
86
|
+
. Heading hashes stay in the fragment, same as the local viewer.
|
|
75
87
|
</P>
|
|
76
88
|
<P>
|
|
77
89
|
Share a secret gist (requires <code className={code}>gh</code>
|
|
78
90
|
):
|
|
79
91
|
</P>
|
|
80
92
|
<P>
|
|
81
|
-
<code className={code}>
|
|
93
|
+
<code className={code}>
|
|
94
|
+
npx @tanishqkancharla/diffmap share path/to.md
|
|
95
|
+
</code>
|
|
82
96
|
</P>
|
|
83
97
|
<P>
|
|
84
98
|
Secret gists are unlisted, not private. Anyone with the id can read
|
package/src/gist/main.tsx
CHANGED
|
@@ -2,7 +2,7 @@ import { StrictMode } from "react";
|
|
|
2
2
|
import { createRoot } from "react-dom/client";
|
|
3
3
|
import { MauiProvider } from "maui";
|
|
4
4
|
import { rewritePiGistUrl } from "./route.ts";
|
|
5
|
-
import {
|
|
5
|
+
import { HostApp } from "../host/HostApp.tsx";
|
|
6
6
|
import "../viewer/styles.css";
|
|
7
7
|
|
|
8
8
|
const rewritten = rewritePiGistUrl(
|
|
@@ -19,7 +19,7 @@ if (root === null) throw new Error("diffmap root element is missing");
|
|
|
19
19
|
createRoot(root).render(
|
|
20
20
|
<StrictMode>
|
|
21
21
|
<MauiProvider>
|
|
22
|
-
<
|
|
22
|
+
<HostApp />
|
|
23
23
|
</MauiProvider>
|
|
24
24
|
</StrictMode>,
|
|
25
25
|
);
|
package/src/gist/pin.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { createContext, useContext } from "react";
|
|
2
|
+
|
|
3
|
+
/** Gist snapshot: identify the gist file, never a fake owner/repo. */
|
|
4
|
+
export type GistPin = {
|
|
5
|
+
gistId: string;
|
|
6
|
+
file: string;
|
|
7
|
+
htmlUrl: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export const GistPinContext = createContext<GistPin | undefined>(undefined);
|
|
11
|
+
|
|
12
|
+
export function useGistPin() {
|
|
13
|
+
return useContext(GistPinContext);
|
|
14
|
+
}
|