@tequila99/githuman 0.1.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/LICENSE +21 -0
- package/README.md +152 -0
- package/dist/cli/commands/export.js +55 -0
- package/dist/cli/commands/list.js +39 -0
- package/dist/cli/config.js +65 -0
- package/dist/cli/index.js +50 -0
- package/dist/cli/server-runtime.js +66 -0
- package/dist/cli/startup-message.js +7 -0
- package/dist/server/app.js +62 -0
- package/dist/server/db/index.js +17 -0
- package/dist/server/db/migrations.js +34 -0
- package/dist/server/event-bus.js +14 -0
- package/dist/server/repositories/comment.repo.js +52 -0
- package/dist/server/repositories/review.repo.js +41 -0
- package/dist/server/routes/comments.js +114 -0
- package/dist/server/routes/diff.js +39 -0
- package/dist/server/routes/events.js +16 -0
- package/dist/server/routes/export.js +27 -0
- package/dist/server/routes/git.js +100 -0
- package/dist/server/routes/health.js +5 -0
- package/dist/server/routes/reviews.js +90 -0
- package/dist/server/services/comment.service.js +57 -0
- package/dist/server/services/diff.service.js +144 -0
- package/dist/server/services/export.service.js +71 -0
- package/dist/server/services/file-watcher.service.js +124 -0
- package/dist/server/services/git.service.js +219 -0
- package/dist/server/services/review.service.js +71 -0
- package/dist/shared/types.js +4 -0
- package/dist/web/assets/ErrorNotFound-C0A8n_dh.js +1 -0
- package/dist/web/assets/IndexPage-Ca7Xb-lc.js +3 -0
- package/dist/web/assets/IndexPage-Dn15ye8s.css +1 -0
- package/dist/web/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuYjalmUiAw-BCOjigVR.woff +0 -0
- package/dist/web/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuZtalmUiAw-Ocp2tNig.woff +0 -0
- package/dist/web/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWuaabVmUiAw-6jEn9xEl.woff +0 -0
- package/dist/web/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWub2bVmUiAw-CCs8I9zJ.woff +0 -0
- package/dist/web/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbFmUiAw-BLaboPi0.woff +0 -0
- package/dist/web/assets/KFOMCnqEu92Fr1ME7kSn66aGLdTylUAMQXC89YmC2DPNWubEbVmUiAw-DHuK6FBl.woff +0 -0
- package/dist/web/assets/MainLayout-CC5JJvA9.css +1 -0
- package/dist/web/assets/MainLayout-DHuQjMOV.js +1 -0
- package/dist/web/assets/QPage-DLIDrL1Z.js +1 -0
- package/dist/web/assets/ReviewsPage-f8fJEuU9.js +1 -0
- package/dist/web/assets/bash-CLZ0U2zV.js +1 -0
- package/dist/web/assets/client-TNhDHmkt.js +1 -0
- package/dist/web/assets/core-Ruj7F7dx.js +12 -0
- package/dist/web/assets/create-C46qMo0y.js +1 -0
- package/dist/web/assets/css-CdtkT07m.js +1 -0
- package/dist/web/assets/css-LbU1hoFO.js +1 -0
- package/dist/web/assets/engine-oniguruma-Bon2Icja.js +1 -0
- package/dist/web/assets/fira-code-cyrillic-400-normal-UC0NFL4U.woff2 +0 -0
- package/dist/web/assets/fira-code-cyrillic-400-normal-dc1q3VD7.woff +0 -0
- package/dist/web/assets/fira-code-cyrillic-500-normal-Bu7VCL72.woff +0 -0
- package/dist/web/assets/fira-code-cyrillic-500-normal-CX35h3Mg.woff2 +0 -0
- package/dist/web/assets/fira-code-cyrillic-600-normal-BD56RyRT.woff +0 -0
- package/dist/web/assets/fira-code-cyrillic-600-normal-CPRdbepc.woff2 +0 -0
- package/dist/web/assets/fira-code-cyrillic-ext-400-normal-Buh61xzB.woff +0 -0
- package/dist/web/assets/fira-code-cyrillic-ext-400-normal-txZ9Fk_1.woff2 +0 -0
- package/dist/web/assets/fira-code-cyrillic-ext-500-normal-CYRspHgm.woff2 +0 -0
- package/dist/web/assets/fira-code-cyrillic-ext-500-normal-CwtLmSFD.woff +0 -0
- package/dist/web/assets/fira-code-cyrillic-ext-600-normal-B9wE0zmr.woff2 +0 -0
- package/dist/web/assets/fira-code-cyrillic-ext-600-normal-DldcyWhZ.woff +0 -0
- package/dist/web/assets/fira-code-greek-400-normal-B2Gh_Y8s.woff2 +0 -0
- package/dist/web/assets/fira-code-greek-400-normal-DobhaxvF.woff +0 -0
- package/dist/web/assets/fira-code-greek-500-normal-CVO0IRNt.woff2 +0 -0
- package/dist/web/assets/fira-code-greek-500-normal-DEXIWiey.woff +0 -0
- package/dist/web/assets/fira-code-greek-600-normal-D_h3X2X5.woff +0 -0
- package/dist/web/assets/fira-code-greek-600-normal-Dxp52_By.woff2 +0 -0
- package/dist/web/assets/fira-code-greek-ext-400-normal-DR7mBgIM.woff2 +0 -0
- package/dist/web/assets/fira-code-greek-ext-400-normal-DSPUheWg.woff +0 -0
- package/dist/web/assets/fira-code-greek-ext-500-normal-BO3iq16n.woff2 +0 -0
- package/dist/web/assets/fira-code-greek-ext-500-normal-CMiNLXER.woff +0 -0
- package/dist/web/assets/fira-code-greek-ext-600-normal-C-cQDoOk.woff +0 -0
- package/dist/web/assets/fira-code-greek-ext-600-normal-DtBpFRLc.woff2 +0 -0
- package/dist/web/assets/fira-code-latin-400-normal-C-QZfXAs.woff +0 -0
- package/dist/web/assets/fira-code-latin-400-normal-DGosTW8U.woff2 +0 -0
- package/dist/web/assets/fira-code-latin-500-normal-B6gioGu8.woff2 +0 -0
- package/dist/web/assets/fira-code-latin-500-normal-aMLC2AMm.woff +0 -0
- package/dist/web/assets/fira-code-latin-600-normal-BmSSHshu.woff +0 -0
- package/dist/web/assets/fira-code-latin-600-normal-CvYZpPHg.woff2 +0 -0
- package/dist/web/assets/fira-code-latin-ext-400-normal-DG73JjOf.woff +0 -0
- package/dist/web/assets/fira-code-latin-ext-400-normal-KSMg0QLl.woff2 +0 -0
- package/dist/web/assets/fira-code-latin-ext-500-normal-CaANqXjI.woff +0 -0
- package/dist/web/assets/fira-code-latin-ext-500-normal-Crd9e_oI.woff2 +0 -0
- package/dist/web/assets/fira-code-latin-ext-600-normal-0PFQL1eg.woff +0 -0
- package/dist/web/assets/fira-code-latin-ext-600-normal-Cb7jxoAi.woff2 +0 -0
- package/dist/web/assets/fira-code-symbols2-400-normal-_QEZCEwe.woff +0 -0
- package/dist/web/assets/fira-code-symbols2-500-normal-DwsgbIjQ.woff +0 -0
- package/dist/web/assets/fira-code-symbols2-600-normal-Dvq6IQE5.woff +0 -0
- package/dist/web/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNa-Dr0goTwe.woff +0 -0
- package/dist/web/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ-D-x-0Q06.woff2 +0 -0
- package/dist/web/assets/format-SW_H2PBI.js +1 -0
- package/dist/web/assets/github-dark-C-LZuMrd.js +1 -0
- package/dist/web/assets/github-light-EUqPIrTm.js +1 -0
- package/dist/web/assets/go-rLFTqkRN.js +1 -0
- package/dist/web/assets/html-BcACZWSP.js +1 -0
- package/dist/web/assets/html-DDNsd9lV.js +1 -0
- package/dist/web/assets/i18n-RyrLuvXU.js +1 -0
- package/dist/web/assets/index-CRwZKCt6.js +2 -0
- package/dist/web/assets/index-Fo431P2N.css +1 -0
- package/dist/web/assets/init-app-DSQzny_G.js +1 -0
- package/dist/web/assets/java-BXcb2rRD.js +1 -0
- package/dist/web/assets/javascript-BzbQS41l.js +1 -0
- package/dist/web/assets/javascript-Yelw-5ZN.js +1 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-500-normal-DJqRU3vO.woff +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-500-normal-DmUKJPL_.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-600-normal-8K4wrrwR.woff +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-600-normal-EVf6-Yzo.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-greek-500-normal-D7SFKleX.woff +0 -0
- package/dist/web/assets/jetbrains-mono-greek-500-normal-JpySY46c.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-greek-600-normal-H7WoG9Et.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-greek-600-normal-mc2nkWzM.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-500-normal-CJOVTJB7.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-600-normal-BfsvjouI.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-600-normal-C8RAYTDA.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-500-normal-Cut-4mMH.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-500-normal-ckzbgY84.woff +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-600-normal-BfB_LPfz.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-latin-ext-600-normal-DObL3zCW.woff +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-500-normal-DNRqzVM1.woff +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-600-normal-OWROknRo.woff +0 -0
- package/dist/web/assets/json-B88Oqtu5.js +1 -0
- package/dist/web/assets/json-CaljABEy.js +1 -0
- package/dist/web/assets/jsx-Bz0zcwM4.js +1 -0
- package/dist/web/assets/kotlin-DhhofPvG.js +1 -0
- package/dist/web/assets/markdown-BYOwaDjH.js +1 -0
- package/dist/web/assets/pinia-BDCzxZ18.js +1 -0
- package/dist/web/assets/pinia-BJouyyXU.js +1 -0
- package/dist/web/assets/python-gzcpVVnB.js +1 -0
- package/dist/web/assets/repository-store-DqnwJIJW.js +1 -0
- package/dist/web/assets/runtime-core.esm-bundler-oS9U9Vp7.js +1 -0
- package/dist/web/assets/rust-Cfkwpbl8.js +1 -0
- package/dist/web/assets/scss-DHtrkMkT.js +1 -0
- package/dist/web/assets/syne-greek-400-normal-B4XGRaFa.woff2 +0 -0
- package/dist/web/assets/syne-greek-400-normal-C1nzy5ms.woff +0 -0
- package/dist/web/assets/syne-greek-500-normal-DUQv5E33.woff2 +0 -0
- package/dist/web/assets/syne-greek-500-normal-L1Ji5iNA.woff +0 -0
- package/dist/web/assets/syne-greek-600-normal-BJs4kvyH.woff +0 -0
- package/dist/web/assets/syne-greek-600-normal-BpFII5Ja.woff2 +0 -0
- package/dist/web/assets/syne-greek-700-normal-C01yjWE_.woff +0 -0
- package/dist/web/assets/syne-greek-700-normal-RbvlO3f8.woff2 +0 -0
- package/dist/web/assets/syne-greek-800-normal-DtzFnRR0.woff +0 -0
- package/dist/web/assets/syne-greek-800-normal-n2LZS_yH.woff2 +0 -0
- package/dist/web/assets/syne-latin-400-normal-CULFKXub.woff +0 -0
- package/dist/web/assets/syne-latin-400-normal-CcHADMBd.woff2 +0 -0
- package/dist/web/assets/syne-latin-500-normal-DR6mUQpY.woff +0 -0
- package/dist/web/assets/syne-latin-500-normal-emxikWQ7.woff2 +0 -0
- package/dist/web/assets/syne-latin-600-normal-BiwQbQXw.woff2 +0 -0
- package/dist/web/assets/syne-latin-600-normal-dRu9QuIh.woff +0 -0
- package/dist/web/assets/syne-latin-700-normal-AF3Rs61n.woff2 +0 -0
- package/dist/web/assets/syne-latin-700-normal-Cd-_iCn5.woff +0 -0
- package/dist/web/assets/syne-latin-800-normal-BuJcbPcX.woff +0 -0
- package/dist/web/assets/syne-latin-800-normal-EehdNYzx.woff2 +0 -0
- package/dist/web/assets/syne-latin-ext-400-normal-B5s6XpS5.woff2 +0 -0
- package/dist/web/assets/syne-latin-ext-400-normal-DCnt6qEA.woff +0 -0
- package/dist/web/assets/syne-latin-ext-500-normal-C5b4wHnS.woff +0 -0
- package/dist/web/assets/syne-latin-ext-500-normal-DHUXVE3f.woff2 +0 -0
- package/dist/web/assets/syne-latin-ext-600-normal-DEzBMW8B.woff +0 -0
- package/dist/web/assets/syne-latin-ext-600-normal-ZnizrDKU.woff2 +0 -0
- package/dist/web/assets/syne-latin-ext-700-normal-C-qCH-vM.woff +0 -0
- package/dist/web/assets/syne-latin-ext-700-normal-DuyXglMe.woff2 +0 -0
- package/dist/web/assets/syne-latin-ext-800-normal-B2iBRfEf.woff +0 -0
- package/dist/web/assets/syne-latin-ext-800-normal-CAQAsWuS.woff2 +0 -0
- package/dist/web/assets/theme-DUV6mF_4.js +1 -0
- package/dist/web/assets/tsx-udAQXfEw.js +1 -0
- package/dist/web/assets/typescript-Utq2Cl8c.js +1 -0
- package/dist/web/assets/typescript-j_1H8WHN.js +1 -0
- package/dist/web/assets/use-app-theme-DrR0AvTg.js +1 -0
- package/dist/web/assets/vue-CSBlZ1UP.js +1 -0
- package/dist/web/assets/vue-i18n.runtime-nPdXRlNg.js +3 -0
- package/dist/web/assets/wasm-BnjxR4X6.js +1 -0
- package/dist/web/assets/yaml-rwi0_p6S.js +1 -0
- package/dist/web/favicon.ico +0 -0
- package/dist/web/icons/favicon-128x128.png +0 -0
- package/dist/web/icons/favicon-16x16.png +0 -0
- package/dist/web/icons/favicon-32x32.png +0 -0
- package/dist/web/icons/favicon-96x96.png +0 -0
- package/dist/web/index.html +5 -0
- package/package.json +89 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { findReviewById } from "../repositories/review.repo.js";
|
|
2
|
+
import { listCommentsByReview } from "../repositories/comment.repo.js";
|
|
3
|
+
export class ExportNotFoundError extends Error {
|
|
4
|
+
}
|
|
5
|
+
const STATUS_LABELS = {
|
|
6
|
+
in_progress: 'в процессе',
|
|
7
|
+
approved: 'одобрено',
|
|
8
|
+
changes_requested: 'нужны правки'
|
|
9
|
+
};
|
|
10
|
+
function getReviewOrThrow(db, reviewId) {
|
|
11
|
+
const review = findReviewById(db, reviewId);
|
|
12
|
+
if (!review) {
|
|
13
|
+
throw new ExportNotFoundError(`Review ${reviewId} not found`);
|
|
14
|
+
}
|
|
15
|
+
return review;
|
|
16
|
+
}
|
|
17
|
+
export function exportAsJson(db, reviewId) {
|
|
18
|
+
const review = getReviewOrThrow(db, reviewId);
|
|
19
|
+
const comments = listCommentsByReview(db, reviewId);
|
|
20
|
+
return { review, comments };
|
|
21
|
+
}
|
|
22
|
+
function diffLinePrefix(line) {
|
|
23
|
+
if (line.type === 'added')
|
|
24
|
+
return '+';
|
|
25
|
+
if (line.type === 'removed')
|
|
26
|
+
return '-';
|
|
27
|
+
return ' ';
|
|
28
|
+
}
|
|
29
|
+
export function formatReviewAsMarkdown(review, files, comments) {
|
|
30
|
+
const lines = [];
|
|
31
|
+
lines.push(`# Ревью: ${review.repositoryPath}`);
|
|
32
|
+
lines.push('');
|
|
33
|
+
lines.push(`- Источник: ${review.sourceType}${review.sourceRef ? ` (${review.sourceRef})` : ''}`);
|
|
34
|
+
lines.push(`- Статус: ${STATUS_LABELS[review.status]}`);
|
|
35
|
+
lines.push(`- Создано: ${review.createdAt}`);
|
|
36
|
+
lines.push('');
|
|
37
|
+
const commentsByFile = new Map();
|
|
38
|
+
for (const comment of comments) {
|
|
39
|
+
const existing = commentsByFile.get(comment.filePath) ?? [];
|
|
40
|
+
commentsByFile.set(comment.filePath, [...existing, comment]);
|
|
41
|
+
}
|
|
42
|
+
for (const file of files) {
|
|
43
|
+
lines.push(`## ${file.newPath} (+${file.additions}/-${file.deletions})`);
|
|
44
|
+
lines.push('');
|
|
45
|
+
lines.push('```diff');
|
|
46
|
+
for (const hunk of file.hunks) {
|
|
47
|
+
for (const line of hunk.lines) {
|
|
48
|
+
lines.push(`${diffLinePrefix(line)}${line.content}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
lines.push('```');
|
|
52
|
+
lines.push('');
|
|
53
|
+
const fileComments = commentsByFile.get(file.newPath) ?? [];
|
|
54
|
+
if (fileComments.length > 0) {
|
|
55
|
+
lines.push('**Комментарии:**');
|
|
56
|
+
lines.push('');
|
|
57
|
+
for (const comment of fileComments) {
|
|
58
|
+
const location = comment.lineNumber !== null ? `Строка ${comment.lineNumber}` : 'Файл';
|
|
59
|
+
lines.push(`- ${location}: ${comment.content}`);
|
|
60
|
+
}
|
|
61
|
+
lines.push('');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return lines.join('\n');
|
|
65
|
+
}
|
|
66
|
+
export function exportAsMarkdown(db, reviewId) {
|
|
67
|
+
const review = getReviewOrThrow(db, reviewId);
|
|
68
|
+
const comments = listCommentsByReview(db, reviewId);
|
|
69
|
+
const files = JSON.parse(review.snapshotData);
|
|
70
|
+
return formatReviewAsMarkdown(review, files, comments);
|
|
71
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { watch, readdirSync, statSync } from 'node:fs';
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import { join, basename, relative, sep } from 'node:path';
|
|
4
|
+
const IGNORED_DIR_NAMES = new Set([
|
|
5
|
+
'node_modules',
|
|
6
|
+
'.git',
|
|
7
|
+
'dist',
|
|
8
|
+
'build',
|
|
9
|
+
'.venv',
|
|
10
|
+
'.githuman',
|
|
11
|
+
'.cache'
|
|
12
|
+
]);
|
|
13
|
+
const DEBOUNCE_MS = 300;
|
|
14
|
+
/**
|
|
15
|
+
* Directories git considers fully ignored, one level at a time — i.e. a
|
|
16
|
+
* directory appears here only if *everything* inside it is ignored (a
|
|
17
|
+
* partially-ignored directory still needs walking, for its non-ignored
|
|
18
|
+
* files), matching the granularity `IGNORED_DIR_NAMES` already works at.
|
|
19
|
+
* `git ls-files --directory` collapses such a directory into one entry
|
|
20
|
+
* instead of recursing into it, so this is cheap even for a huge ignored
|
|
21
|
+
* tree like `node_modules`.
|
|
22
|
+
*/
|
|
23
|
+
function listGitignoredDirs(repoPath) {
|
|
24
|
+
try {
|
|
25
|
+
const stdout = execFileSync('git', [
|
|
26
|
+
'ls-files',
|
|
27
|
+
'-z',
|
|
28
|
+
'--others',
|
|
29
|
+
'--ignored',
|
|
30
|
+
'--exclude-standard',
|
|
31
|
+
'--directory'
|
|
32
|
+
], { cwd: repoPath, encoding: 'utf-8' });
|
|
33
|
+
return new Set(stdout
|
|
34
|
+
.split('\0')
|
|
35
|
+
.filter(entry => entry.endsWith('/'))
|
|
36
|
+
.map(entry => entry.slice(0, -1)));
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return new Set(); // Not a git repo, or git isn't available — IGNORED_DIR_NAMES still applies.
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Watches a repository's working tree for changes, so the app can tell
|
|
44
|
+
* clients to refresh their diff/file-tree view.
|
|
45
|
+
*
|
|
46
|
+
* Node's `fs.watch(dir, { recursive: true })` registers an OS-level watch on
|
|
47
|
+
* every descendant directory unconditionally, which is prohibitively
|
|
48
|
+
* expensive (and can exceed inotify watch limits) for a tree that includes
|
|
49
|
+
* `node_modules`. This walks the tree itself instead, skipping known-heavy
|
|
50
|
+
* or irrelevant directories, and watches `.git` non-recursively (so changes
|
|
51
|
+
* to `.git/index`/`.git/HEAD` — staging, branch switches — are still picked
|
|
52
|
+
* up without descending into `.git/objects`).
|
|
53
|
+
*/
|
|
54
|
+
export function watchRepository(repoPath, onChange, options = {}) {
|
|
55
|
+
const gitignoredDirs = (options.respectGitignore ?? true)
|
|
56
|
+
? listGitignoredDirs(repoPath)
|
|
57
|
+
: new Set();
|
|
58
|
+
const watchers = [];
|
|
59
|
+
let debounceTimer;
|
|
60
|
+
function isIgnoredDir(path) {
|
|
61
|
+
if (IGNORED_DIR_NAMES.has(basename(path))) {
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
return gitignoredDirs.has(relative(repoPath, path).split(sep).join('/'));
|
|
65
|
+
}
|
|
66
|
+
function scheduleChange() {
|
|
67
|
+
clearTimeout(debounceTimer);
|
|
68
|
+
debounceTimer = setTimeout(onChange, DEBOUNCE_MS);
|
|
69
|
+
}
|
|
70
|
+
function watchNewEntry(path) {
|
|
71
|
+
let stat;
|
|
72
|
+
try {
|
|
73
|
+
stat = statSync(path);
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return; // Already gone (e.g. a rapid create+delete) — nothing to watch.
|
|
77
|
+
}
|
|
78
|
+
if (stat.isDirectory() && !isIgnoredDir(path)) {
|
|
79
|
+
watchDir(path);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function watchDir(dir) {
|
|
83
|
+
let watcher;
|
|
84
|
+
try {
|
|
85
|
+
watcher = watch(dir, { persistent: false }, (eventType, filename) => {
|
|
86
|
+
scheduleChange();
|
|
87
|
+
if (eventType === 'rename' && filename) {
|
|
88
|
+
watchNewEntry(join(dir, filename));
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return; // Directory may have been removed between readdir and watch.
|
|
94
|
+
}
|
|
95
|
+
watchers.push(watcher);
|
|
96
|
+
let entries;
|
|
97
|
+
try {
|
|
98
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
for (const entry of entries) {
|
|
104
|
+
const entryPath = join(dir, entry.name);
|
|
105
|
+
if (entry.isDirectory() && !isIgnoredDir(entryPath)) {
|
|
106
|
+
watchDir(entryPath);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
watchDir(repoPath);
|
|
111
|
+
try {
|
|
112
|
+
watchers.push(watch(join(repoPath, '.git'), { persistent: false }, () => scheduleChange()));
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// Not a git repo, or .git is missing — nothing more to watch.
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
close: () => {
|
|
119
|
+
clearTimeout(debounceTimer);
|
|
120
|
+
for (const watcher of watchers)
|
|
121
|
+
watcher.close();
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
import { readFile } from 'node:fs/promises';
|
|
4
|
+
import { basename, resolve, sep } from 'node:path';
|
|
5
|
+
import { simpleGit } from 'simple-git';
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
/**
|
|
8
|
+
* Resolves `path` against `repoPath` and rejects anything that escapes the
|
|
9
|
+
* repository root (e.g. `../../etc/passwd`), since `path` here is untrusted
|
|
10
|
+
* input coming straight off the HTTP request.
|
|
11
|
+
*/
|
|
12
|
+
function resolveWithinRepo(repoPath, path) {
|
|
13
|
+
const repoRoot = resolve(repoPath);
|
|
14
|
+
const resolved = resolve(repoRoot, path);
|
|
15
|
+
if (resolved !== repoRoot && !resolved.startsWith(repoRoot + sep)) {
|
|
16
|
+
throw new Error(`Path "${path}" escapes the repository root`);
|
|
17
|
+
}
|
|
18
|
+
return resolved;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Rejects a git ref/revision string that could be misparsed as a command
|
|
22
|
+
* option by git itself (e.g. `--output=/etc/passwd` smuggled in as a
|
|
23
|
+
* "branch name"). No real branch, tag, or SHA can start with `-` — git
|
|
24
|
+
* itself refuses to create one (`git branch -- -foo` errors with "not a
|
|
25
|
+
* valid branch name") — so any ref starting with `-` is definitely hostile
|
|
26
|
+
* input, not a legitimate ref a caller could have meant.
|
|
27
|
+
*/
|
|
28
|
+
export function assertSafeRef(ref) {
|
|
29
|
+
if (ref.startsWith('-')) {
|
|
30
|
+
throw new Error(`Invalid git ref "${ref}": refs may not start with "-"`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Reads a file's content at a given git ref.
|
|
35
|
+
*
|
|
36
|
+
* `ref` may be any git ref/SHA, or the literal string 'INDEX' to read the
|
|
37
|
+
* staged (index) version of the file instead of a committed ref.
|
|
38
|
+
*
|
|
39
|
+
* If the path does not exist at that ref (e.g. a newly added file has no
|
|
40
|
+
* HEAD version), returns empty content rather than throwing — the caller
|
|
41
|
+
* treats this as "the file did not exist on this side of the diff".
|
|
42
|
+
*/
|
|
43
|
+
export async function getFileAtRef(repoPath, ref, path) {
|
|
44
|
+
if (ref === 'WORKTREE') {
|
|
45
|
+
try {
|
|
46
|
+
const absolutePath = resolveWithinRepo(repoPath, path);
|
|
47
|
+
const buffer = await readFile(absolutePath);
|
|
48
|
+
return {
|
|
49
|
+
content: buffer.includes(0) ? '' : buffer.toString('utf-8'),
|
|
50
|
+
isBinary: buffer.includes(0)
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return { content: '', isBinary: false };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (ref !== 'INDEX') {
|
|
58
|
+
assertSafeRef(ref);
|
|
59
|
+
}
|
|
60
|
+
const gitRef = ref === 'INDEX' ? `:${path}` : `${ref}:${path}`;
|
|
61
|
+
try {
|
|
62
|
+
// No `--` before gitRef: for the INDEX form (`:path`) it would make git
|
|
63
|
+
// treat the pathspec-looking `:path` as a plain path instead of the
|
|
64
|
+
// special "file staged in the index" syntax, silently changing meaning
|
|
65
|
+
// (falls back to showing HEAD). assertSafeRef above already rules out
|
|
66
|
+
// the injection this would otherwise guard against.
|
|
67
|
+
const { stdout } = await execFileAsync('git', ['show', gitRef], {
|
|
68
|
+
cwd: repoPath,
|
|
69
|
+
encoding: 'buffer',
|
|
70
|
+
maxBuffer: 1024 * 1024 * 100
|
|
71
|
+
});
|
|
72
|
+
const isBinary = stdout.includes(0);
|
|
73
|
+
return {
|
|
74
|
+
content: isBinary ? '' : stdout.toString('utf-8'),
|
|
75
|
+
isBinary
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return { content: '', isBinary: false };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Lists every file path visible at a git ref, for browsing the whole
|
|
84
|
+
* codebase rather than just a diff. `'WORKTREE'` lists the working directory
|
|
85
|
+
* (tracked + untracked, honoring .gitignore, matching `getFileAtRef`'s
|
|
86
|
+
* 'WORKTREE' semantics), `'INDEX'` lists the staged snapshot, and any other
|
|
87
|
+
* ref lists that commit's tree.
|
|
88
|
+
*/
|
|
89
|
+
export async function getFilesAtRef(repoPath, ref) {
|
|
90
|
+
if (ref === 'WORKTREE') {
|
|
91
|
+
const { stdout } = await execFileAsync('git', ['ls-files', '-z', '--cached', '--others', '--exclude-standard'], {
|
|
92
|
+
cwd: repoPath,
|
|
93
|
+
encoding: 'utf-8'
|
|
94
|
+
});
|
|
95
|
+
return [...new Set(stdout.split('\0').filter(Boolean))].sort();
|
|
96
|
+
}
|
|
97
|
+
if (ref === 'INDEX') {
|
|
98
|
+
const { stdout } = await execFileAsync('git', ['ls-files', '-z', '--cached'], { cwd: repoPath, encoding: 'utf-8' });
|
|
99
|
+
return stdout.split('\0').filter(Boolean).sort();
|
|
100
|
+
}
|
|
101
|
+
assertSafeRef(ref);
|
|
102
|
+
const { stdout } = await execFileAsync('git', ['ls-tree', '-r', '--name-only', '-z', '--', ref], {
|
|
103
|
+
cwd: repoPath,
|
|
104
|
+
encoding: 'utf-8'
|
|
105
|
+
});
|
|
106
|
+
return stdout.split('\0').filter(Boolean).sort();
|
|
107
|
+
}
|
|
108
|
+
function statusCodeToStatus(code) {
|
|
109
|
+
switch (code[0]) {
|
|
110
|
+
case 'A':
|
|
111
|
+
return 'added';
|
|
112
|
+
case 'D':
|
|
113
|
+
return 'deleted';
|
|
114
|
+
case 'R':
|
|
115
|
+
return 'renamed';
|
|
116
|
+
default:
|
|
117
|
+
return 'modified';
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Lists changed paths for a diff, given the git ref arguments that would
|
|
122
|
+
* normally follow `git diff --name-status -M` (e.g. `['--cached']` for
|
|
123
|
+
* staged changes, or `[base, 'HEAD']` for a branch comparison).
|
|
124
|
+
*/
|
|
125
|
+
export async function listChangedPaths(repoPath, diffArgs) {
|
|
126
|
+
const { stdout } = await execFileAsync('git', ['diff', '--name-status', '-M', '-z', ...diffArgs], {
|
|
127
|
+
cwd: repoPath,
|
|
128
|
+
encoding: 'utf-8'
|
|
129
|
+
});
|
|
130
|
+
// With -z, git replaces both the record terminator and the inter-field
|
|
131
|
+
// tab with NUL, so the whole output is a flat stream of NUL-separated
|
|
132
|
+
// tokens: <status> <path>, or <status> <oldPath> <newPath> for renames.
|
|
133
|
+
const tokens = stdout.split('\0').filter(token => token !== '');
|
|
134
|
+
const results = [];
|
|
135
|
+
let i = 0;
|
|
136
|
+
while (i < tokens.length) {
|
|
137
|
+
const code = tokens[i++];
|
|
138
|
+
const status = statusCodeToStatus(code);
|
|
139
|
+
if (code[0] === 'R' || code[0] === 'C') {
|
|
140
|
+
const oldPath = tokens[i++];
|
|
141
|
+
const newPath = tokens[i++];
|
|
142
|
+
results.push({ oldPath, newPath, status });
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
const path = tokens[i++];
|
|
146
|
+
results.push({ oldPath: path, newPath: path, status });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return results;
|
|
150
|
+
}
|
|
151
|
+
/** Lists paths that git has never tracked (respects .gitignore). */
|
|
152
|
+
export async function listUntrackedPaths(repoPath) {
|
|
153
|
+
const { stdout } = await execFileAsync('git', ['ls-files', '-z', '--others', '--exclude-standard'], {
|
|
154
|
+
cwd: repoPath,
|
|
155
|
+
encoding: 'utf-8'
|
|
156
|
+
});
|
|
157
|
+
return stdout.split('\0').filter(path => path !== '');
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Stages the given paths (`git add -- <paths>`). With an empty array,
|
|
161
|
+
* stages everything — tracked edits and untracked new files alike
|
|
162
|
+
* (`git add -A`), matching the "Stage all" action in the UI.
|
|
163
|
+
*/
|
|
164
|
+
export async function stagePaths(repoPath, paths) {
|
|
165
|
+
const args = paths.length > 0 ? ['add', '--', ...paths] : ['add', '-A'];
|
|
166
|
+
await execFileAsync('git', args, { cwd: repoPath });
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Unstages the given paths (`git restore --staged -- <paths>`), leaving
|
|
170
|
+
* their working-tree content untouched. With an empty array, unstages
|
|
171
|
+
* everything, matching `stagePaths`'s "empty = everything" convention.
|
|
172
|
+
*/
|
|
173
|
+
export async function unstagePaths(repoPath, paths) {
|
|
174
|
+
const args = paths.length > 0
|
|
175
|
+
? ['restore', '--staged', '--', ...paths]
|
|
176
|
+
: ['restore', '--staged', '.'];
|
|
177
|
+
await execFileAsync('git', args, { cwd: repoPath });
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Discards unstaged working-tree changes for the given paths: an untracked
|
|
181
|
+
* file is deleted outright (`git clean`), a tracked file is restored to its
|
|
182
|
+
* last-staged content (`git restore`). Paths are classified before either
|
|
183
|
+
* git command runs, rather than running `restore` and swallowing a "not
|
|
184
|
+
* found" error for the ones `clean` already removed — `git restore`
|
|
185
|
+
* validates every pathspec up front, so if the batch mixed tracked and
|
|
186
|
+
* untracked paths, an already-removed untracked path would abort the whole
|
|
187
|
+
* call and silently no-op the legitimate tracked restores too.
|
|
188
|
+
*/
|
|
189
|
+
export async function discardPaths(repoPath, paths) {
|
|
190
|
+
if (paths.length === 0)
|
|
191
|
+
return;
|
|
192
|
+
const untrackedPaths = new Set(await listUntrackedPaths(repoPath));
|
|
193
|
+
const toClean = paths.filter(path => untrackedPaths.has(path));
|
|
194
|
+
const toRestore = paths.filter(path => !untrackedPaths.has(path));
|
|
195
|
+
if (toClean.length > 0) {
|
|
196
|
+
await execFileAsync('git', ['clean', '-f', '-d', '--', ...toClean], {
|
|
197
|
+
cwd: repoPath
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
if (toRestore.length > 0) {
|
|
201
|
+
await execFileAsync('git', ['restore', '--', ...toRestore], {
|
|
202
|
+
cwd: repoPath
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
export async function getRepositoryInfo(repoPath) {
|
|
207
|
+
const git = simpleGit(repoPath);
|
|
208
|
+
const [branchSummary, remotes] = await Promise.all([
|
|
209
|
+
git.branch(),
|
|
210
|
+
git.getRemotes(true)
|
|
211
|
+
]);
|
|
212
|
+
const origin = remotes.find(remote => remote.name === 'origin');
|
|
213
|
+
return {
|
|
214
|
+
name: basename(repoPath),
|
|
215
|
+
branch: branchSummary.current,
|
|
216
|
+
remote: origin?.refs.fetch ?? null,
|
|
217
|
+
path: repoPath
|
|
218
|
+
};
|
|
219
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { createReview as insertReview, findReviewById, listReviews, updateReviewStatus as updateReviewStatusRepo, deleteReview as deleteReviewRepo } from "../repositories/review.repo.js";
|
|
2
|
+
import { listCommentsByReview, deleteComment } from "../repositories/comment.repo.js";
|
|
3
|
+
import { getStagedDiff, getUnstagedDiff, getBranchDiff, getCommitsDiff } from "./diff.service.js";
|
|
4
|
+
export class ValidationError extends Error {
|
|
5
|
+
}
|
|
6
|
+
export async function createReview(db, repositoryPath, input, eventBus) {
|
|
7
|
+
const sourceType = input.sourceType ?? 'staged';
|
|
8
|
+
let snapshot;
|
|
9
|
+
let baseRef = null;
|
|
10
|
+
let sourceRef = null;
|
|
11
|
+
if (sourceType === 'staged') {
|
|
12
|
+
snapshot = await getStagedDiff(repositoryPath);
|
|
13
|
+
}
|
|
14
|
+
else if (sourceType === 'unstaged') {
|
|
15
|
+
snapshot = await getUnstagedDiff(repositoryPath);
|
|
16
|
+
}
|
|
17
|
+
else if (sourceType === 'branch') {
|
|
18
|
+
if (!input.baseRef) {
|
|
19
|
+
throw new ValidationError('"baseRef" is required when sourceType is "branch"');
|
|
20
|
+
}
|
|
21
|
+
baseRef = input.baseRef;
|
|
22
|
+
sourceRef = input.sourceRef ?? null;
|
|
23
|
+
snapshot = await getBranchDiff(repositoryPath, input.baseRef);
|
|
24
|
+
}
|
|
25
|
+
else if (sourceType === 'commits') {
|
|
26
|
+
if (!input.baseRef || !input.sourceRef) {
|
|
27
|
+
throw new ValidationError('"baseRef" (from) and "sourceRef" (to) are both required when sourceType is "commits"');
|
|
28
|
+
}
|
|
29
|
+
baseRef = input.baseRef;
|
|
30
|
+
sourceRef = input.sourceRef;
|
|
31
|
+
snapshot = await getCommitsDiff(repositoryPath, input.baseRef, input.sourceRef);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
throw new ValidationError(`Unknown sourceType: "${String(sourceType)}"`);
|
|
35
|
+
}
|
|
36
|
+
const now = new Date().toISOString();
|
|
37
|
+
const review = {
|
|
38
|
+
id: crypto.randomUUID(),
|
|
39
|
+
repositoryPath,
|
|
40
|
+
baseRef,
|
|
41
|
+
sourceType,
|
|
42
|
+
sourceRef,
|
|
43
|
+
snapshotData: JSON.stringify(snapshot),
|
|
44
|
+
status: 'in_progress',
|
|
45
|
+
createdAt: now,
|
|
46
|
+
updatedAt: now
|
|
47
|
+
};
|
|
48
|
+
const created = insertReview(db, review);
|
|
49
|
+
eventBus?.publish({ type: 'review:created', reviewId: created.id });
|
|
50
|
+
return created;
|
|
51
|
+
}
|
|
52
|
+
export function getReview(db, id) {
|
|
53
|
+
return findReviewById(db, id);
|
|
54
|
+
}
|
|
55
|
+
export function getReviews(db) {
|
|
56
|
+
return listReviews(db);
|
|
57
|
+
}
|
|
58
|
+
export function setReviewStatus(db, id, status, eventBus) {
|
|
59
|
+
const updated = updateReviewStatusRepo(db, id, status);
|
|
60
|
+
if (updated) {
|
|
61
|
+
eventBus?.publish({ type: 'review:updated', reviewId: updated.id });
|
|
62
|
+
}
|
|
63
|
+
return updated;
|
|
64
|
+
}
|
|
65
|
+
export function removeReview(db, id, eventBus) {
|
|
66
|
+
for (const comment of listCommentsByReview(db, id)) {
|
|
67
|
+
deleteComment(db, comment.id);
|
|
68
|
+
}
|
|
69
|
+
deleteReviewRepo(db, id);
|
|
70
|
+
eventBus?.publish({ type: 'review:deleted', reviewId: id });
|
|
71
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{c as e,d as t,j as n,m as r}from"./runtime-core.esm-bundler-oS9U9Vp7.js";import{c as i,q as a}from"./index-CRwZKCt6.js";var o={},s={class:`fullscreen bg-blue text-white text-center q-pa-md flex flex-center`};function c(a,o){return n(),t(`div`,s,[e(`div`,null,[o[0]||=e(`div`,{style:{"font-size":`30vh`}},`404`,-1),o[1]||=e(`div`,{class:`text-h2`,style:{opacity:`0.4`}},`Oops. Nothing here...`,-1),r(i,{class:`q-mt-xl`,color:`white`,"text-color":`blue`,unelevated:``,to:`/`,label:`Go Home`,"no-caps":``})])])}var l=a(o,[[`render`,c]]);export{l as default};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/typescript-Utq2Cl8c.js","assets/typescript-j_1H8WHN.js","assets/javascript-Yelw-5ZN.js","assets/javascript-BzbQS41l.js","assets/vue-CSBlZ1UP.js","assets/css-LbU1hoFO.js","assets/html-BcACZWSP.js","assets/json-B88Oqtu5.js","assets/json-CaljABEy.js","assets/css-CdtkT07m.js","assets/scss-DHtrkMkT.js","assets/html-DDNsd9lV.js","assets/core-Ruj7F7dx.js","assets/engine-oniguruma-Bon2Icja.js"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{B as e,C as t,D as n,F as r,H as i,K as a,M as o,N as s,O as c,P as l,Q as u,R as d,S as f,T as p,V as m,X as h,_t as g,b as _,c as v,d as y,g as b,gt as x,h as S,j as C,k as w,l as T,m as E,nt as D,p as O,r as k,s as A,u as j,v as M,vt as N,w as ee,x as te}from"./runtime-core.esm-bundler-oS9U9Vp7.js";import{t as P}from"./create-C46qMo0y.js";import{$ as ne,G as F,J as re,M as ie,U as ae,V as oe,Y as se,_ as I,a as L,b as R,c as z,d as B,et as V,f as H,g as ce,i as le,j as ue,n as U,o as de,p as W,pt as fe,q as G,r as pe,rt as me,s as he,t as K,tt as ge,vt as _e,x as ve,z as ye}from"./index-CRwZKCt6.js";import{n as q}from"./vue-i18n.runtime-nPdXRlNg.js";import{n as be,r as J}from"./pinia-BDCzxZ18.js";import{n as xe,t as Y}from"./client-TNhDHmkt.js";import{a as X,c as Se,i as Z,l as Ce,n as we,o as Te,r as Ee,s as De,t as Oe}from"./format-SW_H2PBI.js";import{t as ke}from"./QPage-DLIDrL1Z.js";function Ae(e){if(e.length<2||e[0]!==`"`||e[e.length-1]!==`"`)return e;let t=e.slice(1,-1),n=[];for(let e=0;e<t.length;e++){let r=t[e];r===`\\`&&/^[0-7]{3}$/.test(t.slice(e+1,e+4))?(n.push(parseInt(t.slice(e+1,e+4),8)),e+=3):r===`\\`&&t[e+1]===`"`?(n.push(34),e+=1):r===`\\`&&t[e+1]===`\\`?(n.push(92),e+=1):n.push((r??``).charCodeAt(0))}return new TextDecoder(`utf-8`).decode(new Uint8Array(n))}function je(e){return{...e,oldPath:Ae(e.oldPath),newPath:Ae(e.newPath)}}function Me(e,t){let n=new Map(e.map(e=>[e.newPath||e.oldPath,e]));return t.map(e=>{let t=n.get(e.newPath||e.oldPath);return t&&JSON.stringify(t)===JSON.stringify(e)?t:e})}var Ne=be(`diff`,()=>{let e=h([]),t=h([]),n=h(!1),r=h(null),i=A(()=>[...e.value,...t.value].map(e=>e.newPath||e.oldPath));async function a(){n.value=!0,r.value=null;try{let[n,r]=await Promise.all([Y(`/api/diff/staged`),Y(`/api/diff/unstaged`)]);e.value=Me(e.value,n.map(je)),t.value=Me(t.value,r.map(je))}catch(n){e.value=[],t.value=[],r.value=n instanceof Error?n.message:String(n)}finally{n.value=!1}}return{stagedFiles:e,unstagedFiles:t,loading:n,error:r,changedPaths:i,fetchDiff:a}});function Pe(e){return e.toSorted((e,t)=>e.type===t.type?e.name.localeCompare(t.name):e.type===`directory`?-1:1).map(e=>({...e,children:e.children?Pe(e.children):void 0}))}function Fe(e,t){let n=[];for(let r of e){let e=r.split(`/`),i=n;for(let n=0;n<e.length;n++){let a=e[n];if(!a)continue;let o=n===e.length-1,s=e.slice(0,n+1).join(`/`),c=i.find(e=>e.name===a);c||(c={name:a,path:s,type:o?`file`:`directory`,isChanged:o?t.has(r):!1,children:o?void 0:[]},i.push(c)),!o&&c.children&&(i=c.children)}}return Pe(n)}function Ie(e,t){if(!t.trim())return e;let n=t.toLowerCase(),r=e=>{if(e.type===`file`)return e.path.toLowerCase().includes(n)?e:null;let t=e.children?.map(r).filter(e=>e!==null);return t&&t.length>0?{...e,children:t}:e.name.toLowerCase().includes(n)?e:null};return e.map(r).filter(e=>e!==null)}function Le(){let e=h([]),t=h(!1),n=h(null);async function r(r,i=[]){t.value=!0,n.value=null;try{let t=await Y(`/api/git/tree/${encodeURIComponent(r)}`);e.value=Fe(t.files.map(Ae),new Set(i))}catch(t){n.value=t instanceof Error?t.message:String(t),e.value=[]}finally{t.value=!1}}return{tree:e,loading:t,error:n,fetchTree:r}}function Re(){let e=h([]),t=h(!1),n=h(!1),r=h(null),i=0;async function a(a,o){let s=++i;n.value=!0,r.value=null;try{let n=a.split(`/`).map(encodeURIComponent).join(`/`),r=await Y(`/api/git/file/${n}?ref=${encodeURIComponent(o)}`);if(s!==i)return;e.value=r.lines,t.value=r.isBinary}catch(n){if(s!==i)return;r.value=n instanceof Error?n.message:String(n),e.value=[],t.value=!1}finally{s===i&&(n.value=!1)}}function o(){e.value=[],t.value=!1,r.value=null}return{lines:e,isBinary:t,loading:n,error:r,fetchContent:a,reset:o}}function Q(e){return e.newPath||e.oldPath}function ze(e){return e.type===`file`?e.isChanged:e.children?.some(ze)??!1}var Be=be(`file-explorer`,()=>{let n=Ne(),{tree:r,loading:i,fetchTree:a}=Le(),{lines:o,isBinary:s,loading:c,fetchContent:l,reset:u}=Re(),d=h(`staged`),f=h(!1),p=h(``),m=h(null),g=h(new Set),_=h(new Set),v=A(()=>d.value===`staged`?n.stagedFiles:n.unstagedFiles),y=A(()=>{let e=(p.value??``).trim().toLowerCase();return e?v.value.filter(t=>Q(t).toLowerCase().includes(e)):v.value}),b=A(()=>Ie(r.value,p.value??``)),x=A(()=>{let e=0,t=n=>{for(let r of n)r.type===`file`?e++:r.children&&t(r.children)};return t(r.value),e});e(r,e=>{let t=new Set,n=e=>{for(let r of e)r.type===`directory`&&r.children&&(ze(r)&&t.add(r.path),n(r.children))};n(e),g.value=t},{immediate:!0}),e(f,e=>{p.value=``,m.value=null,e&&a(`WORKTREE`,n.changedPaths)}),e(d,()=>{m.value=null,_.value=new Set}),e(m,e=>{if(f.value){if(!e){u();return}l(e,`WORKTREE`)}});function S(e){let t=new Set(g.value);t.has(e)?t.delete(e):t.add(e),g.value=t}function C(e){let t=new Set(_.value);t.has(e)?t.delete(e):t.add(e),_.value=t}function w(e){m.value=e,C(e)}function T(e){m.value=e,_.value.has(e)||(_.value=new Set(_.value).add(e))}function E(){_.value=new Set(v.value.map(Q))}function D(){_.value=new Set}function O(e){m.value=e,!f.value&&(_.value.has(e)||(_.value=new Set(_.value).add(e)),t(()=>{document.getElementById(`diff-file-${e}`)?.scrollIntoView({block:`nearest`})}))}let k=null,j=0;function M(){return k??=(async()=>{await n.fetchDiff(),f.value&&(await a(`WORKTREE`,n.changedPaths),m.value&&await l(m.value,`WORKTREE`))})().finally(()=>{k=null})}async function N(){j=Date.now(),await M()}async function ee(){Date.now()-j<400||await M()}return{source:d,browseMode:f,filter:p,selectedPath:m,expandedFolders:g,expandedFiles:_,tree:r,treeLoading:i,browseFileLines:o,browseFileIsBinary:s,browseFileLoading:c,diffFiles:v,filteredDiffFiles:y,filteredTree:b,totalTreeFiles:x,toggleFolder:S,toggleFileCard:C,handleCardToggle:w,expandFile:T,expandAllFiles:E,collapseAllFiles:D,selectFile:O,refresh:N,refreshFromServerEvent:ee}});function Ve(e,t){let n=new EventSource(`/api/events`);for(let r of e)n.addEventListener(r,()=>t());return n.addEventListener(`open`,()=>t()),{close:()=>n.close()}}var He=P({name:`QSplitter`,props:{...he,modelValue:{type:Number,required:!0},reverse:Boolean,unit:{type:String,default:`%`,validator:e=>[`%`,`px`].includes(e)},limits:{type:Array,validator:e=>e.length!==2||typeof e[0]!=`number`||typeof e[1]!=`number`?!1:e[0]>=0&&e[0]<=e[1]},emitImmediately:Boolean,horizontal:Boolean,disable:Boolean,beforeClass:[Array,String,Object],afterClass:[Array,String,Object],separatorClass:[Array,String,Object],separatorStyle:[Array,String,Object],separatorAriaLabel:String},emits:[`update:modelValue`],setup(n,{slots:r,emit:a}){let o=ae(),s=de(n,o),c=u(null),l={before:u(null),after:u(null)},d=pe(),f=A(()=>`q-splitter no-wrap ${n.horizontal?`q-splitter--horizontal column`:`q-splitter--vertical row`} q-splitter--${n.disable?`disabled`:`workable`}`+(s()?` q-splitter--dark`:``)),p=A(()=>n.horizontal?`height`:`width`),m=A(()=>n.reverse?`after`:`before`),h=A(()=>n.limits===void 0?n.unit===`%`?[10,90]:[50,1/0]:n.limits);function g(e){return(n.unit===`%`?e:Math.round(e))+n.unit}let _=A(()=>({[m.value]:{[p.value]:g(n.modelValue)}})),v,y,b,x,S;function C(e){if(e.isFirst){let e=c.value.getBoundingClientRect()[p.value];v=n.horizontal?`up`:`left`,y=n.unit===`%`?100:e,b=Math.min(y,h.value[1],Math.max(h.value[0],n.modelValue)),x=(n.reverse?-1:1)*(n.horizontal?1:o.lang.rtl?-1:1)*(n.unit===`%`?e===0?0:100/e:1),c.value.classList.add(`q-splitter--active`);return}if(e.isFinal){S!==n.modelValue&&a(`update:modelValue`,S),c.value.classList.remove(`q-splitter--active`);return}let t=b+x*(e.direction===v?-1:1)*e.distance[n.horizontal?`y`:`x`];S=Math.min(y,h.value[1],Math.max(h.value[0],t)),l[m.value].value.style[p.value]=g(S),n.emitImmediately&&n.modelValue!==S&&a(`update:modelValue`,S)}let w=A(()=>[[we,n.disable?void 0:C,void 0,{[n.horizontal?`vertical`:`horizontal`]:!0,prevent:!0,stop:!0,mouse:!0,mouseAllDir:!0}]]),T=A(()=>{let e={role:`separator`,"aria-orientation":n.horizontal?`horizontal`:`vertical`,"aria-label":n.separatorAriaLabel||o.lang.label.resize};if(n.disable)e[`aria-disabled`]=`true`;else{let t=h.value;e.tabindex=0,e[`aria-controls`]=d.value,e[`aria-valuemin`]=t[0],t[1]!==1/0&&(e[`aria-valuemax`]=t[1]),e[`aria-valuenow`]=Math.round(n.modelValue*100)/100}return e}),E=null;function D(e){let{keyCode:t}=e,r=n.horizontal?[38,40]:[37,39];if(t!==13&&t!==36&&t!==35&&!r.includes(t))return;fe(e);let i=h.value,s=n.unit===`%`?100:c.value.getBoundingClientRect()[p.value],l;if(t===13)n.modelValue===i[0]?l=E===null?n.modelValue:E:(E=n.modelValue,l=i[0]);else if(t===36)l=i[0];else if(t===35)l=i[1];else{let e=(t===r[0]?-1:1)*(n.reverse?-1:1)*(n.horizontal?1:o.lang.rtl?-1:1);l=n.modelValue+e*(n.unit===`%`?1:10)}let u=Math.min(s,i[1],Math.max(i[0],l));u!==n.modelValue&&a(`update:modelValue`,u)}function O(e,t){e<t[0]?a(`update:modelValue`,t[0]):e>t[1]&&a(`update:modelValue`,t[1])}return e(()=>n.modelValue,e=>{O(e,h.value)}),e(()=>n.limits,()=>{t(()=>{O(n.modelValue,h.value)})}),()=>{let e=[M(`div`,{ref:l.before,id:m.value===`before`?d.value:void 0,class:[`q-splitter__panel q-splitter__before`+(n.reverse?` col`:``),n.beforeClass],style:_.value.before},ve(r.before)),M(`div`,{class:[`q-splitter__separator`,n.separatorClass],style:n.separatorStyle,...T.value,onKeydown:n.disable?void 0:D},[i(M(`div`,{class:`q-splitter__separator-area absolute-full`},ve(r.separator)),w.value)]),M(`div`,{ref:l.after,id:m.value===`after`?d.value:void 0,class:[`q-splitter__panel q-splitter__after`+(n.reverse?``:` col`),n.afterClass],style:_.value.after},ve(r.after))];return M(`div`,{class:f.value,ref:c},R(r.default,e))}}}),Ue=260,We=220,Ge=600,Ke=G(S({__name:`PanelResize`,setup(e){let t=h(Ue),n=A(()=>[We,Ge]);return(e,r)=>(C(),T(He,{modelValue:t.value,"onUpdate:modelValue":r[0]||=e=>t.value=e,limits:n.value,unit:`px`,class:`file-explorer`,"before-class":`file-explorer__panel`,"after-class":`file-explorer__content`},{before:m(()=>[l(e.$slots,`panel`,{},void 0,!0)]),after:m(()=>[l(e.$slots,`content`,{},void 0,!0)]),_:3},8,[`modelValue`,`limits`]))}}),[[`__scopeId`,`data-v-1ce1b104`]]),qe=P({props:[`store`,`barStyle`,`verticalBarStyle`,`horizontalBarStyle`],setup(e){return()=>[M(`div`,{class:e.store.scroll.vertical.barClass.value,style:[e.barStyle,e.verticalBarStyle],"aria-hidden":`true`,onMousedown:e.store.onVerticalMousedown}),M(`div`,{class:e.store.scroll.horizontal.barClass.value,style:[e.barStyle,e.horizontalBarStyle],"aria-hidden":`true`,onMousedown:e.store.onHorizontalMousedown}),i(M(`div`,{ref:e.store.scroll.vertical.ref,class:e.store.scroll.vertical.thumbClass.value,style:e.store.scroll.vertical.style.value,"aria-hidden":`true`}),e.store.thumbVertDir),i(M(`div`,{ref:e.store.scroll.horizontal.ref,class:e.store.scroll.horizontal.thumbClass.value,style:e.store.scroll.horizontal.style.value,"aria-hidden":`true`}),e.store.thumbHorizDir)]}}),Je=[`vertical`,`horizontal`],Ye={vertical:{offset:`offsetY`,scroll:`scrollTop`,dir:`down`,dist:`y`},horizontal:{offset:`offsetX`,scroll:`scrollLeft`,dir:`right`,dist:`x`}},Xe={prevent:!0,mouse:!0,mouseAllDir:!0},Ze=e=>e>=250?50:Math.ceil(e/5),$=(e,t)=>t?-e:e;function Qe(e,t){return Je.includes(e)?!0:(console.error(`[QScrollArea]: wrong first param of ${t} (vertical/horizontal)`),!1)}var $e=P({name:`QScrollArea`,props:{...he,thumbStyle:Object,verticalThumbStyle:Object,horizontalThumbStyle:Object,barStyle:[Array,String,Object],verticalBarStyle:[Array,String,Object],horizontalBarStyle:[Array,String,Object],verticalOffset:{type:Array,default:()=>[0,0]},horizontalOffset:{type:Array,default:()=>[0,0]},contentStyle:[Array,String,Object],contentActiveStyle:[Array,String,Object],delay:{type:[String,Number],default:1e3},visible:{type:Boolean,default:null},tabindex:[String,Number],onScroll:Function},setup(t,{slots:r,emit:i}){let a=h(!1),o=h(!1),s=h(!1),c={vertical:h(0),horizontal:h(0)},l={vertical:{ref:u(null),position:h(0),size:h(0)},horizontal:{ref:u(null),position:h(0),size:h(0)}},{proxy:d}=b(),f=ae(),m=de(t,f),g=null,_,v=u(null),y=A(()=>`q-scrollarea`+(m()?` q-scrollarea--dark`:``));Object.assign(c,{verticalInner:A(()=>c.vertical.value-t.verticalOffset[0]-t.verticalOffset[1]),horizontalInner:A(()=>c.horizontal.value-t.horizontalOffset[0]-t.horizontalOffset[1])}),l.vertical.percentage=A(()=>{let e=l.vertical.size.value-c.vertical.value;if(e<=0)return 0;let t=Oe(l.vertical.position.value/e,0,1);return Math.round(t*1e4)/1e4}),l.vertical.thumbHidden=A(()=>!(t.visible===null?s.value:t.visible)&&!a.value&&!o.value||l.vertical.size.value<=c.vertical.value+1),l.vertical.thumbStart=A(()=>t.verticalOffset[0]+l.vertical.percentage.value*(c.verticalInner.value-l.vertical.thumbSize.value)),l.vertical.thumbSize=A(()=>Math.round(Oe(c.verticalInner.value*c.verticalInner.value/l.vertical.size.value,Ze(c.verticalInner.value),c.verticalInner.value))),l.vertical.style=A(()=>({...t.thumbStyle,...t.verticalThumbStyle,top:`${l.vertical.thumbStart.value}px`,height:`${l.vertical.thumbSize.value}px`,[f.lang.rtl?`left`:`right`]:`${t.horizontalOffset[+!f.lang.rtl]}px`})),l.vertical.thumbClass=A(()=>`q-scrollarea__thumb q-scrollarea__thumb--v absolute-right`+(l.vertical.thumbHidden.value?` q-scrollarea__thumb--invisible`:``)),l.vertical.barClass=A(()=>`q-scrollarea__bar q-scrollarea__bar--v absolute-right`+(l.vertical.thumbHidden.value?` q-scrollarea__bar--invisible`:``)),l.horizontal.percentage=A(()=>{let e=l.horizontal.size.value-c.horizontal.value;if(e<=0)return 0;let t=Oe($(l.horizontal.position.value,f.lang.rtl)/e,0,1);return Math.round(t*1e4)/1e4}),l.horizontal.thumbHidden=A(()=>!(t.visible===null?s.value:t.visible)&&!a.value&&!o.value||l.horizontal.size.value<=c.horizontal.value+1),l.horizontal.thumbStart=A(()=>t.horizontalOffset[+!!f.lang.rtl]+l.horizontal.percentage.value*(c.horizontalInner.value-l.horizontal.thumbSize.value)),l.horizontal.thumbSize=A(()=>Math.round(Oe(c.horizontalInner.value*c.horizontalInner.value/l.horizontal.size.value,Ze(c.horizontalInner.value),c.horizontalInner.value))),l.horizontal.style=A(()=>({...t.thumbStyle,...t.horizontalThumbStyle,[f.lang.rtl?`right`:`left`]:`${l.horizontal.thumbStart.value}px`,width:`${l.horizontal.thumbSize.value}px`,bottom:`${t.verticalOffset[1]}px`})),l.horizontal.thumbClass=A(()=>`q-scrollarea__thumb q-scrollarea__thumb--h absolute-bottom`+(l.horizontal.thumbHidden.value?` q-scrollarea__thumb--invisible`:``)),l.horizontal.barClass=A(()=>`q-scrollarea__bar q-scrollarea__bar--h absolute-bottom`+(l.horizontal.thumbHidden.value?` q-scrollarea__bar--invisible`:``));let x=A(()=>t.tabindex===void 0?l.vertical.size.value>c.vertical.value+1||l.horizontal.size.value>c.horizontal.value+1?0:void 0:t.tabindex),S=A(()=>l.vertical.thumbHidden.value&&l.horizontal.thumbHidden.value?t.contentStyle:t.contentActiveStyle);function C(){return{verticalPosition:l.vertical.position.value,verticalPercentage:l.vertical.percentage.value,verticalSize:l.vertical.size.value,verticalContainerSize:c.vertical.value,verticalContainerInnerSize:c.verticalInner.value,horizontalPosition:l.horizontal.position.value,horizontalPercentage:l.horizontal.percentage.value,horizontalSize:l.horizontal.size.value,horizontalContainerSize:c.horizontal.value,horizontalContainerInnerSize:c.horizontalInner.value}}let w=me(()=>{let e=C();e.ref=d,i(`scroll`,e)},0);function T(e,t,n){Qe(e,`setScrollPosition`)&&(e===`vertical`?ie:ue)(v.value,t,n)}function E({height:e,width:t}){let n=!1;c.vertical.value!==e&&(c.vertical.value=e,n=!0),c.horizontal.value!==t&&(c.horizontal.value=t,n=!0),n&&N()}function D({position:e}){let t=!1;l.vertical.position.value!==e.top&&(l.vertical.position.value=e.top,t=!0),l.horizontal.position.value!==e.left&&(l.horizontal.position.value=e.left,t=!0),t&&N()}function O({height:e,width:t}){l.horizontal.size.value!==t&&(l.horizontal.size.value=t,N()),l.vertical.size.value!==e&&(l.vertical.size.value=e,N())}function k(e,t){let n=l[t];if(e.isFirst){if(n.thumbHidden.value)return;_=t===`horizontal`?$(n.position.value,f.lang.rtl):n.position.value,o.value=!0}else if(!o.value)return;e.isFinal&&(o.value=!1);let r=Ye[t],i=(n.size.value-c[t].value)/(c[t+`Inner`].value-n.thumbSize.value),a=e.distance[r.dist],s=(e.direction===r.dir?1:-1)*(t===`horizontal`&&f.lang.rtl?-1:1),u=_+s*a*i;te(t===`horizontal`?$(u,f.lang.rtl):u,t)}function j(e,n){let r=l[n];if(!r.thumbHidden.value){let i=n===`horizontal`&&f.lang.rtl,a=n===`vertical`?t.verticalOffset[0]:t.horizontalOffset[+!!i],o=(i?c.horizontal.value-e.offsetX:e[Ye[n].offset])-a,s=r.thumbStart.value-a;if(o<s||o>s+r.thumbSize.value){let e=o-r.thumbSize.value/2,t=Oe(e/(c[n+`Inner`].value-r.thumbSize.value),0,1);te(i?$(t*Math.max(0,r.size.value-c[n].value),f.lang.rtl):t*Math.max(0,r.size.value-c[n].value),n)}r.ref.value!==null&&r.ref.value.dispatchEvent(new MouseEvent(e.type,e))}}function N(){a.value=!0,g!==null&&clearTimeout(g),g=setTimeout(()=>{g=null,a.value=!1},t.delay),t.onScroll!==void 0&&w()}function te(e,t){v.value[Ye[t].scroll]=e}let P=null;function ne(){P!==null&&clearTimeout(P),P=setTimeout(()=>{P=null,s.value=!0},f.platform.is.ios?50:0)}function F(){P!==null&&(clearTimeout(P),P=null),s.value=!1}let re=null;e(()=>f.lang.rtl,(e,t)=>{v.value!==null&&ue(v.value,$($(l.horizontal.position.value,t),e))}),n(()=>{re={top:l.vertical.position.value,left:l.horizontal.position.value}}),ee(()=>{if(re===null)return;let e=v.value;e!==null&&(ue(e,re.left),ie(e,re.top))}),p(w.cancel),Object.assign(d,{getScrollTarget:()=>v.value,getScroll:C,getScrollPosition:()=>({top:l.vertical.position.value,left:l.horizontal.position.value}),getScrollPercentage:()=>({top:l.vertical.percentage.value,left:l.horizontal.percentage.value}),setScrollPosition:T,setScrollPercentage(e,t,n){if(!Qe(e,`setScrollPercentage`))return;let r=t*(l[e].size.value-c[e].value);T(e,e===`horizontal`?$(r,f.lang.rtl):r,n)}});let oe={scroll:l,thumbVertDir:[[we,e=>{k(e,`vertical`)},void 0,{vertical:!0,...Xe}]],thumbHorizDir:[[we,e=>{k(e,`horizontal`)},void 0,{horizontal:!0,...Xe}]],onVerticalMousedown(e){j(e,`vertical`)},onHorizontalMousedown(e){j(e,`horizontal`)}};return()=>M(`div`,{class:y.value,onMouseenter:ne,onMouseleave:F},[M(`div`,{ref:v,class:`q-scrollarea__container scroll relative-position fit hide-scrollbar`,tabindex:x.value},[M(`div`,{class:`q-scrollarea__content absolute`,style:S.value},R(r.default,[M(Se,{debounce:0,onResize:O})])),M(Ce,{axis:`both`,onScroll:D})]),M(Se,{debounce:0,onResize:E}),M(qe,{store:oe,barStyle:t.barStyle,verticalBarStyle:t.verticalBarStyle,horizontalBarStyle:t.horizontalBarStyle})])}}),et=0,tt=[`click`,`keydown`],nt={icon:String,label:[Number,String],alert:[Boolean,String],alertIcon:String,name:{type:[Number,String],default:()=>`t_${et++}`},noCaps:Boolean,tabindex:[String,Number],disable:Boolean,contentClass:String,ripple:{type:[Boolean,Object],default:!0}},rt=[13,32];function it(e,t,n,r){let a=_(ne,se);if(a===se)return console.error(`QTab/QRouteTab component needs to be child of QTabs`),se;let{proxy:o}=b(),s=u(null),l=u(null),d=u(null);function f(){return e.disable||e.ripple===!1?!1:{keyCodes:rt,early:!0,...e.ripple===!0?{}:e.ripple}}function m(t){let n=a.tabProps.value;return`q-tab relative-position self-stretch flex flex-center text-center`+(t?` q-tab--active`+(n.activeClass?` `+n.activeClass:``)+(n.activeColor?` text-${n.activeColor}`:``)+(n.activeBgColor?` bg-${n.activeBgColor}`:``):` q-tab--inactive`)+(e.icon&&e.label&&!n.inlineLabel?` q-tab--full`:``)+(e.noCaps||n.noCaps?` q-tab--no-caps`:``)+(e.disable?` disabled`:` q-focusable q-hoverable cursor-pointer`)+(r===void 0?``:r.linkClass.value)}function h(){return`q-tab__content self-stretch flex-center relative-position q-anchor--skip non-selectable `+(a.tabProps.value.inlineLabel?`row no-wrap q-tab__content--inline`:`column`)+(e.contentClass===void 0?``:` ${e.contentClass}`)}let g=A(()=>a.currentModel.value===e.name),v=A(()=>{if(e.disable||a.hasFocus.value)return-1;let t=a.tabStopName.value;return t===null||t===e.name?e.tabindex||0:-1});function y(t,i){if(!i&&!t?.qAvoidFocus&&s.value?.focus({preventScroll:!0}),e.disable){r?.hasRouterLink.value===!0&&fe(t);return}if(r===void 0){a.updateModel({name:e.name}),n(`click`,t);return}if(r.hasRouterLink.value){let i=(n={})=>{let i,o=n.to===void 0||re(n.to,e.to)?a.avoidRouteWatcher=le():null;return r.navigateToRouterLink(t,{...n,returnRouterError:!0}).catch(e=>{i=e}).then(t=>{if(o===a.avoidRouteWatcher&&(a.avoidRouteWatcher=!1,i===void 0&&(t===void 0||t.message?.startsWith(`Avoided redundant navigation`)===!0)&&a.updateModel({name:e.name})),n.returnRouterError)return i===void 0?t:Promise.reject(i)})};n(`click`,t,i),t.defaultPrevented||i();return}n(`click`,t)}function x(e){n(`keydown`,e),!e.defaultPrevented&&(V(e,[13,32])?y(e,!0):e.keyCode>=35&&e.keyCode<=40&&!e.altKey&&!e.metaKey&&!ge(e)&&a.onKbdNavigate(e.keyCode,o.$el)&&fe(e))}function S(){let n=a.tabProps.value,r=n.narrowIndicator,i=[],o=M(`div`,{ref:d,class:[`q-tab__indicator`,n.indicatorClass]});e.icon!==void 0&&i.push(M(W,{class:`q-tab__icon`,name:e.icon})),e.label!==void 0&&i.push(M(`div`,{class:`q-tab__label`},e.label)),e.alert&&i.push(e.alertIcon===void 0?M(`div`,{class:`q-tab__alert`+(e.alert===!0?``:` text-${e.alert}`)}):M(W,{class:`q-tab__alert-icon`,color:e.alert===!0?void 0:e.alert,name:e.alertIcon})),r&&i.push(o);let c=[M(`div`,{class:`q-focus-helper`,tabindex:-1,ref:s}),M(`div`,{class:h()},R(t.default,i))];return r||c.push(o),c}let C={name:A(()=>e.name),disable:A(()=>e.disable),rootRef:l,tabIndicatorRef:d,routeData:r};p(()=>{a.unregisterTab(C)}),c(()=>{a.registerTab(C)});function w(t,n){let r={ref:l,class:m(g.value),tabindex:v.value,role:`tab`,"aria-selected":g.value?`true`:`false`,"aria-disabled":e.disable?`true`:void 0,onClick:y,onKeydown:x,...n};return i(M(t,r,S()),[[B,f()]])}return{renderTab:w,$tabs:a}}var at=P({name:`QTab`,props:nt,emits:tt,setup(e,{slots:t,emit:n}){let{renderTab:r}=it(e,t,n);return()=>r(`div`)}});function ot(e,t,n){let r=n?[`left`,`right`]:[`top`,`bottom`];return`absolute-${t?r[0]:r[1]}${e?` text-${e}`:``}`}function st(e,t){for(let n in e)if(e[n]!==t[n])return!1;return!0}var ct=[`left`,`center`,`right`,`justify`],lt=P({name:`QTabs`,props:{modelValue:[Number,String],align:{type:String,default:`center`,validator:e=>ct.includes(e)},breakpoint:{type:[String,Number],default:600},vertical:Boolean,shrink:Boolean,stretch:Boolean,activeClass:String,activeColor:String,activeBgColor:String,indicatorColor:String,leftIcon:String,rightIcon:String,outsideArrows:Boolean,mobileArrows:Boolean,switchIndicator:Boolean,narrowIndicator:Boolean,inlineLabel:Boolean,noCaps:Boolean,dense:Boolean,contentClass:String,"onUpdate:modelValue":[Function,Array]},setup(t,{slots:r,emit:i}){let{proxy:a}=b(),s=ae(),{registerTick:c}=ye(),{registerTick:l}=ye(),{registerTick:d}=ye(),{registerTimeout:f,removeTimeout:m}=oe(),{registerTimeout:g,removeTimeout:_}=oe(),v=u(null),y=u(null),x=h(t.modelValue),S=h(!1),C=h(!0),w=h(!1),T=h(!1),E=[],D=h(0),O=h(!1),k=null,j=null,N,te=A(()=>({activeClass:t.activeClass,activeColor:t.activeColor,activeBgColor:t.activeBgColor,indicatorClass:ot(t.indicatorColor,t.switchIndicator,t.vertical),narrowIndicator:t.narrowIndicator,inlineLabel:t.inlineLabel,noCaps:t.noCaps})),P=A(()=>{let e=D.value,t=x.value,n=null;for(let r=0;r<e;r++){let e=E[r];if(!e.disable.value){if(e.name.value===t)return t;n===null&&(n=e.name.value)}}return n}),F=A(()=>`q-tabs__content--align-${S.value?`left`:T.value?`justify`:t.align}`),re=A(()=>`q-tabs row no-wrap items-center q-tabs--${S.value?``:`not-`}scrollable q-tabs--${t.vertical?`vertical`:`horizontal`} q-tabs__arrows--${t.outsideArrows?`outside`:`inside`} q-tabs--mobile-with${t.mobileArrows?``:`out`}-arrows`+(t.dense?` q-tabs--dense`:``)+(t.shrink?` col-shrink`:``)+(t.stretch?` self-stretch`:``)),ie=A(()=>`q-tabs__content row no-wrap items-center self-stretch hide-scrollbar relative-position `+F.value+(t.contentClass===void 0?``:` ${t.contentClass}`)),se=A(()=>t.vertical?`height`:`width`),I=A(()=>!t.vertical&&s.lang.rtl===!0);e(I,H),e(()=>t.modelValue,e=>{L({name:e,setCurrent:!0,skipEmit:!0})}),e(()=>t.outsideArrows,R);function L({name:e,setCurrent:n,skipEmit:r}){x.value!==e&&(!r&&t[`onUpdate:modelValue`]!==void 0&&i(`update:modelValue`,e),(n||t[`onUpdate:modelValue`]===void 0)&&(B(x.value,e),x.value=e))}function R(){c(()=>{v.value&&z({width:v.value.offsetWidth,height:v.value.offsetHeight})})}function z(e){if(y.value===null)return;let n=e[se.value],r=Array.prototype.reduce.call(y.value.children,(e,t)=>e+t.getBoundingClientRect()[se.value],0),i=n>0&&r-n>1;S.value=i,i&&l(H),T.value=n<Number.parseInt(t.breakpoint,10)}function B(e,n){let r=e!=null&&e!==``?E.find(t=>t.name.value===e):null,i=n!=null&&n!==``?E.find(e=>e.name.value===n):null;if(Y)Y=!1;else if(r&&i){let e=r.tabIndicatorRef.value,n=i.tabIndicatorRef.value;k!==null&&(clearTimeout(k),k=null),e.style.transition=`none`,e.style.transform=`none`,n.style.transition=`none`,n.style.transform=`none`;let a=e.getBoundingClientRect(),o=n.getBoundingClientRect();n.style.transform=t.vertical?`translate3d(0,${a.top-o.top}px,0) scale3d(1,${o.height?a.height/o.height:1},1)`:`translate3d(${a.left-o.left}px,0,0) scale3d(${o.width?a.width/o.width:1},1,1)`,d(()=>{k=setTimeout(()=>{k=null,n.style.transition=`transform .25s cubic-bezier(.4, 0, .2, 1)`,n.style.transform=`none`},70)})}i&&S.value&&V(i.rootRef.value)}function V(e){let{left:n,width:r,top:i,height:a}=y.value.getBoundingClientRect(),o=e.getBoundingClientRect(),s=t.vertical?o.top-i:o.left-n;if(s<0){y.value[t.vertical?`scrollTop`:`scrollLeft`]+=Math.floor(s),H();return}s+=t.vertical?o.height-a:o.width-r,s>0&&(y.value[t.vertical?`scrollTop`:`scrollLeft`]+=Math.ceil(s),H())}function H(){let e=y.value;if(e===null)return;let n=e.getBoundingClientRect(),r=t.vertical?e.scrollTop:Math.abs(e.scrollLeft);I.value?(C.value=Math.ceil(r+n.width)<e.scrollWidth-1,w.value=r>0):(C.value=r>0,w.value=t.vertical?Math.ceil(r+n.height)<e.scrollHeight:Math.ceil(r+n.width)<e.scrollWidth)}function ce(e){j!==null&&clearInterval(j),j=setInterval(()=>{G(e)&&U()},5)}function le(){ce(I.value?2**53-1:0)}function ue(){ce(I.value?0:2**53-1)}function U(){j!==null&&(clearInterval(j),j=null)}function de(e,n){let r=Array.prototype.filter.call(y.value.children,e=>e===n||e.matches?.(`.q-tab.q-focusable`)),i=r.length;if(i===0)return;if(e===36)return V(r[0]),r[0].focus(),!0;if(e===35)return V(r[i-1]),r[i-1].focus(),!0;let a=e===(t.vertical?38:37),o=e===(t.vertical?40:39),s=a?-1:o?1:void 0;if(s!==void 0){let e=t.vertical!==!0&&I.value?-1:1,a=(r.indexOf(n)+s*e+i)%i;return V(r[a]),r[a].focus({preventScroll:!0}),!0}}let fe=A(()=>I.value?{get:e=>Math.abs(e.scrollLeft),set:(e,t)=>{e.scrollLeft=-t}}:t.vertical?{get:e=>e.scrollTop,set:(e,t)=>{e.scrollTop=t}}:{get:e=>e.scrollLeft,set:(e,t)=>{e.scrollLeft=t}});function G(e){let t=y.value,{get:n,set:r}=fe.value,i=!1,a=n(t),o=e<a?-1:1;return a+=o*5,a<0?(i=!0,a=0):(o===-1&&a<=e||o===1&&a>=e)&&(i=!0,a=e),r(t,a),H(),i}function pe(){let e=null,t={matchedLen:0,queryDiff:9999,hrefLen:0},n=E.filter(e=>e.routeData?.hasRouterLink.value===!0),{hash:r,query:i}=a.$route,o=Object.keys(i).length;for(let a of n){let n=a.routeData.exact.value===!0;if(!a.routeData[n?`linkIsExactActive`:`linkIsActive`].value)continue;let{hash:s,query:c,matched:l,href:u}=a.routeData.resolvedLink.value,d=Object.keys(c).length;if(n){if(s!==r||d!==o||!st(i,c))continue;e=a.name.value;break}if(s!==``&&s!==r||d!==0&&!st(c,i))continue;let f={matchedLen:l.length,queryDiff:o-d,hrefLen:u.length-s.length};if(f.matchedLen>t.matchedLen){e=a.name.value,t=f;continue}if(f.matchedLen===t.matchedLen){if(f.queryDiff<t.queryDiff)e=a.name.value,t=f;else if(f.queryDiff!==t.queryDiff)continue;f.hrefLen>t.hrefLen&&(e=a.name.value,t=f)}}if(e===null&&E.some(e=>e.routeData===void 0&&e.name.value===x.value)){Y=!1;return}L({name:e,setCurrent:!0})}function me(e){if(m(),!O.value&&v.value!==null&&e.target&&typeof e.target.closest==`function`){let t=e.target.closest(`.q-tab`);t&&v.value.contains(t)&&(O.value=!0,S.value&&V(t))}}function he(){f(()=>{O.value=!1},30)}function K(){be.avoidRouteWatcher===!1?g(pe):_()}function ge(){if(N===void 0){let t=e(()=>a.$route.fullPath,K);N=()=>{t(),N=void 0}}}function _e(e){E.push(e),D.value++,R(),e.routeData===void 0||a.$route===void 0?g(()=>{if(S.value){let e=x.value,t=e!=null&&e!==``?E.find(t=>t.name.value===e):null;t&&V(t.rootRef.value)}}):(ge(),e.routeData.hasRouterLink.value&&K())}function q(e){E.splice(E.indexOf(e),1),D.value--,R(),N!==void 0&&e.routeData!==void 0&&(E.every(e=>e.routeData===void 0)&&N(),K())}let be={currentModel:x,tabProps:te,hasFocus:O,tabStopName:P,registerTab:_e,unregisterTab:q,verifyRouteModel:K,updateModel:L,onKbdNavigate:de,avoidRouteWatcher:!1};o(ne,be);function J(){k!==null&&clearTimeout(k),U(),N?.()}let xe=!1,Y=!1;return p(J),n(()=>{xe=N!==void 0,J()}),ee(()=>{xe&&(ge(),Y=!0,K()),R()}),()=>M(`div`,{ref:v,class:re.value,role:`tablist`,"aria-orientation":t.vertical?`vertical`:`horizontal`,onFocusin:me,onFocusout:he},[M(Se,{onResize:z}),M(`div`,{ref:y,class:ie.value,onScroll:H},ve(r.default)),M(W,{class:`q-tabs__arrow q-tabs__arrow--left absolute q-tab__icon`+(C.value?``:` q-tabs__arrow--faded`),name:t.leftIcon||s.iconSet.tabs[t.vertical?`up`:`left`],onMousedownPassive:le,onTouchstartPassive:le,onMouseupPassive:U,onMouseleavePassive:U,onTouchendPassive:U}),M(W,{class:`q-tabs__arrow q-tabs__arrow--right absolute q-tab__icon`+(w.value?``:` q-tabs__arrow--faded`),name:t.rightIcon||s.iconSet.tabs[t.vertical?`down`:`right`],onMousedownPassive:ue,onTouchstartPassive:ue,onMouseupPassive:U,onMouseleavePassive:U,onTouchendPassive:U})])}}),ut={class:`row items-center no-wrap q-gutter-x-xs`},dt={class:`row items-center no-wrap q-gutter-x-xs`},ft=S({__name:`FileExplorerTabs`,props:te({stagedFilesLength:{},unstagedFilesLength:{}},{modelValue:{default:`staged`},modelModifiers:{}}),emits:[`update:modelValue`],setup(e){let t=d(e,`modelValue`),{t:n}=q();return(r,i)=>(C(),T(lt,{modelValue:t.value,"onUpdate:modelValue":i[0]||=e=>t.value=e,dense:``,"no-caps":``,align:`left`,"narrow-indicator":``,class:`text-primary q-mb-sm`},{default:m(()=>[E(at,{name:`staged`},{default:m(()=>[v(`div`,ut,[v(`span`,null,N(D(n)(`changes.staged`)),1),E(De,{color:`positive`,rounded:``},{default:m(()=>[O(N(e.stagedFilesLength),1)]),_:1})])]),_:1}),E(at,{name:`unstaged`},{default:m(()=>[v(`div`,dt,[v(`span`,null,N(D(n)(`changes.unstaged`)),1),E(De,{color:`warning`,rounded:``},{default:m(()=>[O(N(e.unstagedFilesLength),1)]),_:1})])]),_:1})]),_:1},8,[`modelValue`]))}}),pt=S({__name:`SearchInput`,props:{modelValue:{},modelModifiers:{}},emits:[`update:modelValue`],setup(e){let t=d(e,`modelValue`);return(e,n)=>(C(),T(U,f({modelValue:t.value,"onUpdate:modelValue":n[0]||=e=>t.value=e},e.$attrs,{dense:``,outlined:``,clearable:``}),{prepend:m(()=>[E(W,{name:`search`,size:`xs`})]),_:1},16,[`modelValue`]))}}),mt={class:`file-tree-node`},ht={class:`row items-center no-wrap q-gutter-x-xs`},gt={class:`ellipsis file-tree-node__label`},_t={class:`ellipsis file-tree-node__label`},vt={key:2},yt=G(S({__name:`FileTreeNode`,props:{node:{},selectedPath:{},expandedFolders:{},level:{}},emits:[`toggle-folder`,`file-select`],setup(e,{emit:t}){let n=e,a=t,o=8+n.level*12;return(t,n)=>{let c=r(`FileTreeNode`,!0);return C(),y(`div`,mt,[e.node.type===`directory`?i((C(),T(Ee,{key:0,clickable:``,dense:``,style:g({paddingLeft:`${o}px`}),onClick:n[0]||=t=>a(`toggle-folder`,e.node.path)},{default:m(()=>[E(Z,{avatar:``,class:`file-tree-node__icon-section`},{default:m(()=>[v(`div`,ht,[E(W,{name:`chevron_right`,size:`xs`,class:x([`file-tree-node__chevron`,{"file-tree-node__chevron--expanded":e.expandedFolders.has(e.node.path)}])},null,8,[`class`]),E(W,{name:e.expandedFolders.has(e.node.path)?`folder_open`:`folder`,size:`xs`,color:`warning`},null,8,[`name`])])]),_:1}),E(Z,{class:`text-mono`},{default:m(()=>[v(`div`,gt,[O(N(e.node.name)+` `,1),E(X,{anchor:`top middle`,self:`bottom middle`},{default:m(()=>[O(N(e.node.path),1)]),_:1})])]),_:1}),e.node.children?(C(),T(Z,{key:0,side:``,class:`text-caption text-grey-6`},{default:m(()=>[O(N(e.node.children.length),1)]),_:1})):j(``,!0)]),_:1},8,[`style`])),[[B]]):i((C(),T(Ee,{key:1,clickable:``,dense:``,active:e.node.path===e.selectedPath,"active-class":`file-tree-node--selected`,style:g({paddingLeft:`${o+20}px`}),onClick:n[1]||=t=>a(`file-select`,e.node.path)},{default:m(()=>[E(Z,{avatar:``,class:`file-tree-node__icon-section`},{default:m(()=>[E(W,{name:`insert_drive_file`,size:`xs`,color:`grey-6`})]),_:1}),E(Z,{class:`text-mono`},{default:m(()=>[v(`div`,_t,[O(N(e.node.name)+` `,1),E(X,{anchor:`top middle`,self:`bottom middle`},{default:m(()=>[O(N(e.node.path),1)]),_:1})])]),_:1}),e.node.isChanged?(C(),T(Z,{key:0,side:``},{default:m(()=>[E(W,{name:`fiber_manual_record`,size:`8px`,color:`warning`})]),_:1})):j(``,!0)]),_:1},8,[`active`,`style`])),[[B]]),e.node.type===`directory`&&e.expandedFolders.has(e.node.path)&&e.node.children?(C(),y(`div`,vt,[(C(!0),y(k,null,s(e.node.children,t=>(C(),T(c,{key:t.path,node:t,"selected-path":e.selectedPath,"expanded-folders":e.expandedFolders,level:e.level+1,onToggleFolder:n[2]||=e=>a(`toggle-folder`,e),onFileSelect:n[3]||=e=>a(`file-select`,e)},null,8,[`node`,`selected-path`,`expanded-folders`,`level`]))),128))])):j(``,!0)])}}}),[[`__scopeId`,`data-v-43168dd7`]]);function bt(){let e=ae(),{t}=q(),n=Be();async function r(r,i,a){try{await xe(i,{paths:[r]}),await n.refresh()}catch(n){e.notify({type:`negative`,message:t(a),caption:n instanceof Error?n.message:String(n)})}}function i(e){return r(e,`/api/git/stage`,`changes.actions.stageError`)}function a(e){return r(e,`/api/git/unstage`,`changes.actions.unstageError`)}function o(n){e.dialog({title:t(`changes.actions.discardConfirmTitle`),message:t(`changes.actions.discardConfirmMessage`,{path:n}),persistent:!0,ok:{label:t(`changes.actions.discardConfirmOk`),color:`negative`,flat:!0},cancel:{label:t(`changes.actions.discardConfirmCancel`),flat:!0}}).onOk(()=>{r(n,`/api/git/discard`,`changes.actions.discardError`)})}return{stage:i,unstage:a,discard:o}}var xt={class:`ellipsis file-list-item__path-label`},St={class:`text-caption`},Ct={class:`text-positive`},wt={class:`text-negative q-ml-xs`},Tt=G(S({__name:`FileListItem`,props:{file:{},selected:{type:Boolean},source:{}},emits:[`click`],setup(e){let{t}=q(),{stage:n,unstage:r,discard:a}=bt(),o={added:`A`,modified:`M`,deleted:`D`,renamed:`R`},s={added:`positive`,modified:`warning`,deleted:`negative`,renamed:`purple`};return(c,l)=>i((C(),T(Ee,{clickable:``,dense:``,active:e.selected,"active-class":`file-list-item--selected`,onClick:l[3]||=e=>c.$emit(`click`)},{default:m(()=>[E(Z,{avatar:``,class:`file-list-item__icon-section`},{default:m(()=>[v(`span`,{class:x([`text-caption text-weight-bold text-mono`,`text-${s[e.file.status]}`])},N(o[e.file.status]),3)]),_:1}),E(Z,{class:`text-mono`},{default:m(()=>[v(`div`,xt,[O(N(D(Q)(e.file))+` `,1),E(X,{anchor:`top middle`,self:`bottom middle`},{default:m(()=>[O(N(D(Q)(e.file)),1)]),_:1})])]),_:1}),E(Z,{side:``,class:`file-list-item__stats`},{default:m(()=>[v(`span`,St,[v(`span`,Ct,`+`+N(e.file.additions),1),v(`span`,wt,`-`+N(e.file.deletions),1)])]),_:1}),E(Z,{side:``,class:`file-list-item__actions`},{default:m(()=>[e.source===`unstaged`?(C(),y(k,{key:0},[i((C(),T(z,{flat:``,dense:``,round:``,size:`sm`,icon:`undo`,"aria-label":D(t)(`changes.actions.discard`),onClick:l[0]||=_e(t=>D(a)(D(Q)(e.file)),[`stop`])},{default:m(()=>[E(X,null,{default:m(()=>[O(N(D(t)(`changes.actions.discard`)),1)]),_:1})]),_:1},8,[`aria-label`])),[[B]]),i((C(),T(z,{flat:``,dense:``,round:``,size:`sm`,icon:`add`,"aria-label":D(t)(`changes.actions.stage`),onClick:l[1]||=_e(t=>D(n)(D(Q)(e.file)),[`stop`])},{default:m(()=>[E(X,null,{default:m(()=>[O(N(D(t)(`changes.actions.stage`)),1)]),_:1})]),_:1},8,[`aria-label`])),[[B]])],64)):i((C(),T(z,{key:1,flat:``,dense:``,round:``,size:`sm`,icon:`remove`,"aria-label":D(t)(`changes.actions.unstage`),onClick:l[2]||=_e(t=>D(r)(D(Q)(e.file)),[`stop`])},{default:m(()=>[E(X,null,{default:m(()=>[O(N(D(t)(`changes.actions.unstage`)),1)]),_:1})]),_:1},8,[`aria-label`])),[[B]])]),_:1})]),_:1},8,[`active`])),[[B]])}}),[[`__scopeId`,`data-v-77d2a0e7`]]),Et=P({name:`QItemLabel`,props:{overline:Boolean,caption:Boolean,header:Boolean,lines:[Number,String]},setup(e,{slots:t}){let n=A(()=>Number.parseInt(e.lines,10)),r=A(()=>`q-item__label`+(e.overline?` q-item__label--overline text-overline`:``)+(e.caption?` q-item__label--caption text-caption`:``)+(e.header?` q-item__label--header`:``)+(n.value===1?` ellipsis`:``)),i=A(()=>e.lines!==void 0&&n.value>1?{overflow:`hidden`,display:`-webkit-box`,"-webkit-box-orient":`vertical`,"-webkit-line-clamp":n.value}:null);return()=>M(`div`,{style:i.value,class:r.value},ve(t.default))}}),Dt=S({__name:`BrowseModeToggle`,props:{modelValue:{type:Boolean,default:!1},modelModifiers:{}},emits:[`update:modelValue`],setup(e){let t=d(e,`modelValue`),{t:n}=q();return(e,r)=>(C(),T(Ee,null,{default:m(()=>[E(Z,null,{default:m(()=>[E(Et,null,{default:m(()=>[O(N(D(n)(`browse.toggle`)),1)]),_:1})]),_:1}),E(Z,{side:``},{default:m(()=>[E(K,{modelValue:t.value,"onUpdate:modelValue":r[0]||=e=>t.value=e,dense:``,size:`xs`},null,8,[`modelValue`])]),_:1})]),_:1}))}}),Ot={class:`file-list-panel__header q-pa-sm`},kt={key:1,class:`text-subtitle2 q-mb-sm`},At={class:`text-primary`},jt={key:0,class:`row justify-center q-pa-lg`},Mt={key:0,class:`text-caption text-grey-6 q-px-md q-py-sm`},Nt={key:0,class:`text-caption text-grey-6 q-px-md q-py-sm`},Pt=G(S({__name:`FileListPanel`,setup(e){let{t}=q(),{stagedFiles:n,unstagedFiles:r,loading:i}=J(Ne()),o=Be(),{source:c,browseMode:l,filter:u,selectedPath:d,expandedFolders:f,treeLoading:p,filteredDiffFiles:h,filteredTree:g,totalTreeFiles:_}=J(o);return(e,b)=>(C(),y(k,null,[v(`div`,Ot,[D(l)?(C(),y(`div`,kt,[O(N(D(t)(`browse.allFiles`))+` `,1),v(`span`,At,`(`+N(D(_))+`)`,1)])):(C(),T(ft,{key:0,modelValue:D(c),"onUpdate:modelValue":b[0]||=e=>a(c)?c.value=e:null,"staged-files-length":D(n).length,"unstaged-files-length":D(r).length},null,8,[`modelValue`,`staged-files-length`,`unstaged-files-length`])),E(pt,{modelValue:D(u),"onUpdate:modelValue":b[1]||=e=>a(u)?u.value=e:null,placeholder:D(l)?D(t)(`browse.searchPlaceholder`):D(t)(`changes.filterPlaceholder`)},null,8,[`modelValue`,`placeholder`])]),E(L),E($e,{class:`col file-list-panel__scroll`,"content-style":`padding: 4px 0`,"content-active-style":`padding: 4px 0`},{default:m(()=>[(D(l)?D(p):D(i))?(C(),y(`div`,jt,[E(H,{color:`primary`,size:`2em`})])):D(l)?(C(),y(k,{key:1},[D(g).length===0?(C(),y(`p`,Mt,N(D(u)?D(t)(`browse.noMatchingFiles`):D(t)(`browse.noFiles`)),1)):j(``,!0),(C(!0),y(k,null,s(D(g),e=>(C(),T(yt,{key:e.path,node:e,"selected-path":D(d),"expanded-folders":D(f),level:0,onToggleFolder:D(o).toggleFolder,onFileSelect:D(o).selectFile},null,8,[`node`,`selected-path`,`expanded-folders`,`onToggleFolder`,`onFileSelect`]))),128))],64)):(C(),y(k,{key:2},[D(h).length===0?(C(),y(`p`,Nt,N(D(u)?D(t)(`browse.noMatchingFiles`):D(t)(`changes.emptyFileList`)),1)):j(``,!0),(C(!0),y(k,null,s(D(h),e=>(C(),T(Tt,{key:D(Q)(e),file:e,selected:D(d)===D(Q)(e),source:D(c),onClick:t=>D(o).selectFile(D(Q)(e))},null,8,[`file`,`selected`,`source`,`onClick`]))),128))],64))]),_:1}),E(L),E(Dt,{modelValue:D(l),"onUpdate:modelValue":b[2]||=e=>a(l)?l.value=e:null},null,8,[`modelValue`])],64))}}),[[`__scopeId`,`data-v-704ed3e1`]]),Ft={class:`diff-status-bar row items-center q-gutter-x-md q-pa-sm text-body2`},It={class:`text-weight-medium`},Lt={class:`text-positive`},Rt={class:`text-negative`},zt={key:0,class:`text-grey-6`},Bt={key:1,class:`text-grey-6`},Vt={key:2,class:`text-grey-6`},Ht={key:3,class:`text-grey-6`},Ut=S({__name:`DiffStatusBar`,props:{files:{}},emits:[`expand-all`,`collapse-all`],setup(e,{emit:t}){let n=e,r=t,{t:i}=q(),a=A(()=>{let e=n.files.reduce((e,t)=>e+t.additions,0),t=n.files.reduce((e,t)=>e+t.deletions,0),r=n.files.filter(e=>e.status===`added`).length,i=n.files.filter(e=>e.status===`modified`).length,a=n.files.filter(e=>e.status===`deleted`).length,o=n.files.filter(e=>e.status===`renamed`).length;return{total:n.files.length,additions:e,deletions:t,added:r,modified:i,deleted:a,renamed:o}});return(e,t)=>(C(),y(`div`,Ft,[v(`span`,It,N(D(i)(`changes.filesChanged`,{count:a.value.total})),1),v(`span`,Lt,` +`+N(a.value.additions)+` `+N(D(i)(`changes.additions`)),1),v(`span`,Rt,` -`+N(a.value.deletions)+` `+N(D(i)(`changes.deletions`)),1),a.value.added?(C(),y(`span`,zt,N(a.value.added)+` `+N(D(i)(`changes.added`)),1)):j(``,!0),a.value.modified?(C(),y(`span`,Bt,N(a.value.modified)+` `+N(D(i)(`changes.modified`)),1)):j(``,!0),a.value.deleted?(C(),y(`span`,Vt,N(a.value.deleted)+` `+N(D(i)(`changes.deletedPlural`)),1)):j(``,!0),a.value.renamed?(C(),y(`span`,Ht,N(a.value.renamed)+` `+N(D(i)(`changes.renamed`)),1)):j(``,!0),E(Te),E(z,{dense:``,flat:``,"no-caps":``,size:`sm`,icon:`unfold_more`,label:D(i)(`changes.expandAll`),onClick:t[0]||=e=>r(`expand-all`)},null,8,[`label`]),E(z,{dense:``,flat:``,"no-caps":``,size:`sm`,icon:`unfold_less`,label:D(i)(`changes.collapseAll`),onClick:t[1]||=e=>r(`collapse-all`)},null,8,[`label`])]))}}),Wt={class:`diff-line__gutter`},Gt={class:`diff-line__gutter`},Kt={class:`diff-line__prefix`},qt={class:`diff-line__content`},Jt=G(S({__name:`DiffLineRow`,props:{line:{},tokens:{}},setup(e){let t=e,n=A(()=>({added:`+`,removed:`-`,context:` `})[t.line.type]),r=A(()=>t.line.type===`added`?`diff-line--added`:t.line.type===`removed`?`diff-line--removed`:``);function i(e){return e.colorLight&&e.colorDark?`light-dark(${e.colorLight}, ${e.colorDark})`:e.colorLight??e.colorDark}return(t,a)=>(C(),y(`div`,{class:x([`diff-line row no-wrap text-mono`,r.value])},[v(`span`,Wt,N(e.line.oldLineNumber??``),1),v(`span`,Gt,N(e.line.newLineNumber??``),1),v(`span`,Kt,N(n.value),1),v(`span`,qt,[e.tokens?(C(!0),y(k,{key:0},s(e.tokens,(e,t)=>(C(),y(`span`,{key:t,style:g({color:i(e)})},N(e.content),5))),128)):(C(),y(k,{key:1},[O(N(e.line.content),1)],64))])],2))}}),[[`__scopeId`,`data-v-e66c5ab4`]]),Yt={class:`diff-hunk`},Xt={class:`diff-hunk__header text-mono text-primary`},Zt=G(S({__name:`DiffHunkView`,props:{hunk:{},lineTokens:{}},setup(e){let t=e,n=A(()=>`@@ -${t.hunk.oldStart},${t.hunk.oldLines} +${t.hunk.newStart},${t.hunk.newLines} @@`);return(t,r)=>(C(),y(`div`,Yt,[v(`div`,Xt,N(n.value),1),(C(!0),y(k,null,s(e.hunk.lines,(t,n)=>(C(),T(Jt,{key:n,line:t,tokens:e.lineTokens?.[n]},null,8,[`line`,`tokens`]))),128))]))}}),[[`__scopeId`,`data-v-f47860bf`]]),Qt={class:`file-content-line row no-wrap text-mono`},$t={class:`file-content-line__gutter`},en={class:`file-content-line__content`},tn=G(S({__name:`FileContentLine`,props:{lineNumber:{},content:{},tokens:{}},setup(e){function t(e){return e.colorLight&&e.colorDark?`light-dark(${e.colorLight}, ${e.colorDark})`:e.colorLight??e.colorDark}return(n,r)=>(C(),y(`div`,Qt,[v(`span`,$t,N(e.lineNumber),1),v(`span`,en,[e.tokens?(C(!0),y(k,{key:0},s(e.tokens,(e,n)=>(C(),y(`span`,{key:n,style:g({color:t(e)})},N(e.content),5))),128)):(C(),y(k,{key:1},[O(N(e.content||` `),1)],64))])]))}}),[[`__scopeId`,`data-v-edbf04e6`]]),nn={ts:`typescript`,tsx:`tsx`,js:`javascript`,jsx:`jsx`,mjs:`javascript`,cjs:`javascript`,vue:`vue`,json:`json`,md:`markdown`,css:`css`,scss:`scss`,html:`html`,yml:`yaml`,yaml:`yaml`,sh:`bash`,py:`python`,go:`go`,rs:`rust`,java:`java`,kt:`kotlin`,kts:`kotlin`},rn={typescript:()=>F(()=>import(`./typescript-Utq2Cl8c.js`),__vite__mapDeps([0,1])),tsx:()=>F(()=>import(`./tsx-udAQXfEw.js`),[]),javascript:()=>F(()=>import(`./javascript-Yelw-5ZN.js`),__vite__mapDeps([2,3])),jsx:()=>F(()=>import(`./jsx-Bz0zcwM4.js`),[]),vue:()=>F(()=>import(`./vue-CSBlZ1UP.js`),__vite__mapDeps([4,5,3,6,7,1])),json:()=>F(()=>import(`./json-CaljABEy.js`),__vite__mapDeps([8,7])),markdown:()=>F(()=>import(`./markdown-BYOwaDjH.js`),[]),css:()=>F(()=>import(`./css-CdtkT07m.js`),__vite__mapDeps([9,5])),scss:()=>F(()=>import(`./scss-DHtrkMkT.js`),__vite__mapDeps([10,5])),html:()=>F(()=>import(`./html-DDNsd9lV.js`),__vite__mapDeps([11,6,5,3])),yaml:()=>F(()=>import(`./yaml-rwi0_p6S.js`),[]),bash:()=>F(()=>import(`./bash-CLZ0U2zV.js`),[]),python:()=>F(()=>import(`./python-gzcpVVnB.js`),[]),go:()=>F(()=>import(`./go-rLFTqkRN.js`),[]),rust:()=>F(()=>import(`./rust-Cfkwpbl8.js`),[]),java:()=>F(()=>import(`./java-BXcb2rRD.js`),[]),kotlin:()=>F(()=>import(`./kotlin-DhhofPvG.js`),[])},an={light:()=>F(()=>import(`./github-light-EUqPIrTm.js`),[]),dark:()=>F(()=>import(`./github-dark-C-LZuMrd.js`),[])};function on(e){let t=e.split(`.`);return t.length<2?`text`:nn[t.pop().toLowerCase()]??`text`}var sn=null,cn=new Set;async function ln(e){sn||=(async()=>{let[{createHighlighterCore:e},{createOnigurumaEngine:t}]=await Promise.all([F(()=>import(`./core-Ruj7F7dx.js`),__vite__mapDeps([12,13])),F(()=>import(`./engine-oniguruma-Bon2Icja.js`),[])]);return e({themes:[an.light(),an.dark()],langs:[],engine:t(()=>F(()=>import(`./wasm-BnjxR4X6.js`),[]))})})();let t=await sn;return cn.has(e)||(await t.loadLanguage(rn[e]()),cn.add(e)),t}async function un(e,t){if(e===`text`||!rn[e]||t.length===0)return null;try{return(await ln(e)).codeToTokensWithThemes(t.join(`
|
|
3
|
+
`),{lang:e,themes:{light:`github-light`,dark:`github-dark`}}).map(e=>e.map(e=>({content:e.content,colorLight:e.variants.light?.color,colorDark:e.variants.dark?.color})))}catch{return null}}async function dn(e){let t=e.hunks.flatMap(e=>e.lines.map(e=>e.content));return un(on(e.newPath||e.oldPath),t)}async function fn(e,t){return un(on(e),t)}var pn={class:`file-content-view`},mn=S({__name:`FileContentView`,props:{path:{},lines:{}},setup(t){let n=t,r=h(null),i=0;return e(()=>[n.path,n.lines],async([e,t])=>{let n=++i;if(r.value=null,t.length>0){let a=await fn(e,t);n===i&&(r.value=a)}},{immediate:!0}),(e,n)=>(C(),y(`div`,pn,[(C(!0),y(k,null,s(t.lines,(e,t)=>(C(),T(tn,{key:t,"line-number":t+1,content:e,tokens:r.value?.[t]},null,8,[`line-number`,`content`,`tokens`]))),128))]))}}),hn={key:0,class:`row justify-center q-pa-lg`},gn={key:1,class:`text-caption text-grey-6 q-pa-md q-mb-none`},_n=S({__name:`DiffFileFullView`,props:{path:{},source:{}},setup(t){let n=t,{t:r}=q(),{lines:i,isBinary:a,loading:o,fetchContent:s}=Re(),c=A(()=>n.source===`staged`?`INDEX`:`WORKTREE`);return e(()=>[n.path,c.value],([e,t])=>{s(e,t)},{immediate:!0}),(e,n)=>D(o)?(C(),y(`div`,hn,[E(H,{color:`primary`,size:`2em`})])):D(a)?(C(),y(`p`,gn,N(D(r)(`changes.binaryFile`)),1)):(C(),T(mn,{key:2,path:t.path,lines:D(i)},null,8,[`path`,`lines`]))}}),vn={},yn={class:`file-card-frame`};function bn(e,t){return C(),y(`div`,yn,[l(e.$slots,`header`,{},void 0,!0),l(e.$slots,`default`,{},void 0,!0)])}var xn=G(vn,[[`render`,bn],[`__scopeId`,`data-v-e02b3e52`]]);function Sn(e){let t=document.createElement(`textarea`);t.value=e,t.contentEditable=`true`,t.style.position=`fixed`;let n=()=>{};ce(n),document.body.append(t),t.focus(),t.select();let r=document.execCommand(`copy`);return t.remove(),I(n),r}function Cn(e){if(navigator.clipboard)return navigator.clipboard.writeText(e);let t=Sn(e);return t?Promise.resolve(!0):Promise.reject(t)}var wn=S({__name:`CopyButton`,props:{value:{},tooltip:{}},setup(e){let t=e,n=h(!1),r;async function a(){try{await Cn(t.value),n.value=!0,clearTimeout(r),r=setTimeout(()=>{n.value=!1},1500)}catch{}}return(t,r)=>i((C(),T(z,{flat:``,dense:``,round:``,size:`sm`,color:n.value?`positive`:void 0,icon:n.value?`check`:`content_copy`,"aria-label":e.tooltip,onClick:_e(a,[`stop`])},{default:m(()=>[E(X,null,{default:m(()=>[O(N(e.tooltip),1)]),_:1})]),_:1},8,[`color`,`icon`,`aria-label`])),[[B]])}}),Tn={class:`file-card-header row items-center no-wrap`},En={class:`file-card-header__name row items-center no-wrap`},Dn={class:`ellipsis text-mono file-card-header__path`},On=G(S({__name:`FileCardHeader`,props:{path:{}},setup(e){let{t}=q();return(n,r)=>(C(),y(`div`,Tn,[l(n.$slots,`leading`,{},void 0,!0),v(`div`,En,[v(`span`,Dn,N(e.path),1),E(wn,{value:e.path,tooltip:D(t)(`common.copyPath`)},null,8,[`value`,`tooltip`])]),E(Te),l(n.$slots,`default`,{},void 0,!0)]))}}),[[`__scopeId`,`data-v-f297ccaa`]]),kn={class:`diff-file-card__status-section`},An={class:`text-caption diff-file-card__stats`},jn={class:`text-positive`},Mn={class:`text-negative q-ml-xs`},Nn={key:0,class:`diff-file-card__body`},Pn={key:0,class:`text-caption text-grey-6 q-pa-md q-mb-none`},Fn={key:1,class:`text-caption text-grey-6 q-pa-md q-mb-none`},In=G(S({__name:`DiffFileCard`,props:{file:{},source:{},expanded:{type:Boolean}},emits:[`toggle`,`expand`],setup(t,{emit:n}){let r=t,a=n,{t:o}=q(),c={added:`positive`,modified:`warning`,deleted:`negative`,renamed:`purple`},l=A(()=>Q(r.file)),u=h(`diff`),d=A({get:()=>u.value===`full`,set:e=>{e?(u.value=`full`,r.expanded||a(`expand`)):r.expanded&&(u.value=`diff`)}}),f=h(null),p=0;e(()=>r.file,async e=>{let t=++p;u.value=`diff`,f.value=null;let n=await dn(e);t===p&&(f.value=n)},{immediate:!0});function g(e){if(!f.value)return null;let t=r.file.hunks.slice(0,e).reduce((e,t)=>e+t.lines.length,0);return f.value.slice(t,t+r.file.hunks[e].lines.length)}return(e,n)=>(C(),T(xn,{id:`diff-file-${l.value}`,class:`diff-file-card`},{header:m(()=>[i((C(),T(Ee,{clickable:``,dense:``,class:`diff-file-card__header-item`,onClick:n[2]||=e=>a(`toggle`)},{default:m(()=>[E(On,{path:l.value},{leading:m(()=>[E(W,{name:`chevron_right`,size:`xs`,class:x([`diff-file-card__chevron`,{"diff-file-card__chevron--expanded":t.expanded}])},null,8,[`class`])]),default:m(()=>[v(`div`,{class:`diff-file-card__toggle-section`,onClick:n[1]||=_e(()=>{},[`stop`])},[E(K,{modelValue:d.value,"onUpdate:modelValue":n[0]||=e=>d.value=e,"left-label":``,dense:``,size:`xs`,label:D(o)(`changes.showFullFile`)},null,8,[`modelValue`,`label`])]),v(`div`,kn,[E(De,{color:c[t.file.status],outline:``,rounded:``},{default:m(()=>[O(N(D(o)(`changes.fileStatus.${t.file.status}`)),1)]),_:1},8,[`color`])]),v(`span`,An,[v(`span`,jn,`+`+N(t.file.additions),1),v(`span`,Mn,`-`+N(t.file.deletions),1)])]),_:1},8,[`path`])]),_:1})),[[B]])]),default:m(()=>[t.expanded?(C(),y(`div`,Nn,[u.value===`full`?(C(),T(_n,{key:0,path:l.value,source:t.source},null,8,[`path`,`source`])):(C(),y(k,{key:1},[t.file.isBinary?(C(),y(`p`,Pn,N(D(o)(`changes.binaryFile`)),1)):t.file.hunks.length===0?(C(),y(`p`,Fn,N(D(o)(`changes.noTextChanges`)),1)):j(``,!0),(C(!0),y(k,null,s(t.file.hunks,(e,t)=>(C(),T(Zt,{key:t,hunk:e,"line-tokens":g(t)},null,8,[`hunk`,`line-tokens`]))),128))],64))])):j(``,!0)]),_:1},8,[`id`]))}}),[[`__scopeId`,`data-v-d4e4f4cd`]]),Ln={key:0,class:`q-pa-md text-grey-6`},Rn=S({__name:`DiffPanel`,setup(e){let{t}=q(),n=Be(),{source:r,diffFiles:i,expandedFiles:a}=J(n);return(e,o)=>D(i).length===0?(C(),y(`div`,Ln,N(D(t)(`changes.emptyDiffPanel`)),1)):(C(),y(k,{key:1},[E(Ut,{files:D(i),onExpandAll:D(n).expandAllFiles,onCollapseAll:D(n).collapseAllFiles},null,8,[`files`,`onExpandAll`,`onCollapseAll`]),E(L),E($e,{class:`col`,"content-style":`padding: 8px`,"content-active-style":`padding: 8px`},{default:m(()=>[(C(!0),y(k,null,s(D(i),e=>(C(),T(In,{key:D(Q)(e),file:e,source:D(r),expanded:D(a).has(D(Q)(e)),onToggle:t=>D(n).handleCardToggle(D(Q)(e)),onExpand:t=>D(n).expandFile(D(Q)(e))},null,8,[`file`,`source`,`expanded`,`onToggle`,`onExpand`]))),128))]),_:1})],64))}}),zn={key:0,class:`q-pa-md text-grey-6`},Bn={key:0,class:`row justify-center q-pa-lg`},Vn={key:1,class:`q-pa-md text-grey-6`},Hn=S({__name:`FileContentPanel`,setup(e){let{t}=q(),n=Be(),{selectedPath:r,browseFileLines:i,browseFileIsBinary:a,browseFileLoading:o}=J(n);return(e,n)=>D(r)?(C(),T(xn,{key:1,class:`col`},{header:m(()=>[E(On,{path:D(r)},null,8,[`path`])]),default:m(()=>[D(o)?(C(),y(`div`,Bn,[E(H,{color:`primary`,size:`2em`})])):D(a)?(C(),y(`div`,Vn,N(D(t)(`changes.binaryFile`)),1)):(C(),T($e,{key:2,class:`col`,"content-style":`padding: 8px`,"content-active-style":`padding: 8px`},{default:m(()=>[E(mn,{path:D(r),lines:D(i)},null,8,[`path`,`lines`])]),_:1}))]),_:1})):(C(),y(`div`,zn,N(D(t)(`browse.selectFile`)),1))}}),Un=S({__name:`FileExplorer`,setup(e){let t=Be(),{browseMode:n}=J(t);c(()=>void t.refresh());let r=Ve([`files:changed`],()=>void t.refreshFromServerEvent());return w(()=>r.close()),(e,t)=>(C(),T(Ke,null,{panel:m(()=>[E(Pt)]),content:m(()=>[D(n)?(C(),T(Hn,{key:0})):(C(),T(Rn,{key:1}))]),_:1}))}}),Wn=S({__name:`IndexPage`,setup(e){function t(e,t){return{height:`${t-e}px`}}return(e,n)=>(C(),T(ke,{"style-fn":t},{default:m(()=>[E(Un)]),_:1}))}});export{Wn as default};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.file-explorer[data-v-1ce1b104]{height:100%}[data-v-1ce1b104] .file-explorer__panel{flex-direction:column;flex-shrink:0;min-width:0;height:100%;display:flex}[data-v-1ce1b104] .file-explorer__content{flex-direction:column;flex-wrap:no-wrap;display:flex}.file-tree-node__icon-section[data-v-43168dd7]{min-width:0!important;padding-right:6px!important}.file-tree-node__label[data-v-43168dd7]{width:100%;min-width:0}.file-tree-node__chevron[data-v-43168dd7]{transition:transform .15s}.file-tree-node__chevron--expanded[data-v-43168dd7]{transform:rotate(90deg)}.file-tree-node--selected[data-v-43168dd7]{font-weight:500}.file-list-item__icon-section[data-v-77d2a0e7]{min-width:0!important;padding-right:6px!important}.file-list-item__path-label[data-v-77d2a0e7]{width:100%;min-width:0}.file-list-item--selected[data-v-77d2a0e7],[data-v-77d2a0e7] .file-list-item--selected{font-weight:500}.file-list-item__actions[data-v-77d2a0e7]{gap:2px;flex-direction:row!important;padding:0 0 0 6px!important}.file-list-item__stats[data-v-77d2a0e7]{padding-right:0!important}.file-list-panel__scroll[data-v-704ed3e1] .q-scrollarea__content{width:100%}.diff-line[data-v-e66c5ab4]{font-size:12px;line-height:20px}.diff-line--added[data-v-e66c5ab4]{background:var(--diff-added-bg)}.diff-line--removed[data-v-e66c5ab4]{background:var(--diff-removed-bg)}.diff-line__gutter[data-v-e66c5ab4]{text-align:right;-webkit-user-select:none;user-select:none;width:40px;color:var(--diff-gutter-color);flex:none;padding-right:8px}.diff-line__prefix[data-v-e66c5ab4]{text-align:center;-webkit-user-select:none;user-select:none;flex:none;width:16px;font-weight:600}.diff-line--added .diff-line__prefix[data-v-e66c5ab4]{color:var(--q-positive)}.diff-line--removed .diff-line__prefix[data-v-e66c5ab4]{color:var(--q-negative)}.diff-line__content[data-v-e66c5ab4]{white-space:pre;flex:1}.diff-hunk__header[data-v-f47860bf]{padding:2px 12px;font-size:12px}.file-content-line[data-v-edbf04e6]{font-size:12px;line-height:20px}.file-content-line__gutter[data-v-edbf04e6]{text-align:right;-webkit-user-select:none;user-select:none;width:48px;color:var(--diff-gutter-color);flex:none;padding-right:8px}.file-content-line__content[data-v-edbf04e6]{white-space:pre;flex:1}.file-card-frame[data-v-e02b3e52]{border:1px solid #80808033;border-radius:4px;flex-direction:column;min-height:0;display:flex;overflow:hidden}.file-card-header[data-v-f297ccaa]{background:var(--diff-header-bg);column-gap:8px;width:100%;min-width:0;padding:6px 12px}.file-card-header__name[data-v-f297ccaa]{flex:0 auto;column-gap:4px;min-width:0}.file-card-header__path[data-v-f297ccaa]{min-width:0}.diff-file-card[data-v-d4e4f4cd]{margin-bottom:8px}.diff-file-card__header-item[data-v-d4e4f4cd]{min-height:0;padding:0}.diff-file-card__chevron[data-v-d4e4f4cd]{transition:transform .15s}.diff-file-card__chevron--expanded[data-v-d4e4f4cd]{transform:rotate(90deg)}.diff-file-card__toggle-section[data-v-d4e4f4cd]{justify-content:flex-end;min-width:160px;display:flex}.diff-file-card__status-section[data-v-d4e4f4cd]{justify-content:flex-end;min-width:92px;display:flex}.diff-file-card__stats[data-v-d4e4f4cd]{text-align:right;min-width:76px;display:inline-block}.diff-file-card__body[data-v-d4e4f4cd]{border-top:1px solid #80808033;overflow-x:auto}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.repo-info[data-v-bce37a65]{line-height:1}
|