@happy-nut/monacori 0.1.22 → 0.1.23
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/dist/app-main.js +57 -1
- package/dist/git-log.d.ts +23 -0
- package/dist/git-log.js +60 -0
- package/dist/i18n.js +34 -0
- package/dist/preload.cjs +8 -0
- package/dist/render.js +20 -0
- package/dist/viewer.client.js +360 -0
- package/dist/viewer.client.min.js +1 -1
- package/dist/viewer.css +62 -7
- package/package.json +1 -1
package/dist/app-main.js
CHANGED
|
@@ -2,9 +2,10 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
3
|
import { basename, dirname, join, resolve } from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { app, BrowserWindow, dialog, ipcMain, Menu, nativeImage, Notification } from "electron";
|
|
5
|
+
import { app, BrowserWindow, dialog, ipcMain, Menu, nativeImage, Notification, shell } from "electron";
|
|
6
6
|
import { buildDiffReview, performHttpRequest } from "./cli.js";
|
|
7
7
|
import { sanitizeTerminalEnv, ensureUtf8Locale } from "./util.js";
|
|
8
|
+
import { readGitLog, readCommitDiff } from "./git-log.js";
|
|
8
9
|
import { readUnifiedDiff } from "./diff.js";
|
|
9
10
|
import { isGitRepository } from "./git.js";
|
|
10
11
|
import { renderWelcomeHtml } from "./render.js";
|
|
@@ -96,6 +97,61 @@ ipcMain.handle("monacori:get-file", (event, request) => {
|
|
|
96
97
|
});
|
|
97
98
|
// Phase 2b lazy-LOAD: serve the full source files JSON (with content) for the calling window on demand.
|
|
98
99
|
ipcMain.handle("monacori:get-source-data", (event) => stateFromEvent(event)?.sourceData ?? "[]");
|
|
100
|
+
// Git history view (Cmd+9): the log list and a single commit's full diff, for the calling window's repo.
|
|
101
|
+
ipcMain.handle("monacori:git-log", (event, request) => {
|
|
102
|
+
const state = stateFromEvent(event);
|
|
103
|
+
if (!state)
|
|
104
|
+
return [];
|
|
105
|
+
try {
|
|
106
|
+
return readGitLog(state.options.root, { limit: request?.limit, skip: request?.skip });
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
return [];
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
ipcMain.handle("monacori:git-commit-diff", (event, request) => {
|
|
113
|
+
const state = stateFromEvent(event);
|
|
114
|
+
if (!state || !request?.sha)
|
|
115
|
+
return null;
|
|
116
|
+
try {
|
|
117
|
+
return readCommitDiff(state.options.root, request.sha);
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
// Sidebar row actions (Opt+Enter menu): reveal a file in Finder / open a terminal at its directory.
|
|
124
|
+
// `path` is repo-root-relative (the tree's data-source-file / data-file), resolved against this window's root.
|
|
125
|
+
ipcMain.handle("monacori:reveal-in-finder", (event, request) => {
|
|
126
|
+
const state = stateFromEvent(event);
|
|
127
|
+
if (!state || !request?.path)
|
|
128
|
+
return { ok: false };
|
|
129
|
+
try {
|
|
130
|
+
shell.showItemInFolder(join(state.options.root, request.path));
|
|
131
|
+
return { ok: true };
|
|
132
|
+
}
|
|
133
|
+
catch (e) {
|
|
134
|
+
return { ok: false, error: String(e) };
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
ipcMain.handle("monacori:open-terminal-at", (event, request) => {
|
|
138
|
+
const state = stateFromEvent(event);
|
|
139
|
+
if (!state || !request?.path)
|
|
140
|
+
return { ok: false };
|
|
141
|
+
const dir = dirname(join(state.options.root, request.path)); // the file's containing directory
|
|
142
|
+
try {
|
|
143
|
+
if (process.platform === "darwin")
|
|
144
|
+
spawn("open", ["-a", "Terminal", dir], { detached: true, stdio: "ignore" }).unref();
|
|
145
|
+
else if (process.platform === "win32")
|
|
146
|
+
spawn("cmd", ["/c", "start", "cmd", "/k", `cd /d "${dir}"`], { detached: true, stdio: "ignore", shell: true }).unref();
|
|
147
|
+
else
|
|
148
|
+
spawn("x-terminal-emulator", [], { cwd: dir, detached: true, stdio: "ignore" }).unref();
|
|
149
|
+
return { ok: true };
|
|
150
|
+
}
|
|
151
|
+
catch (e) {
|
|
152
|
+
return { ok: false, error: String(e) };
|
|
153
|
+
}
|
|
154
|
+
});
|
|
99
155
|
// Welcome screen's "Open Folder" button: pick a directory; load it into the window that asked if it's a
|
|
100
156
|
// git repo, else return the "not-git" code so the welcome renderer can show its inline hint (it keys off
|
|
101
157
|
// r.error === "not-git"). This flow reports errors in-page, so — unlike the File menu — no native box.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type GitCommit = {
|
|
2
|
+
hash: string;
|
|
3
|
+
parents: string[];
|
|
4
|
+
author: string;
|
|
5
|
+
email: string;
|
|
6
|
+
date: string;
|
|
7
|
+
refs: string;
|
|
8
|
+
subject: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function readGitLog(root: string, options?: {
|
|
11
|
+
limit?: number;
|
|
12
|
+
skip?: number;
|
|
13
|
+
}): GitCommit[];
|
|
14
|
+
export declare function readCommitDiff(root: string, sha: string): {
|
|
15
|
+
hash: string;
|
|
16
|
+
author: string;
|
|
17
|
+
email: string;
|
|
18
|
+
date: string;
|
|
19
|
+
refs: string;
|
|
20
|
+
message: string;
|
|
21
|
+
diffHtml: string;
|
|
22
|
+
isMerge: boolean;
|
|
23
|
+
} | null;
|
package/dist/git-log.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { git } from "./git.js";
|
|
2
|
+
import { renderDiff2Html } from "./highlight.js";
|
|
3
|
+
// Field/record separators that can't occur in git output, so subjects with spaces/commas parse cleanly.
|
|
4
|
+
const FS = "\x1f";
|
|
5
|
+
const RS = "\x1e";
|
|
6
|
+
// Read up to `limit` commits (optionally skipping `skip`). Newest first — the order the graph walker expects.
|
|
7
|
+
export function readGitLog(root, options = {}) {
|
|
8
|
+
const limit = options.limit && options.limit > 0 ? options.limit : 200;
|
|
9
|
+
const args = [
|
|
10
|
+
"-c", "log.showSignature=false",
|
|
11
|
+
"log", "--no-color",
|
|
12
|
+
"--date=iso-strict",
|
|
13
|
+
`--pretty=format:%H${FS}%P${FS}%an${FS}%ae${FS}%ad${FS}%D${FS}%s${RS}`,
|
|
14
|
+
`-n`, String(limit),
|
|
15
|
+
];
|
|
16
|
+
if (options.skip && options.skip > 0)
|
|
17
|
+
args.push(`--skip=${options.skip}`);
|
|
18
|
+
const out = git(root, args);
|
|
19
|
+
if (!out)
|
|
20
|
+
return [];
|
|
21
|
+
return out
|
|
22
|
+
.split(RS)
|
|
23
|
+
.map((rec) => rec.replace(/^\n/, ""))
|
|
24
|
+
.filter((rec) => rec.trim().length > 0)
|
|
25
|
+
.map((rec) => {
|
|
26
|
+
const f = rec.split(FS);
|
|
27
|
+
return {
|
|
28
|
+
hash: f[0] || "",
|
|
29
|
+
parents: (f[1] || "").trim() ? f[1].trim().split(/\s+/) : [],
|
|
30
|
+
author: f[2] || "",
|
|
31
|
+
email: f[3] || "",
|
|
32
|
+
date: f[4] || "",
|
|
33
|
+
refs: f[5] || "",
|
|
34
|
+
subject: f[6] || "",
|
|
35
|
+
};
|
|
36
|
+
})
|
|
37
|
+
.filter((c) => c.hash);
|
|
38
|
+
}
|
|
39
|
+
// Full detail for one commit: metadata, full message body, and the rendered diff (diff2html HTML).
|
|
40
|
+
// Merge commits show no diff under plain `git show`; the renderer notes that case.
|
|
41
|
+
export function readCommitDiff(root, sha) {
|
|
42
|
+
if (!sha || !/^[0-9a-fA-F]{4,64}$/.test(sha))
|
|
43
|
+
return null; // guard: only a hash reaches `git`
|
|
44
|
+
const meta = git(root, ["show", "-s", `--pretty=format:%H${FS}%an${FS}%ae${FS}%ad${FS}%D${FS}%P${FS}%B`, "--date=iso-strict", sha]);
|
|
45
|
+
if (!meta)
|
|
46
|
+
return null;
|
|
47
|
+
const f = meta.split(FS);
|
|
48
|
+
const parents = (f[5] || "").trim() ? f[5].trim().split(/\s+/) : [];
|
|
49
|
+
const diffText = git(root, ["show", sha, "--no-color", "--pretty=format:"]).replace(/^\n+/, "");
|
|
50
|
+
return {
|
|
51
|
+
hash: f[0] || sha,
|
|
52
|
+
author: f[1] || "",
|
|
53
|
+
email: f[2] || "",
|
|
54
|
+
date: f[3] || "",
|
|
55
|
+
refs: f[4] || "",
|
|
56
|
+
message: (f[6] || "").trim(),
|
|
57
|
+
diffHtml: renderDiff2Html(diffText),
|
|
58
|
+
isMerge: parents.length > 1,
|
|
59
|
+
};
|
|
60
|
+
}
|
package/dist/i18n.js
CHANGED
|
@@ -19,6 +19,20 @@ export const MESSAGES = {
|
|
|
19
19
|
"rail.questions": "Questions",
|
|
20
20
|
"rail.changeRequests": "Change requests",
|
|
21
21
|
"rail.branch": "Current branch",
|
|
22
|
+
"rail.history": "History",
|
|
23
|
+
"history.title": "History",
|
|
24
|
+
"history.search": "Filter by message or author",
|
|
25
|
+
"history.close": "Close",
|
|
26
|
+
"history.empty": "No commits.",
|
|
27
|
+
"history.loading": "Loading…",
|
|
28
|
+
"history.selectCommit": "Select a commit to view its changes.",
|
|
29
|
+
"history.merge": "Merge commit — no single-parent diff to show.",
|
|
30
|
+
"history.noDiff": "No changes in this commit.",
|
|
31
|
+
"goto.placeholder": "Go to line…",
|
|
32
|
+
"goto.copied": "Copied",
|
|
33
|
+
"menu.copyPath": "Copy path",
|
|
34
|
+
"menu.revealFinder": "Reveal in Finder",
|
|
35
|
+
"menu.openTerminal": "Open terminal here",
|
|
22
36
|
// Sidebar footer / About
|
|
23
37
|
"sidebar.updateAvailable": "update available",
|
|
24
38
|
"about.title": "About monacori",
|
|
@@ -91,6 +105,9 @@ export const MESSAGES = {
|
|
|
91
105
|
"settings.kbd.cat.review": "Review",
|
|
92
106
|
"settings.kbd.cat.terminal": "Terminal",
|
|
93
107
|
// Settings — keyboard-shortcut labels (descriptions only; <kbd> key names stay literal)
|
|
108
|
+
"kbd.gotoLine": "Go to line",
|
|
109
|
+
"kbd.copyLocation": "Copy file:line",
|
|
110
|
+
"kbd.rowActions": "Sidebar file actions (path / Finder / terminal)",
|
|
94
111
|
"kbd.openFolder": "Open folder",
|
|
95
112
|
"kbd.openNewWindow": "Open in new window",
|
|
96
113
|
"kbd.openSettings": "Settings",
|
|
@@ -173,6 +190,20 @@ export const MESSAGES = {
|
|
|
173
190
|
"rail.questions": "질문",
|
|
174
191
|
"rail.changeRequests": "변경 요청",
|
|
175
192
|
"rail.branch": "현재 브랜치",
|
|
193
|
+
"rail.history": "히스토리",
|
|
194
|
+
"history.title": "히스토리",
|
|
195
|
+
"history.search": "메시지·작성자로 필터",
|
|
196
|
+
"history.close": "닫기",
|
|
197
|
+
"history.empty": "커밋이 없습니다.",
|
|
198
|
+
"history.loading": "불러오는 중…",
|
|
199
|
+
"history.selectCommit": "커밋을 선택하면 변경 내용이 표시됩니다.",
|
|
200
|
+
"history.merge": "머지 커밋 — 표시할 단일 부모 diff가 없습니다.",
|
|
201
|
+
"history.noDiff": "이 커밋에는 변경이 없습니다.",
|
|
202
|
+
"goto.placeholder": "이동할 줄 번호…",
|
|
203
|
+
"goto.copied": "복사함",
|
|
204
|
+
"menu.copyPath": "경로 복사",
|
|
205
|
+
"menu.revealFinder": "Finder에서 열기",
|
|
206
|
+
"menu.openTerminal": "여기서 터미널 열기",
|
|
176
207
|
"tab.changes.title": "변경사항 (⌘0)",
|
|
177
208
|
"tab.files.title": "파일 (⌘1)",
|
|
178
209
|
// Sidebar footer / About
|
|
@@ -247,6 +278,9 @@ export const MESSAGES = {
|
|
|
247
278
|
"settings.kbd.cat.review": "리뷰",
|
|
248
279
|
"settings.kbd.cat.terminal": "터미널",
|
|
249
280
|
// Settings — keyboard-shortcut labels
|
|
281
|
+
"kbd.gotoLine": "줄로 이동",
|
|
282
|
+
"kbd.copyLocation": "파일:줄 복사",
|
|
283
|
+
"kbd.rowActions": "사이드바 파일 작업 (경로 / Finder / 터미널)",
|
|
250
284
|
"kbd.openFolder": "폴더 열기",
|
|
251
285
|
"kbd.openNewWindow": "새 창에서 열기",
|
|
252
286
|
"kbd.openSettings": "설정",
|
package/dist/preload.cjs
CHANGED
|
@@ -47,6 +47,11 @@ electron_1.contextBridge.exposeInMainWorld("monacoriFile", {
|
|
|
47
47
|
get: (index, kind) => electron_1.ipcRenderer.invoke("monacori:get-file", { index, kind }),
|
|
48
48
|
getSourceData: () => electron_1.ipcRenderer.invoke("monacori:get-source-data"),
|
|
49
49
|
});
|
|
50
|
+
// Git history view (Cmd+9): list commits and fetch one commit's full diff for the current window's repo.
|
|
51
|
+
electron_1.contextBridge.exposeInMainWorld("monacoriGit", {
|
|
52
|
+
log: (request) => electron_1.ipcRenderer.invoke("monacori:git-log", request),
|
|
53
|
+
commitDiff: (sha) => electron_1.ipcRenderer.invoke("monacori:git-commit-diff", { sha }),
|
|
54
|
+
});
|
|
50
55
|
// Self-update: ask the main process to install the latest version globally and relaunch. Only present
|
|
51
56
|
// in the Electron app (not browser/watch mode), so the renderer hides the in-app update button there.
|
|
52
57
|
electron_1.contextBridge.exposeInMainWorld("monacoriUpdate", {
|
|
@@ -58,6 +63,9 @@ electron_1.contextBridge.exposeInMainWorld("monacoriApp", {
|
|
|
58
63
|
openFolder: () => electron_1.ipcRenderer.invoke("monacori:open-folder"),
|
|
59
64
|
// Welcome screen's Recent Projects: open a remembered repo path in the current window.
|
|
60
65
|
openRecent: (path) => electron_1.ipcRenderer.invoke("monacori:open-recent", { path }),
|
|
66
|
+
// Sidebar Opt+Enter menu: reveal a file in Finder, or open a terminal at its directory.
|
|
67
|
+
revealInFinder: (path) => electron_1.ipcRenderer.invoke("monacori:reveal-in-finder", { path }),
|
|
68
|
+
openTerminalAt: (path) => electron_1.ipcRenderer.invoke("monacori:open-terminal-at", { path }),
|
|
61
69
|
});
|
|
62
70
|
// Integrated terminal: bridge the renderer's xterm view to a node-pty owned by the main process (the
|
|
63
71
|
// sandboxed renderer can't spawn a pty). Only present in the Electron app; browser/serve mode lacks it,
|
package/dist/render.js
CHANGED
|
@@ -197,6 +197,10 @@ export function renderDiffHtml(input) {
|
|
|
197
197
|
railButton("memo", "memo.title", "Prompt memo", "⌘⇧N", '<rect x="5.5" y="4" width="13" height="16" rx="1.5"/><line x1="8.5" y1="9" x2="15.5" y2="9"/><line x1="8.5" y1="12.5" x2="15.5" y2="12.5"/><line x1="8.5" y1="16" x2="12.5" y2="16"/>'),
|
|
198
198
|
"</div>",
|
|
199
199
|
'<div class="rail-group rail-bottom">',
|
|
200
|
+
// History (Cmd+9): Electron only — the git-log bridge (window.monacoriGit) is exposed there.
|
|
201
|
+
input.app
|
|
202
|
+
? railButton("history", "rail.history", "History", "⌘9", '<circle cx="12" cy="12" r="8.3"/><path d="M12 7.4v5l3.2 1.9"/>')
|
|
203
|
+
: "",
|
|
200
204
|
// Terminal (Electron only; #terminal-toggle stays hidden until a pty exists). Same id → the existing
|
|
201
205
|
// toggle handler + is-active sync in dock-terminal.js bind to it unchanged.
|
|
202
206
|
input.app
|
|
@@ -301,6 +305,9 @@ export function renderDiffHtml(input) {
|
|
|
301
305
|
'<kbd>⌘O</kbd><span data-i18n="kbd.openFolder">Open folder</span>' +
|
|
302
306
|
'<kbd>⌘⇧O</kbd><span data-i18n="kbd.openNewWindow">Open in new window</span>' +
|
|
303
307
|
'<kbd>⌘,</kbd><span data-i18n="kbd.openSettings">Settings</span>' +
|
|
308
|
+
'<kbd>⌘L</kbd><span data-i18n="kbd.gotoLine">Go to line</span>' +
|
|
309
|
+
'<kbd>⌘K</kbd><span data-i18n="kbd.copyLocation">Copy file:line</span>' +
|
|
310
|
+
'<kbd>⌥Enter</kbd><span data-i18n="kbd.rowActions">Sidebar file actions (path / Finder / terminal)</span>' +
|
|
304
311
|
'<kbd>Esc</kbd><span data-i18n="kbd.closeDialog">Close dialog / cancel</span>' +
|
|
305
312
|
'</div>' +
|
|
306
313
|
'<div class="keys-cat" data-i18n="settings.kbd.cat.nav">Navigation</div>' +
|
|
@@ -359,6 +366,19 @@ export function renderDiffHtml(input) {
|
|
|
359
366
|
"</div>",
|
|
360
367
|
"</div>",
|
|
361
368
|
"</div>",
|
|
369
|
+
// Git history (Cmd+9): full-screen overlay — commit list (with graph lanes) on the left, the selected
|
|
370
|
+
// commit's message + diff on the right. Populated lazily by the renderer from window.monacoriGit.
|
|
371
|
+
'<div id="history-view" class="history-view hidden" role="dialog" aria-modal="true" data-i18n-aria="history.title" aria-label="Git history">',
|
|
372
|
+
'<div class="history-bar">',
|
|
373
|
+
'<span class="history-title" data-i18n="history.title">History</span>',
|
|
374
|
+
'<input id="history-search" type="search" class="history-search" autocomplete="off" spellcheck="false" data-i18n-ph="history.search" placeholder="Filter by message or author">',
|
|
375
|
+
'<button type="button" id="history-close" class="dock-btn" data-i18n-title="history.close" title="Close" aria-label="Close">×</button>',
|
|
376
|
+
"</div>",
|
|
377
|
+
'<div class="history-body">',
|
|
378
|
+
'<div id="history-list" class="history-list"></div>',
|
|
379
|
+
'<div id="history-detail" class="history-detail"></div>',
|
|
380
|
+
"</div>",
|
|
381
|
+
"</div>",
|
|
362
382
|
input.diffIslands || "",
|
|
363
383
|
`<script type="application/json" id="review-meta" data-watch="${input.watch ? "true" : "false"}" data-signature="${escapeAttr(input.signature ?? "")}" data-generated-at="${escapeAttr(input.generatedAt ?? "")}" data-lazy="${input.lazy ? "true" : "false"}" data-lazy-load="${input.lazyLoad ? "true" : "false"}">{}</script>`,
|
|
364
384
|
`<script type="application/json" id="i18n-data">${jsonForScript(MESSAGES)}</script>`,
|