@vemjs/renderer-vecto 0.1.1 → 0.1.2

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.
@@ -45,6 +45,22 @@ export class WorkspaceExplorer extends UIComponent {
45
45
  onDidOpenDirectory(cb) {
46
46
  this.openDirectoryCallbacks.push(cb);
47
47
  }
48
+ flattenFiles(nodes) {
49
+ const list = [];
50
+ const recurse = (nodeList, prefix) => {
51
+ for (const node of nodeList) {
52
+ const path = prefix ? `${prefix}/${node.label}` : node.label;
53
+ if (node.children && node.children.length > 0) {
54
+ recurse(node.children, path);
55
+ }
56
+ else if (!node.children || node.children.length === 0) {
57
+ list.push(path);
58
+ }
59
+ }
60
+ };
61
+ recurse(nodes, '');
62
+ return list;
63
+ }
48
64
  async handleOpenFolder() {
49
65
  if (typeof window === 'undefined' || !window.showDirectoryPicker) {
50
66
  console.warn('File System Access API is not supported in this environment.');
@@ -53,6 +69,12 @@ export class WorkspaceExplorer extends UIComponent {
53
69
  try {
54
70
  const rootHandle = await window.showDirectoryPicker();
55
71
  const nodes = await this.fsHandler.readDirectory(rootHandle);
72
+ // Cache all file paths for search plugins (like Telescope)
73
+ const fileList = this.flattenFiles(nodes);
74
+ const activeState = this.getActiveEditorState();
75
+ if (activeState) {
76
+ activeState.projectFiles = fileList;
77
+ }
56
78
  this.treeView = new TreeView({
57
79
  nodes,
58
80
  width: this.leftPanel.width,
@@ -85,8 +107,58 @@ export class WorkspaceExplorer extends UIComponent {
85
107
  console.error('Error selecting directory:', err);
86
108
  }
87
109
  }
110
+ getActiveEditorState() {
111
+ return this.workspace.getActiveLayout()?.getActiveState() || null;
112
+ }
113
+ lastSidebarPosition = 'left';
114
+ lastSidebarWidth = 240;
115
+ syncLayout(activeState) {
116
+ const layout = activeState.layoutConfig;
117
+ this.remove(this.panelGroup);
118
+ this.panelGroup = new PanelGroup({
119
+ direction: 'horizontal',
120
+ width: this.width,
121
+ height: this.height,
122
+ });
123
+ this.leftPanel = new Panel({
124
+ minSize: 150,
125
+ defaultSize: layout.sidebarWidth / Math.max(1, this.width),
126
+ });
127
+ this.rightPanel = new Panel({ minSize: 300 });
128
+ if (layout.sidebarPosition === 'left') {
129
+ this.leftPanel.add(this.openBtn);
130
+ if (this.treeView)
131
+ this.leftPanel.add(this.treeView);
132
+ this.rightPanel.add(this.workspace);
133
+ this.panelGroup.addPanel(this.leftPanel);
134
+ this.panelGroup.addPanel(this.rightPanel);
135
+ }
136
+ else if (layout.sidebarPosition === 'right') {
137
+ this.leftPanel.add(this.openBtn);
138
+ if (this.treeView)
139
+ this.leftPanel.add(this.treeView);
140
+ this.rightPanel.add(this.workspace);
141
+ this.panelGroup.addPanel(this.rightPanel);
142
+ this.panelGroup.addPanel(this.leftPanel);
143
+ }
144
+ else {
145
+ this.rightPanel.add(this.workspace);
146
+ this.panelGroup.addPanel(this.rightPanel);
147
+ }
148
+ this.add(this.panelGroup);
149
+ }
88
150
  update(dt, time) {
89
151
  super.update(dt, time);
152
+ const activeState = this.getActiveEditorState();
153
+ if (activeState) {
154
+ const layout = activeState.layoutConfig;
155
+ if (layout.sidebarPosition !== this.lastSidebarPosition ||
156
+ layout.sidebarWidth !== this.lastSidebarWidth) {
157
+ this.lastSidebarPosition = layout.sidebarPosition;
158
+ this.lastSidebarWidth = layout.sidebarWidth;
159
+ this.syncLayout(activeState);
160
+ }
161
+ }
90
162
  if (this.panelGroup.width !== this.width || this.panelGroup.height !== this.height) {
91
163
  this.panelGroup.width = this.width;
92
164
  this.panelGroup.height = this.height;
@@ -102,14 +174,32 @@ export class WorkspaceExplorer extends UIComponent {
102
174
  this.workspace.height = this.rightPanel.height;
103
175
  }
104
176
  }
105
- render(_r) {
106
- _r.beginPath();
107
- _r.moveTo(0, 0);
108
- _r.lineTo(this.leftPanel.width, 0);
109
- _r.lineTo(this.leftPanel.width, this.height);
110
- _r.lineTo(0, this.height);
111
- _r.closePath();
112
- _r.fill('#090d16'); // deep slate sidebar background
177
+ render(r) {
178
+ const activeState = this.getActiveEditorState();
179
+ if (!activeState)
180
+ return;
181
+ const theme = activeState.theme;
182
+ const layout = activeState.layoutConfig;
183
+ // Apply button styling
184
+ this.openBtn.bg = theme.statusBarBg;
185
+ this.openBtn.hoverBg = theme.statusBarBg;
186
+ this.openBtn.color = theme.fg;
187
+ if (this.treeView) {
188
+ /* eslint-disable-next-line no-underscore-dangle */
189
+ this.treeView._color = theme.fg;
190
+ /* eslint-disable-next-line no-underscore-dangle */
191
+ this.treeView._selColor = theme.accent + '33';
192
+ }
193
+ if (layout.sidebarPosition !== 'hidden') {
194
+ const startX = layout.sidebarPosition === 'left' ? 0 : this.width - this.leftPanel.width;
195
+ r.beginPath();
196
+ r.moveTo(startX, 0);
197
+ r.lineTo(startX + this.leftPanel.width, 0);
198
+ r.lineTo(startX + this.leftPanel.width, this.height);
199
+ r.lineTo(startX, this.height);
200
+ r.closePath();
201
+ r.fill(theme.sidebarBg);
202
+ }
113
203
  }
114
204
  }
115
205
  //# sourceMappingURL=WorkspaceExplorer.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"WorkspaceExplorer.js","sourceRoot":"","sources":["../src/WorkspaceExplorer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"WorkspaceExplorer.js","sourceRoot":"","sources":["../src/WorkspaceExplorer.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vemjs/renderer-vecto",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "VectoUI integration renderer for the Vem text editor",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/src/CommandBar.ts CHANGED
@@ -58,12 +58,19 @@ export class CommandBar extends UIComponent {
58
58
  }
59
59
 
60
60
  public render(r: IRenderer): void {
61
+ const theme = this.editorState.theme;
62
+
63
+ // Sync theme colors dynamically
64
+ this.prefixText.color = theme.accent;
65
+ this.input.color = theme.statusBarFg;
66
+ this.input.bg = theme.statusBarBg;
67
+
61
68
  r.beginPath();
62
69
  r.moveTo(0, 0);
63
70
  r.lineTo(this.width, 0);
64
71
  r.lineTo(this.width, this.height);
65
72
  r.lineTo(0, this.height);
66
73
  r.closePath();
67
- r.fill('#1e293b'); // slate-800
74
+ r.fill(theme.statusBarBg);
68
75
  }
69
76
  }
@@ -57,28 +57,46 @@ export class VemEditorEntity extends UIComponent {
57
57
  const buffer = this.editorState.getBuffer();
58
58
  const cursor = this.editorState.getCursor();
59
59
  const lineCount = buffer.getLineCount();
60
+ const theme = this.editorState.theme;
61
+ const layout = this.editorState.layoutConfig;
60
62
 
61
63
  // 1. Calculate gutter width dynamically
62
64
  const maxLineDigits = Math.max(2, lineCount.toString().length);
63
65
  const gutterWidth = maxLineDigits * this.charWidth + 15;
64
66
 
65
- // 2. Set line numbers text
67
+ // 2. Sync theme colors
68
+ this.gutterText.color = theme.gutterFg;
69
+ this.bodyText.color = theme.fg;
70
+
71
+ // 3. Set line numbers text
66
72
  const lineNums: string[] = [];
67
73
  for (let i = 1; i <= lineCount; i++) {
68
74
  lineNums.push(i.toString().padStart(maxLineDigits, ' '));
69
75
  }
70
76
  this.gutterText.setText(lineNums.join('\n'));
71
- this.gutterText.setPosition(5, 5);
72
77
 
73
- // 3. Set editor body text
74
- const spans = buffer.getLines().map((line, idx) => {
78
+ // 4. Set editor body text
79
+ const spans: any[] = [];
80
+ const lines = buffer.getLines();
81
+ lines.forEach((line, idx) => {
75
82
  const suffix = idx === lineCount - 1 ? '' : '\n';
76
- return { text: line + suffix };
83
+ const highlight = (this.editorState as any).highlightLine;
84
+ if (highlight) {
85
+ const lineSpans = highlight(line, idx);
86
+ if (lineSpans.length > 0) {
87
+ const lastSpan = { ...lineSpans[lineSpans.length - 1] };
88
+ lastSpan.text += suffix;
89
+ spans.push(...lineSpans.slice(0, -1), lastSpan);
90
+ } else {
91
+ spans.push({ text: suffix });
92
+ }
93
+ } else {
94
+ spans.push({ text: line + suffix });
95
+ }
77
96
  });
78
97
  this.bodyText.setSpans(spans);
79
- this.bodyText.setPosition(gutterWidth + 5, 5);
80
98
 
81
- // 4. Handle viewport scrolling to keep cursor visible
99
+ // 5. Handle viewport scrolling to keep cursor visible
82
100
  const visibleLines = Math.floor((this.height - 35) / this.lineHeight); // reserve 35px for status bar
83
101
  if (cursor.line >= this.scrollY + visibleLines) {
84
102
  this.scrollY = cursor.line - visibleLines + 1;
@@ -86,13 +104,9 @@ export class VemEditorEntity extends UIComponent {
86
104
  this.scrollY = cursor.line;
87
105
  }
88
106
 
89
- // Update children scroll positions
90
- const scrollOffsetY = -this.scrollY * this.lineHeight;
91
- this.gutterText.setPosition(5, 5 + scrollOffsetY);
92
- this.bodyText.setPosition(gutterWidth + 5, 5 + scrollOffsetY);
93
-
94
- // 5. Handle CommandBar visibility
95
- if (this.editorState.getMode() === 'COMMAND') {
107
+ // 6. Position and layout based on statusBarPosition
108
+ const hasCommandBar = this.editorState.getMode() === 'COMMAND';
109
+ if (hasCommandBar) {
96
110
  if (!this.children.includes(this.commandBar)) {
97
111
  this.add(this.commandBar);
98
112
  }
@@ -102,6 +116,17 @@ export class VemEditorEntity extends UIComponent {
102
116
  this.remove(this.commandBar);
103
117
  }
104
118
  }
119
+
120
+ const scrollOffsetY = -this.scrollY * this.lineHeight;
121
+ if (layout.statusBarPosition === 'top') {
122
+ this.commandBar.setPosition(0, 0);
123
+ this.gutterText.setPosition(5, 5 + scrollOffsetY + 30);
124
+ this.bodyText.setPosition(gutterWidth + 5, 5 + scrollOffsetY + 30);
125
+ } else {
126
+ this.commandBar.setPosition(0, this.height - 30);
127
+ this.gutterText.setPosition(5, 5 + scrollOffsetY);
128
+ this.bodyText.setPosition(gutterWidth + 5, 5 + scrollOffsetY);
129
+ }
105
130
  }
106
131
 
107
132
  public setAutocompleteItems(items: { label: string; detail?: string }[]): void {
@@ -140,6 +165,9 @@ export class VemEditorEntity extends UIComponent {
140
165
  }
141
166
 
142
167
  public render(r: IRenderer): void {
168
+ const theme = this.editorState.theme;
169
+ const layout = this.editorState.layoutConfig;
170
+
143
171
  // 1. Draw editor background
144
172
  r.beginPath();
145
173
  r.moveTo(0, 0);
@@ -147,7 +175,7 @@ export class VemEditorEntity extends UIComponent {
147
175
  r.lineTo(this.width, this.height);
148
176
  r.lineTo(0, this.height);
149
177
  r.closePath();
150
- r.fill('#0f172a'); // slate-900
178
+ r.fill(theme.bg);
151
179
 
152
180
  const lineCount = this.editorState.getBuffer().getLineCount();
153
181
  const maxLineDigits = Math.max(2, lineCount.toString().length);
@@ -160,11 +188,28 @@ export class VemEditorEntity extends UIComponent {
160
188
  r.lineTo(gutterWidth, this.height);
161
189
  r.lineTo(0, this.height);
162
190
  r.closePath();
163
- r.fill('#1e293b'); // slate-800
191
+ r.fill(theme.gutterBg);
192
+
193
+ // Apply scrolling transformation (with offset if statusBar is top)
194
+ const contentOffsetY = layout.statusBarPosition === 'top' ? 30 : 0;
164
195
 
165
- // Apply scrolling transformation for cursor and selections
166
196
  r.save();
167
- r.translate(0, -this.scrollY * this.lineHeight);
197
+ r.translate(0, -this.scrollY * this.lineHeight + contentOffsetY);
198
+
199
+ // 2.5. Draw Gutter Decorations (Git diff signs)
200
+ const decs = (this.editorState as any).gutterDecorations;
201
+ if (decs && decs.size > 0) {
202
+ for (let l = 0; l < lineCount; l++) {
203
+ const dec = decs.get(l);
204
+ if (dec) {
205
+ const decY = 5 + l * this.lineHeight;
206
+ r.beginPath();
207
+ r.roundRect(1, decY + 2, 3, this.lineHeight - 4, 1.5);
208
+ r.closePath();
209
+ r.fill(dec.color);
210
+ }
211
+ }
212
+ }
168
213
 
169
214
  // 3. Draw Visual Mode selections
170
215
  const selection = this.editorState.getVisualSelection();
@@ -191,7 +236,7 @@ export class VemEditorEntity extends UIComponent {
191
236
  r.lineTo(x + w, y + h);
192
237
  r.lineTo(x, y + h);
193
238
  r.closePath();
194
- r.fill('rgba(56, 189, 248, 0.3)'); // sky-400 opacity
239
+ r.fill(theme.accent + '44'); // Theme accent with 25% opacity
195
240
  };
196
241
 
197
242
  if (type === 'line') {
@@ -259,40 +304,102 @@ export class VemEditorEntity extends UIComponent {
259
304
  r.lineTo(cursorX + 2, cursorY + this.lineHeight);
260
305
  r.lineTo(cursorX, cursorY + this.lineHeight);
261
306
  r.closePath();
262
- r.fill('#f43f5e'); // rose-500
307
+ r.fill(theme.accent);
263
308
  } else {
264
309
  r.moveTo(cursorX, cursorY);
265
310
  r.lineTo(cursorX + this.charWidth, cursorY);
266
311
  r.lineTo(cursorX + this.charWidth, cursorY + this.lineHeight);
267
312
  r.lineTo(cursorX, cursorY + this.lineHeight);
268
313
  r.closePath();
269
- r.fill('rgba(56, 189, 248, 0.7)'); // sky-400 opacity
314
+ r.fill(theme.accent + '88'); // 50% opacity accent
270
315
  }
271
316
 
272
317
  r.restore(); // Restore scroll transform
273
318
 
274
- // 5. Draw status bar at the bottom
319
+ // 5. Draw status bar
275
320
  const statusBarHeight = 30;
276
- const statusY = this.height - statusBarHeight;
321
+ const statusY = layout.statusBarPosition === 'top' ? 0 : this.height - statusBarHeight;
277
322
  r.beginPath();
278
323
  r.moveTo(0, statusY);
279
324
  r.lineTo(this.width, statusY);
280
- r.lineTo(this.width, this.height);
281
- r.lineTo(0, this.height);
325
+ r.lineTo(this.width, statusY + statusBarHeight);
326
+ r.lineTo(0, statusY + statusBarHeight);
282
327
  r.closePath();
283
- r.fill('#1e293b'); // slate-800
328
+ r.fill(theme.statusBarBg);
329
+
330
+ const sl = this.editorState.statuslineLayout;
331
+ if (
332
+ mode !== 'COMMAND' &&
333
+ ((sl.left && sl.left.length > 0) || (sl.right && sl.right.length > 0))
334
+ ) {
335
+ // Custom statusline layout (lualine-like)
336
+ let startX = 0;
337
+ if (sl.left) {
338
+ for (const segment of sl.left) {
339
+ const textWidth = (r as any).measureText
340
+ ? (r as any).measureText(segment.text).width
341
+ : segment.text.length * 8;
342
+ const font = segment.bold ? 'bold 12px monospace' : '12px monospace';
343
+ const color = segment.color || theme.statusBarFg;
344
+
345
+ if (segment.bg) {
346
+ const blockWidth = textWidth + 20;
347
+ r.beginPath();
348
+ r.moveTo(startX, statusY);
349
+ r.lineTo(startX + blockWidth, statusY);
350
+ r.lineTo(startX + blockWidth, statusY + statusBarHeight);
351
+ r.lineTo(startX, statusY + statusBarHeight);
352
+ r.closePath();
353
+ r.fill(segment.bg);
354
+
355
+ r.fillText(segment.text, startX + 10, statusY + 18, font, color);
356
+ startX += blockWidth;
357
+ } else {
358
+ r.fillText(segment.text, startX + 10, statusY + 18, font, color);
359
+ startX += textWidth + 15;
360
+ }
361
+ }
362
+ }
284
363
 
285
- if (mode !== 'COMMAND') {
364
+ let endX = this.width;
365
+ if (sl.right) {
366
+ for (const segment of sl.right) {
367
+ const textWidth = (r as any).measureText
368
+ ? (r as any).measureText(segment.text).width
369
+ : segment.text.length * 8;
370
+ const font = segment.bold ? 'bold 12px monospace' : '12px monospace';
371
+ const color = segment.color || theme.statusBarFg;
372
+
373
+ if (segment.bg) {
374
+ const blockWidth = textWidth + 20;
375
+ endX -= blockWidth;
376
+ r.beginPath();
377
+ r.moveTo(endX, statusY);
378
+ r.lineTo(endX + blockWidth, statusY);
379
+ r.lineTo(endX + blockWidth, statusY + statusBarHeight);
380
+ r.lineTo(endX, statusY + statusBarHeight);
381
+ r.closePath();
382
+ r.fill(segment.bg);
383
+
384
+ r.fillText(segment.text, endX + 10, statusY + 18, font, color);
385
+ } else {
386
+ endX -= textWidth + 15;
387
+ r.fillText(segment.text, endX + 10, statusY + 18, font, color);
388
+ }
389
+ }
390
+ }
391
+ } else if (mode !== 'COMMAND') {
392
+ // Default fallback status bar
286
393
  const modeText = `-- ${mode} --`;
287
394
  const posText = `${cursor.line + 1}:${cursor.character + 1}`;
288
395
  const pendingKeys = this.editorState.getPendingKeys();
289
396
  const pendingText = pendingKeys.length > 0 ? pendingKeys.join('') : '';
290
397
 
291
- r.fillText(modeText, 10, statusY + 18, 'bold 12px monospace', '#38bdf8');
398
+ r.fillText(modeText, 10, statusY + 18, 'bold 12px monospace', theme.accent);
292
399
  if (pendingText) {
293
- r.fillText(pendingText, 120, statusY + 18, '12px monospace', '#e2e8f0');
400
+ r.fillText(pendingText, 120, statusY + 18, '12px monospace', theme.statusBarFg);
294
401
  }
295
- r.fillText(posText, this.width - 60, statusY + 18, '12px monospace', '#94a3b8');
402
+ r.fillText(posText, this.width - 60, statusY + 18, '12px monospace', theme.statusBarFg);
296
403
  }
297
404
 
298
405
  // 6. Draw autocomplete popup menu
@@ -308,23 +415,24 @@ export class VemEditorEntity extends UIComponent {
308
415
 
309
416
  // Translate coordinates from buffer space to screen space
310
417
  let popupX = cursorX;
311
- let popupY = cursorY + this.lineHeight - this.scrollY * this.lineHeight;
418
+ let popupY = cursorY + this.lineHeight - this.scrollY * this.lineHeight + contentOffsetY;
312
419
 
313
420
  // Adjust positioning if it overflows screen boundaries
314
421
  if (popupX + popupWidth > this.width) {
315
422
  popupX = Math.max(gutterWidth + 5, this.width - popupWidth - 5);
316
423
  }
317
- if (popupY + popupHeight > statusY) {
318
- // Render above the cursor if it would overlay/go past the status bar
424
+ if (layout.statusBarPosition === 'bottom' && popupY + popupHeight > statusY) {
319
425
  popupY = cursorY - this.scrollY * this.lineHeight - popupHeight;
426
+ } else if (layout.statusBarPosition === 'top' && popupY < 30) {
427
+ popupY = cursorY + this.lineHeight - this.scrollY * this.lineHeight + 30;
320
428
  }
321
429
 
322
430
  // Draw background panel
323
431
  r.beginPath();
324
432
  r.roundRect(popupX, popupY, popupWidth, popupHeight, 4);
325
433
  r.closePath();
326
- r.fill('#1e293b'); // slate-800
327
- r.stroke('#475569', 1.2); // slate-600 border
434
+ r.fill(theme.statusBarBg);
435
+ r.stroke(theme.accent + '88', 1.2);
328
436
 
329
437
  // Draw menu items
330
438
  r.save();
@@ -342,10 +450,10 @@ export class VemEditorEntity extends UIComponent {
342
450
  r.lineTo(popupX + popupWidth - 2, itemY + 18);
343
451
  r.lineTo(popupX + 2, itemY + 18);
344
452
  r.closePath();
345
- r.fill('#334155'); // slate-700
453
+ r.fill(theme.accent + '44');
346
454
  }
347
455
 
348
- const labelColor = i === this.selectedAutocompleteIndex ? '#38bdf8' : '#f8fafc'; // sky-400 or slate-50
456
+ const labelColor = i === this.selectedAutocompleteIndex ? theme.accent : theme.fg;
349
457
  r.fillText(item.label, popupX + 8, itemY + 13, '12px monospace', labelColor);
350
458
  if (item.detail) {
351
459
  r.fillText(
@@ -353,11 +461,131 @@ export class VemEditorEntity extends UIComponent {
353
461
  popupX + 8 + item.label.length * 7.5,
354
462
  itemY + 13,
355
463
  '10px monospace',
356
- '#64748b',
464
+ theme.gutterFg,
357
465
  );
358
466
  }
359
467
  }
360
468
  r.restore();
361
469
  }
470
+
471
+ // 7. Draw centered Floating Popup Picker Modal (Telescope-like)
472
+ const popup = this.editorState.activePopup;
473
+ if (popup) {
474
+ const modalW = Math.min(550, this.width - 40);
475
+ const modalH = Math.min(380, this.height - 80);
476
+ const modalX = (this.width - modalW) / 2;
477
+ const modalY = (this.height - modalH) / 2;
478
+
479
+ // Draw modal backdrop shade
480
+ r.beginPath();
481
+ r.roundRect(0, 0, this.width, this.height, 0);
482
+ r.closePath();
483
+ r.fill('rgba(15, 23, 42, 0.6)'); // Translucent dark overlay
484
+
485
+ // Draw main panel
486
+ r.beginPath();
487
+ r.roundRect(modalX, modalY, modalW, modalH, 6);
488
+ r.closePath();
489
+ r.fill(theme.bg);
490
+ r.stroke(theme.accent, 1.5);
491
+
492
+ // Title
493
+ r.fillText(
494
+ popup.title.toUpperCase(),
495
+ modalX + 15,
496
+ modalY + 28,
497
+ 'bold 13px Outfit, monospace',
498
+ theme.accent,
499
+ );
500
+
501
+ // Input Search Bar
502
+ r.beginPath();
503
+ r.roundRect(modalX + 15, modalY + 42, modalW - 30, 28, 4);
504
+ r.closePath();
505
+ r.fill(theme.statusBarBg);
506
+ r.stroke(theme.accent + '44', 1);
507
+
508
+ const queryText = `> ${this.editorState.popupFilterText}`;
509
+ r.fillText(queryText, modalX + 25, modalY + 60, '13px monospace', theme.fg);
510
+
511
+ // Draw flashing block cursor in search bar
512
+ const cursorOffset = (r as any).measureText
513
+ ? (r as any).measureText(queryText).width
514
+ : queryText.length * 8;
515
+ r.beginPath();
516
+ r.roundRect(modalX + 25 + cursorOffset + 2, modalY + 48, 8, 15, 0);
517
+ r.closePath();
518
+ r.fill(theme.accent);
519
+
520
+ // Divider line
521
+ r.beginPath();
522
+ r.moveTo(modalX + 15, modalY + 82);
523
+ r.lineTo(modalX + modalW - 15, modalY + 82);
524
+ r.closePath();
525
+ r.stroke(theme.gutterBg, 1.2);
526
+
527
+ // Render filtered list items
528
+ const items = this.editorState.getFilteredPopupItems();
529
+ const listStartY = modalY + 95;
530
+ const itemH = 22;
531
+ const maxVisibleItems = 11;
532
+ const startIndex = Math.max(
533
+ 0,
534
+ Math.min(
535
+ this.editorState.activePopupIndex - Math.floor(maxVisibleItems / 2),
536
+ items.length - maxVisibleItems,
537
+ ),
538
+ );
539
+
540
+ r.save();
541
+ r.clip(modalX + 15, modalY + 85, modalW - 30, modalH - 100);
542
+
543
+ for (let i = 0; i < Math.min(maxVisibleItems, items.length); i++) {
544
+ const itemIdx = startIndex + i;
545
+ const item = items[itemIdx];
546
+ if (!item) break;
547
+
548
+ const rowY = listStartY + i * itemH;
549
+
550
+ if (itemIdx === this.editorState.activePopupIndex) {
551
+ // Draw active row highlight background
552
+ r.beginPath();
553
+ r.roundRect(modalX + 15, rowY, modalW - 30, itemH, 4);
554
+ r.closePath();
555
+ r.fill(theme.accent + '33');
556
+ }
557
+
558
+ const labelColor = itemIdx === this.editorState.activePopupIndex ? theme.accent : theme.fg;
559
+ r.fillText(
560
+ item.label,
561
+ modalX + 25,
562
+ rowY + 15,
563
+ itemIdx === this.editorState.activePopupIndex ? 'bold 12px monospace' : '12px monospace',
564
+ labelColor,
565
+ );
566
+
567
+ if (item.detail) {
568
+ const detailX = modalX + modalW - 35 - item.detail.length * 7.5;
569
+ r.fillText(
570
+ item.detail,
571
+ Math.max(modalX + 250, detailX),
572
+ rowY + 15,
573
+ '10px monospace',
574
+ theme.gutterFg,
575
+ );
576
+ }
577
+ }
578
+ r.restore();
579
+
580
+ // Draw item counts indicator
581
+ const countText = `${this.editorState.activePopupIndex + 1}/${items.length}`;
582
+ r.fillText(
583
+ countText,
584
+ modalX + modalW - 20 - countText.length * 7.5,
585
+ modalY + 28,
586
+ '11px monospace',
587
+ theme.gutterFg,
588
+ );
589
+ }
362
590
  }
363
591
  }