@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/dist/viewer.css CHANGED
@@ -1,8 +1,8 @@
1
1
 
2
2
  :root {
3
3
  color-scheme: dark;
4
- --bg: #161616;
5
- --panel: #1b1b1b;
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: #1c1c1c;
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
- .tree-focus { box-shadow: inset 0 0 0 1px var(--active); border-radius: 6px; }
528
- summary.tree-focus { background: var(--bg); }
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, .file-link.active { background: var(--bg); border-color: var(--border); }
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
- body:has(#mc-modal) .code-cursor,
843
- body:has(#mc-memo) .code-cursor,
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.20",
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)) return;
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
- // Refresh LaunchServices so the Dock / Cmd+Tab show "monacori" instead of a cached "Electron".
53
- // Without this, macOS keeps the previously-registered bundle name even after the plist is patched.
54
- spawnSync(
55
- "/System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister",
56
- ["-f", appDir],
57
- { stdio: "ignore" },
58
- );
59
- console.log('monacori: branded Electron app + executable as "' + APP_NAME + '"');
60
- } catch {
61
- // read-only / permission-denied environments harmless, this is a convenience step
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