megit-app 0.9.0 → 0.11.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/CHANGELOG.md +24 -1
- package/README.md +8 -0
- package/dist/assets/{ConflictView-C2Luxy2W.js → ConflictView-D9Dqg-ka.js} +1 -1
- package/dist/assets/{DiffView-CTUpzVA_.js → DiffView-Bl_RtWm9.js} +20 -20
- package/dist/assets/TerminalPanel-BMFn7MA6.js +18 -0
- package/dist/assets/index-BXzSxW-2.css +1 -0
- package/dist/assets/index-JAmzg-th.js +251 -0
- package/dist/index.html +2 -2
- package/dist-server/index.js +61 -1
- package/package.json +6 -6
- package/dist/assets/TerminalPanel-i6ekLzMJ.js +0 -18
- package/dist/assets/index-BlIDqqEW.js +0 -251
- package/dist/assets/index-ByiPZ5H3.css +0 -1
package/dist/index.html
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
|
7
7
|
<title>megit</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
9
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-JAmzg-th.js"></script>
|
|
9
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BXzSxW-2.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
12
12
|
<div id="root"></div>
|
package/dist-server/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createApp, serveStatic } from './http.js';
|
|
2
2
|
import { execFile } from 'node:child_process';
|
|
3
|
-
import { existsSync, readdirSync } from 'node:fs';
|
|
3
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
4
4
|
import { readFile, realpath, writeFile } from 'node:fs/promises';
|
|
5
5
|
import { homedir } from 'node:os';
|
|
6
6
|
import { dirname, join, resolve, sep } from 'node:path';
|
|
@@ -941,6 +941,66 @@ app.get('/api/blob', repoGuard, async (req, res) => {
|
|
|
941
941
|
res.status(404).end();
|
|
942
942
|
}
|
|
943
943
|
});
|
|
944
|
+
// The repo's own favicon, used as its icon in the picker. Checked against the
|
|
945
|
+
// framework conventions people actually ship; no hit is a 404 and the client
|
|
946
|
+
// falls back to a letter chip. Not repoGuard'd: a recent repo need not be open,
|
|
947
|
+
// but it must still be one the user has added at some point.
|
|
948
|
+
// Most projects don't ship a file literally called favicon.*: they declare one
|
|
949
|
+
// in their entry html (megit's own is /logo.svg). Reading that link first is
|
|
950
|
+
// what makes the icon right rather than merely present.
|
|
951
|
+
function declaredIcon(repo) {
|
|
952
|
+
for (const html of ['index.html', 'public/index.html', 'app/index.html', 'src/index.html']) {
|
|
953
|
+
const file = join(repo, html);
|
|
954
|
+
if (!existsSync(file))
|
|
955
|
+
continue;
|
|
956
|
+
const href = /<link[^>]+rel=["'][^"']*icon[^"']*["'][^>]*>/i.exec(readFileSync(file, 'utf8'))?.[0]
|
|
957
|
+
?.match(/href=["']([^"']+)["']/i)?.[1];
|
|
958
|
+
// data: and http: hrefs have nothing on disk to serve
|
|
959
|
+
if (!href || /^[a-z]+:/i.test(href))
|
|
960
|
+
continue;
|
|
961
|
+
const rel = href.split(/[?#]/)[0].replace(/^\/+/, '');
|
|
962
|
+
return [rel, join('public', rel), join('static', rel), join(dirname(html), rel)];
|
|
963
|
+
}
|
|
964
|
+
return [];
|
|
965
|
+
}
|
|
966
|
+
const FAVICONS = [
|
|
967
|
+
'public/favicon.svg', 'public/favicon.ico', 'public/favicon.png', 'public/favicon-32x32.png',
|
|
968
|
+
'app/favicon.ico', 'src/favicon.svg', 'static/favicon.svg', 'static/favicon.ico', 'static/favicon.png',
|
|
969
|
+
'assets/favicon.ico', 'favicon.svg', 'favicon.ico', 'favicon.png',
|
|
970
|
+
];
|
|
971
|
+
app.get('/api/favicon', async (req, res) => {
|
|
972
|
+
const repo = String(req.query.repo ?? '');
|
|
973
|
+
const cfg = loadConfig();
|
|
974
|
+
if (!cfg.repos.includes(repo) && !cfg.recent.includes(repo)) {
|
|
975
|
+
res.status(400).json({ error: 'unknown repo' });
|
|
976
|
+
return;
|
|
977
|
+
}
|
|
978
|
+
const hit = [...declaredIcon(repo), ...FAVICONS].map(f => join(repo, f)).find(f => existsSync(f) && MIME[f.split('.').pop().toLowerCase()]);
|
|
979
|
+
if (!hit) {
|
|
980
|
+
res.status(404).end();
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
try {
|
|
984
|
+
// same symlink escape as /api/blob: a clone can carry public/favicon.ico ->
|
|
985
|
+
// /etc/passwd, and serving it would leak the bytes under an image type
|
|
986
|
+
const abs = await realpath(hit);
|
|
987
|
+
const root = await realpath(repo);
|
|
988
|
+
if (!abs.startsWith(root + sep)) {
|
|
989
|
+
res.status(400).json({ error: 'path outside repo' });
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
// repo content is untrusted (an SVG can carry script)
|
|
993
|
+
res.set('Content-Security-Policy', "sandbox; default-src 'none'").set('X-Content-Type-Options', 'nosniff');
|
|
994
|
+
// A project's icon changes about never, and every picker open would otherwise
|
|
995
|
+
// re-walk the filesystem for each recent repo. Short enough that swapping a
|
|
996
|
+
// favicon shows up without a restart.
|
|
997
|
+
res.set('Cache-Control', 'private, max-age=300');
|
|
998
|
+
res.type(MIME[hit.split('.').pop().toLowerCase()]).send(await readFile(abs));
|
|
999
|
+
}
|
|
1000
|
+
catch {
|
|
1001
|
+
res.status(404).end();
|
|
1002
|
+
}
|
|
1003
|
+
});
|
|
944
1004
|
const dist = join(import.meta.dirname, '..', 'dist');
|
|
945
1005
|
if (existsSync(dist))
|
|
946
1006
|
app.fallback(serveStatic(dist));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "megit-app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "Git repository viewer in the browser: commit graph with branch lanes, diffs, stashes and WIP",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Hoang Vuong Vu",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"test": "vitest run",
|
|
39
39
|
"prepublishOnly": "pnpm test && pnpm build && pnpm build:server"
|
|
40
40
|
},
|
|
41
|
-
"packageManager": "pnpm@11.
|
|
41
|
+
"packageManager": "pnpm@11.25.0+sha512.5cde925b4f075f725eb71fbae18a42ffe784524789f19b61c731cb8721ec28aaee160e01a8d5af4fedb2a42cdbf300efe23db356b0d4a17b4d63e11f8ab7c956",
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"ws": "^8.21.3"
|
|
44
44
|
},
|
|
@@ -46,11 +46,11 @@
|
|
|
46
46
|
"node-pty": "^1.1.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@types/node": "^26.4.
|
|
49
|
+
"@types/node": "^26.4.1",
|
|
50
50
|
"@types/react": "^19.2.18",
|
|
51
|
-
"@types/react-dom": "^19.2.
|
|
51
|
+
"@types/react-dom": "^19.2.7",
|
|
52
52
|
"@types/ws": "^8.18.1",
|
|
53
|
-
"@vitejs/plugin-react": "^6.1.
|
|
53
|
+
"@vitejs/plugin-react": "^6.1.1",
|
|
54
54
|
"@xterm/addon-fit": "^0.11.0",
|
|
55
55
|
"@xterm/xterm": "^6.0.0",
|
|
56
56
|
"concurrently": "^10.0.5",
|
|
@@ -60,6 +60,6 @@
|
|
|
60
60
|
"react-dom": "^19.2.8",
|
|
61
61
|
"typescript": "^7.0.2",
|
|
62
62
|
"vite": "^8.2.2",
|
|
63
|
-
"vitest": "^
|
|
63
|
+
"vitest": "^5.0.0"
|
|
64
64
|
}
|
|
65
65
|
}
|