@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/dist/viewer.css CHANGED
@@ -1,18 +1,18 @@
1
1
 
2
2
  :root {
3
3
  color-scheme: dark;
4
- --bg: #2b2b2b;
5
- --panel: #2b2b2b;
4
+ --bg: #161616;
5
+ --panel: #1b1b1b;
6
6
  --text: #a9b7c6;
7
7
  --muted: #808080;
8
8
  --border: #393b3d;
9
9
  --line: #313335;
10
- --add: #2f3d2c;
11
- --del: #4b3434;
12
- --add-strong: #3d5238;
13
- --del-strong: #6b4242;
10
+ --add: #16351f;
11
+ --del: #3f1d1d;
12
+ --add-strong: #1f5b34;
13
+ --del-strong: #6e2c2c;
14
14
  --active: #4a88c7;
15
- --sidebar: #3c3f41;
15
+ --sidebar: #1c1c1c;
16
16
  --token-comment: #808080;
17
17
  --token-keyword: #cc7832;
18
18
  --token-string: #6a8759;
@@ -127,6 +127,10 @@ body {
127
127
  }
128
128
  .tab.active, .plain-button:hover { border-color: var(--active); color: var(--active); }
129
129
  .hidden { display: none !important; }
130
+ /* IntelliJ-style git status on sidebar file names: untracked(new)=red, modified=blue, staged(git add)=green. */
131
+ .vcs-new.source-link .path, .vcs-new.change-row .change-name { color: #d36c6c; }
132
+ .vcs-edited.source-link .path, .vcs-edited.change-row .change-name { color: #6c9fd4; }
133
+ .vcs-staged.source-link .path, .vcs-staged.change-row .change-name { color: #7faf6b; }
130
134
  .sidebar-footer {
131
135
  flex: 0 0 auto;
132
136
  padding: 8px 12px;
@@ -138,10 +142,9 @@ body {
138
142
  font-size: 11px;
139
143
  color: var(--muted);
140
144
  }
141
- .sidebar-footer .app-version { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
145
+ .sidebar-footer .app-version { margin-right: auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
142
146
  .app-update-flag { color: var(--active); font-weight: 600; cursor: pointer; white-space: nowrap; }
143
147
  .settings-btn {
144
- margin-left: auto;
145
148
  flex: none;
146
149
  border: 1px solid transparent;
147
150
  border-radius: 6px;
@@ -152,7 +155,7 @@ body {
152
155
  padding: 3px 6px;
153
156
  cursor: pointer;
154
157
  }
155
- .settings-btn:hover { color: var(--active); border-color: var(--border); }
158
+ .settings-btn:hover { color: var(--text); background: color-mix(in srgb, var(--active) 12%, transparent); }
156
159
  .app-info {
157
160
  position: fixed;
158
161
  left: 12px;
@@ -174,24 +177,58 @@ body {
174
177
  .app-info-head .app-info-ver { color: var(--muted); font-weight: 500; }
175
178
  .app-info-status { color: var(--muted); margin-bottom: 10px; }
176
179
  .app-info-status.has-update { color: var(--active); font-weight: 600; }
177
- .app-info-cmd { display: flex; align-items: center; gap: 6px; }
178
- .app-info-cmd code {
179
- flex: 1 1 auto;
180
- min-width: 0;
181
- overflow: hidden;
182
- text-overflow: ellipsis;
183
- white-space: nowrap;
184
- padding: 5px 8px;
185
- border-radius: 6px;
186
- background: var(--bg);
187
- border: 1px solid var(--border);
188
- font: 11px Monaco, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
189
- color: var(--text);
190
- }
191
- .app-info-cmd .plain-button { padding: 5px 9px; font-size: 11px; }
180
+ .app-info-update { width: 100%; margin: 0 0 10px; border-color: var(--active); color: var(--active); font-weight: 600; }
181
+ .app-info-update:hover { background: color-mix(in srgb, var(--active) 14%, transparent); color: var(--active); }
182
+ .app-info-update:disabled { opacity: 0.55; cursor: default; }
192
183
  .app-info-keys { margin-top: 12px; border-top: 1px solid var(--border); padding-top: 10px; }
184
+
185
+ /* Settings modal (Cmd+Shift+/ merge prompts, etc.) — sidebar categories + form, like a desktop app. */
186
+ .settings-modal {
187
+ position: fixed; inset: 0; z-index: 70;
188
+ display: flex; align-items: center; justify-content: center;
189
+ background: rgba(0, 0, 0, 0.6); padding: 32px;
190
+ }
191
+ .settings-modal.hidden { display: none; }
192
+ .settings-panel {
193
+ display: flex;
194
+ width: 980px; max-width: 90vw; height: 700px; max-height: 88vh;
195
+ background: var(--panel); border: 1px solid var(--border); border-radius: 12px;
196
+ overflow: hidden; box-shadow: 0 12px 48px rgba(0, 0, 0, 0.55);
197
+ }
198
+ .settings-nav {
199
+ flex: 0 0 200px; padding: 16px 10px;
200
+ background: var(--sidebar); border-right: 1px solid var(--border);
201
+ display: flex; flex-direction: column; gap: 4px;
202
+ }
203
+ .settings-nav-title { font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted); padding: 4px 10px 8px; }
204
+ .settings-cat { text-align: left; border: 0; background: transparent; color: var(--text); padding: 7px 10px; border-radius: 6px; font-size: 13px; cursor: pointer; }
205
+ .settings-cat:hover { background: color-mix(in srgb, var(--active) 12%, transparent); }
206
+ .settings-cat.active { background: color-mix(in srgb, var(--active) 20%, transparent); color: var(--active); font-weight: 600; }
207
+ .settings-body { flex: 1 1 auto; min-width: 0; padding: 24px 28px; overflow: auto; }
208
+ .settings-h { font-size: 16px; font-weight: 650; color: var(--text); margin-bottom: 6px; }
209
+ .settings-ver { color: var(--muted); font-weight: 500; font-size: 0.62em; letter-spacing: 0.02em; }
210
+ .settings-desc { font-size: 12px; color: var(--muted); line-height: 1.6; margin-bottom: 18px; }
211
+ .settings-label { display: block; font-size: 12px; font-weight: 600; color: var(--text); margin: 14px 0 6px; }
212
+ .settings-textarea {
213
+ width: 100%; box-sizing: border-box; resize: vertical; min-height: 70px;
214
+ background: var(--bg); color: var(--text);
215
+ border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px;
216
+ font: 12px/1.55 ui-sans-serif, system-ui, sans-serif;
217
+ }
218
+ .settings-textarea:focus { outline: none; border-color: var(--active); }
219
+ .settings-select {
220
+ width: auto; box-sizing: border-box;
221
+ background: var(--bg); color: var(--text);
222
+ border: 1px solid var(--border); border-radius: 8px; padding: 8px 12px;
223
+ font: 12px/1.55 ui-sans-serif, system-ui, sans-serif;
224
+ }
225
+ .settings-select:focus { outline: none; border-color: var(--active); }
226
+ .settings-actions { display: flex; align-items: center; gap: 12px; margin-top: 18px; }
227
+ .settings-saved { font-size: 12px; color: var(--active); }
193
228
  .app-info-keys-h { font-weight: 600; color: var(--text); margin-bottom: 8px; }
194
- .keys-grid { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 5px 10px; align-items: center; }
229
+ .keys-cat { font-size: 10px; font-weight: 600; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted); margin: 14px 0 6px; }
230
+ .app-info-keys .keys-cat:first-of-type { margin-top: 4px; }
231
+ .keys-grid { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 5px 10px; align-items: center; margin-bottom: 2px; }
195
232
  .keys-grid kbd {
196
233
  justify-self: start;
197
234
  font: 10px Monaco, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
@@ -299,6 +336,10 @@ body {
299
336
  -webkit-user-select: text;
300
337
  user-select: text;
301
338
  }
339
+ /* Changed lines: lift the base text brighter than the gray context so additions/deletions read as
340
+ edits, not the same gray. hljs tokens keep their own colors; this only affects unhighlighted text. */
341
+ td.d2h-ins:not(.d2h-code-side-linenumber),
342
+ td.d2h-del:not(.d2h-code-side-linenumber) { color: #d8e0e8; }
302
343
  .d2h-code-line-prefix { -webkit-user-select: none; user-select: none; }
303
344
  .d2h-code-side-linenumber, .d2h-code-linenumber { -webkit-user-select: none; user-select: none; }
304
345
  .d2h-code-line-ctn .hljs-keyword,
@@ -547,6 +588,40 @@ h1 { margin: 0; font-size: 18px; }
547
588
  text-overflow: ellipsis;
548
589
  white-space: nowrap;
549
590
  }
591
+ /* Files-mode tabs: one row of tabs above the source toolbar (open files, click to switch, × to close). */
592
+ .source-tabs {
593
+ display: flex;
594
+ overflow-x: auto;
595
+ gap: 1px;
596
+ margin-bottom: 8px;
597
+ background: var(--sidebar);
598
+ border: 1px solid var(--border);
599
+ border-radius: 8px;
600
+ scrollbar-width: thin;
601
+ }
602
+ .source-tab {
603
+ display: inline-flex;
604
+ align-items: center;
605
+ gap: 6px;
606
+ flex: 0 0 auto;
607
+ max-width: 220px;
608
+ padding: 6px 8px 6px 12px;
609
+ border-right: 1px solid var(--border);
610
+ background: var(--panel);
611
+ color: var(--muted);
612
+ font-size: 12px;
613
+ cursor: pointer;
614
+ white-space: nowrap;
615
+ }
616
+ .source-tab:first-child { border-top-left-radius: 8px; border-bottom-left-radius: 8px; }
617
+ .source-tab:hover { color: var(--text); }
618
+ .source-tab.active { color: var(--text); background: var(--bg); box-shadow: inset 0 -2px 0 var(--active); }
619
+ .source-tab-name { overflow: hidden; text-overflow: ellipsis; }
620
+ .source-tab-close {
621
+ flex: none; border: 0; background: transparent; color: var(--muted);
622
+ font-size: 14px; line-height: 1; padding: 0 3px; cursor: pointer; border-radius: 3px;
623
+ }
624
+ .source-tab-close:hover { background: var(--line); color: var(--text); }
550
625
  .source-body {
551
626
  border: 1px solid var(--border);
552
627
  border-radius: 8px;
@@ -578,6 +653,10 @@ h1 { margin: 0; font-size: 18px; }
578
653
  gracefully where unsupported. contain-intrinsic-size keeps the scrollbar stable. */
579
654
  .source-row { content-visibility: auto; contain-intrinsic-size: auto 19px; }
580
655
  .d2h-diff-table tr { content-visibility: auto; contain-intrinsic-size: auto 18px; }
656
+ /* Comment/composer rows are tall and interactive (a textarea lives here). Skip-rendering them
657
+ with a tiny 18px placeholder made the browser re-evaluate their render state on every
658
+ keystroke, so typing in the composer stuttered — keep these rows always rendered. */
659
+ .d2h-diff-table tr.mc-comment-row, .d2h-diff-table tr.mc-spacer-row { content-visibility: visible; contain-intrinsic-size: auto; }
581
660
  .source-row.search-hit .source-code { background: color-mix(in srgb, var(--active) 14%, transparent); }
582
661
  .source-row.changed-line .source-code { background: color-mix(in srgb, var(--active) 9%, transparent); box-shadow: inset 2px 0 0 color-mix(in srgb, var(--active) 55%, transparent); }
583
662
  .source-row.symbol-target .source-code {
@@ -601,6 +680,13 @@ h1 { margin: 0; font-size: 18px; }
601
680
  @keyframes cursor-blink {
602
681
  50% { opacity: 0; }
603
682
  }
683
+ /* Keep the caret solid while it is actively moving (held arrow key / typing); blink only when idle. */
684
+ .caret-busy .code-cursor { animation: none; opacity: 1; }
685
+ /* Markdown/CSV rendered rows have no inline caret span — mark the whole cursor row instead. */
686
+ .source-row.md-row.cursor-line .md-cell,
687
+ .source-row.csv-row.cursor-line .csv-cell { background: color-mix(in srgb, #000 26%, transparent); }
688
+ .source-row.md-row.cursor-line .num,
689
+ .source-row.csv-row.cursor-line .num { color: var(--active); }
604
690
  .num {
605
691
  width: 58px;
606
692
  user-select: none;
@@ -631,29 +717,41 @@ h1 { margin: 0; font-size: 18px; }
631
717
  .mc-thread-cell { padding: 4px 12px 8px 64px; }
632
718
  .source-table .mc-thread-cell { padding: 4px 12px 8px 66px; }
633
719
  .mc-card {
634
- border: 1px solid var(--border); border-left: 3px solid var(--muted);
635
- border-radius: 6px; background: var(--panel); margin: 6px 0; max-width: 760px;
720
+ border: 1px solid var(--border);
721
+ border-radius: 8px; background: var(--panel); margin: 8px 0; max-width: 760px;
636
722
  font: 12px/1.5 Monaco, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
723
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.28);
724
+ overflow: hidden;
637
725
  }
638
- .mc-card.mc-q { border-left-color: var(--token-number); }
639
- .mc-card.mc-c { border-left-color: var(--token-tag); }
640
- .mc-card-head { display: flex; align-items: center; gap: 8px; padding: 5px 9px; border-bottom: 1px solid var(--border); color: var(--muted); }
641
- .mc-kind { font-weight: 650; color: var(--text); }
642
- .mc-del { margin-left: auto; background: transparent; border: 0; color: var(--muted); cursor: pointer; font-size: 15px; line-height: 1; padding: 0 2px; }
643
- .mc-del:hover { color: var(--del-strong); }
644
- .mc-card-body { padding: 7px 10px; color: var(--text); white-space: pre-wrap; overflow-wrap: anywhere; }
726
+ /* Question vs change-request is a small colored pill on the kind label — no heavy left bar. */
727
+ .mc-card-head { display: flex; align-items: center; gap: 8px; padding: 8px 10px 6px; color: var(--muted); }
728
+ .mc-kind {
729
+ font-weight: 700; font-size: 10px; letter-spacing: 0.05em; text-transform: uppercase;
730
+ padding: 2px 8px; border-radius: 999px;
731
+ color: var(--muted); background: color-mix(in srgb, var(--muted) 16%, transparent);
732
+ }
733
+ .mc-card.mc-q .mc-kind { color: var(--token-number); background: color-mix(in srgb, var(--token-number) 18%, transparent); }
734
+ .mc-card.mc-c .mc-kind { color: var(--token-tag); background: color-mix(in srgb, var(--token-tag) 18%, transparent); }
735
+ .mc-del { margin-left: auto; background: transparent; border: 0; color: var(--muted); cursor: pointer; font-size: 15px; line-height: 1; padding: 1px 5px; border-radius: 5px; }
736
+ .mc-del:hover { color: var(--del-strong); background: color-mix(in srgb, var(--del-strong) 16%, transparent); }
737
+ .mc-card-body { padding: 2px 12px 11px; color: var(--text); white-space: pre-wrap; overflow-wrap: anywhere; }
645
738
  .mc-input {
646
739
  display: block; box-sizing: border-box; resize: vertical;
647
- margin: 8px 10px; width: calc(100% - 20px); min-height: 56px;
740
+ margin: 0 10px 10px; width: calc(100% - 20px); min-height: 60px;
648
741
  background: var(--bg); color: var(--text);
649
- border: 1px solid var(--border); border-radius: 6px; padding: 7px 9px;
650
- font: 12px/1.5 Monaco, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
742
+ border: 1px solid var(--border); border-radius: 7px; padding: 9px 11px;
743
+ font: 12px/1.55 Monaco, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
744
+ transition: border-color 120ms ease, box-shadow 120ms ease;
651
745
  }
652
- .mc-input:focus { outline: none; border-color: var(--active); }
746
+ .mc-input:focus { outline: none; border-color: var(--active); box-shadow: 0 0 0 3px color-mix(in srgb, var(--active) 22%, transparent); }
653
747
  .mc-actions { display: flex; align-items: center; gap: 8px; padding: 0 10px 9px; }
654
748
  .mc-btn { background: var(--active); color: #fff; border: 0; border-radius: 6px; padding: 5px 12px; font-size: 12px; cursor: pointer; }
655
749
  .mc-btn:hover { filter: brightness(1.1); }
656
750
  .mc-btn.mc-ghost { background: transparent; border: 1px solid var(--border); color: var(--text); }
751
+ /* Terminal-send buttons in the merged modal: green to read as "send to the live session", with a clear
752
+ focus ring so arrow-key selection between split panes is visible. */
753
+ .mc-send-term { background: #2f8f46; }
754
+ .mc-send-term:focus, .mc-send-term:focus-visible { outline: 2px solid #fff; outline-offset: 1px; }
657
755
  .mc-hint { color: var(--muted); font-size: 11px; }
658
756
  .mc-modal { position: fixed; inset: 0; z-index: 60; display: grid; place-items: start center; padding-top: min(10vh, 80px); background: color-mix(in srgb, #000 32%, transparent); }
659
757
  .mc-modal.hidden { display: none; }
@@ -937,3 +1035,60 @@ h1 { margin: 0; font-size: 18px; }
937
1035
  .csv-head .csv-cell { background: var(--line); color: #d7e0ea; font-weight: 650; }
938
1036
  .csv-row:nth-child(even) .csv-cell { background: rgba(255, 255, 255, 0.018); }
939
1037
  .csv-empty { color: var(--muted); font-style: italic; padding: 16px; }
1038
+
1039
+ /* --- Integrated terminal panel (Electron only): fixed to the content column's bottom, height-resizable.
1040
+ The active border-top marks it as the merged-prompt send target ("highlighted while it's open"). --- */
1041
+ .terminal-panel {
1042
+ position: fixed;
1043
+ left: var(--sidebar-width, 280px);
1044
+ right: 0;
1045
+ bottom: 0;
1046
+ height: var(--terminal-height, 320px);
1047
+ z-index: 40;
1048
+ display: flex;
1049
+ flex-direction: column;
1050
+ background: var(--bg);
1051
+ border-top: 2px solid var(--active);
1052
+ box-shadow: 0 -10px 28px rgba(0, 0, 0, 0.45);
1053
+ }
1054
+ .terminal-resizer { flex: none; height: 6px; margin-top: -3px; cursor: row-resize; }
1055
+ .terminal-resizer:hover, .terminal-resizer.resizing { background: var(--active); }
1056
+ .terminal-bar {
1057
+ flex: none; display: flex; align-items: center; justify-content: space-between;
1058
+ padding: 4px 10px; background: var(--sidebar); border-bottom: 1px solid var(--border);
1059
+ font-size: 11px; color: var(--muted);
1060
+ }
1061
+ .terminal-title { letter-spacing: 0.08em; text-transform: uppercase; }
1062
+ .terminal-x { border: 0; background: transparent; color: var(--muted); font-size: 16px; line-height: 1; padding: 0 4px; cursor: pointer; }
1063
+ .terminal-x:hover { color: var(--text); }
1064
+ /* Split panes sit side by side (no tabs); the 1px gap shows the --border underneath as a divider. */
1065
+ .terminal-host { flex: 1 1 auto; min-height: 0; display: flex; gap: 1px; overflow: hidden; background: var(--border); }
1066
+ .terminal-pane { flex: 1 1 0; min-width: 0; background: var(--bg); overflow: hidden; display: flex; flex-direction: column; }
1067
+ /* Per-pane name strip at the top; double-click or Cmd/Ctrl+Alt+R to rename inline. */
1068
+ .terminal-pane-label {
1069
+ flex: none; padding: 2px 10px; font-size: 10px; letter-spacing: 0.04em;
1070
+ color: var(--muted); background: var(--sidebar); border-bottom: 1px solid var(--border);
1071
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: text;
1072
+ }
1073
+ .terminal-pane-label:hover { color: var(--text); }
1074
+ .terminal-pane-label[contenteditable="true"] { color: var(--text); background: var(--bg); outline: none; }
1075
+ .terminal-pane.is-active .terminal-pane-label { color: var(--text); }
1076
+ .terminal-pane-host { flex: 1 1 auto; min-width: 0; min-height: 0; padding: 4px 0 4px 8px; }
1077
+ .terminal-pane.is-active { box-shadow: inset 2px 0 0 var(--active); }
1078
+ /* Pane-pick mode (merged "Send to terminal"): chosen pane ringed + full opacity, the rest dimmed. */
1079
+ .terminal-panel.send-mode .terminal-pane { transition: opacity 120ms ease; }
1080
+ .terminal-pane.is-dimmed { opacity: 0.3; }
1081
+ .terminal-pane.is-send-target { box-shadow: inset 0 0 0 2px var(--active); opacity: 1; position: relative; }
1082
+ /* Faint ⏎ hint floating over the chosen pane; it vanishes the instant Enter exits send mode. */
1083
+ .terminal-pane.is-send-target::after {
1084
+ content: "⏎";
1085
+ position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
1086
+ font-size: 56px; color: var(--active); opacity: 0.32; pointer-events: none; z-index: 5;
1087
+ }
1088
+ /* Send mode dims the rest of the app (sidebar + file/diff view) so only the terminal pops. */
1089
+ body.terminal-send-mode .sidebar,
1090
+ body.terminal-send-mode .content,
1091
+ body.terminal-send-mode .sidebar-resizer { opacity: 0.25; pointer-events: none; }
1092
+ /* Pad the content tail so it isn't hidden behind the fixed panel while the terminal is open. */
1093
+ body.terminal-open .content { padding-bottom: var(--terminal-height, 320px); }
1094
+ .terminal-toggle.is-active { color: var(--active); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happy-nut/monacori",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Validation control plane for AI-generated code changes.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -50,8 +50,12 @@
50
50
  "node": ">=20"
51
51
  },
52
52
  "dependencies": {
53
+ "@electron/rebuild": "^4.0.4",
54
+ "@xterm/addon-fit": "^0.11.0",
55
+ "@xterm/xterm": "^6.0.0",
53
56
  "diff2html": "^3.4.56",
54
57
  "electron": "^42.4.1",
55
- "highlight.js": "^11.11.1"
58
+ "highlight.js": "^11.11.1",
59
+ "node-pty": "^1.1.0"
56
60
  }
57
61
  }
@@ -1,4 +1,5 @@
1
1
  import { existsSync, readFileSync, renameSync, writeFileSync } from "node:fs";
2
+ import { spawnSync } from "node:child_process";
2
3
  import { createRequire } from "node:module";
3
4
  import { dirname, join } from "node:path";
4
5
 
@@ -48,6 +49,13 @@ function main() {
48
49
  const fixed = pt.replace("MacOS/Electron", "MacOS/" + APP_NAME);
49
50
  if (fixed !== pt) writeFileSync(pathTxt, fixed);
50
51
  }
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
+ );
51
59
  console.log('monacori: branded Electron app + executable as "' + APP_NAME + '"');
52
60
  } catch {
53
61
  // read-only / permission-denied environments — harmless, this is a convenience step