@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/viewer.css
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
:root {
|
|
3
3
|
color-scheme: dark;
|
|
4
|
-
--bg: #
|
|
5
|
-
--panel: #
|
|
4
|
+
--bg: #1e1e1e;
|
|
5
|
+
--panel: #252526;
|
|
6
6
|
--text: #a9b7c6;
|
|
7
7
|
--muted: #808080;
|
|
8
8
|
--border: #393b3d;
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
--add-strong: #1f5b34;
|
|
13
13
|
--del-strong: #6e2c2c;
|
|
14
14
|
--active: #4a88c7;
|
|
15
|
-
--sidebar: #
|
|
15
|
+
--sidebar: #2a2a2b;
|
|
16
16
|
--token-comment: #808080;
|
|
17
17
|
--token-keyword: #cc7832;
|
|
18
18
|
--token-string: #6a8759;
|
|
@@ -524,8 +524,11 @@ td.d2h-del:not(.d2h-code-side-linenumber) { color: #d8e0e8; }
|
|
|
524
524
|
.tree-dir[open] > summary .fi-closed { display: none; }
|
|
525
525
|
.tree-dir[open] > summary .fi-open { display: block; }
|
|
526
526
|
.file-link.tree-file { padding-left: calc(8px + (var(--depth) * 14px)); }
|
|
527
|
-
|
|
528
|
-
|
|
527
|
+
/* Arrow-key focus in the tree (changes/files) matches the active-file highlight — a tinted fill + accent
|
|
528
|
+
border — so navigating the file tree reads the same as the diff-side selection (not a faint 1px outline). */
|
|
529
|
+
.tree-focus { background: color-mix(in srgb, var(--active) 20%, transparent); border-radius: 6px; }
|
|
530
|
+
.file-link.tree-focus { border-color: var(--active); }
|
|
531
|
+
summary.tree-focus { background: color-mix(in srgb, var(--active) 20%, transparent); }
|
|
529
532
|
.file-link {
|
|
530
533
|
display: grid;
|
|
531
534
|
grid-template-columns: auto minmax(0, 1fr) auto;
|
|
@@ -543,7 +546,10 @@ summary.tree-focus { background: var(--bg); }
|
|
|
543
546
|
font: inherit;
|
|
544
547
|
cursor: pointer;
|
|
545
548
|
}
|
|
546
|
-
.file-link:hover
|
|
549
|
+
.file-link:hover { background: color-mix(in srgb, var(--text) 7%, transparent); border-color: var(--border); }
|
|
550
|
+
/* The file being viewed stands out clearly: a tinted fill + accent border, not just a faint outline. */
|
|
551
|
+
.file-link.active { background: color-mix(in srgb, var(--active) 26%, transparent); border-color: var(--active); }
|
|
552
|
+
.file-link.active:hover { background: color-mix(in srgb, var(--active) 32%, transparent); }
|
|
547
553
|
.file-link.viewed { opacity: 0.58; }
|
|
548
554
|
.file-link.viewed:hover, .file-link.viewed.active { opacity: 1; }
|
|
549
555
|
.path { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }
|
|
@@ -839,8 +845,9 @@ body.mc-composing .source-row.csv-row.cursor-line .csv-cell { background: transp
|
|
|
839
845
|
body.mc-composing .source-row.cursor-line .num { color: inherit; }
|
|
840
846
|
/* Same single-caret rule for a floating overlay (merged comments / prompt memo / settings): it owns the only
|
|
841
847
|
caret while open, so hide the file's blinking caret behind it — never two carets across visible panels. */
|
|
842
|
-
|
|
843
|
-
|
|
848
|
+
/* A focused merged/memo dock owns the only caret: hide the file caret behind it while it has focus
|
|
849
|
+
(single-caret rule). Focus elsewhere with the dock still open -> the file caret returns. */
|
|
850
|
+
body:has(.dock-panel:focus-within) .code-cursor,
|
|
844
851
|
body:has(#settings-modal:not(.hidden)) .code-cursor { display: none; }
|
|
845
852
|
.mc-kind {
|
|
846
853
|
font-weight: 700; font-size: 10px; letter-spacing: 0.05em; text-transform: uppercase;
|
|
@@ -856,6 +863,10 @@ body:has(#settings-modal:not(.hidden)) .code-cursor { display: none; }
|
|
|
856
863
|
display: block; box-sizing: border-box; resize: vertical;
|
|
857
864
|
margin: 0 10px 10px; width: calc(100% - 20px); min-height: 60px;
|
|
858
865
|
background: var(--bg); color: var(--text);
|
|
866
|
+
/* The composer is injected INSIDE #diff2html-container, which sets caret-color: transparent (the file
|
|
867
|
+
view uses a fake .code-cursor). caret-color inherits, so restore it here — a focused textarea must show
|
|
868
|
+
its real caret. The single-caret rule hides the FILE caret while composing, not the textarea's own. */
|
|
869
|
+
caret-color: var(--text);
|
|
859
870
|
border: 1px solid var(--border); border-radius: 7px; padding: 9px 11px;
|
|
860
871
|
font: 12px/1.55 Monaco, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
861
872
|
transition: border-color 120ms ease, box-shadow 120ms ease;
|
|
@@ -890,6 +901,38 @@ body:has(#settings-modal:not(.hidden)) .code-cursor { display: none; }
|
|
|
890
901
|
.mc-memo-preview { height: 100%; overflow: auto; padding: 12px 16px; background: var(--panel); color: var(--text); }
|
|
891
902
|
.mc-memo-preview > :first-child { margin-top: 0; }
|
|
892
903
|
.mc-memo-empty { color: var(--muted); font-size: 12px; font-style: italic; }
|
|
904
|
+
|
|
905
|
+
/* ===== Merged-prompt & memo: a large FLOATING overlay (~90% of the window), sharing ONE slot with the
|
|
906
|
+
terminal (opening one closes the others). Cmd/Ctrl+Shift+' maximizes the active panel to full screen. =====
|
|
907
|
+
(These used to dock below the editor; they now float so a big writing surface doesn't shrink the diff.) */
|
|
908
|
+
.dock-panel {
|
|
909
|
+
position: fixed; inset: 5vh 5vw; z-index: 60; min-height: 0; /* ~90vw x 90vh, centered */
|
|
910
|
+
display: flex; flex-direction: column; background: var(--panel);
|
|
911
|
+
border: 1px solid var(--border); border-radius: 12px;
|
|
912
|
+
box-shadow: 0 24px 80px rgba(0, 0, 0, 0.55);
|
|
913
|
+
overflow: hidden;
|
|
914
|
+
}
|
|
915
|
+
/* Dim the editor behind the floating panel; clicking it closes (handler wired in mountDock). */
|
|
916
|
+
.dock-backdrop { position: fixed; inset: 0; z-index: 59; background: color-mix(in srgb, #000 40%, transparent); }
|
|
917
|
+
.dock-resizer { display: none; } /* a centered ~90% floating panel has no bottom edge to drag */
|
|
918
|
+
.dock-bar {
|
|
919
|
+
flex: none; display: flex; align-items: center; gap: 8px; padding: 6px 10px;
|
|
920
|
+
background: var(--sidebar); border-bottom: 1px solid var(--border);
|
|
921
|
+
color: var(--text); font-weight: 650; font-size: 12px;
|
|
922
|
+
}
|
|
923
|
+
.dock-title { margin-right: auto; letter-spacing: 0.02em; }
|
|
924
|
+
.dock-btn { border: 0; background: transparent; color: var(--muted); cursor: pointer; padding: 2px 7px; border-radius: 5px; font: inherit; font-weight: 500; }
|
|
925
|
+
.dock-btn:hover { color: var(--text); background: color-mix(in srgb, var(--muted) 22%, transparent); }
|
|
926
|
+
.dock-max { font-size: 14px; line-height: 1; }
|
|
927
|
+
.dock-body { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
|
|
928
|
+
.dock-body > .mc-modal-text { flex: 1 1 auto; height: auto; }
|
|
929
|
+
.dock-body > .mc-memo-body { flex: 1 1 auto; height: auto; }
|
|
930
|
+
/* Maximize. The floating merged/memo panel fills the whole viewport. The terminal is still a grid dock, so
|
|
931
|
+
its maximize hides the editor area (next rule) — but the floating panels do NOT hide content (their overlay
|
|
932
|
+
already covers it, and hiding the diff would force an expensive reflow on every maximize toggle). */
|
|
933
|
+
body.dock-maximized .dock-panel { inset: 0; border-radius: 0; }
|
|
934
|
+
body.dock-maximized:not(.floating-dock) .content { display: none; }
|
|
935
|
+
body.dock-maximized .terminal-panel:not(.hidden) { grid-row: 1 / 3; height: auto; }
|
|
893
936
|
.tok-comment { color: var(--token-comment); font-style: italic; }
|
|
894
937
|
.tok-keyword { color: var(--token-keyword); font-weight: 650; }
|
|
895
938
|
.tok-string { color: var(--token-string); }
|
|
@@ -1004,6 +1047,7 @@ body:has(#settings-modal:not(.hidden)) .code-cursor { display: none; }
|
|
|
1004
1047
|
.sidebar { grid-row: 1; grid-column: 1; height: auto; border-right: 0; border-bottom: 1px solid var(--border); }
|
|
1005
1048
|
.content { grid-row: 2; grid-column: 1; padding: 16px; }
|
|
1006
1049
|
.terminal-panel { grid-column: 1; grid-row: 3; }
|
|
1050
|
+
.dock-panel { grid-column: 1; grid-row: 3; }
|
|
1007
1051
|
.toolbar { margin: -16px -16px 16px; padding: 12px 16px; }
|
|
1008
1052
|
.d2h-files-diff { grid-template-columns: 1fr; }
|
|
1009
1053
|
.d2h-file-side-diff:first-child { border-right: 0; border-bottom: 1px solid var(--border); }
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happy-nut/monacori",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.21",
|
|
4
4
|
"description": "Validation control plane for AI-generated code changes.",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"main": "dist/app-main.js",
|
|
6
7
|
"repository": {
|
|
7
8
|
"type": "git",
|
|
8
9
|
"url": "git+https://github.com/happy-nut/monacori.git"
|
|
@@ -35,7 +36,9 @@
|
|
|
35
36
|
"prepare": "npm run build",
|
|
36
37
|
"smoke": "npm run build && node dist/cli.js --help",
|
|
37
38
|
"pretest": "npm run build",
|
|
38
|
-
"test": "node --test test/*.test.mjs"
|
|
39
|
+
"test": "node --test test/*.test.mjs",
|
|
40
|
+
"dist:mac": "npm run build && electron-packager . Monacori --platform=darwin --arch=arm64 --icon=assets/icon.icns --app-bundle-id=dev.happynut.monacori --app-category-type=public.app-category.developer-tools --out=release --overwrite --no-asar --ignore=/src/ --ignore=/test/ --ignore=/release --ignore=/.git --ignore=/.github --ignore=/.omc --ignore=/openspec --ignore=/node_modules/electron/ --ignore=/node_modules/@electron --ignore=/node_modules/typescript --ignore=/node_modules/jsdom",
|
|
41
|
+
"dist:mac:dmg": "npm run dist:mac && create-dmg --overwrite --no-code-sign --dmg-title=Monacori 'release/Monacori-darwin-arm64/Monacori.app' release/ || true"
|
|
39
42
|
},
|
|
40
43
|
"keywords": [
|
|
41
44
|
"ai",
|
|
@@ -47,6 +50,7 @@
|
|
|
47
50
|
"author": "happy-nut <happynut.dev@gmail.com>",
|
|
48
51
|
"license": "MIT",
|
|
49
52
|
"devDependencies": {
|
|
53
|
+
"@electron/packager": "^20.0.1",
|
|
50
54
|
"@types/node": "^22.15.21",
|
|
51
55
|
"jsdom": "^29.1.1",
|
|
52
56
|
"typescript": "^5.8.3"
|
|
@@ -62,5 +66,8 @@
|
|
|
62
66
|
"electron": "^42.4.1",
|
|
63
67
|
"highlight.js": "^11.11.1",
|
|
64
68
|
"node-pty": "^1.1.0"
|
|
69
|
+
},
|
|
70
|
+
"optionalDependencies": {
|
|
71
|
+
"create-dmg": "^8.1.0"
|
|
65
72
|
}
|
|
66
73
|
}
|
|
@@ -29,36 +29,45 @@ function main() {
|
|
|
29
29
|
const oldExe = join(macosDir, "Electron");
|
|
30
30
|
const newExe = join(macosDir, APP_NAME);
|
|
31
31
|
const pathTxt = join(root, "path.txt");
|
|
32
|
-
if (!existsSync(plistPath))
|
|
32
|
+
if (!existsSync(plistPath)) {
|
|
33
|
+
console.warn('monacori: Electron.app not found at ' + appDir + ' — skipping rebrand (Dock/menu may show "Electron")');
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
33
36
|
|
|
34
37
|
try {
|
|
38
|
+
let changed = false;
|
|
35
39
|
// 1. Bundle metadata: name, display name, AND executable -> monacori.
|
|
36
40
|
const before = readFileSync(plistPath, "utf8");
|
|
37
41
|
const after = before
|
|
38
42
|
.replace(/(<key>CFBundleName<\/key>\s*<string>)[^<]*(<\/string>)/, "$1" + APP_NAME + "$2")
|
|
39
43
|
.replace(/(<key>CFBundleDisplayName<\/key>\s*<string>)[^<]*(<\/string>)/, "$1" + APP_NAME + "$2")
|
|
40
44
|
.replace(/(<key>CFBundleExecutable<\/key>\s*<string>)[^<]*(<\/string>)/, "$1" + APP_NAME + "$2");
|
|
41
|
-
if (after !== before) writeFileSync(plistPath, after);
|
|
45
|
+
if (after !== before) { writeFileSync(plistPath, after); changed = true; }
|
|
42
46
|
|
|
43
47
|
// 2. Rename the executable so the directly-spawned process is "monacori" (idempotent).
|
|
44
|
-
if (existsSync(oldExe) && !existsSync(newExe)) renameSync(oldExe, newExe);
|
|
48
|
+
if (existsSync(oldExe) && !existsSync(newExe)) { renameSync(oldExe, newExe); changed = true; }
|
|
45
49
|
|
|
46
50
|
// 3. Repoint electron's path.txt at the renamed binary so require("electron") resolves it.
|
|
47
51
|
if (existsSync(pathTxt)) {
|
|
48
52
|
const pt = readFileSync(pathTxt, "utf8");
|
|
49
53
|
const fixed = pt.replace("MacOS/Electron", "MacOS/" + APP_NAME);
|
|
50
|
-
if (fixed !== pt) writeFileSync(pathTxt, fixed);
|
|
54
|
+
if (fixed !== pt) { writeFileSync(pathTxt, fixed); changed = true; }
|
|
51
55
|
}
|
|
52
|
-
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
|
|
57
|
+
// Only when something actually changed: refresh LaunchServices so the Dock / Cmd+Tab show "monacori"
|
|
58
|
+
// instead of a cached "Electron". Skipping it when already-branded keeps the startup re-run cheap.
|
|
59
|
+
if (changed) {
|
|
60
|
+
spawnSync(
|
|
61
|
+
"/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister",
|
|
62
|
+
["-f", appDir],
|
|
63
|
+
{ stdio: "ignore" },
|
|
64
|
+
);
|
|
65
|
+
console.log('monacori: branded Electron app + executable as "' + APP_NAME + '"');
|
|
66
|
+
}
|
|
67
|
+
} catch (e) {
|
|
68
|
+
// Surface the reason (perms / read-only) instead of failing SILENTLY — otherwise the Dock/Cmd+Tab/menu
|
|
69
|
+
// keep showing "Electron" with no hint why. Non-fatal: app-main.ts re-runs this at startup.
|
|
70
|
+
console.warn('monacori: could not rebrand the Electron app to "' + APP_NAME + '". Dock/Cmd+Tab/menu may stay "Electron". Reason: ' + (e && e.message ? e.message : e));
|
|
62
71
|
}
|
|
63
72
|
}
|
|
64
73
|
|