vibespot 0.9.2 → 0.9.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/ui/dialog.js CHANGED
@@ -114,3 +114,139 @@ function vibePrompt(title, defaultValue, placeholder) {
114
114
  });
115
115
  });
116
116
  }
117
+
118
+ // ---------------------------------------------------------------------------
119
+ // Feedback form → HubSpot Forms API
120
+ // ---------------------------------------------------------------------------
121
+
122
+ const _FEEDBACK_PORTAL = "144870572";
123
+ const _FEEDBACK_FORM = "4f18572f-a3ec-4193-bcfd-a373f54d86d2";
124
+ let _feedbackCooldown = false;
125
+
126
+ /**
127
+ * Show a feedback dialog that submits to a HubSpot form.
128
+ * @returns {Promise<void>}
129
+ */
130
+ function vibeFeedback() {
131
+ if (_feedbackCooldown) {
132
+ return vibeAlert("Please wait a moment before submitting another report.", "Cooldown");
133
+ }
134
+
135
+ return new Promise((resolve) => {
136
+ const overlay = document.createElement("div");
137
+ overlay.className = "confirm-overlay";
138
+ overlay.innerHTML = `
139
+ <div class="confirm-dialog" style="max-width:440px">
140
+ <div class="confirm-dialog__title">Submit Feedback</div>
141
+ <div style="display:flex;flex-direction:column;gap:10px;margin:12px 0">
142
+ <label style="font-size:13px;color:var(--text-dim)">Type
143
+ <select data-role="type" style="display:block;width:100%;margin-top:4px;padding:8px 10px;border-radius:var(--radius-sm);border:1px solid var(--border);background:var(--bg-input);color:var(--text);font-size:13px">
144
+ <option value="Bug Report">Bug Report</option>
145
+ <option value="Feature Request">Feature Request</option>
146
+ <option value="Comment">Comment</option>
147
+ </select>
148
+ </label>
149
+ <label style="font-size:13px;color:var(--text-dim)">Email (optional)
150
+ <input type="email" data-role="email" placeholder="you@example.com" style="display:block;width:100%;margin-top:4px;padding:8px 10px;border-radius:var(--radius-sm);border:1px solid var(--border);background:var(--bg-input);color:var(--text);font-size:13px;box-sizing:border-box" />
151
+ </label>
152
+ <label style="font-size:13px;color:var(--text-dim)">Message
153
+ <textarea data-role="message" rows="5" maxlength="2000" placeholder="Describe the issue or idea..." style="display:block;width:100%;margin-top:4px;padding:8px 10px;border-radius:var(--radius-sm);border:1px solid var(--border);background:var(--bg-input);color:var(--text);font-size:13px;resize:vertical;font-family:inherit;box-sizing:border-box"></textarea>
154
+ </label>
155
+ </div>
156
+ <div class="confirm-dialog__actions">
157
+ <button class="btn btn--secondary" data-action="cancel">Cancel</button>
158
+ <button class="btn btn--primary" data-action="submit">Submit</button>
159
+ </div>
160
+ </div>
161
+ `;
162
+ document.body.appendChild(overlay);
163
+
164
+ const typeEl = overlay.querySelector('[data-role="type"]');
165
+ const emailEl = overlay.querySelector('[data-role="email"]');
166
+ const msgEl = overlay.querySelector('[data-role="message"]');
167
+ const submitBtn = overlay.querySelector('[data-action="submit"]');
168
+
169
+ setTimeout(() => msgEl.focus(), 50);
170
+
171
+ const close = () => { overlay.remove(); resolve(); };
172
+
173
+ overlay.querySelector('[data-action="cancel"]').addEventListener("click", close);
174
+ overlay.addEventListener("click", (e) => { if (e.target === overlay) close(); });
175
+
176
+ submitBtn.addEventListener("click", async () => {
177
+ const message = msgEl.value.trim();
178
+ if (!message) { msgEl.style.borderColor = "var(--error)"; msgEl.focus(); return; }
179
+
180
+ submitBtn.disabled = true;
181
+ submitBtn.textContent = "Sending...";
182
+
183
+ try {
184
+ const res = await fetch(
185
+ `https://api.hsforms.com/submissions/v3/integration/submit/${_FEEDBACK_PORTAL}/${_FEEDBACK_FORM}`,
186
+ {
187
+ method: "POST",
188
+ headers: { "Content-Type": "application/json" },
189
+ body: JSON.stringify({
190
+ fields: [
191
+ { name: "report_type", value: typeEl.value },
192
+ { name: "email", value: emailEl.value },
193
+ { name: "message", value: message },
194
+ ],
195
+ context: { pageUri: window.location.href, pageName: "vibespot" },
196
+ }),
197
+ }
198
+ );
199
+ if (!res.ok) throw new Error("HTTP " + res.status);
200
+ close();
201
+ _feedbackCooldown = true;
202
+ setTimeout(() => { _feedbackCooldown = false; }, 30000);
203
+ await vibeAlert("Thank you! Your feedback has been submitted.", "Feedback Sent");
204
+ } catch (err) {
205
+ submitBtn.disabled = false;
206
+ submitBtn.textContent = "Submit";
207
+ await vibeAlert("Failed to submit: " + err.message, "Error");
208
+ }
209
+ });
210
+ });
211
+ }
212
+
213
+ /**
214
+ * Show a large scrollable content viewer dialog with rendered markdown.
215
+ * Uses the `marked` library (loaded via vendor/marked.umd.js).
216
+ * @param {string} content — markdown text to display
217
+ * @param {string} [title] — dialog title
218
+ * @returns {Promise<void>}
219
+ */
220
+ function vibeViewContent(content, title) {
221
+ return new Promise((resolve) => {
222
+ const overlay = document.createElement("div");
223
+ overlay.className = "confirm-overlay";
224
+
225
+ const dialog = document.createElement("div");
226
+ dialog.className = "confirm-dialog confirm-dialog--wide";
227
+
228
+ if (title) {
229
+ dialog.innerHTML = '<div class="confirm-dialog__title">' + esc(title) + '</div>';
230
+ }
231
+
232
+ const body = document.createElement("div");
233
+ body.className = "confirm-dialog__content-view md-body";
234
+ body.innerHTML = typeof marked !== "undefined" ? marked.parse(content) : esc(content);
235
+ dialog.appendChild(body);
236
+
237
+ const actions = document.createElement("div");
238
+ actions.className = "confirm-dialog__actions";
239
+ actions.innerHTML = '<button class="btn btn--primary" data-action="ok">Close</button>';
240
+ dialog.appendChild(actions);
241
+
242
+ overlay.appendChild(dialog);
243
+ document.body.appendChild(overlay);
244
+
245
+ const close = () => { overlay.remove(); resolve(); };
246
+ overlay.querySelector('[data-action="ok"]').addEventListener("click", close);
247
+ overlay.addEventListener("click", (e) => { if (e.target === overlay) close(); });
248
+ document.addEventListener("keydown", function onKey(e) {
249
+ if (e.key === "Escape") { document.removeEventListener("keydown", onKey); close(); }
250
+ });
251
+ });
252
+ }
package/ui/index.html CHANGED
@@ -44,7 +44,10 @@
44
44
  <div class="topbar__logo-icon"><svg viewBox="0 0 512 512" width="18" height="18"><path d="M256 76 Q280 220 436 256 Q280 292 256 436 Q232 292 76 256 Q232 220 256 76 Z" fill="currentColor"/></svg></div>
45
45
  <span class="topbar__brand-name">vibeSpot</span>
46
46
  </div>
47
- <div style="margin-left:auto">
47
+ <div style="margin-left:auto; display:flex; align-items:center; gap:4px">
48
+ <button class="btn btn--ghost btn--sm" id="feedback-btn" title="Submit feedback" onclick="vibeFeedback()">
49
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" style="vertical-align:-2px;margin-right:4px"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>Feedback
50
+ </button>
48
51
  <button class="topbar__icon-btn theme-toggle" title="Toggle light/dark mode" onclick="toggleTheme()">
49
52
  <svg class="theme-toggle__icon--dark" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="12" cy="12" r="5"/><path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/></svg>
50
53
  <svg class="theme-toggle__icon--light" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
@@ -156,6 +159,9 @@
156
159
  <span class="topbar__project-pill" id="dashboard-theme-name"></span>
157
160
  </div>
158
161
  <div class="topbar__right">
162
+ <button class="topbar__icon-btn" title="Submit feedback" onclick="vibeFeedback()">
163
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
164
+ </button>
159
165
  <button class="topbar__icon-btn theme-toggle" title="Toggle light/dark mode" onclick="toggleTheme()">
160
166
  <svg class="theme-toggle__icon--dark" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="12" cy="12" r="5"/><path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/></svg>
161
167
  <svg class="theme-toggle__icon--light" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
@@ -209,6 +215,8 @@
209
215
  <span class="brand-asset-upload__icon" id="brand-icon-brandvoice">+</span>
210
216
  <span class="brand-asset-upload__label">brandvoice.md</span>
211
217
  </label>
218
+ <button class="btn btn--sm btn--outline" id="btn-extract-design" title="Auto-extract design system from this theme's CSS and modules">Extract from Theme</button>
219
+ <button class="btn btn--sm btn--outline" id="btn-import-reference" title="Import design from another HubSpot theme or local folder">Import Reference</button>
212
220
  </div>
213
221
  </section>
214
222
 
@@ -307,6 +315,9 @@
307
315
  </div>
308
316
  </div>
309
317
  <div class="topbar__right">
318
+ <button class="topbar__icon-btn" title="Submit feedback" onclick="vibeFeedback()">
319
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
320
+ </button>
310
321
  <button class="topbar__icon-btn theme-toggle" title="Toggle light/dark mode" onclick="toggleTheme()">
311
322
  <svg class="theme-toggle__icon--dark" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="12" cy="12" r="5"/><path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/></svg>
312
323
  <svg class="theme-toggle__icon--light" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
@@ -400,6 +411,11 @@
400
411
  <div class="resize-handle" id="resize-handle"></div>
401
412
 
402
413
  <main class="panel panel--right" id="panel-right">
414
+ <div class="view-toggle" id="view-toggle">
415
+ <button class="view-toggle__btn active" data-view="preview">Preview</button>
416
+ <button class="view-toggle__btn" data-view="code">Code</button>
417
+ </div>
418
+
403
419
  <div class="preview" id="preview-container">
404
420
  <div class="browser-chrome" id="browser-chrome">
405
421
  <div class="browser-chrome__bar">
@@ -413,6 +429,24 @@
413
429
  <iframe id="preview-frame" class="browser-chrome__frame" sandbox="allow-scripts allow-same-origin"></iframe>
414
430
  </div>
415
431
  </div>
432
+
433
+ <div class="code-view hidden" id="code-view">
434
+ <div class="code-view__sidebar">
435
+ <div class="code-view__sidebar-header">Files</div>
436
+ <div class="code-view__file-list" id="code-file-list"></div>
437
+ </div>
438
+ <div class="code-view__editor">
439
+ <div class="code-view__editor-header">
440
+ <span class="code-view__filename" id="code-filename">Select a file</span>
441
+ <div class="code-view__editor-actions">
442
+ <span class="code-view__dirty-dot hidden" id="code-dirty-dot" title="Unsaved changes"></span>
443
+ <button class="btn btn--sm btn--accent" id="code-save-btn" disabled>Save</button>
444
+ </div>
445
+ </div>
446
+ <div class="code-view__editor-area" id="code-editor-area"></div>
447
+ </div>
448
+ </div>
449
+
416
450
  <aside class="history-panel hidden" id="history-panel">
417
451
  <div class="history-panel__header">
418
452
  <h3>Version History</h3>
@@ -469,6 +503,8 @@
469
503
 
470
504
  </div><!-- /app-body -->
471
505
 
506
+ <script src="/vendor/marked.umd.js"></script>
507
+ <script src="/vendor/codemirror-bundle.global.js"></script>
472
508
  <script src="/dialog.js"></script>
473
509
  <script src="/setup.js"></script>
474
510
  <script src="/dashboard.js"></script>
@@ -477,5 +513,6 @@
477
513
  <script src="/chat.js"></script>
478
514
  <script src="/preview.js"></script>
479
515
  <script src="/field-editor.js"></script>
516
+ <script src="/code-editor.js"></script>
480
517
  </body>
481
518
  </html>
package/ui/styles.css CHANGED
@@ -51,7 +51,7 @@
51
51
  --shadow-sm: 0 4px 16px rgba(0,0,0,0.3);
52
52
  --shadow-lg: 0 16px 48px rgba(0,0,0,0.5);
53
53
  --shadow-xl: 0 20px 60px rgba(0,0,0,0.6);
54
- --overlay-bg: rgba(0,0,0,0.6);
54
+ --overlay-bg: rgba(0,0,0,0.75);
55
55
  --scrollbar-thumb: rgba(255,255,255,0.08);
56
56
  --scrollbar-hover: rgba(255,255,255,0.14);
57
57
  --bg-deep: #0a0a0a;
@@ -88,7 +88,7 @@
88
88
  --shadow-sm: 0 4px 16px rgba(0,0,0,0.08);
89
89
  --shadow-lg: 0 16px 48px rgba(0,0,0,0.12);
90
90
  --shadow-xl: 0 20px 60px rgba(0,0,0,0.15);
91
- --overlay-bg: rgba(0,0,0,0.3);
91
+ --overlay-bg: rgba(0,0,0,0.5);
92
92
  --scrollbar-thumb: rgba(0,0,0,0.12);
93
93
  --scrollbar-hover: rgba(0,0,0,0.2);
94
94
  --bg-deep: #f0efed;
@@ -686,9 +686,9 @@ body { display: flex; }
686
686
  background: none;
687
687
  border: none;
688
688
  color: var(--text-muted);
689
- font-size: 18px;
689
+ font-size: 24px;
690
690
  cursor: pointer;
691
- padding: 0 4px;
691
+ padding: 0 6px;
692
692
  opacity: 0;
693
693
  transition: opacity 0.15s, color 0.15s;
694
694
  }
@@ -701,6 +701,25 @@ body { display: flex; }
701
701
  color: var(--error);
702
702
  }
703
703
 
704
+ .dashboard__template-clone {
705
+ background: none;
706
+ border: none;
707
+ color: var(--text-muted);
708
+ font-size: 20px;
709
+ cursor: pointer;
710
+ padding: 0 6px;
711
+ opacity: 0;
712
+ transition: opacity 0.15s, color 0.15s;
713
+ }
714
+
715
+ .dashboard__template-item:hover .dashboard__template-clone {
716
+ opacity: 1;
717
+ }
718
+
719
+ .dashboard__template-clone:hover {
720
+ color: var(--accent);
721
+ }
722
+
704
723
  /* Module library chips */
705
724
  .dashboard__module-library {
706
725
  display: flex;
@@ -783,6 +802,8 @@ body { display: flex; }
783
802
  /* Brand asset upload */
784
803
  .dashboard__brand-assets {
785
804
  display: flex;
805
+ flex-wrap: wrap;
806
+ align-items: center;
786
807
  gap: 12px;
787
808
  }
788
809
 
@@ -819,6 +840,23 @@ body { display: flex; }
819
840
  color: var(--success);
820
841
  }
821
842
 
843
+ .brand-asset-view {
844
+ font-size: 11px;
845
+ padding: 2px 6px;
846
+ margin-left: 4px;
847
+ background: none;
848
+ border: none;
849
+ color: var(--accent);
850
+ cursor: pointer;
851
+ text-decoration: underline;
852
+ text-underline-offset: 2px;
853
+ opacity: 0.7;
854
+ transition: opacity 0.15s ease;
855
+ }
856
+ .brand-asset-view:hover {
857
+ opacity: 1;
858
+ }
859
+
822
860
  .brand-asset-upload__label {
823
861
  font-size: 13px;
824
862
  color: var(--text);
@@ -996,6 +1034,31 @@ body { display: flex; }
996
1034
  border-color: var(--border-hover);
997
1035
  }
998
1036
 
1037
+ .btn--outline {
1038
+ background: transparent;
1039
+ color: var(--text-muted);
1040
+ border: 1px solid var(--border);
1041
+ }
1042
+ .btn--outline:hover {
1043
+ color: var(--text);
1044
+ border-color: var(--accent);
1045
+ background: rgba(99, 102, 241, 0.08);
1046
+ }
1047
+
1048
+ .btn--accent {
1049
+ background: var(--accent);
1050
+ color: #fff;
1051
+ border: 1px solid var(--accent);
1052
+ }
1053
+ .btn--accent:hover {
1054
+ background: var(--accent-hover);
1055
+ border-color: var(--accent-hover);
1056
+ }
1057
+ .btn--accent:disabled {
1058
+ opacity: 0.35;
1059
+ cursor: default;
1060
+ }
1061
+
999
1062
  .btn--danger {
1000
1063
  background: var(--error);
1001
1064
  color: var(--text-on-accent);
@@ -1025,6 +1088,69 @@ body { display: flex; }
1025
1088
  max-width: 90vw;
1026
1089
  box-shadow: var(--shadow-lg);
1027
1090
  }
1091
+ .confirm-dialog--wide {
1092
+ width: 640px;
1093
+ max-width: 90vw;
1094
+ max-height: 80vh;
1095
+ display: flex;
1096
+ flex-direction: column;
1097
+ }
1098
+ .confirm-dialog__content-view {
1099
+ flex: 1;
1100
+ overflow-y: auto;
1101
+ font-size: 13px;
1102
+ line-height: 1.6;
1103
+ color: var(--text-muted);
1104
+ background: var(--bg-input);
1105
+ border: 1px solid var(--border);
1106
+ border-radius: var(--radius);
1107
+ padding: 20px;
1108
+ margin-bottom: 16px;
1109
+ max-height: 60vh;
1110
+ }
1111
+
1112
+ /* Rendered markdown inside content viewer */
1113
+ .md-body h1, .md-body h2, .md-body h3, .md-body h4 {
1114
+ color: var(--text);
1115
+ margin: 1.2em 0 0.4em;
1116
+ line-height: 1.3;
1117
+ }
1118
+ .md-body h1 { font-size: 18px; }
1119
+ .md-body h2 { font-size: 15px; border-bottom: 1px solid var(--border); padding-bottom: 6px; }
1120
+ .md-body h3 { font-size: 13px; }
1121
+ .md-body h4 { font-size: 12px; color: var(--text-muted); }
1122
+ .md-body h1:first-child, .md-body h2:first-child { margin-top: 0; }
1123
+ .md-body p { margin: 0.5em 0; }
1124
+ .md-body ul { margin: 0.4em 0; padding-left: 1.4em; }
1125
+ .md-body li { margin: 0.2em 0; }
1126
+ .md-body strong { color: var(--text); font-weight: 600; }
1127
+ .md-body hr { border: none; border-top: 1px solid var(--border); margin: 1em 0; }
1128
+ .md-body code {
1129
+ background: rgba(99, 102, 241, 0.1);
1130
+ color: var(--accent);
1131
+ padding: 1px 5px;
1132
+ border-radius: 3px;
1133
+ font-family: var(--font-mono, 'SF Mono', SFMono-Regular, Consolas, monospace);
1134
+ font-size: 0.9em;
1135
+ }
1136
+ .md-body pre {
1137
+ background: rgba(0, 0, 0, 0.3);
1138
+ border: 1px solid var(--border);
1139
+ border-radius: var(--radius);
1140
+ padding: 12px;
1141
+ margin: 0.6em 0;
1142
+ overflow-x: auto;
1143
+ font-size: 12px;
1144
+ line-height: 1.5;
1145
+ color: var(--text-dim);
1146
+ }
1147
+ .md-body pre code {
1148
+ background: none;
1149
+ color: inherit;
1150
+ padding: 0;
1151
+ border-radius: 0;
1152
+ font-size: inherit;
1153
+ }
1028
1154
  .confirm-dialog__title {
1029
1155
  font-size: 16px;
1030
1156
  font-weight: 600;
@@ -1221,6 +1347,7 @@ body { display: flex; }
1221
1347
  .panel--right {
1222
1348
  flex: 1;
1223
1349
  display: flex;
1350
+ flex-direction: column;
1224
1351
  position: relative;
1225
1352
  background: var(--bg-deep);
1226
1353
  }
@@ -1236,6 +1363,151 @@ body { display: flex; }
1236
1363
  background: var(--accent);
1237
1364
  }
1238
1365
 
1366
+ /* ================================================================
1367
+ VIEW TOGGLE (Preview / Code tabs)
1368
+ ================================================================ */
1369
+ .view-toggle {
1370
+ display: flex;
1371
+ gap: 2px;
1372
+ padding: 6px 12px;
1373
+ background: var(--bg-panel-solid);
1374
+ border-bottom: 1px solid var(--border);
1375
+ flex-shrink: 0;
1376
+ }
1377
+ .view-toggle__btn {
1378
+ padding: 4px 14px;
1379
+ font-size: 12px;
1380
+ font-weight: 500;
1381
+ font-family: var(--font);
1382
+ border-radius: var(--radius);
1383
+ background: transparent;
1384
+ color: var(--text-dim);
1385
+ border: none;
1386
+ cursor: pointer;
1387
+ transition: all 0.15s;
1388
+ }
1389
+ .view-toggle__btn:hover {
1390
+ color: var(--text);
1391
+ background: var(--bg-hover);
1392
+ }
1393
+ .view-toggle__btn.active {
1394
+ background: var(--bg-input);
1395
+ color: var(--text);
1396
+ }
1397
+
1398
+ /* ================================================================
1399
+ CODE VIEW (file browser + editor)
1400
+ ================================================================ */
1401
+ .code-view {
1402
+ display: flex;
1403
+ flex: 1;
1404
+ overflow: hidden;
1405
+ }
1406
+ .code-view__sidebar {
1407
+ width: 200px;
1408
+ min-width: 160px;
1409
+ border-right: 1px solid var(--border);
1410
+ background: var(--bg-panel-solid);
1411
+ overflow-y: auto;
1412
+ flex-shrink: 0;
1413
+ }
1414
+ .code-view__sidebar-header {
1415
+ padding: 10px 14px;
1416
+ font-size: 11px;
1417
+ font-weight: 600;
1418
+ text-transform: uppercase;
1419
+ letter-spacing: 0.5px;
1420
+ color: var(--text-dim);
1421
+ border-bottom: 1px solid var(--border);
1422
+ }
1423
+ .code-view__file-group {
1424
+ border-bottom: 1px solid var(--border);
1425
+ }
1426
+ .code-view__group-header {
1427
+ padding: 8px 14px 4px;
1428
+ font-size: 11px;
1429
+ font-weight: 600;
1430
+ color: var(--text-muted);
1431
+ }
1432
+ .code-view__file-item {
1433
+ display: flex;
1434
+ align-items: center;
1435
+ gap: 6px;
1436
+ padding: 5px 14px 5px 22px;
1437
+ font-size: 12px;
1438
+ color: var(--text-dim);
1439
+ cursor: pointer;
1440
+ transition: all 0.1s;
1441
+ }
1442
+ .code-view__file-item:hover {
1443
+ background: var(--bg-hover);
1444
+ color: var(--text);
1445
+ }
1446
+ .code-view__file-item--active {
1447
+ background: var(--bg-input);
1448
+ color: var(--accent);
1449
+ }
1450
+ .code-view__file-item--active:hover {
1451
+ color: var(--accent);
1452
+ }
1453
+ .code-view__file-name {
1454
+ overflow: hidden;
1455
+ text-overflow: ellipsis;
1456
+ white-space: nowrap;
1457
+ }
1458
+
1459
+ /* Editor area */
1460
+ .code-view__editor {
1461
+ flex: 1;
1462
+ display: flex;
1463
+ flex-direction: column;
1464
+ min-width: 0;
1465
+ }
1466
+ .code-view__editor-header {
1467
+ display: flex;
1468
+ justify-content: space-between;
1469
+ align-items: center;
1470
+ padding: 6px 12px;
1471
+ border-bottom: 1px solid var(--border);
1472
+ background: var(--bg-panel-solid);
1473
+ flex-shrink: 0;
1474
+ }
1475
+ .code-view__filename {
1476
+ font-size: 12px;
1477
+ color: var(--text-muted);
1478
+ font-family: var(--font-mono, 'SF Mono', SFMono-Regular, Consolas, monospace);
1479
+ }
1480
+ .code-view__editor-actions {
1481
+ display: flex;
1482
+ align-items: center;
1483
+ gap: 8px;
1484
+ }
1485
+ .code-view__dirty-dot {
1486
+ width: 8px;
1487
+ height: 8px;
1488
+ border-radius: 50%;
1489
+ background: var(--accent);
1490
+ }
1491
+ .code-view__editor-area {
1492
+ flex: 1;
1493
+ overflow: auto;
1494
+ background: var(--bg-deep);
1495
+ }
1496
+
1497
+ /* CodeMirror overrides */
1498
+ .code-view__editor-area .cm-editor {
1499
+ height: 100%;
1500
+ }
1501
+ .code-view__editor-area .cm-editor .cm-scroller {
1502
+ overflow: auto;
1503
+ font-family: var(--font-mono, 'SF Mono', SFMono-Regular, Consolas, monospace);
1504
+ font-size: 13px;
1505
+ line-height: 1.5;
1506
+ }
1507
+ .code-view__editor-area .cm-editor.cm-focused {
1508
+ outline: none;
1509
+ }
1510
+
1239
1511
  /* ================================================================
1240
1512
  MODULE BAR (button at top of left panel)
1241
1513
  ================================================================ */