feat: create patch event
This commit is contained in:
parent
39cb400cda
commit
7aa0fe214b
@ -5,6 +5,7 @@
|
||||
"dependencies": {
|
||||
"@isomorphic-git/lightning-fs": "^4.6.0",
|
||||
"@noble/secp256k1": "^2.0.0",
|
||||
"@protobufjs/base64": "^1.1.2",
|
||||
"@types/node": "^16.7.13",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
@ -15,6 +16,7 @@
|
||||
"isomorphic-git": "^1.23.0",
|
||||
"moment": "^2.29.4",
|
||||
"nostr-relaypool": "^0.6.27",
|
||||
"nostr-tools": "^1.10.1",
|
||||
"parse-diff": "^0.11.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
|
288
patch
Normal file
288
patch
Normal file
@ -0,0 +1,288 @@
|
||||
diff --git a/package.json b/package.json
|
||||
index 000f4b0..6fff3ef 100644
|
||||
--- a/package.json
|
||||
+++ b/package.json
|
||||
@@ -5,6 +5,7 @@
|
||||
"dependencies": {
|
||||
"@isomorphic-git/lightning-fs": "^4.6.0",
|
||||
"@noble/secp256k1": "^2.0.0",
|
||||
+ "@protobufjs/base64": "^1.1.2",
|
||||
"@types/node": "^16.7.13",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
@@ -15,6 +16,7 @@
|
||||
"isomorphic-git": "^1.23.0",
|
||||
"moment": "^2.29.4",
|
||||
"nostr-relaypool": "^0.6.27",
|
||||
+ "nostr-tools": "^1.10.1",
|
||||
"parse-diff": "^0.11.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
diff --git a/src/App.tsx b/src/App.tsx
|
||||
index 13d8e19..05246df 100644
|
||||
--- a/src/App.tsx
|
||||
+++ b/src/App.tsx
|
||||
@@ -16,7 +16,7 @@ const relays = [
|
||||
export const PatchKind = 19691228;
|
||||
|
||||
const Store = new PatchCache("Patches", PatchstrDb.events);
|
||||
-const Nostr = new RelayPool(relays);
|
||||
+export const Nostr = new RelayPool(relays);
|
||||
const sub = Nostr.subscribe([
|
||||
{
|
||||
kinds: [PatchKind],
|
||||
diff --git a/src/NewPatch.css b/src/NewPatch.css
|
||||
index fb08796..479a2db 100644
|
||||
--- a/src/NewPatch.css
|
||||
+++ b/src/NewPatch.css
|
||||
@@ -1,4 +1,11 @@
|
||||
.new-patch {
|
||||
padding: 20px;
|
||||
- width: 720px;
|
||||
+ display: flex;
|
||||
+ flex-direction: row;
|
||||
+}
|
||||
+
|
||||
+.new-patch>div {
|
||||
+ flex: 1;
|
||||
+ height: calc(100vh - 40px);
|
||||
+ overflow: auto;
|
||||
}
|
||||
\ No newline at end of file
|
||||
diff --git a/src/NewPatch.tsx b/src/NewPatch.tsx
|
||||
index 15b304c..65f505f 100644
|
||||
--- a/src/NewPatch.tsx
|
||||
+++ b/src/NewPatch.tsx
|
||||
@@ -1,9 +1,14 @@
|
||||
-import { useState } from "react";
|
||||
+import { useMemo, useState } from "react";
|
||||
import git from "isomorphic-git";
|
||||
import http from "isomorphic-git/http/web";
|
||||
import LightningFS from "@isomorphic-git/lightning-fs";
|
||||
|
||||
import "./NewPatch.css";
|
||||
+import PatchView from "./PathView";
|
||||
+import { ParsedPatch, parseDiffEvent } from "./Diff";
|
||||
+import { unixNow } from "./Util";
|
||||
+import buildPatchEvent from "./PatchBuilder";
|
||||
+import { Nostr } from "./App";
|
||||
|
||||
const fs = new LightningFS("patchstr-fs");
|
||||
|
||||
@@ -14,52 +19,61 @@ export default function NewPatch() {
|
||||
const [relay, setRelay] = useState("");
|
||||
|
||||
async function testRepo() {
|
||||
- const testDir = `/${new Date().getTime() / 1000}_test`;
|
||||
- await git.init({
|
||||
- fs,
|
||||
- dir: testDir
|
||||
+ const ev = await buildPatchEvent(subject, "", repo, diff);
|
||||
+ console.debug(ev);
|
||||
+
|
||||
+ Nostr.publish(ev, relay.split(/[,; ]/));
|
||||
+ }
|
||||
+
|
||||
+ function inputs() {
|
||||
+ return <div>
|
||||
+ <a href="/">< Back</a>
|
||||
+ <h1>
|
||||
+ New Patch
|
||||
+ </h1>
|
||||
+ <p>
|
||||
+ Patch Title:
|
||||
+ </p>
|
||||
+ <input type="text" placeholder="chore: tweak NIP's" value={subject} onChange={e => setSubject(e.target.value)} />
|
||||
+ <p>
|
||||
+ Enter Diff:
|
||||
+ </p>
|
||||
+ <textarea cols={80} rows={40} value={diff} onChange={e => setDiff(e.target.value)}></textarea>
|
||||
+ <p>
|
||||
+ Git Repo:
|
||||
+ </p>
|
||||
+ <input type="text" placeholder="https://github.com/user/repo" value={repo} onChange={e => setRepo(e.target.value)} />
|
||||
+ <p>
|
||||
+ Relay(s):
|
||||
+ </p>
|
||||
+ <input type="text" placeholder="wss://nostr.mutinywallet.com wss://nos.lol" value={relay} onChange={e => setRelay(e.target.value)} />
|
||||
+ <br /><br />
|
||||
+ <button onClick={() => testRepo()}>
|
||||
+ Submit
|
||||
+ </button>
|
||||
+ </div>;
|
||||
+ }
|
||||
+
|
||||
+ const tmpDiff = useMemo(() => {
|
||||
+ return parseDiffEvent({
|
||||
+ created_at: unixNow(),
|
||||
+ content: diff,
|
||||
+ pubkey: "",
|
||||
+ sig: "",
|
||||
+ id: "",
|
||||
+ tags: [],
|
||||
+ kind: 0
|
||||
})
|
||||
- await git.addRemote({
|
||||
- fs,
|
||||
- dir: testDir,
|
||||
- remote: "upstream",
|
||||
- url: repo
|
||||
- });
|
||||
- const info = await git.fetch({
|
||||
- fs,
|
||||
- http,
|
||||
- remote: "upstream",
|
||||
- dir: testDir,
|
||||
- corsProxy: "https://cors.isomorphic-git.org"
|
||||
- });
|
||||
- fs.rmdir(testDir, undefined, console.error);
|
||||
- console.debug(info);
|
||||
+ }, [diff]);
|
||||
+
|
||||
+ function preview() {
|
||||
+ return <div>
|
||||
+ <PatchView patch={tmpDiff} />
|
||||
+ </div>
|
||||
}
|
||||
|
||||
return <div className="new-patch">
|
||||
- <a href="/">< Back</a>
|
||||
- <h1>
|
||||
- New Patch
|
||||
- </h1>
|
||||
- <p>
|
||||
- Patch Title:
|
||||
- </p>
|
||||
- <input type="text" placeholder="chore: tweak NIP's" value={subject} onChange={e => setSubject(e.target.value)} />
|
||||
- <p>
|
||||
- Enter Diff:
|
||||
- </p>
|
||||
- <textarea cols={80} rows={40} value={diff} onChange={e => setDiff(e.target.value)}></textarea>
|
||||
- <p>
|
||||
- Git Repo:
|
||||
- </p>
|
||||
- <input type="text" placeholder="https://github.com/user/repo" value={repo} onChange={e => setRepo(e.target.value)} />
|
||||
- <p>
|
||||
- Relay(s):
|
||||
- </p>
|
||||
- <input type="text" placeholder="wss://nostr.mutinywallet.com wss://nos.lol" value={relay} onChange={e => setRelay(e.target.value)} />
|
||||
- <br /><br />
|
||||
- <button onClick={() => testRepo()}>
|
||||
- Submit
|
||||
- </button>
|
||||
+ {inputs()}
|
||||
+ {preview()}
|
||||
</div>
|
||||
}
|
||||
\ No newline at end of file
|
||||
diff --git a/src/PatchReview.tsx b/src/PatchReview.tsx
|
||||
index fe2a853..c00d161 100644
|
||||
--- a/src/PatchReview.tsx
|
||||
+++ b/src/PatchReview.tsx
|
||||
@@ -1,10 +1,8 @@
|
||||
-import { Chunk, File } from "parse-diff";
|
||||
import { useLocation } from "react-router-dom";
|
||||
-import hljs from "highlight.js";
|
||||
-import 'highlight.js/styles/dark.css';
|
||||
|
||||
import "./PatchReview.css";
|
||||
import { ParsedPatch } from "./Diff";
|
||||
+import PatchView from "./PathView";
|
||||
|
||||
export default function PatchReview() {
|
||||
const location = useLocation();
|
||||
@@ -13,52 +11,5 @@ export default function PatchReview() {
|
||||
return <b>Missing route data</b>
|
||||
}
|
||||
|
||||
- function renderChunk(c: Chunk) {
|
||||
- var oldY = c.oldStart;
|
||||
- var newY = c.newStart;
|
||||
- return <>
|
||||
- <div className="diff chunk">
|
||||
- <div></div>
|
||||
- <div></div>
|
||||
- <div>
|
||||
- {c.content}
|
||||
- </div>
|
||||
- </div>
|
||||
- {c.changes.map(v => <div className={`diff ${v.type}`}>
|
||||
- <div>{v.type === "del" || v.type === "normal" ? ++oldY : ""}</div>
|
||||
- <div>{v.type === "add" || v.type === "normal" ? ++newY : ""}</div>
|
||||
- <div dangerouslySetInnerHTML={{
|
||||
- __html: hljs.highlightAuto(v.content, [""]).value
|
||||
- }}>
|
||||
- </div>
|
||||
- </div>)}
|
||||
- </>
|
||||
- }
|
||||
-
|
||||
- function renderFileChanges(f: File) {
|
||||
- const k = `${f.from}=${f.to}`;
|
||||
- return <div className="file" key={k}>
|
||||
- <div className="header">
|
||||
- <div>
|
||||
- {f.from}{f.to && f.to !== f.from && `...${f.to}`}
|
||||
- </div>
|
||||
- <div>
|
||||
- <div className="add">
|
||||
- +{f.additions ?? 0}
|
||||
- </div>
|
||||
- <div className="del">
|
||||
- -{f.deletions ?? 0}
|
||||
- </div>
|
||||
- </div>
|
||||
- </div>
|
||||
- <div className="body">
|
||||
- {f.chunks.map(renderChunk)}
|
||||
- </div>
|
||||
- </div>
|
||||
- }
|
||||
-
|
||||
- const patch = location.state as ParsedPatch
|
||||
- return <>
|
||||
- {patch.diff.map(renderFileChanges)}
|
||||
- </>
|
||||
+ return <PatchView patch={location.state as ParsedPatch} />
|
||||
}
|
||||
\ No newline at end of file
|
||||
diff --git a/src/Util.ts b/src/Util.ts
|
||||
index 76e421e..3023652 100644
|
||||
--- a/src/Util.ts
|
||||
+++ b/src/Util.ts
|
||||
@@ -36,6 +36,10 @@ export function unixNowMs() {
|
||||
return new Date().getTime();
|
||||
}
|
||||
|
||||
+export function unixNow() {
|
||||
+ return Math.floor(unixNowMs() / 1000);
|
||||
+}
|
||||
+
|
||||
export function unwrap<T>(value?: T) {
|
||||
if (!value) {
|
||||
throw new Error("Missing value");
|
||||
diff --git a/yarn.lock b/yarn.lock
|
||||
index f6f2d62..ce0a062 100644
|
||||
--- a/yarn.lock
|
||||
+++ b/yarn.lock
|
||||
@@ -1942,6 +1942,11 @@
|
||||
schema-utils "^3.0.0"
|
||||
source-map "^0.7.3"
|
||||
|
||||
+"@protobufjs/base64@^1.1.2":
|
||||
+ version "1.1.2"
|
||||
+ resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735"
|
||||
+ integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==
|
||||
+
|
||||
"@remix-run/router@1.6.0":
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.6.0.tgz#45010e1826f4d81a1b2cfaf874f1aac93998cd28"
|
||||
@@ -6990,7 +6995,7 @@ nostr-relaypool@^0.6.27:
|
||||
nostr-tools "^1.10.0"
|
||||
safe-stable-stringify "^2.4.2"
|
||||
|
||||
-nostr-tools@^1.10.0:
|
||||
+nostr-tools@^1.10.0, nostr-tools@^1.10.1:
|
||||
version "1.10.1"
|
||||
resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-1.10.1.tgz#b52043b3031f4314478d0a3bfaa8ffb9cc4f98a0"
|
||||
integrity sha512-zgTYJeuZQ3CDASsmBEcB5i6V6l0IaA6cjnll6OVik3FoZcvbCaL7yP8I40hYnOIi3KlJykV7jEF9fn8h1NzMnA==
|
@ -16,7 +16,7 @@ const relays = [
|
||||
export const PatchKind = 19691228;
|
||||
|
||||
const Store = new PatchCache("Patches", PatchstrDb.events);
|
||||
const Nostr = new RelayPool(relays);
|
||||
export const Nostr = new RelayPool(relays);
|
||||
const sub = Nostr.subscribe([
|
||||
{
|
||||
kinds: [PatchKind],
|
||||
@ -41,7 +41,7 @@ export function App() {
|
||||
const [tag, setTag] = useState<string>();
|
||||
|
||||
const patches = useMemo(() => {
|
||||
return store.filter(a => tag === undefined || a.tag === tag);
|
||||
return [...store.filter(a => tag === undefined || a.tag === tag)].sort(a => -a.created);
|
||||
}, [tag, store]);
|
||||
|
||||
return (
|
||||
|
@ -30,7 +30,7 @@ export function parseDiffEvent(ev: Event) {
|
||||
pubkey: ev.pubkey,
|
||||
tag,
|
||||
author: {
|
||||
name: matches?.[1],
|
||||
name: matches?.[1] ?? ev.pubkey.slice(0, 12),
|
||||
email: matches?.[2]
|
||||
},
|
||||
subject,
|
||||
|
@ -1,4 +1,11 @@
|
||||
.new-patch {
|
||||
padding: 20px;
|
||||
width: 720px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.new-patch>div {
|
||||
flex: 1;
|
||||
height: calc(100vh - 40px);
|
||||
overflow: auto;
|
||||
}
|
116
src/NewPatch.tsx
116
src/NewPatch.tsx
@ -1,65 +1,81 @@
|
||||
import { useState } from "react";
|
||||
import git from "isomorphic-git";
|
||||
import http from "isomorphic-git/http/web";
|
||||
import LightningFS from "@isomorphic-git/lightning-fs";
|
||||
import { useMemo, useState } from "react";
|
||||
|
||||
import "./NewPatch.css";
|
||||
|
||||
const fs = new LightningFS("patchstr-fs");
|
||||
import PatchView from "./PathView";
|
||||
import { parseDiffEvent } from "./Diff";
|
||||
import { unixNow } from "./Util";
|
||||
import buildPatchEvent from "./PatchBuilder";
|
||||
import { Nostr } from "./App";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { encodeTLV } from "./TLV";
|
||||
import { NostrPrefix } from "./Nostr";
|
||||
|
||||
export default function NewPatch() {
|
||||
const navigate = useNavigate();
|
||||
const [subject, setSubject] = useState("");
|
||||
const [diff, setDiff] = useState("");
|
||||
const [repo, setRepo] = useState("");
|
||||
const [relay, setRelay] = useState("");
|
||||
|
||||
async function testRepo() {
|
||||
const testDir = `/${new Date().getTime() / 1000}_test`;
|
||||
await git.init({
|
||||
fs,
|
||||
dir: testDir
|
||||
async function submitPatch() {
|
||||
const ev = await buildPatchEvent(subject, "", repo, diff);
|
||||
console.debug(ev);
|
||||
|
||||
Nostr.publish(ev, relay.split(/[,; ]/));
|
||||
navigate(`/e/${encodeTLV(ev.id, NostrPrefix.Event)}`, {
|
||||
state: ev
|
||||
});
|
||||
}
|
||||
|
||||
function inputs() {
|
||||
return <div>
|
||||
<a href="/">< Back</a>
|
||||
<h1>
|
||||
New Patch
|
||||
</h1>
|
||||
<p>
|
||||
Patch Title:
|
||||
</p>
|
||||
<input type="text" placeholder="chore: tweak NIP's" value={subject} onChange={e => setSubject(e.target.value)} />
|
||||
<p>
|
||||
Enter Diff:
|
||||
</p>
|
||||
<textarea cols={80} rows={40} value={diff} onChange={e => setDiff(e.target.value)}></textarea>
|
||||
<p>
|
||||
Git Repo:
|
||||
</p>
|
||||
<input type="text" placeholder="https://github.com/user/repo" value={repo} onChange={e => setRepo(e.target.value)} />
|
||||
<p>
|
||||
Relay(s):
|
||||
</p>
|
||||
<input type="text" placeholder="wss://nostr.mutinywallet.com wss://nos.lol" value={relay} onChange={e => setRelay(e.target.value)} />
|
||||
<br /><br />
|
||||
<button onClick={() => submitPatch()}>
|
||||
Submit
|
||||
</button>
|
||||
</div>;
|
||||
}
|
||||
|
||||
const tmpDiff = useMemo(() => {
|
||||
return parseDiffEvent({
|
||||
created_at: unixNow(),
|
||||
content: diff,
|
||||
pubkey: "",
|
||||
sig: "",
|
||||
id: "",
|
||||
tags: [],
|
||||
kind: 0
|
||||
})
|
||||
await git.addRemote({
|
||||
fs,
|
||||
dir: testDir,
|
||||
remote: "upstream",
|
||||
url: repo
|
||||
});
|
||||
const info = await git.fetch({
|
||||
fs,
|
||||
http,
|
||||
remote: "upstream",
|
||||
dir: testDir,
|
||||
corsProxy: "https://cors.isomorphic-git.org"
|
||||
});
|
||||
fs.rmdir(testDir, undefined, console.error);
|
||||
console.debug(info);
|
||||
}, [diff]);
|
||||
|
||||
function preview() {
|
||||
return <div>
|
||||
<PatchView patch={tmpDiff} />
|
||||
</div>
|
||||
}
|
||||
|
||||
return <div className="new-patch">
|
||||
<a href="/">< Back</a>
|
||||
<h1>
|
||||
New Patch
|
||||
</h1>
|
||||
<p>
|
||||
Patch Title:
|
||||
</p>
|
||||
<input type="text" placeholder="chore: tweak NIP's" value={subject} onChange={e => setSubject(e.target.value)} />
|
||||
<p>
|
||||
Enter Diff:
|
||||
</p>
|
||||
<textarea cols={80} rows={40} value={diff} onChange={e => setDiff(e.target.value)}></textarea>
|
||||
<p>
|
||||
Git Repo:
|
||||
</p>
|
||||
<input type="text" placeholder="https://github.com/user/repo" value={repo} onChange={e => setRepo(e.target.value)} />
|
||||
<p>
|
||||
Relay(s):
|
||||
</p>
|
||||
<input type="text" placeholder="wss://nostr.mutinywallet.com wss://nos.lol" value={relay} onChange={e => setRelay(e.target.value)} />
|
||||
<br /><br />
|
||||
<button onClick={() => testRepo()}>
|
||||
Submit
|
||||
</button>
|
||||
{inputs()}
|
||||
{preview()}
|
||||
</div>
|
||||
}
|
35
src/PatchBuilder.ts
Normal file
35
src/PatchBuilder.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { Event, Kind } from "nostr-tools";
|
||||
import { unixNow } from "./Util";
|
||||
import { PatchKind } from "./App";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
nostr: {
|
||||
getPublicKey: () => Promise<string>;
|
||||
signEvent: (event: Event) => Promise<Event>;
|
||||
getRelays: () => Promise<Record<string, { read: boolean; write: boolean }>>;
|
||||
nip04: {
|
||||
encrypt: (pubkey: string, content: string) => Promise<string>;
|
||||
decrypt: (pubkey: string, content: string) => Promise<string>;
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default async function buildPatchEvent(title: string, author: string, repo: string, diff: string) {
|
||||
const pk = await window.nostr.getPublicKey();
|
||||
|
||||
return await window.nostr.signEvent({
|
||||
id: "",
|
||||
sig: "",
|
||||
kind: PatchKind as Kind,
|
||||
pubkey: pk,
|
||||
content: diff,
|
||||
created_at: unixNow(),
|
||||
tags: [
|
||||
["t", repo.split("/").pop()!.replace(".git", "")],
|
||||
["subject", title],
|
||||
["author", author]
|
||||
]
|
||||
})
|
||||
}
|
@ -1,10 +1,8 @@
|
||||
import { Chunk, File } from "parse-diff";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import hljs from "highlight.js";
|
||||
import 'highlight.js/styles/dark.css';
|
||||
|
||||
import "./PatchReview.css";
|
||||
import { ParsedPatch } from "./Diff";
|
||||
import PatchView from "./PathView";
|
||||
|
||||
export default function PatchReview() {
|
||||
const location = useLocation();
|
||||
@ -13,52 +11,5 @@ export default function PatchReview() {
|
||||
return <b>Missing route data</b>
|
||||
}
|
||||
|
||||
function renderChunk(c: Chunk) {
|
||||
var oldY = c.oldStart;
|
||||
var newY = c.newStart;
|
||||
return <>
|
||||
<div className="diff chunk">
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div>
|
||||
{c.content}
|
||||
</div>
|
||||
</div>
|
||||
{c.changes.map(v => <div className={`diff ${v.type}`}>
|
||||
<div>{v.type === "del" || v.type === "normal" ? ++oldY : ""}</div>
|
||||
<div>{v.type === "add" || v.type === "normal" ? ++newY : ""}</div>
|
||||
<div dangerouslySetInnerHTML={{
|
||||
__html: hljs.highlightAuto(v.content, [""]).value
|
||||
}}>
|
||||
</div>
|
||||
</div>)}
|
||||
</>
|
||||
}
|
||||
|
||||
function renderFileChanges(f: File) {
|
||||
const k = `${f.from}=${f.to}`;
|
||||
return <div className="file" key={k}>
|
||||
<div className="header">
|
||||
<div>
|
||||
{f.from}{f.to && f.to !== f.from && `...${f.to}`}
|
||||
</div>
|
||||
<div>
|
||||
<div className="add">
|
||||
+{f.additions ?? 0}
|
||||
</div>
|
||||
<div className="del">
|
||||
-{f.deletions ?? 0}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="body">
|
||||
{f.chunks.map(renderChunk)}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
const patch = location.state as ParsedPatch
|
||||
return <>
|
||||
{patch.diff.map(renderFileChanges)}
|
||||
</>
|
||||
return <PatchView patch={location.state as ParsedPatch} />
|
||||
}
|
56
src/PathView.tsx
Normal file
56
src/PathView.tsx
Normal file
@ -0,0 +1,56 @@
|
||||
import { File, Chunk } from "parse-diff";
|
||||
import hljs from "highlight.js";
|
||||
import 'highlight.js/styles/dark.css';
|
||||
|
||||
import { ParsedPatch } from "./Diff";
|
||||
|
||||
export default function PatchView({ patch }: { patch: ParsedPatch }) {
|
||||
|
||||
function renderChunk(c: Chunk) {
|
||||
var oldY = c.oldStart;
|
||||
var newY = c.newStart;
|
||||
return <>
|
||||
<div className="diff chunk">
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div>
|
||||
{c.content}
|
||||
</div>
|
||||
</div>
|
||||
{c.changes.map(v => <div className={`diff ${v.type}`}>
|
||||
<div>{v.type === "del" || v.type === "normal" ? ++oldY : ""}</div>
|
||||
<div>{v.type === "add" || v.type === "normal" ? ++newY : ""}</div>
|
||||
<div dangerouslySetInnerHTML={{
|
||||
__html: hljs.highlightAuto(v.content, [""]).value
|
||||
}}>
|
||||
</div>
|
||||
</div>)}
|
||||
</>
|
||||
}
|
||||
|
||||
function renderFileChanges(f: File) {
|
||||
const k = `${f.from}=${f.to}`;
|
||||
return <div className="file" key={k}>
|
||||
<div className="header">
|
||||
<div>
|
||||
{f.from}{f.to && f.to !== f.from && `...${f.to}`}
|
||||
</div>
|
||||
<div>
|
||||
<div className="add">
|
||||
+{f.additions ?? 0}
|
||||
</div>
|
||||
<div className="del">
|
||||
-{f.deletions ?? 0}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="body">
|
||||
{f.chunks.map(renderChunk)}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
return <>
|
||||
{patch.diff.map(renderFileChanges)}
|
||||
</>
|
||||
}
|
@ -36,6 +36,10 @@ export function unixNowMs() {
|
||||
return new Date().getTime();
|
||||
}
|
||||
|
||||
export function unixNow() {
|
||||
return Math.floor(unixNowMs() / 1000);
|
||||
}
|
||||
|
||||
export function unwrap<T>(value?: T) {
|
||||
if (!value) {
|
||||
throw new Error("Missing value");
|
||||
|
@ -1942,6 +1942,11 @@
|
||||
schema-utils "^3.0.0"
|
||||
source-map "^0.7.3"
|
||||
|
||||
"@protobufjs/base64@^1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735"
|
||||
integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==
|
||||
|
||||
"@remix-run/router@1.6.0":
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.6.0.tgz#45010e1826f4d81a1b2cfaf874f1aac93998cd28"
|
||||
@ -6990,7 +6995,7 @@ nostr-relaypool@^0.6.27:
|
||||
nostr-tools "^1.10.0"
|
||||
safe-stable-stringify "^2.4.2"
|
||||
|
||||
nostr-tools@^1.10.0:
|
||||
nostr-tools@^1.10.0, nostr-tools@^1.10.1:
|
||||
version "1.10.1"
|
||||
resolved "https://registry.yarnpkg.com/nostr-tools/-/nostr-tools-1.10.1.tgz#b52043b3031f4314478d0a3bfaa8ffb9cc4f98a0"
|
||||
integrity sha512-zgTYJeuZQ3CDASsmBEcB5i6V6l0IaA6cjnll6OVik3FoZcvbCaL7yP8I40hYnOIi3KlJykV7jEF9fn8h1NzMnA==
|
||||
|
Loading…
Reference in New Issue
Block a user