@happy-nut/monacori 0.1.2 → 0.1.5

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,
@@ -424,14 +465,16 @@ body {
424
465
  }
425
466
  .tree-dir summary::-webkit-details-marker { display: none; }
426
467
  .tree-dir summary:hover { background: var(--bg); }
427
- .tree-dir:not([open]) .folder-icon { transform: rotate(-90deg); }
428
468
  .folder-icon {
429
469
  display: inline-grid;
430
470
  place-items: center;
431
- font-size: 9px;
432
471
  color: var(--muted);
433
- transition: transform 120ms ease;
434
472
  }
473
+ .folder-icon .folder-ic { width: 14px; height: 14px; display: block; }
474
+ /* Closed vs open folder glyph (replaces the old rotated "v" chevron). */
475
+ .tree-dir > summary .fi-open { display: none; }
476
+ .tree-dir[open] > summary .fi-closed { display: none; }
477
+ .tree-dir[open] > summary .fi-open { display: block; }
435
478
  .file-link.tree-file { padding-left: calc(8px + (var(--depth) * 14px)); }
436
479
  .tree-focus { box-shadow: inset 0 0 0 1px var(--active); border-radius: 6px; }
437
480
  summary.tree-focus { background: var(--bg); }
@@ -483,7 +526,20 @@ summary.tree-focus { background: var(--bg); }
483
526
  .status-deleted { background: var(--del); color: #cf222e; }
484
527
  .status-renamed { background: #fff8c5; color: #9a6700; }
485
528
  .status-source { background: var(--line); color: var(--muted); }
486
- .content { min-width: 0; padding: 0; }
529
+ .content { min-width: 0; padding: 0; display: flex; flex-direction: column; min-height: 100vh; }
530
+ /* Pin the diff's horizontal scrollbar to the viewport bottom instead of letting it float
531
+ mid-screen when a file's diff is short: fill the content column vertically so the last
532
+ file's diff body extends all the way down. */
533
+ #diff-view:not(.hidden) { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; }
534
+ #diff-view .diff2html-container { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; }
535
+ .diff2html-container .d2h-wrapper { flex: 1 1 auto; display: flex; flex-direction: column; }
536
+ .diff2html-container .d2h-file-wrapper:last-child { flex: 1 1 auto; }
537
+ .diff2html-container .d2h-file-wrapper:last-child .d2h-files-diff { height: 100%; }
538
+ /* Slimmer scrollbars — the default overlay bars read as chunky on the dark UI. */
539
+ ::-webkit-scrollbar { width: 9px; height: 9px; }
540
+ ::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--muted) 32%, transparent); border-radius: 5px; }
541
+ ::-webkit-scrollbar-thumb:hover { background: color-mix(in srgb, var(--muted) 52%, transparent); }
542
+ ::-webkit-scrollbar-track { background: transparent; }
487
543
  .toolbar {
488
544
  position: sticky;
489
545
  top: 0;
@@ -547,6 +603,40 @@ h1 { margin: 0; font-size: 18px; }
547
603
  text-overflow: ellipsis;
548
604
  white-space: nowrap;
549
605
  }
606
+ /* Files-mode tabs: one row of tabs above the source toolbar (open files, click to switch, × to close). */
607
+ .source-tabs {
608
+ display: flex;
609
+ overflow-x: auto;
610
+ gap: 1px;
611
+ margin-bottom: 8px;
612
+ background: var(--sidebar);
613
+ border: 1px solid var(--border);
614
+ border-radius: 8px;
615
+ scrollbar-width: thin;
616
+ }
617
+ .source-tab {
618
+ display: inline-flex;
619
+ align-items: center;
620
+ gap: 6px;
621
+ flex: 0 0 auto;
622
+ max-width: 220px;
623
+ padding: 6px 8px 6px 12px;
624
+ border-right: 1px solid var(--border);
625
+ background: var(--panel);
626
+ color: var(--muted);
627
+ font-size: 12px;
628
+ cursor: pointer;
629
+ white-space: nowrap;
630
+ }
631
+ .source-tab:first-child { border-top-left-radius: 8px; border-bottom-left-radius: 8px; }
632
+ .source-tab:hover { color: var(--text); }
633
+ .source-tab.active { color: var(--text); background: var(--bg); box-shadow: inset 0 -2px 0 var(--active); }
634
+ .source-tab-name { overflow: hidden; text-overflow: ellipsis; }
635
+ .source-tab-close {
636
+ flex: none; border: 0; background: transparent; color: var(--muted);
637
+ font-size: 14px; line-height: 1; padding: 0 3px; cursor: pointer; border-radius: 3px;
638
+ }
639
+ .source-tab-close:hover { background: var(--line); color: var(--text); }
550
640
  .source-body {
551
641
  border: 1px solid var(--border);
552
642
  border-radius: 8px;
@@ -554,6 +644,31 @@ h1 { margin: 0; font-size: 18px; }
554
644
  background: var(--panel);
555
645
  user-select: text;
556
646
  }
647
+ /* Extend the line-number gutter (and its divider) to the bottom of the panel so it never stops at the
648
+ last line with an empty strip + cut-off border below it — one continuous gutter. The .num cell is 58px
649
+ wide + 8px padding each side + 1px border ≈ 75px; the gradient mirrors that. (pre-wrap = no h-scroll, so
650
+ a fixed background stays aligned.) */
651
+ .source-body:not(.empty):not(.image-body) {
652
+ background-image: linear-gradient(to right, var(--line) 0 74px, var(--border) 74px 75px, var(--panel) 75px);
653
+ background-repeat: no-repeat;
654
+ }
655
+ /* Boot overlay: painted the instant the review HTML loads, removed once the renderer's bootstrap has drawn
656
+ the diff/tree — so there's no blank screen between the startup spinner and first render. Mirrors
657
+ app-main's LOADING_HTML spinner exactly so the hand-off from the loading screen is seamless. */
658
+ #boot-overlay {
659
+ position: fixed; inset: 0; z-index: 200;
660
+ display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 18px;
661
+ background: #2b2b2b; color: #9aa4af;
662
+ font: 13px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
663
+ transition: opacity 0.22s ease;
664
+ }
665
+ #boot-overlay.hide { opacity: 0; pointer-events: none; }
666
+ .boot-spinner {
667
+ width: 34px; height: 34px;
668
+ border: 3px solid #3a3a3a; border-top-color: #4a9eff; border-radius: 50%;
669
+ animation: boot-spin 0.8s linear infinite;
670
+ }
671
+ @keyframes boot-spin { to { transform: rotate(360deg); } }
557
672
  /* Empty state ("Select a file…") centered in the available space, not top-left. */
558
673
  .source-body.empty {
559
674
  display: flex;
@@ -578,6 +693,10 @@ h1 { margin: 0; font-size: 18px; }
578
693
  gracefully where unsupported. contain-intrinsic-size keeps the scrollbar stable. */
579
694
  .source-row { content-visibility: auto; contain-intrinsic-size: auto 19px; }
580
695
  .d2h-diff-table tr { content-visibility: auto; contain-intrinsic-size: auto 18px; }
696
+ /* Comment/composer rows are tall and interactive (a textarea lives here). Skip-rendering them
697
+ with a tiny 18px placeholder made the browser re-evaluate their render state on every
698
+ keystroke, so typing in the composer stuttered — keep these rows always rendered. */
699
+ .d2h-diff-table tr.mc-comment-row, .d2h-diff-table tr.mc-spacer-row { content-visibility: visible; contain-intrinsic-size: auto; }
581
700
  .source-row.search-hit .source-code { background: color-mix(in srgb, var(--active) 14%, transparent); }
582
701
  .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
702
  .source-row.symbol-target .source-code {
@@ -601,8 +720,16 @@ h1 { margin: 0; font-size: 18px; }
601
720
  @keyframes cursor-blink {
602
721
  50% { opacity: 0; }
603
722
  }
723
+ /* Keep the caret solid while it is actively moving (held arrow key / typing); blink only when idle. */
724
+ .caret-busy .code-cursor { animation: none; opacity: 1; }
725
+ /* Markdown/CSV rendered rows have no inline caret span — mark the whole cursor row instead. */
726
+ .source-row.md-row.cursor-line .md-cell,
727
+ .source-row.csv-row.cursor-line .csv-cell { background: color-mix(in srgb, #000 26%, transparent); }
728
+ .source-row.md-row.cursor-line .num,
729
+ .source-row.csv-row.cursor-line .num { color: var(--active); }
604
730
  .num {
605
- width: 58px;
731
+ width: 75px;
732
+ box-sizing: border-box; /* fixed 75px total so the .source-body gutter gradient lines up exactly (no bleed) */
606
733
  user-select: none;
607
734
  text-align: right;
608
735
  color: var(--muted);
@@ -631,29 +758,41 @@ h1 { margin: 0; font-size: 18px; }
631
758
  .mc-thread-cell { padding: 4px 12px 8px 64px; }
632
759
  .source-table .mc-thread-cell { padding: 4px 12px 8px 66px; }
633
760
  .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;
761
+ border: 1px solid var(--border);
762
+ border-radius: 8px; background: var(--panel); margin: 8px 0; max-width: 760px;
636
763
  font: 12px/1.5 Monaco, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
764
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.28);
765
+ overflow: hidden;
637
766
  }
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; }
767
+ /* Question vs change-request is a small colored pill on the kind label — no heavy left bar. */
768
+ .mc-card-head { display: flex; align-items: center; gap: 8px; padding: 8px 10px 6px; color: var(--muted); }
769
+ .mc-kind {
770
+ font-weight: 700; font-size: 10px; letter-spacing: 0.05em; text-transform: uppercase;
771
+ padding: 2px 8px; border-radius: 999px;
772
+ color: var(--muted); background: color-mix(in srgb, var(--muted) 16%, transparent);
773
+ }
774
+ .mc-card.mc-q .mc-kind { color: var(--token-number); background: color-mix(in srgb, var(--token-number) 18%, transparent); }
775
+ .mc-card.mc-c .mc-kind { color: var(--token-tag); background: color-mix(in srgb, var(--token-tag) 18%, transparent); }
776
+ .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; }
777
+ .mc-del:hover { color: var(--del-strong); background: color-mix(in srgb, var(--del-strong) 16%, transparent); }
778
+ .mc-card-body { padding: 2px 12px 11px; color: var(--text); white-space: pre-wrap; overflow-wrap: anywhere; }
645
779
  .mc-input {
646
780
  display: block; box-sizing: border-box; resize: vertical;
647
- margin: 8px 10px; width: calc(100% - 20px); min-height: 56px;
781
+ margin: 0 10px 10px; width: calc(100% - 20px); min-height: 60px;
648
782
  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;
783
+ border: 1px solid var(--border); border-radius: 7px; padding: 9px 11px;
784
+ font: 12px/1.55 Monaco, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
785
+ transition: border-color 120ms ease, box-shadow 120ms ease;
651
786
  }
652
- .mc-input:focus { outline: none; border-color: var(--active); }
787
+ .mc-input:focus { outline: none; border-color: var(--active); box-shadow: 0 0 0 3px color-mix(in srgb, var(--active) 22%, transparent); }
653
788
  .mc-actions { display: flex; align-items: center; gap: 8px; padding: 0 10px 9px; }
654
789
  .mc-btn { background: var(--active); color: #fff; border: 0; border-radius: 6px; padding: 5px 12px; font-size: 12px; cursor: pointer; }
655
790
  .mc-btn:hover { filter: brightness(1.1); }
656
791
  .mc-btn.mc-ghost { background: transparent; border: 1px solid var(--border); color: var(--text); }
792
+ /* Terminal-send buttons in the merged modal: green to read as "send to the live session", with a clear
793
+ focus ring so arrow-key selection between split panes is visible. */
794
+ .mc-send-term { background: #2f8f46; }
795
+ .mc-send-term:focus, .mc-send-term:focus-visible { outline: 2px solid #fff; outline-offset: 1px; }
657
796
  .mc-hint { color: var(--muted); font-size: 11px; }
658
797
  .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
798
  .mc-modal.hidden { display: none; }
@@ -662,6 +801,12 @@ h1 { margin: 0; font-size: 18px; }
662
801
  .mc-modal-head span { margin-right: auto; }
663
802
  .mc-modal-text { width: 100%; height: 100%; box-sizing: border-box; resize: none; border: 0; padding: 12px; background: var(--bg); color: var(--text); font: 12px/1.55 Monaco, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
664
803
  .mc-modal-text:focus { outline: none; }
804
+ /* Prompt memo: split editor | live Markdown preview inside the standard modal shell. */
805
+ .mc-memo-body { display: grid; grid-template-columns: 1fr 1fr; min-height: 0; height: 100%; }
806
+ .mc-memo-edit { height: 100%; border-right: 1px solid var(--border); }
807
+ .mc-memo-preview { height: 100%; overflow: auto; padding: 12px 16px; background: var(--panel); color: var(--text); }
808
+ .mc-memo-preview > :first-child { margin-top: 0; }
809
+ .mc-memo-empty { color: var(--muted); font-size: 12px; font-style: italic; }
665
810
  .tok-comment { color: var(--token-comment); font-style: italic; }
666
811
  .tok-keyword { color: var(--token-keyword); font-weight: 650; }
667
812
  .tok-string { color: var(--token-string); }
@@ -937,3 +1082,62 @@ h1 { margin: 0; font-size: 18px; }
937
1082
  .csv-head .csv-cell { background: var(--line); color: #d7e0ea; font-weight: 650; }
938
1083
  .csv-row:nth-child(even) .csv-cell { background: rgba(255, 255, 255, 0.018); }
939
1084
  .csv-empty { color: var(--muted); font-style: italic; padding: 16px; }
1085
+
1086
+ /* --- Integrated terminal panel (Electron only): fixed to the content column's bottom, height-resizable.
1087
+ The active border-top marks it as the merged-prompt send target ("highlighted while it's open"). --- */
1088
+ .terminal-panel {
1089
+ position: fixed;
1090
+ left: var(--sidebar-width, 280px);
1091
+ right: 0;
1092
+ bottom: 0;
1093
+ height: var(--terminal-height, 320px);
1094
+ z-index: 40;
1095
+ display: flex;
1096
+ flex-direction: column;
1097
+ background: var(--bg);
1098
+ border-top: 2px solid var(--active);
1099
+ box-shadow: 0 -10px 28px rgba(0, 0, 0, 0.45);
1100
+ }
1101
+ .terminal-resizer { flex: none; height: 6px; margin-top: -3px; cursor: row-resize; }
1102
+ .terminal-resizer:hover, .terminal-resizer.resizing { background: var(--active); }
1103
+ .terminal-bar {
1104
+ flex: none; display: flex; align-items: center; justify-content: space-between;
1105
+ padding: 4px 10px; background: var(--sidebar); border-bottom: 1px solid var(--border);
1106
+ font-size: 11px; color: var(--muted);
1107
+ }
1108
+ .terminal-title { letter-spacing: 0.08em; text-transform: uppercase; }
1109
+ .terminal-x { border: 0; background: transparent; color: var(--muted); font-size: 16px; line-height: 1; padding: 0 4px; cursor: pointer; }
1110
+ .terminal-x:hover { color: var(--text); }
1111
+ /* Split panes sit side by side (no tabs); the 1px gap shows the --border underneath as a divider. */
1112
+ .terminal-host { flex: 1 1 auto; min-height: 0; display: flex; gap: 1px; overflow: hidden; background: var(--border); }
1113
+ .terminal-pane { flex: 1 1 0; min-width: 0; background: var(--bg); overflow: hidden; display: flex; flex-direction: column; }
1114
+ /* Per-pane name strip at the top; double-click or Cmd/Ctrl+Alt+R to rename inline. */
1115
+ .terminal-pane-label {
1116
+ flex: none; padding: 2px 10px; font-size: 10px; letter-spacing: 0.04em;
1117
+ color: var(--muted); background: var(--sidebar); border-bottom: 1px solid var(--border);
1118
+ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; cursor: text;
1119
+ }
1120
+ .terminal-pane-label:hover { color: var(--text); }
1121
+ .terminal-pane-label[contenteditable="true"] { color: var(--text); background: var(--bg); outline: none; }
1122
+ .terminal-pane.is-active .terminal-pane-label { color: var(--text); }
1123
+ .terminal-pane-host { flex: 1 1 auto; min-width: 0; min-height: 0; padding: 4px 0 4px 8px; }
1124
+ /* No border on the active pane — the inactive panes dim back instead, so the focused one stands out
1125
+ cleanly without an outline. Only applies with 2+ panes (a lone pane is never dimmed; see setActive). */
1126
+ .terminal-pane { transition: opacity 120ms ease; }
1127
+ .terminal-panel:not(.send-mode) .terminal-pane.is-inactive { opacity: 0.4; }
1128
+ /* Pane-pick mode (merged "Send to terminal"): chosen pane ringed + full opacity, the rest dimmed. */
1129
+ .terminal-pane.is-dimmed { opacity: 0.3; }
1130
+ .terminal-pane.is-send-target { box-shadow: inset 0 0 0 2px var(--active); opacity: 1; position: relative; }
1131
+ /* Faint ⏎ hint floating over the chosen pane; it vanishes the instant Enter exits send mode. */
1132
+ .terminal-pane.is-send-target::after {
1133
+ content: "⏎";
1134
+ position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
1135
+ font-size: 56px; color: var(--active); opacity: 0.32; pointer-events: none; z-index: 5;
1136
+ }
1137
+ /* Send mode dims the rest of the app (sidebar + file/diff view) so only the terminal pops. */
1138
+ body.terminal-send-mode .sidebar,
1139
+ body.terminal-send-mode .content,
1140
+ body.terminal-send-mode .sidebar-resizer { opacity: 0.25; pointer-events: none; }
1141
+ /* Pad the content tail so it isn't hidden behind the fixed panel while the terminal is open. */
1142
+ body.terminal-open .content { padding-bottom: var(--terminal-height, 320px); }
1143
+ .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.5",
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