@happy-nut/monacori 0.1.20 → 0.1.21
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/assets/icon.icns +0 -0
- package/dist/app-main.js +403 -155
- package/dist/build.d.ts +1 -0
- package/dist/build.js +8 -6
- package/dist/diff.d.ts +2 -1
- package/dist/diff.js +3 -3
- package/dist/i18n.js +6 -0
- package/dist/preload.cjs +7 -0
- package/dist/render.d.ts +4 -0
- package/dist/render.js +84 -0
- package/dist/viewer.client.js +317 -126
- package/dist/viewer.css +52 -8
- package/package.json +9 -2
- package/scripts/patch-electron-name.mjs +23 -14
package/dist/build.d.ts
CHANGED
package/dist/build.js
CHANGED
|
@@ -5,9 +5,10 @@ import { collectHttpEnvironments, collectReviewFileStates, collectSourceFiles, p
|
|
|
5
5
|
import { renderDiff2Html } from "./highlight.js";
|
|
6
6
|
import { diffSubtitle, renderDiffHtml, renderDiffTree, renderNotGitRepoHtml, renderReviewStatus, renderSourceTree, shouldLazyRender, splitDiffForLazy } from "./render.js";
|
|
7
7
|
export function buildDiffReview(input) {
|
|
8
|
-
|
|
8
|
+
const root = input.root ?? process.cwd();
|
|
9
|
+
if (!isGitRepository(root)) {
|
|
9
10
|
return {
|
|
10
|
-
html: renderNotGitRepoHtml(
|
|
11
|
+
html: renderNotGitRepoHtml(root),
|
|
11
12
|
files: 0,
|
|
12
13
|
hunks: 0,
|
|
13
14
|
signature: "not-a-git-repo",
|
|
@@ -20,11 +21,12 @@ export function buildDiffReview(input) {
|
|
|
20
21
|
context: input.context,
|
|
21
22
|
includeUntracked: input.includeUntracked,
|
|
22
23
|
ignoreWhitespace: input.ignoreWhitespace,
|
|
24
|
+
root,
|
|
23
25
|
});
|
|
24
26
|
const files = parseUnifiedDiff(diffText);
|
|
25
|
-
const sourceFiles = collectSourceFiles(files);
|
|
27
|
+
const sourceFiles = collectSourceFiles(files, root);
|
|
26
28
|
const fileStates = collectReviewFileStates(files, sourceFiles);
|
|
27
|
-
const httpEnvironments = collectHttpEnvironments(
|
|
29
|
+
const httpEnvironments = collectHttpEnvironments(root);
|
|
28
30
|
const hunks = files.reduce((sum, file) => sum + file.hunks.length, 0);
|
|
29
31
|
const generatedAt = new Date().toISOString();
|
|
30
32
|
const diffHtml = renderDiff2Html(diffText);
|
|
@@ -57,8 +59,8 @@ export function buildDiffReview(input) {
|
|
|
57
59
|
httpEnvironments,
|
|
58
60
|
title: input.title,
|
|
59
61
|
subtitle: diffSubtitle(input),
|
|
60
|
-
projectName: basename(
|
|
61
|
-
projectPath:
|
|
62
|
+
projectName: basename(root),
|
|
63
|
+
projectPath: root,
|
|
62
64
|
watch: Boolean(input.watch),
|
|
63
65
|
ignoreWhitespace: Boolean(input.ignoreWhitespace),
|
|
64
66
|
app: Boolean(input.app),
|
package/dist/diff.d.ts
CHANGED
|
@@ -5,8 +5,9 @@ export declare function readUnifiedDiff(options: {
|
|
|
5
5
|
context: number;
|
|
6
6
|
includeUntracked: boolean;
|
|
7
7
|
ignoreWhitespace?: boolean;
|
|
8
|
+
root?: string;
|
|
8
9
|
}): string;
|
|
9
10
|
export declare function parseUnifiedDiff(content: string): DiffFile[];
|
|
10
|
-
export declare function collectSourceFiles(diffFiles: DiffFile[]): SourceFile[];
|
|
11
|
+
export declare function collectSourceFiles(diffFiles: DiffFile[], rootArg?: string): SourceFile[];
|
|
11
12
|
export declare function collectReviewFileStates(diffFiles: DiffFile[], sourceFiles: SourceFile[]): ReviewFileState[];
|
|
12
13
|
export declare function collectHttpEnvironments(root: string): Record<string, Record<string, string>>;
|
package/dist/diff.js
CHANGED
|
@@ -10,7 +10,7 @@ import { git, repoRoot } from "./git.js";
|
|
|
10
10
|
// IPC/pty. With it an unchanged file costs a single statSync — the per-tick cost collapses to stat-only.
|
|
11
11
|
const sourceContentCache = new Map();
|
|
12
12
|
export function readUnifiedDiff(options) {
|
|
13
|
-
const root = repoRoot();
|
|
13
|
+
const root = repoRoot(options.root);
|
|
14
14
|
const args = ["diff", "--no-ext-diff", "--find-renames", `--unified=${options.context}`];
|
|
15
15
|
if (options.ignoreWhitespace)
|
|
16
16
|
args.push("--ignore-all-space");
|
|
@@ -214,7 +214,7 @@ function gitStatusMap(cwd) {
|
|
|
214
214
|
}
|
|
215
215
|
return map;
|
|
216
216
|
}
|
|
217
|
-
export function collectSourceFiles(diffFiles) {
|
|
217
|
+
export function collectSourceFiles(diffFiles, rootArg) {
|
|
218
218
|
const changed = new Set(diffFiles
|
|
219
219
|
.map((file) => file.displayPath)
|
|
220
220
|
.filter((path) => path && path !== "/dev/null"));
|
|
@@ -231,7 +231,7 @@ export function collectSourceFiles(diffFiles) {
|
|
|
231
231
|
}
|
|
232
232
|
changedLinesByPath.set(file.displayPath, nums);
|
|
233
233
|
}
|
|
234
|
-
const root = repoRoot();
|
|
234
|
+
const root = repoRoot(rootArg);
|
|
235
235
|
const vcsByPath = gitStatusMap(root);
|
|
236
236
|
for (const file of diffFiles) {
|
|
237
237
|
const kind = vcsByPath.get(file.displayPath);
|
package/dist/i18n.js
CHANGED
|
@@ -20,6 +20,8 @@ export const MESSAGES = {
|
|
|
20
20
|
"terminal.title": "Terminal",
|
|
21
21
|
"terminal.toggle": "Toggle terminal (Ctrl+`)",
|
|
22
22
|
"terminal.close": "Close terminal",
|
|
23
|
+
"dock.maximize": "Maximize panel (Cmd/Ctrl+Shift+')",
|
|
24
|
+
"dock.restore": "Restore panel (Cmd/Ctrl+Shift+')",
|
|
23
25
|
// Review status (toolbar) — units; the numeric count stays dynamic and is prepended at runtime.
|
|
24
26
|
"status.files": "files",
|
|
25
27
|
"status.hunks": "hunks",
|
|
@@ -99,6 +101,7 @@ export const MESSAGES = {
|
|
|
99
101
|
"kbd.ignoreWhitespace": "Ignore whitespace",
|
|
100
102
|
"kbd.saveComment": "Save comment",
|
|
101
103
|
"kbd.promptMemo": "Prompt memo",
|
|
104
|
+
"kbd.maximizePanel": "Maximize panel (terminal / merged / memo)",
|
|
102
105
|
"kbd.toggleTerminal": "Toggle terminal",
|
|
103
106
|
"kbd.splitPane": "Split pane",
|
|
104
107
|
"kbd.focusPane": "Focus prev / next pane",
|
|
@@ -152,6 +155,8 @@ export const MESSAGES = {
|
|
|
152
155
|
"terminal.title": "터미널",
|
|
153
156
|
"terminal.toggle": "터미널 토글 (Ctrl+`)",
|
|
154
157
|
"terminal.close": "터미널 닫기",
|
|
158
|
+
"dock.maximize": "패널 최대화 (Cmd/Ctrl+Shift+')",
|
|
159
|
+
"dock.restore": "패널 복원 (Cmd/Ctrl+Shift+')",
|
|
155
160
|
// Review status (toolbar)
|
|
156
161
|
"status.files": "개 파일",
|
|
157
162
|
"status.hunks": "개 변경 묶음",
|
|
@@ -231,6 +236,7 @@ export const MESSAGES = {
|
|
|
231
236
|
"kbd.ignoreWhitespace": "공백 무시",
|
|
232
237
|
"kbd.saveComment": "코멘트 저장",
|
|
233
238
|
"kbd.promptMemo": "프롬프트 메모",
|
|
239
|
+
"kbd.maximizePanel": "패널 최대화 (터미널 / 합본 / 메모)",
|
|
234
240
|
"kbd.toggleTerminal": "터미널 토글",
|
|
235
241
|
"kbd.splitPane": "패널 분할",
|
|
236
242
|
"kbd.focusPane": "이전 / 다음 패널로 이동",
|
package/dist/preload.cjs
CHANGED
|
@@ -52,6 +52,13 @@ electron_1.contextBridge.exposeInMainWorld("monacoriFile", {
|
|
|
52
52
|
electron_1.contextBridge.exposeInMainWorld("monacoriUpdate", {
|
|
53
53
|
run: () => electron_1.ipcRenderer.invoke("monacori:self-update"),
|
|
54
54
|
});
|
|
55
|
+
// Packaged .app (double-clicked, no cwd repo): the welcome screen's "Open Folder" button asks the main
|
|
56
|
+
// process to show a directory picker and load that git repo's review.
|
|
57
|
+
electron_1.contextBridge.exposeInMainWorld("monacoriApp", {
|
|
58
|
+
openFolder: () => electron_1.ipcRenderer.invoke("monacori:open-folder"),
|
|
59
|
+
// Welcome screen's Recent Projects: open a remembered repo path in the current window.
|
|
60
|
+
openRecent: (path) => electron_1.ipcRenderer.invoke("monacori:open-recent", { path }),
|
|
61
|
+
});
|
|
55
62
|
// Integrated terminal: bridge the renderer's xterm view to a node-pty owned by the main process (the
|
|
56
63
|
// sandboxed renderer can't spawn a pty). Only present in the Electron app; browser/serve mode lacks it,
|
|
57
64
|
// so the renderer keeps the terminal panel hidden there.
|
package/dist/render.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { DiffFile, ReviewFileState, SourceFile } from "./types.js";
|
|
2
2
|
export declare function renderNotGitRepoHtml(root: string): string;
|
|
3
|
+
export declare function renderWelcomeHtml(light?: boolean, recent?: {
|
|
4
|
+
path: string;
|
|
5
|
+
name: string;
|
|
6
|
+
}[]): string;
|
|
3
7
|
export declare function shouldLazyRender(fileCount: number, totalLines: number): boolean;
|
|
4
8
|
export declare function splitDiffForLazy(diffHtml: string, files: DiffFile[]): {
|
|
5
9
|
container: string;
|
package/dist/render.js
CHANGED
|
@@ -43,6 +43,89 @@ export function renderNotGitRepoHtml(root) {
|
|
|
43
43
|
"</html>",
|
|
44
44
|
].join("\n");
|
|
45
45
|
}
|
|
46
|
+
// Welcome screen for the packaged .app (double-clicked, no cwd): an "Open Folder" button that asks the main
|
|
47
|
+
// process (window.monacoriApp.openFolder, exposed via preload) to pick a git repo and load its review.
|
|
48
|
+
export function renderWelcomeHtml(light = false, recent = []) {
|
|
49
|
+
const bg = light ? "#ffffff" : "#2b2b2b";
|
|
50
|
+
const fg = light ? "#1f2328" : "#a9b7c6";
|
|
51
|
+
// Recent projects (IntelliJ-style): one click reopens a previously reviewed repo. Each row carries its
|
|
52
|
+
// absolute path in data-path; the click handler hands it to monacoriApp.openRecent.
|
|
53
|
+
const recentItems = recent
|
|
54
|
+
.map((p) => `<button class="recent" type="button" data-path="${escapeAttr(p.path)}">` +
|
|
55
|
+
`<span class="recent-name">${escapeHtml(p.name)}</span>` +
|
|
56
|
+
`<span class="recent-path">${escapeHtml(p.path)}</span>` +
|
|
57
|
+
"</button>")
|
|
58
|
+
.join("");
|
|
59
|
+
const recentsBlock = recent.length
|
|
60
|
+
? `<div class="recents" id="recents"><div class="recents-title">Recent projects</div>${recentItems}</div>`
|
|
61
|
+
: "";
|
|
62
|
+
return [
|
|
63
|
+
"<!doctype html>",
|
|
64
|
+
'<html lang="en">',
|
|
65
|
+
"<head>",
|
|
66
|
+
'<meta charset="utf-8">',
|
|
67
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1">',
|
|
68
|
+
"<title>Monacori</title>",
|
|
69
|
+
"<style>",
|
|
70
|
+
"* { box-sizing: border-box; }",
|
|
71
|
+
`body { margin: 0; min-height: 100vh; display: grid; place-items: center; background: ${bg}; color: ${fg}; font-family: ui-sans-serif, system-ui, -apple-system, sans-serif; }`,
|
|
72
|
+
".card { width: 520px; max-width: calc(100vw - 48px); padding: 40px; text-align: center; }",
|
|
73
|
+
".card .badge { font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; color: #808080; }",
|
|
74
|
+
".card h1 { font-size: 24px; margin: 12px 0 14px; color: #4a88c7; }",
|
|
75
|
+
".card p { font-size: 14px; line-height: 1.7; margin: 10px 0; }",
|
|
76
|
+
".open-btn { margin-top: 22px; padding: 10px 24px; font-size: 14px; font-weight: 600; color: #fff; background: #4a88c7; border: 0; border-radius: 8px; cursor: pointer; }",
|
|
77
|
+
".open-btn:hover { background: #3f78b3; }",
|
|
78
|
+
".open-btn:disabled { opacity: 0.6; cursor: default; }",
|
|
79
|
+
".hint { color: #d36c6c; font-size: 12px; min-height: 16px; margin-top: 16px; }",
|
|
80
|
+
// Recent projects list: left-aligned rows below the Open Folder button.
|
|
81
|
+
".recents { margin-top: 28px; text-align: left; }",
|
|
82
|
+
".recents-title { font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: #808080; margin: 0 0 8px; padding: 0 4px; }",
|
|
83
|
+
".recent { display: block; width: 100%; text-align: left; padding: 9px 12px; border: 0; border-radius: 8px; background: transparent; color: inherit; cursor: pointer; font: inherit; }",
|
|
84
|
+
".recent:hover { background: rgba(74, 136, 199, 0.16); }",
|
|
85
|
+
".recent:disabled { opacity: 0.5; cursor: default; }",
|
|
86
|
+
".recent-name { display: block; font-size: 13px; font-weight: 600; }",
|
|
87
|
+
".recent-path { display: block; font-size: 11px; color: #808080; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }",
|
|
88
|
+
"</style>",
|
|
89
|
+
"</head>",
|
|
90
|
+
"<body>",
|
|
91
|
+
'<div class="card">',
|
|
92
|
+
'<div class="badge">monacori</div>',
|
|
93
|
+
"<h1>Review a Git repository</h1>",
|
|
94
|
+
"<p>Pick a folder under Git version control to review its changes.</p>",
|
|
95
|
+
'<button class="open-btn" id="open" type="button">Open Folder…</button>',
|
|
96
|
+
'<p class="hint" id="hint"></p>',
|
|
97
|
+
recentsBlock,
|
|
98
|
+
"</div>",
|
|
99
|
+
"<script>",
|
|
100
|
+
"var btn = document.getElementById('open'), hint = document.getElementById('hint');",
|
|
101
|
+
"btn.addEventListener('click', function () {",
|
|
102
|
+
" if (!(window.monacoriApp && window.monacoriApp.openFolder)) { hint.textContent = 'Open Folder is unavailable.'; return; }",
|
|
103
|
+
" btn.disabled = true; hint.textContent = '';",
|
|
104
|
+
" window.monacoriApp.openFolder().then(function (r) {",
|
|
105
|
+
" btn.disabled = false;",
|
|
106
|
+
" if (r && r.ok) return;",
|
|
107
|
+
" if (r && r.error === 'not-git') hint.textContent = 'That folder is not a Git repository.';",
|
|
108
|
+
" }).catch(function () { btn.disabled = false; });",
|
|
109
|
+
"});",
|
|
110
|
+
// Recent Projects: click a row to reopen that repo in this window. A removed/non-git folder drops out.
|
|
111
|
+
"var recents = document.getElementById('recents');",
|
|
112
|
+
"if (recents) recents.addEventListener('click', function (e) {",
|
|
113
|
+
" var item = e.target.closest ? e.target.closest('.recent') : null;",
|
|
114
|
+
" if (!item) return;",
|
|
115
|
+
" var path = item.getAttribute('data-path');",
|
|
116
|
+
" if (!path || !(window.monacoriApp && window.monacoriApp.openRecent)) return;",
|
|
117
|
+
" item.disabled = true; hint.textContent = '';",
|
|
118
|
+
" window.monacoriApp.openRecent(path).then(function (r) {",
|
|
119
|
+
" if (r && r.ok) return;",
|
|
120
|
+
" item.disabled = false;",
|
|
121
|
+
" if (r && r.error === 'missing') { item.remove(); hint.textContent = 'That project folder is no longer available.'; }",
|
|
122
|
+
" }).catch(function () { item.disabled = false; });",
|
|
123
|
+
"});",
|
|
124
|
+
"</script>",
|
|
125
|
+
"</body>",
|
|
126
|
+
"</html>",
|
|
127
|
+
].join("\n");
|
|
128
|
+
}
|
|
46
129
|
// Above a size threshold the diff is rendered "lazily": each file's heavy body
|
|
47
130
|
// (the side-by-side tables — hundreds of thousands of rows on big repos) is moved
|
|
48
131
|
// out of the live DOM into an inert <script type="text/html"> island, leaving only
|
|
@@ -211,6 +294,7 @@ export function renderDiffHtml(input) {
|
|
|
211
294
|
'<kbd>Cmd/Ctrl+Shift+W</kbd><span data-i18n="kbd.ignoreWhitespace">Ignore whitespace</span>' +
|
|
212
295
|
'<kbd>Cmd/Ctrl+Enter</kbd><span data-i18n="kbd.saveComment">Save comment</span>' +
|
|
213
296
|
'<kbd>Cmd/Ctrl+Shift+N</kbd><span data-i18n="kbd.promptMemo">Prompt memo</span>' +
|
|
297
|
+
'<kbd>Cmd/Ctrl+Shift+'</kbd><span data-i18n="kbd.maximizePanel">Maximize panel</span>' +
|
|
214
298
|
'</div>' +
|
|
215
299
|
'<div class="keys-cat" data-i18n="settings.kbd.cat.terminal">Terminal</div>' +
|
|
216
300
|
'<div class="keys-grid">' +
|