@happy-nut/monacori 0.1.2 → 0.1.3
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/README.md +44 -132
- package/assets/screenshots/diff-review.png +0 -0
- package/assets/screenshots/terminal.png +0 -0
- package/dist/app-main.js +163 -12
- package/dist/assets.d.ts +2 -0
- package/dist/assets.js +21 -0
- package/dist/build.d.ts +1 -0
- package/dist/build.js +7 -4
- package/dist/diff.js +41 -0
- package/dist/i18n.d.ts +1 -0
- package/dist/i18n.js +256 -0
- package/dist/preload.cjs +61 -0
- package/dist/render.d.ts +1 -0
- package/dist/render.js +91 -19
- package/dist/types.d.ts +2 -0
- package/dist/viewer.client.js +693 -101
- package/dist/viewer.css +194 -39
- package/package.json +6 -2
- package/scripts/patch-electron-name.mjs +8 -0
package/dist/i18n.js
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
// UI message catalog for the live English / Korean switch.
|
|
2
|
+
//
|
|
3
|
+
// The viewer ships both languages and switches client-side with NO reload: every translatable
|
|
4
|
+
// server-rendered element in render.ts carries data-i18n (textContent), data-i18n-ph (placeholder),
|
|
5
|
+
// data-i18n-title (title) or data-i18n-aria (aria-label); applyI18n() in viewer.client.js rewrites
|
|
6
|
+
// them, and t(key) feeds the dynamically-built UI. English is the first-paint default.
|
|
7
|
+
//
|
|
8
|
+
// Keys are stable + dot-namespaced. Excluded by design (NOT translated): diff/code content, file
|
|
9
|
+
// paths, syntax-language names, the "monacori" brand, version strings, and literal <kbd> key names
|
|
10
|
+
// (F7, Cmd/Ctrl+B, …). Korean is written for Korean developers — natural, with common technical
|
|
11
|
+
// terms left readable (커밋, 탭, 인덱스 …) rather than force-translated.
|
|
12
|
+
export const MESSAGES = {
|
|
13
|
+
en: {
|
|
14
|
+
// Tabs (sidebar)
|
|
15
|
+
"tab.changes": "Changes",
|
|
16
|
+
"tab.files": "Files",
|
|
17
|
+
// Sidebar footer / About
|
|
18
|
+
"sidebar.updateAvailable": "update available",
|
|
19
|
+
"about.title": "About monacori",
|
|
20
|
+
"terminal.title": "Terminal",
|
|
21
|
+
"terminal.toggle": "Toggle terminal (Ctrl+`)",
|
|
22
|
+
"terminal.close": "Close terminal",
|
|
23
|
+
// Review status (toolbar) — units; the numeric count stays dynamic and is prepended at runtime.
|
|
24
|
+
"status.files": "files",
|
|
25
|
+
"status.hunks": "hunks",
|
|
26
|
+
"status.wsIgnored": "ws ignored",
|
|
27
|
+
"status.wsIgnored.title": "Whitespace ignored — Cmd/Ctrl+Shift+W",
|
|
28
|
+
"status.indexed": "indexed",
|
|
29
|
+
"status.index.title": "Go-to-definition index",
|
|
30
|
+
"status.indexing": "indexing",
|
|
31
|
+
"status.watching": "watching",
|
|
32
|
+
"status.live.updated": "Live: updated",
|
|
33
|
+
"status.live.waiting": "Live: waiting for diff server",
|
|
34
|
+
// Diff view
|
|
35
|
+
"btn.viewed": "Viewed",
|
|
36
|
+
"btn.viewed.title": "Toggle viewed (<)",
|
|
37
|
+
"diff.noDiff": "No diff to review.",
|
|
38
|
+
// Source toolbar
|
|
39
|
+
"source.title": "Source",
|
|
40
|
+
"source.selectFile": "Select a file from the Files tab.",
|
|
41
|
+
"http.env.title": "HTTP Client environment",
|
|
42
|
+
"http.env.aria": "HTTP environment",
|
|
43
|
+
"btn.diff": "Diff",
|
|
44
|
+
"source.loading": "Loading source…",
|
|
45
|
+
"source.previewUnavailable": "Source preview unavailable.",
|
|
46
|
+
"source.viewRaw": "Raw",
|
|
47
|
+
"source.viewRendered": "Rendered",
|
|
48
|
+
"source.buildingTree": "Building file tree…",
|
|
49
|
+
// Quick open
|
|
50
|
+
"quickopen.aria": "Quick open",
|
|
51
|
+
"quickopen.searchFiles": "Search files",
|
|
52
|
+
"quickopen.recent": "Recent files",
|
|
53
|
+
"quickopen.findInFiles": "Find in Files",
|
|
54
|
+
"quickopen.noFiles": "No files found.",
|
|
55
|
+
// Usages
|
|
56
|
+
"usages.aria": "Usages",
|
|
57
|
+
"usages.title": "Usages",
|
|
58
|
+
// Settings — nav
|
|
59
|
+
"settings.aria": "Settings",
|
|
60
|
+
"settings.title": "Settings",
|
|
61
|
+
"settings.cat.general": "General",
|
|
62
|
+
"settings.cat.prompts": "Merge prompts",
|
|
63
|
+
// Settings — General
|
|
64
|
+
"settings.language": "Language",
|
|
65
|
+
"settings.checkingUpdates": "Checking for updates…",
|
|
66
|
+
"settings.updateRestart": "Update & Restart",
|
|
67
|
+
"settings.upToDate": "Up to date",
|
|
68
|
+
"settings.updateAvailable": "Update available",
|
|
69
|
+
"settings.updating": "Updating… installing latest, the app will restart",
|
|
70
|
+
"settings.updated": "Updated. Restarting…",
|
|
71
|
+
"settings.updateFailed": "Update failed — try again, or run: npm i -g @happy-nut/monacori",
|
|
72
|
+
"settings.kbd.title": "Keyboard shortcuts",
|
|
73
|
+
"settings.kbd.cat.nav": "Navigation",
|
|
74
|
+
"settings.kbd.cat.review": "Review",
|
|
75
|
+
"settings.kbd.cat.terminal": "Terminal",
|
|
76
|
+
// Settings — keyboard-shortcut labels (descriptions only; <kbd> key names stay literal)
|
|
77
|
+
"kbd.nextChange": "Next change",
|
|
78
|
+
"kbd.prevChange": "Previous change",
|
|
79
|
+
"kbd.closeTab": "Close tab",
|
|
80
|
+
"kbd.prevNextTab": "Prev / next tab",
|
|
81
|
+
"kbd.cursorBackForward": "Cursor back / forward",
|
|
82
|
+
"kbd.findFile": "Find file",
|
|
83
|
+
"kbd.findInFiles": "Find in files",
|
|
84
|
+
"kbd.recentFiles": "Recent files",
|
|
85
|
+
"kbd.defUsages": "Definition / usages",
|
|
86
|
+
"kbd.goToDef": "Go to definition",
|
|
87
|
+
"kbd.filesChangesTab": "Files / Changes tab",
|
|
88
|
+
"kbd.sidebarContent": "Sidebar ↔ content",
|
|
89
|
+
"kbd.wordJump": "Word jump (vim w)",
|
|
90
|
+
"kbd.lineStartEnd": "Line start / end",
|
|
91
|
+
"kbd.extendSelection": "Extend selection",
|
|
92
|
+
"kbd.toggleViewed": "Toggle viewed",
|
|
93
|
+
"kbd.addQuestionChange": "Add question / change",
|
|
94
|
+
"kbd.allQuestionsChanges": "All questions / changes",
|
|
95
|
+
"kbd.ignoreWhitespace": "Ignore whitespace",
|
|
96
|
+
"kbd.saveComment": "Save comment",
|
|
97
|
+
"kbd.toggleTerminal": "Toggle terminal",
|
|
98
|
+
"kbd.splitPane": "Split pane",
|
|
99
|
+
"kbd.focusPane": "Focus prev / next pane",
|
|
100
|
+
"kbd.renamePane": "Rename pane",
|
|
101
|
+
"kbd.closeTerminal": "Close terminal (when focused)",
|
|
102
|
+
// Settings — Merge prompts
|
|
103
|
+
"mergePrompts.title": "Merge prompts",
|
|
104
|
+
"mergePrompts.desc": "Heading prepended to the merged prompt opened with Cmd/Ctrl+Shift+/ (questions) and Cmd/Ctrl+Shift+. (change requests). Leave blank to use the default.",
|
|
105
|
+
"mergePrompts.qHeading": "Questions heading",
|
|
106
|
+
"mergePrompts.cHeading": "Change-requests heading",
|
|
107
|
+
"mergePrompts.reset": "Reset to defaults",
|
|
108
|
+
"settings.saved": "Saved",
|
|
109
|
+
// Composer (per-line question / change-request)
|
|
110
|
+
"composer.question": "Ask a question about this line",
|
|
111
|
+
"composer.changeRequest": "Request a change for this line",
|
|
112
|
+
"composer.save": "Comment",
|
|
113
|
+
"composer.cancel": "Cancel",
|
|
114
|
+
"composer.hint": "Cmd/Ctrl+Enter to save, Esc to cancel",
|
|
115
|
+
"composer.delete": "Delete",
|
|
116
|
+
"comment.kind.q": "❓ Question",
|
|
117
|
+
"comment.kind.c": "✎ Change request",
|
|
118
|
+
"badge.questions": "question(s)",
|
|
119
|
+
"badge.changeRequests": "change request(s)",
|
|
120
|
+
// Merged comments modal
|
|
121
|
+
"merged.qTitle": "Question comments",
|
|
122
|
+
"merged.cTitle": "Change-request comments",
|
|
123
|
+
"merged.copyAll": "Copy all",
|
|
124
|
+
"merged.sendToTerminal": "Send to terminal",
|
|
125
|
+
"merged.copied": "Copied",
|
|
126
|
+
"merged.copyFailed": "Copy failed",
|
|
127
|
+
"merged.close": "Close",
|
|
128
|
+
"merged.qHeading": "# Questions",
|
|
129
|
+
"merged.cHeading": "# Change requests",
|
|
130
|
+
// Merge-prompt default agent contracts (these follow the locale — a Korean user gets Korean defaults)
|
|
131
|
+
"mergePrompt.default.q": "The following are questions about code you just wrote. Answer each one — explain the intent, rationale, or context. Do not change any code; this clarifies understanding before any revisions.",
|
|
132
|
+
"mergePrompt.default.c": "The following are change requests for code you just wrote. For each, edit the code at the quoted location to satisfy the request. Keep changes minimal and focused; do not make unrelated edits.",
|
|
133
|
+
},
|
|
134
|
+
ko: {
|
|
135
|
+
// Tabs (sidebar)
|
|
136
|
+
"tab.changes": "변경사항",
|
|
137
|
+
"tab.files": "파일",
|
|
138
|
+
// Sidebar footer / About
|
|
139
|
+
"sidebar.updateAvailable": "업데이트 있음",
|
|
140
|
+
"about.title": "monacori 정보",
|
|
141
|
+
"terminal.title": "터미널",
|
|
142
|
+
"terminal.toggle": "터미널 토글 (Ctrl+`)",
|
|
143
|
+
"terminal.close": "터미널 닫기",
|
|
144
|
+
// Review status (toolbar)
|
|
145
|
+
"status.files": "개 파일",
|
|
146
|
+
"status.hunks": "개 변경 묶음",
|
|
147
|
+
"status.wsIgnored": "공백 무시",
|
|
148
|
+
"status.wsIgnored.title": "공백 무시 — Cmd/Ctrl+Shift+W",
|
|
149
|
+
"status.indexed": "개 인덱싱됨",
|
|
150
|
+
"status.index.title": "정의로 이동 인덱스",
|
|
151
|
+
"status.indexing": "인덱싱 중",
|
|
152
|
+
"status.watching": "감시 중",
|
|
153
|
+
"status.live.updated": "실시간: 업데이트됨",
|
|
154
|
+
"status.live.waiting": "실시간: diff 서버 대기 중",
|
|
155
|
+
// Diff view
|
|
156
|
+
"btn.viewed": "확인함",
|
|
157
|
+
"btn.viewed.title": "확인 표시 토글 (<)",
|
|
158
|
+
"diff.noDiff": "검토할 변경사항이 없습니다.",
|
|
159
|
+
// Source toolbar
|
|
160
|
+
"source.title": "소스",
|
|
161
|
+
"source.selectFile": "파일 탭에서 파일을 선택하세요.",
|
|
162
|
+
"http.env.title": "HTTP 클라이언트 환경",
|
|
163
|
+
"http.env.aria": "HTTP 환경",
|
|
164
|
+
"btn.diff": "Diff",
|
|
165
|
+
"source.loading": "소스 불러오는 중…",
|
|
166
|
+
"source.previewUnavailable": "소스 미리보기를 사용할 수 없습니다.",
|
|
167
|
+
"source.viewRaw": "원문",
|
|
168
|
+
"source.viewRendered": "렌더링",
|
|
169
|
+
"source.buildingTree": "파일 트리 만드는 중…",
|
|
170
|
+
// Quick open
|
|
171
|
+
"quickopen.aria": "빠른 열기",
|
|
172
|
+
"quickopen.searchFiles": "파일 검색",
|
|
173
|
+
"quickopen.recent": "최근 파일",
|
|
174
|
+
"quickopen.findInFiles": "파일 내용 검색",
|
|
175
|
+
"quickopen.noFiles": "파일을 찾을 수 없습니다.",
|
|
176
|
+
// Usages
|
|
177
|
+
"usages.aria": "사용처",
|
|
178
|
+
"usages.title": "사용처",
|
|
179
|
+
// Settings — nav
|
|
180
|
+
"settings.aria": "설정",
|
|
181
|
+
"settings.title": "설정",
|
|
182
|
+
"settings.cat.general": "일반",
|
|
183
|
+
"settings.cat.prompts": "병합 프롬프트",
|
|
184
|
+
// Settings — General
|
|
185
|
+
"settings.language": "언어",
|
|
186
|
+
"settings.checkingUpdates": "업데이트 확인 중…",
|
|
187
|
+
"settings.updateRestart": "업데이트 후 재시작",
|
|
188
|
+
"settings.upToDate": "최신 버전입니다",
|
|
189
|
+
"settings.updateAvailable": "업데이트 있음",
|
|
190
|
+
"settings.updating": "업데이트 중… 최신 버전을 설치하면 앱이 재시작됩니다",
|
|
191
|
+
"settings.updated": "업데이트 완료. 재시작 중…",
|
|
192
|
+
"settings.updateFailed": "업데이트 실패 — 다시 시도하거나 실행하세요: npm i -g @happy-nut/monacori",
|
|
193
|
+
"settings.kbd.title": "키보드 단축키",
|
|
194
|
+
"settings.kbd.cat.nav": "탐색",
|
|
195
|
+
"settings.kbd.cat.review": "리뷰",
|
|
196
|
+
"settings.kbd.cat.terminal": "터미널",
|
|
197
|
+
// Settings — keyboard-shortcut labels
|
|
198
|
+
"kbd.nextChange": "다음 변경",
|
|
199
|
+
"kbd.prevChange": "이전 변경",
|
|
200
|
+
"kbd.closeTab": "탭 닫기",
|
|
201
|
+
"kbd.prevNextTab": "이전 / 다음 탭",
|
|
202
|
+
"kbd.cursorBackForward": "커서 뒤로 / 앞으로",
|
|
203
|
+
"kbd.findFile": "파일 찾기",
|
|
204
|
+
"kbd.findInFiles": "파일 내용 찾기",
|
|
205
|
+
"kbd.recentFiles": "최근 파일",
|
|
206
|
+
"kbd.defUsages": "정의 / 사용처",
|
|
207
|
+
"kbd.goToDef": "정의로 이동",
|
|
208
|
+
"kbd.filesChangesTab": "파일 / 변경사항 탭",
|
|
209
|
+
"kbd.sidebarContent": "사이드바 ↔ 본문",
|
|
210
|
+
"kbd.wordJump": "단어 단위 이동 (vim w)",
|
|
211
|
+
"kbd.lineStartEnd": "줄 시작 / 끝",
|
|
212
|
+
"kbd.extendSelection": "선택 영역 확장",
|
|
213
|
+
"kbd.toggleViewed": "확인 표시 토글",
|
|
214
|
+
"kbd.addQuestionChange": "질문 / 변경요청 추가",
|
|
215
|
+
"kbd.allQuestionsChanges": "전체 질문 / 변경요청",
|
|
216
|
+
"kbd.ignoreWhitespace": "공백 무시",
|
|
217
|
+
"kbd.saveComment": "코멘트 저장",
|
|
218
|
+
"kbd.toggleTerminal": "터미널 토글",
|
|
219
|
+
"kbd.splitPane": "패널 분할",
|
|
220
|
+
"kbd.focusPane": "이전 / 다음 패널로 이동",
|
|
221
|
+
"kbd.renamePane": "패널 이름 변경",
|
|
222
|
+
"kbd.closeTerminal": "터미널 닫기 (포커스 시)",
|
|
223
|
+
// Settings — Merge prompts
|
|
224
|
+
"mergePrompts.title": "병합 프롬프트",
|
|
225
|
+
"mergePrompts.desc": "Cmd/Ctrl+Shift+/ (질문) 및 Cmd/Ctrl+Shift+. (변경요청)로 여는 병합 프롬프트 맨 앞에 붙는 머리말입니다. 비워 두면 기본값을 사용합니다.",
|
|
226
|
+
"mergePrompts.qHeading": "질문 머리말",
|
|
227
|
+
"mergePrompts.cHeading": "변경요청 머리말",
|
|
228
|
+
"mergePrompts.reset": "기본값으로 초기화",
|
|
229
|
+
"settings.saved": "저장됨",
|
|
230
|
+
// Composer
|
|
231
|
+
"composer.question": "이 줄에 대해 질문하기",
|
|
232
|
+
"composer.changeRequest": "이 줄에 대한 변경 요청하기",
|
|
233
|
+
"composer.save": "코멘트",
|
|
234
|
+
"composer.cancel": "취소",
|
|
235
|
+
"composer.hint": "Cmd/Ctrl+Enter로 저장, Esc로 취소",
|
|
236
|
+
"composer.delete": "삭제",
|
|
237
|
+
"comment.kind.q": "❓ 질문",
|
|
238
|
+
"comment.kind.c": "✎ 변경 요청",
|
|
239
|
+
"badge.questions": "개 질문",
|
|
240
|
+
"badge.changeRequests": "개 변경 요청",
|
|
241
|
+
// Merged comments modal
|
|
242
|
+
"merged.qTitle": "질문 코멘트",
|
|
243
|
+
"merged.cTitle": "변경 요청 코멘트",
|
|
244
|
+
"merged.copyAll": "전체 복사",
|
|
245
|
+
"merged.sendToTerminal": "터미널로 전송",
|
|
246
|
+
"merged.copied": "복사됨",
|
|
247
|
+
"merged.copyFailed": "복사 실패",
|
|
248
|
+
"merged.close": "닫기",
|
|
249
|
+
// Structural markers stay English in both locales (the preamble prose below follows the locale).
|
|
250
|
+
"merged.qHeading": "# Questions",
|
|
251
|
+
"merged.cHeading": "# Change requests",
|
|
252
|
+
// Merge-prompt default agent contracts (Korean default for Korean users)
|
|
253
|
+
"mergePrompt.default.q": "다음은 방금 작성한 코드에 대한 질문입니다. 각 질문에 답하면서 의도, 근거, 맥락을 설명하세요. 코드는 변경하지 마세요. 이 단계는 수정에 앞서 이해를 명확히 하기 위한 것입니다.",
|
|
254
|
+
"mergePrompt.default.c": "다음은 방금 작성한 코드에 대한 변경 요청입니다. 각 요청에 대해 인용된 위치의 코드를 수정하여 요구사항을 충족하세요. 변경은 최소한으로 집중해서 하고, 관련 없는 수정은 하지 마세요.",
|
|
255
|
+
},
|
|
256
|
+
};
|
package/dist/preload.cjs
CHANGED
|
@@ -13,6 +13,24 @@ electron_1.contextBridge.exposeInMainWorld("monacoriMenu", {
|
|
|
13
13
|
onMergedView: (cb) => {
|
|
14
14
|
electron_1.ipcRenderer.on("monacori:merged-view", (_event, kind) => cb(kind));
|
|
15
15
|
},
|
|
16
|
+
// Cmd/Ctrl+W from the Window menu -> close the active Files-mode tab in the renderer.
|
|
17
|
+
onCloseTab: (cb) => {
|
|
18
|
+
electron_1.ipcRenderer.on("monacori:close-tab", () => cb());
|
|
19
|
+
},
|
|
20
|
+
// Terminal menu accelerators (Ctrl+`/Alt+F12 toggle, Cmd+D split) — routed via the menu because
|
|
21
|
+
// Chromium swallows Cmd+D before it reaches the renderer's keydown handler.
|
|
22
|
+
onTerminalToggle: (cb) => {
|
|
23
|
+
electron_1.ipcRenderer.on("monacori:terminal-toggle", () => cb());
|
|
24
|
+
},
|
|
25
|
+
onTerminalSplit: (cb) => {
|
|
26
|
+
electron_1.ipcRenderer.on("monacori:terminal-split", () => cb());
|
|
27
|
+
},
|
|
28
|
+
onTerminalPaneFocus: (cb) => {
|
|
29
|
+
electron_1.ipcRenderer.on("monacori:terminal-pane-focus", (_event, delta) => cb(delta));
|
|
30
|
+
},
|
|
31
|
+
onTerminalPaneRename: (cb) => {
|
|
32
|
+
electron_1.ipcRenderer.on("monacori:terminal-pane-rename", () => cb());
|
|
33
|
+
},
|
|
16
34
|
});
|
|
17
35
|
// Phase 2 lazy-LOAD: fetch a single file's diff body from the main process on demand, so the initial
|
|
18
36
|
// HTML can omit the embedded diff bodies (tens of MB on big repos) and stay small.
|
|
@@ -20,3 +38,46 @@ electron_1.contextBridge.exposeInMainWorld("monacoriFile", {
|
|
|
20
38
|
get: (index, kind) => electron_1.ipcRenderer.invoke("monacori:get-file", { index, kind }),
|
|
21
39
|
getSourceData: () => electron_1.ipcRenderer.invoke("monacori:get-source-data"),
|
|
22
40
|
});
|
|
41
|
+
// Self-update: ask the main process to install the latest version globally and relaunch. Only present
|
|
42
|
+
// in the Electron app (not browser/watch mode), so the renderer hides the in-app update button there.
|
|
43
|
+
electron_1.contextBridge.exposeInMainWorld("monacoriUpdate", {
|
|
44
|
+
run: () => electron_1.ipcRenderer.invoke("monacori:self-update"),
|
|
45
|
+
});
|
|
46
|
+
// Integrated terminal: bridge the renderer's xterm view to a node-pty owned by the main process (the
|
|
47
|
+
// sandboxed renderer can't spawn a pty). Only present in the Electron app; browser/serve mode lacks it,
|
|
48
|
+
// so the renderer keeps the terminal panel hidden there.
|
|
49
|
+
electron_1.contextBridge.exposeInMainWorld("monacoriPty", {
|
|
50
|
+
spawn: (size) => electron_1.ipcRenderer.invoke("monacori:pty-spawn", size),
|
|
51
|
+
write: (msg) => electron_1.ipcRenderer.send("monacori:pty-write", msg),
|
|
52
|
+
resize: (msg) => electron_1.ipcRenderer.send("monacori:pty-resize", msg),
|
|
53
|
+
kill: (msg) => electron_1.ipcRenderer.send("monacori:pty-kill", msg),
|
|
54
|
+
onData: (cb) => {
|
|
55
|
+
electron_1.ipcRenderer.on("monacori:pty-data", (_event, msg) => cb(msg));
|
|
56
|
+
},
|
|
57
|
+
onExit: (cb) => {
|
|
58
|
+
electron_1.ipcRenderer.on("monacori:pty-exit", (_event, msg) => cb(msg));
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
// Global settings (locale, …) persisted by the main process under userData so they survive app
|
|
62
|
+
// restarts — the renderer's file:// localStorage is not reliably persisted across reopens. `all` is
|
|
63
|
+
// read synchronously at preload so the renderer can pick the locale before first paint; `set` writes
|
|
64
|
+
// asynchronously. Only present in the Electron app; browser/serve mode falls back to localStorage.
|
|
65
|
+
const persistedSettings = (() => {
|
|
66
|
+
try {
|
|
67
|
+
return electron_1.ipcRenderer.sendSync("monacori:get-settings") || {};
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
72
|
+
})();
|
|
73
|
+
electron_1.contextBridge.exposeInMainWorld("monacoriSettings", {
|
|
74
|
+
all: persistedSettings,
|
|
75
|
+
set: (key, value) => {
|
|
76
|
+
try {
|
|
77
|
+
electron_1.ipcRenderer.send("monacori:set-setting", { key, value });
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
/* noop */
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
});
|
package/dist/render.d.ts
CHANGED
package/dist/render.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { escapeAttr, escapeHtml, jsonForScript } from "./util.js";
|
|
3
|
-
import { diff2HtmlCss, diffCss, diffScript } from "./assets.js";
|
|
3
|
+
import { diff2HtmlCss, diffCss, diffScript, xtermCss, xtermScript } from "./assets.js";
|
|
4
|
+
import { MESSAGES } from "./i18n.js";
|
|
4
5
|
const nodeRequire = createRequire(import.meta.url);
|
|
5
6
|
const packageVersion = (() => {
|
|
6
7
|
try {
|
|
@@ -97,6 +98,7 @@ export function renderDiffHtml(input) {
|
|
|
97
98
|
"<style>",
|
|
98
99
|
diff2HtmlCss(),
|
|
99
100
|
diffCss(),
|
|
101
|
+
input.app ? xtermCss() : "",
|
|
100
102
|
"</style>",
|
|
101
103
|
"</head>",
|
|
102
104
|
"<body>",
|
|
@@ -104,8 +106,8 @@ export function renderDiffHtml(input) {
|
|
|
104
106
|
'<div class="sidebar-scroll">',
|
|
105
107
|
`<div class="sidebar-brand" title="${escapeAttr(input.projectPath)}"><span class="brand-mark">monacori</span><span class="brand-project">${escapeHtml(input.projectName)}</span></div>`,
|
|
106
108
|
input.lazy
|
|
107
|
-
? '<div class="tabs"><button type="button" class="tab active" data-tab="changes">Changes</button><button type="button" class="tab" data-tab="files">Files</button></div>'
|
|
108
|
-
: '<div class="tabs"><button type="button" class="tab" data-tab="changes">Changes</button><button type="button" class="tab active" data-tab="files">Files</button></div>',
|
|
109
|
+
? '<div class="tabs"><button type="button" class="tab active" data-tab="changes" data-i18n="tab.changes">Changes</button><button type="button" class="tab" data-tab="files" data-i18n="tab.files">Files</button></div>'
|
|
110
|
+
: '<div class="tabs"><button type="button" class="tab" data-tab="changes" data-i18n="tab.changes">Changes</button><button type="button" class="tab active" data-tab="files" data-i18n="tab.files">Files</button></div>',
|
|
109
111
|
`<div class="tab-panel${input.lazy ? "" : " hidden"}" id="changes-panel">${fileNav}</div>`,
|
|
110
112
|
// Big repos: defer the (potentially huge) source tree — ship it as an inert island, materialized on
|
|
111
113
|
// the first Files-tab open, so it never builds/lays-out at startup. Small repos render it inline.
|
|
@@ -113,48 +115,118 @@ export function renderDiffHtml(input) {
|
|
|
113
115
|
? `<div class="tab-panel hidden" id="files-panel"></div><script type="text/html" id="files-tree-html">${sourceNav}</script>`
|
|
114
116
|
: `<div class="tab-panel" id="files-panel">${sourceNav}</div>`,
|
|
115
117
|
"</div>",
|
|
116
|
-
`<div class="sidebar-footer"><span class="app-version">monacori${packageVersion ? " v" + escapeHtml(packageVersion) : ""}</span><span id="app-update-flag" class="app-update-flag hidden" title="Update available">update available</span><button type="button" id="app-info-btn" class="settings-btn" aria-haspopup="dialog" aria-label="About monacori" title="About monacori">⚙</button></div>`,
|
|
118
|
+
`<div class="sidebar-footer"><span class="app-version">monacori${packageVersion ? " v" + escapeHtml(packageVersion) : ""}</span><span id="app-update-flag" class="app-update-flag hidden" data-i18n="sidebar.updateAvailable" data-i18n-title="settings.updateAvailable" title="Update available">update available</span><button type="button" id="terminal-toggle" class="settings-btn terminal-toggle hidden" data-i18n-title="terminal.toggle" title="Toggle terminal (Ctrl+\`)" aria-label="Toggle terminal"><svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 7l4 5-4 5"/><path d="M13 17h6"/></svg></button><button type="button" id="app-info-btn" class="settings-btn" aria-haspopup="dialog" data-i18n-aria="about.title" data-i18n-title="about.title" aria-label="About monacori" title="About monacori">⚙</button></div>`,
|
|
117
119
|
"</aside>",
|
|
118
120
|
'<div class="sidebar-resizer" aria-hidden="true"></div>',
|
|
119
121
|
'<main class="content">',
|
|
120
122
|
'<section id="diff-view" class="hidden">',
|
|
121
123
|
'<div class="toolbar">',
|
|
122
124
|
'<div class="breadcrumb" id="diff-breadcrumb"></div>',
|
|
123
|
-
`<div class="review-status"><span>${input.files.length} files</span><span>${totalHunks} hunks</span>${input.ignoreWhitespace ? '<span class="ws-ignored" title="Whitespace ignored — Cmd/Ctrl+Shift+W">ws ignored</span>' : ""}<span class="index-status" id="index-status" title="Go-to-definition index">${embeddedFiles}/${input.sourceFiles.length} indexed</span><span class="index-progress hidden" id="index-progress" aria-hidden="true"><span class="index-progress-bar"></span></span><span class="live-status ${input.watch ? "watching" : ""}" id="live-status">${input.watch ? "watching" : escapeHtml(input.generatedAt ?? new Date().toISOString())}</span></div>`,
|
|
124
|
-
'<button type="button" id="diff-viewed-toggle" class="diff-viewed-toggle" aria-pressed="false" title="Toggle viewed (<)" hidden>Viewed</button>',
|
|
125
|
+
`<div class="review-status"><span>${input.files.length} <span data-i18n="status.files">files</span></span><span>${totalHunks} <span data-i18n="status.hunks">hunks</span></span>${input.ignoreWhitespace ? '<span class="ws-ignored" data-i18n="status.wsIgnored" data-i18n-title="status.wsIgnored.title" title="Whitespace ignored — Cmd/Ctrl+Shift+W">ws ignored</span>' : ""}<span class="index-status" id="index-status" data-i18n-title="status.index.title" title="Go-to-definition index">${embeddedFiles}/${input.sourceFiles.length} indexed</span><span class="index-progress hidden" id="index-progress" aria-hidden="true"><span class="index-progress-bar"></span></span><span class="live-status ${input.watch ? "watching" : ""}" id="live-status"${input.watch ? ' data-i18n="status.watching"' : ""}>${input.watch ? "watching" : escapeHtml(input.generatedAt ?? new Date().toISOString())}</span></div>`,
|
|
126
|
+
'<button type="button" id="diff-viewed-toggle" class="diff-viewed-toggle" aria-pressed="false" data-i18n="btn.viewed" data-i18n-title="btn.viewed.title" title="Toggle viewed (<)" hidden>Viewed</button>',
|
|
125
127
|
"</div>",
|
|
126
|
-
`<div id="diff2html-container" class="diff2html-container">${input.diffHtml || '<div class="empty">No diff to review.</div>'}</div>`,
|
|
128
|
+
`<div id="diff2html-container" class="diff2html-container">${input.diffHtml || '<div class="empty" data-i18n="diff.noDiff">No diff to review.</div>'}</div>`,
|
|
127
129
|
"</section>",
|
|
128
130
|
'<section id="source-viewer" class="source-viewer">',
|
|
131
|
+
'<div id="source-tabs" class="source-tabs hidden" role="tablist"></div>',
|
|
129
132
|
'<div class="toolbar source-toolbar">',
|
|
130
|
-
'<div class="source-file-meta"><span id="source-type-icon" class="source-type-icon" aria-hidden="true"></span><span id="source-title">Source</span><span id="source-meta">Select a file from the Files tab.</span></div>',
|
|
131
|
-
'<select id="http-env-select" class="http-env-select hidden" title="HTTP Client environment" aria-label="HTTP environment"></select>',
|
|
132
|
-
'<button type="button" id="
|
|
133
|
+
'<div class="source-file-meta"><span id="source-type-icon" class="source-type-icon" aria-hidden="true"></span><span id="source-title" data-i18n="source.title">Source</span><span id="source-meta" data-i18n="source.selectFile">Select a file from the Files tab.</span></div>',
|
|
134
|
+
'<select id="http-env-select" class="http-env-select hidden" data-i18n-title="http.env.title" data-i18n-aria="http.env.aria" title="HTTP Client environment" aria-label="HTTP environment"></select>',
|
|
135
|
+
'<button type="button" id="render-toggle" class="plain-button hidden" aria-pressed="false">Raw</button>',
|
|
136
|
+
'<button type="button" id="back-to-diff" class="plain-button" data-i18n="btn.diff">Diff</button>',
|
|
133
137
|
"</div>",
|
|
134
|
-
'<div id="source-body" class="source-body empty">Select a file from the Files tab.</div>',
|
|
138
|
+
'<div id="source-body" class="source-body empty" data-i18n="source.selectFile">Select a file from the Files tab.</div>',
|
|
135
139
|
"</section>",
|
|
136
140
|
"</main>",
|
|
137
|
-
|
|
138
|
-
'
|
|
141
|
+
// Integrated terminal panel (Electron only — shown when window.monacoriPty exists). Fixed to the
|
|
142
|
+
// content column's bottom; a top resizer drags its height. The merged prompt is sent here.
|
|
143
|
+
input.app
|
|
144
|
+
? '<div id="terminal-panel" class="terminal-panel hidden"><div class="terminal-resizer" aria-hidden="true"></div><div class="terminal-bar"><span class="terminal-title" data-i18n="terminal.title">Terminal</span><button type="button" id="terminal-close" class="terminal-x" data-i18n-title="terminal.close" title="Close terminal" aria-label="Close terminal">×</button></div><div id="terminal-host" class="terminal-host"></div></div>'
|
|
145
|
+
: "",
|
|
146
|
+
'<div id="quick-open" class="quick-open hidden" role="dialog" aria-modal="true" data-i18n-aria="quickopen.aria" aria-label="Quick open">',
|
|
139
147
|
'<div class="quick-open-panel">',
|
|
140
|
-
'<div class="quick-open-title"><span id="quick-open-mode">Search files</span></div>',
|
|
141
|
-
'<input id="quick-open-input" type="search" autocomplete="off" spellcheck="false" placeholder="Search files">',
|
|
148
|
+
'<div class="quick-open-title"><span id="quick-open-mode" data-i18n="quickopen.searchFiles">Search files</span></div>',
|
|
149
|
+
'<input id="quick-open-input" type="search" autocomplete="off" spellcheck="false" data-i18n-ph="quickopen.searchFiles" placeholder="Search files">',
|
|
142
150
|
'<div id="quick-open-results" class="quick-open-results"></div>',
|
|
143
151
|
'<div id="quick-open-preview" class="quick-open-preview"></div>',
|
|
144
152
|
"</div>",
|
|
145
153
|
"</div>",
|
|
146
|
-
'<div id="usages" class="quick-open hidden" role="dialog" aria-modal="true" aria-label="Usages">',
|
|
154
|
+
'<div id="usages" class="quick-open hidden" role="dialog" aria-modal="true" data-i18n-aria="usages.aria" aria-label="Usages">',
|
|
147
155
|
'<div class="quick-open-panel">',
|
|
148
|
-
'<div class="quick-open-title"><span id="usages-title">Usages</span></div>',
|
|
156
|
+
'<div class="quick-open-title"><span id="usages-title" data-i18n="usages.title">Usages</span></div>',
|
|
149
157
|
'<div id="usages-results" class="quick-open-results"></div>',
|
|
150
158
|
"</div>",
|
|
151
159
|
"</div>",
|
|
160
|
+
'<div id="settings-modal" class="settings-modal hidden" role="dialog" aria-modal="true" data-i18n-aria="settings.aria" aria-label="Settings">',
|
|
161
|
+
'<div class="settings-panel">',
|
|
162
|
+
'<aside class="settings-nav"><div class="settings-nav-title" data-i18n="settings.title">Settings</div><button type="button" class="settings-cat active" data-cat="general" data-i18n="settings.cat.general">General</button><button type="button" class="settings-cat" data-cat="prompts" data-i18n="settings.cat.prompts">Merge prompts</button></aside>',
|
|
163
|
+
'<div class="settings-body">',
|
|
164
|
+
'<section class="settings-section" data-cat="general">',
|
|
165
|
+
`<div class="settings-h">monacori <span class="settings-ver">${packageVersion ? "v" + escapeHtml(packageVersion) : ""}</span></div>`,
|
|
166
|
+
'<div id="app-info-status" class="app-info-status" data-i18n="settings.checkingUpdates">Checking for updates…</div>',
|
|
167
|
+
'<button type="button" id="app-info-update" class="plain-button app-info-update hidden" data-i18n="settings.updateRestart">Update & Restart</button>',
|
|
168
|
+
'<label class="settings-label" for="settings-language" data-i18n="settings.language">Language</label>',
|
|
169
|
+
'<select id="settings-language" class="settings-select"><option value="en">English</option><option value="ko">한국어</option></select>',
|
|
170
|
+
'<div class="app-info-keys">' +
|
|
171
|
+
'<div class="app-info-keys-h" data-i18n="settings.kbd.title">Keyboard shortcuts</div>' +
|
|
172
|
+
'<div class="keys-cat" data-i18n="settings.kbd.cat.nav">Navigation</div>' +
|
|
173
|
+
'<div class="keys-grid">' +
|
|
174
|
+
'<kbd>F7</kbd><span data-i18n="kbd.nextChange">Next change</span>' +
|
|
175
|
+
'<kbd>Shift+F7</kbd><span data-i18n="kbd.prevChange">Previous change</span>' +
|
|
176
|
+
'<kbd>Cmd/Ctrl+1 / 0</kbd><span data-i18n="kbd.filesChangesTab">Files / Changes tab</span>' +
|
|
177
|
+
'<kbd>Tab</kbd><span data-i18n="kbd.sidebarContent">Sidebar ↔ content</span>' +
|
|
178
|
+
'<kbd>Shift Shift</kbd><span data-i18n="kbd.findFile">Find file</span>' +
|
|
179
|
+
'<kbd>Cmd/Ctrl+Shift+F</kbd><span data-i18n="kbd.findInFiles">Find in files</span>' +
|
|
180
|
+
'<kbd>Cmd/Ctrl+E</kbd><span data-i18n="kbd.recentFiles">Recent files</span>' +
|
|
181
|
+
'<kbd>Cmd/Ctrl+B</kbd><span data-i18n="kbd.defUsages">Definition / usages</span>' +
|
|
182
|
+
'<kbd>Cmd/Ctrl+↓</kbd><span data-i18n="kbd.goToDef">Go to definition</span>' +
|
|
183
|
+
'<kbd>Cmd/Ctrl+Shift+[ / ]</kbd><span data-i18n="kbd.prevNextTab">Prev / next tab</span>' +
|
|
184
|
+
'<kbd>Cmd/Ctrl+[ / ]</kbd><span data-i18n="kbd.cursorBackForward">Cursor back / forward</span>' +
|
|
185
|
+
'<kbd>Opt/Alt+←/→</kbd><span data-i18n="kbd.wordJump">Word jump (vim w)</span>' +
|
|
186
|
+
'<kbd>Cmd/Ctrl+←/→</kbd><span data-i18n="kbd.lineStartEnd">Line start / end</span>' +
|
|
187
|
+
'<kbd>Shift+arrows</kbd><span data-i18n="kbd.extendSelection">Extend selection</span>' +
|
|
188
|
+
'<kbd>Cmd/Ctrl+W</kbd><span data-i18n="kbd.closeTab">Close tab</span>' +
|
|
189
|
+
'</div>' +
|
|
190
|
+
'<div class="keys-cat" data-i18n="settings.kbd.cat.review">Review</div>' +
|
|
191
|
+
'<div class="keys-grid">' +
|
|
192
|
+
'<kbd><</kbd><span data-i18n="kbd.toggleViewed">Toggle viewed</span>' +
|
|
193
|
+
'<kbd>? ></kbd><span data-i18n="kbd.addQuestionChange">Add question / change</span>' +
|
|
194
|
+
'<kbd>Cmd/Ctrl+Shift+/ .</kbd><span data-i18n="kbd.allQuestionsChanges">All questions / changes</span>' +
|
|
195
|
+
'<kbd>Cmd/Ctrl+Shift+W</kbd><span data-i18n="kbd.ignoreWhitespace">Ignore whitespace</span>' +
|
|
196
|
+
'<kbd>Cmd/Ctrl+Enter</kbd><span data-i18n="kbd.saveComment">Save comment</span>' +
|
|
197
|
+
'</div>' +
|
|
198
|
+
'<div class="keys-cat" data-i18n="settings.kbd.cat.terminal">Terminal</div>' +
|
|
199
|
+
'<div class="keys-grid">' +
|
|
200
|
+
'<kbd>Ctrl+`</kbd><span data-i18n="kbd.toggleTerminal">Toggle terminal</span>' +
|
|
201
|
+
'<kbd>Cmd/Ctrl+D</kbd><span data-i18n="kbd.splitPane">Split pane</span>' +
|
|
202
|
+
'<kbd>Cmd/Ctrl+Alt+[ / ]</kbd><span data-i18n="kbd.focusPane">Focus prev / next pane</span>' +
|
|
203
|
+
'<kbd>F2</kbd><span data-i18n="kbd.renamePane">Rename pane</span>' +
|
|
204
|
+
'<kbd>Cmd/Ctrl+W</kbd><span data-i18n="kbd.closeTerminal">Close terminal (when focused)</span>' +
|
|
205
|
+
'</div>' +
|
|
206
|
+
'</div>',
|
|
207
|
+
"</section>",
|
|
208
|
+
'<section class="settings-section hidden" data-cat="prompts">',
|
|
209
|
+
'<div class="settings-h" data-i18n="mergePrompts.title">Merge prompts</div>',
|
|
210
|
+
'<div class="settings-desc" data-i18n="mergePrompts.desc">Heading prepended to the merged prompt opened with Cmd/Ctrl+Shift+/ (questions) and Cmd/Ctrl+Shift+. (change requests). Leave blank to use the default.</div>',
|
|
211
|
+
'<label class="settings-label" for="settings-prompt-q" data-i18n="mergePrompts.qHeading">Questions heading</label>',
|
|
212
|
+
'<textarea id="settings-prompt-q" class="settings-textarea" rows="4" spellcheck="false"></textarea>',
|
|
213
|
+
'<label class="settings-label" for="settings-prompt-c" data-i18n="mergePrompts.cHeading">Change-requests heading</label>',
|
|
214
|
+
'<textarea id="settings-prompt-c" class="settings-textarea" rows="4" spellcheck="false"></textarea>',
|
|
215
|
+
'<div class="settings-actions"><button type="button" id="settings-reset" class="plain-button" data-i18n="mergePrompts.reset">Reset to defaults</button><span id="settings-saved" class="settings-saved"></span></div>',
|
|
216
|
+
"</section>",
|
|
217
|
+
"</div>",
|
|
218
|
+
"</div>",
|
|
219
|
+
"</div>",
|
|
152
220
|
input.diffIslands || "",
|
|
153
221
|
`<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>`,
|
|
222
|
+
`<script type="application/json" id="i18n-data">${jsonForScript(MESSAGES)}</script>`,
|
|
154
223
|
`<script type="application/json" id="source-files-data">${jsonForScript(input.lazyLoad ? input.sourceFiles.map((f) => ({ ...f, content: "", image: "" })) : input.sourceFiles)}</script>`,
|
|
155
224
|
`<script type="application/json" id="file-state-data">${jsonForScript(input.fileStates)}</script>`,
|
|
156
225
|
`<script type="application/json" id="http-env-data">${jsonForScript(input.httpEnvironments)}</script>`,
|
|
157
226
|
`<script>window.__MONACORI_VERSION__=${JSON.stringify(packageVersion)};</script>`,
|
|
227
|
+
// xterm ships as an inert island (type=text/html, not parsed/compiled at startup) and is injected on
|
|
228
|
+
// the first terminal open — ~490KB the renderer would otherwise parse on every launch even unused.
|
|
229
|
+
input.app ? `<script type="text/html" id="xterm-code">${xtermScript()}</script>` : "",
|
|
158
230
|
"<script>",
|
|
159
231
|
diffScript(),
|
|
160
232
|
"</script>",
|
|
@@ -184,7 +256,7 @@ function renderDiffTree(files) {
|
|
|
184
256
|
const name = slash >= 0 ? file.displayPath.slice(slash + 1) : file.displayPath;
|
|
185
257
|
const dir = slash > 0 ? file.displayPath.slice(0, slash) : "";
|
|
186
258
|
return [
|
|
187
|
-
`<a class="file-link change-row" href="#file-${fileIndex}" data-hunk="${firstHunk}" data-file="${escapeAttr(file.displayPath)}" title="${escapeAttr(file.displayPath + " — " + file.status)}">`,
|
|
259
|
+
`<a class="file-link change-row${file.vcs ? " vcs-" + file.vcs : ""}" href="#file-${fileIndex}" data-hunk="${firstHunk}" data-file="${escapeAttr(file.displayPath)}" title="${escapeAttr(file.displayPath + " — " + file.status)}">`,
|
|
188
260
|
fileTypeIcon(file.displayPath),
|
|
189
261
|
`<span class="status status-${escapeAttr(file.status)}">${escapeHtml(file.status)}</span>`,
|
|
190
262
|
`<span class="change-name"><span class="path" title="${escapeAttr(file.displayPath)}">${escapeHtml(name)}</span>${dir ? `<span class="change-dir">${escapeHtml(dir)}</span>` : ""}</span>`,
|
|
@@ -302,7 +374,7 @@ function fileTypeIcon(path) {
|
|
|
302
374
|
function renderSourceNode(node, depth) {
|
|
303
375
|
if (node.file) {
|
|
304
376
|
const file = node.file;
|
|
305
|
-
const classes = ["file-link", "source-link", "tree-file", file.embedded ? "" : "not-embedded"].filter(Boolean).join(" ");
|
|
377
|
+
const classes = ["file-link", "source-link", "tree-file", file.embedded ? "" : "not-embedded", file.vcs ? "vcs-" + file.vcs : ""].filter(Boolean).join(" ");
|
|
306
378
|
const tip = file.path + (file.embedded ? "" : " — not embedded");
|
|
307
379
|
return [
|
|
308
380
|
`<button type="button" class="${classes}" data-source-file="${escapeAttr(file.path)}" style="--depth:${depth}" title="${escapeAttr(tip)}">`,
|
package/dist/types.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ export type DiffFile = {
|
|
|
35
35
|
status: string;
|
|
36
36
|
binary: boolean;
|
|
37
37
|
hunks: DiffHunk[];
|
|
38
|
+
vcs?: "new" | "edited" | "staged";
|
|
38
39
|
};
|
|
39
40
|
export type SourceFile = {
|
|
40
41
|
path: string;
|
|
@@ -48,6 +49,7 @@ export type SourceFile = {
|
|
|
48
49
|
signature: string;
|
|
49
50
|
skippedReason?: string;
|
|
50
51
|
image?: string;
|
|
52
|
+
vcs?: "new" | "edited" | "staged";
|
|
51
53
|
};
|
|
52
54
|
export type HttpSendRequest = {
|
|
53
55
|
method: string;
|