• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

worktile / slate-angular / 8fd3d244-a948-4c01-bc5b-8174089e528f

11 Dec 2025 09:45AM UTC coverage: 38.191% (-2.4%) from 40.626%
8fd3d244-a948-4c01-bc5b-8174089e528f

push

circleci

web-flow
chore: optimize debug view (#320)

386 of 1204 branches covered (32.06%)

Branch coverage included in aggregate %.

5 of 226 new or added lines in 2 files covered. (2.21%)

3 existing lines in 1 file now uncovered.

1071 of 2611 relevant lines covered (41.02%)

24.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

1.12
/packages/src/components/editable/debug.ts
1
type OverlayState = {
2
    left: number;
3
    top: number;
4
    collapsed: boolean;
5
    width: number;
6
    height: number;
7
};
8

9
export class VirtualScrollDebugOverlay {
10
    private static instance?: VirtualScrollDebugOverlay;
11
    private static readonly storageKey = 'slate_virtual_scroll_debug_overlay_state';
1✔
12
    private static readonly minWidth = 320;
1✔
13
    private static readonly minHeight = 240;
1✔
14
    private static readonly defaultWidth = 410;
1✔
15
    private static readonly defaultHeight = 480;
1✔
16

17
    static getInstance(doc: Document) {
NEW
18
        if (!this.instance) {
×
NEW
19
            this.instance = new VirtualScrollDebugOverlay(doc);
×
20
        }
NEW
21
        this.instance.init();
×
NEW
22
        return this.instance;
×
23
    }
24

25
    static log(doc: Document, type: 'log' | 'warn', ...args: any[]) {
NEW
26
        this.getInstance(doc).log(type, ...args);
×
27
    }
28

29
    static syncScrollTop(doc: Document, value: number) {
NEW
30
        const instance = this.getInstance(doc);
×
NEW
31
        instance.setScrollTopValue(value);
×
32
    }
33

34
    private container?: HTMLElement;
35
    private contentWrapper?: HTMLElement;
36
    private bubble?: HTMLElement;
37
    private logList?: HTMLElement;
38
    private selectorInput?: HTMLInputElement;
39
    private distanceInput?: HTMLInputElement;
40
    private collapseToggle?: HTMLButtonElement;
41
    private resizeHandle?: HTMLElement;
NEW
42
    private state: OverlayState = {
×
43
        left: 16,
44
        top: 16,
45
        collapsed: false,
46
        width: VirtualScrollDebugOverlay.defaultWidth,
47
        height: VirtualScrollDebugOverlay.defaultHeight
48
    };
49
    private readonly originalConsoleLog = console.log.bind(console);
×
50
    private readonly originalConsoleWarn = console.warn.bind(console);
×
51
    private dragOffsetX = 0;
×
52
    private dragOffsetY = 0;
×
53
    private isDragging = false;
×
NEW
54
    private isResizing = false;
×
NEW
55
    private resizeStartX = 0;
×
NEW
56
    private resizeStartY = 0;
×
NEW
57
    private resizeStartWidth = 0;
×
NEW
58
    private resizeStartHeight = 0;
×
NEW
59
    private dragMoved = false;
×
NEW
60
    private wasDragged = false;
×
61

62
    private readonly onDragging = (event: MouseEvent) => {
×
63
        if (!this.isDragging || !this.container) {
×
64
            return;
×
65
        }
NEW
66
        this.dragMoved = true;
×
67
        const nextLeft = event.clientX - this.dragOffsetX;
×
68
        const nextTop = event.clientY - this.dragOffsetY;
×
69
        this.container.style.left = `${nextLeft}px`;
×
70
        this.container.style.top = `${nextTop}px`;
×
71
        this.container.style.right = 'auto';
×
72
        this.container.style.bottom = 'auto';
×
73
    };
74

75
    private readonly onDragEnd = () => {
×
76
        if (!this.isDragging) {
×
77
            return;
×
78
        }
79
        this.isDragging = false;
×
NEW
80
        this.wasDragged = this.dragMoved;
×
NEW
81
        this.dragMoved = false;
×
82
        this.doc.removeEventListener('mousemove', this.onDragging);
×
83
        this.doc.removeEventListener('mouseup', this.onDragEnd);
×
84
        if (this.container) {
×
NEW
85
            const rect = this.container.getBoundingClientRect();
×
NEW
86
            this.state.left = rect.left;
×
NEW
87
            this.state.top = rect.top;
×
NEW
88
            this.persistState();
×
UNCOV
89
            this.container.style.transition = '';
×
90
        }
91
    };
92

NEW
93
    private readonly onResizing = (event: MouseEvent) => {
×
NEW
94
        if (!this.isResizing || !this.container) {
×
NEW
95
            return;
×
96
        }
NEW
97
        const deltaX = event.clientX - this.resizeStartX;
×
NEW
98
        const deltaY = event.clientY - this.resizeStartY;
×
NEW
99
        const nextWidth = Math.max(VirtualScrollDebugOverlay.minWidth, this.resizeStartWidth + deltaX);
×
NEW
100
        const nextHeight = Math.max(VirtualScrollDebugOverlay.minHeight, this.resizeStartHeight + deltaY);
×
NEW
101
        this.state.width = nextWidth;
×
NEW
102
        this.state.height = nextHeight;
×
NEW
103
        this.applySize();
×
104
    };
105

NEW
106
    private readonly onResizeEnd = () => {
×
NEW
107
        if (!this.isResizing) {
×
NEW
108
            return;
×
109
        }
NEW
110
        this.isResizing = false;
×
NEW
111
        this.doc.removeEventListener('mousemove', this.onResizing);
×
NEW
112
        this.doc.removeEventListener('mouseup', this.onResizeEnd);
×
NEW
113
        this.persistState();
×
114
    };
115

NEW
116
    private constructor(private doc: Document) {}
×
117

118
    init() {
119
        if (!this.container) {
×
120
            this.createContainer();
×
121
        } else {
NEW
122
            this.applyState();
×
123
        }
124
    }
125

126
    log(type: 'log' | 'warn', ...args: any[]) {
127
        this.init();
×
128
        if (type === 'warn') {
×
129
            this.originalConsoleWarn(...args);
×
130
        } else {
131
            this.originalConsoleLog(...args);
×
132
        }
133
        this.appendLog(type, ...args);
×
134
    }
135

136
    dispose() {
137
        this.container?.remove();
×
138
        this.container = undefined;
×
NEW
139
        this.contentWrapper = undefined;
×
NEW
140
        this.bubble = undefined;
×
141
        this.logList = undefined;
×
142
        this.selectorInput = undefined;
×
143
        this.distanceInput = undefined;
×
NEW
144
        this.collapseToggle = undefined;
×
NEW
145
        this.resizeHandle = undefined;
×
146
        this.doc.removeEventListener('mousemove', this.onDragging);
×
147
        this.doc.removeEventListener('mouseup', this.onDragEnd);
×
NEW
148
        this.doc.removeEventListener('mousemove', this.onResizing);
×
NEW
149
        this.doc.removeEventListener('mouseup', this.onResizeEnd);
×
UNCOV
150
        this.isDragging = false;
×
NEW
151
        this.isResizing = false;
×
152
    }
153

154
    private createContainer() {
NEW
155
        this.loadState();
×
156
        const doc = this.doc;
×
157
        const container = doc.createElement('div');
×
158
        container.style.position = 'fixed';
×
159
        container.style.right = 'auto';
×
160
        container.style.bottom = 'auto';
×
161
        container.style.boxSizing = 'border-box';
×
162
        container.style.background = 'rgba(17, 24, 39, 0.95)';
×
163
        container.style.color = '#e5e7eb';
×
164
        container.style.fontSize = '12px';
×
165
        container.style.border = '1px solid #1f2937';
×
166
        container.style.borderRadius = '10px';
×
NEW
167
        container.style.fontFamily = 'Menlo, Consolas, monospace';
×
168
        container.style.boxShadow = '0 10px 30px rgba(0, 0, 0, 0.35)';
×
169
        container.style.zIndex = '9999';
×
170
        container.style.display = 'flex';
×
171
        container.style.flexDirection = 'column';
×
172
        container.style.gap = '10px';
×
NEW
173
        container.style.minWidth = `${VirtualScrollDebugOverlay.minWidth}px`;
×
NEW
174
        container.style.minHeight = `${VirtualScrollDebugOverlay.minHeight}px`;
×
NEW
175
        container.style.maxHeight = '80vh';
×
NEW
176
        container.style.cursor = 'default';
×
NEW
177
        container.addEventListener('mousedown', event => {
×
NEW
178
            if (this.state.collapsed) {
×
NEW
179
                this.startDrag(event);
×
180
            }
181
        });
182

183
        const header = doc.createElement('div');
×
184
        header.style.display = 'flex';
×
185
        header.style.alignItems = 'center';
×
186
        header.style.justifyContent = 'space-between';
×
187
        header.style.cursor = 'move';
×
188
        header.addEventListener('mousedown', event => {
×
NEW
189
            this.startDrag(event);
×
190
        });
191

192
        const title = doc.createElement('div');
×
193
        title.textContent = 'Virtual Scroll Debug';
×
194
        title.style.fontWeight = '600';
×
195
        title.style.letterSpacing = '0.3px';
×
196

NEW
197
        const actions = doc.createElement('div');
×
NEW
198
        actions.style.display = 'flex';
×
NEW
199
        actions.style.gap = '6px';
×
200

NEW
201
        const collapseButton = doc.createElement('button');
×
NEW
202
        collapseButton.type = 'button';
×
NEW
203
        collapseButton.textContent = '折叠';
×
NEW
204
        collapseButton.style.background = '#1f2937';
×
NEW
205
        collapseButton.style.color = '#e5e7eb';
×
NEW
206
        collapseButton.style.border = '1px solid #374151';
×
NEW
207
        collapseButton.style.borderRadius = '6px';
×
NEW
208
        collapseButton.style.padding = '4px 8px';
×
NEW
209
        collapseButton.style.cursor = 'pointer';
×
NEW
210
        collapseButton.addEventListener('click', () => {
×
NEW
211
            this.setCollapsed(!this.state.collapsed);
×
212
        });
213

214
        const clearButton = doc.createElement('button');
×
215
        clearButton.type = 'button';
×
216
        clearButton.textContent = '清除日志';
×
217
        clearButton.style.background = '#374151';
×
218
        clearButton.style.color = '#e5e7eb';
×
219
        clearButton.style.border = '1px solid #4b5563';
×
220
        clearButton.style.borderRadius = '6px';
×
221
        clearButton.style.padding = '4px 10px';
×
222
        clearButton.style.cursor = 'pointer';
×
223
        clearButton.addEventListener('click', () => {
×
224
            if (this.logList) {
×
225
                this.logList.innerHTML = '';
×
226
            }
227
        });
228

NEW
229
        actions.appendChild(collapseButton);
×
NEW
230
        actions.appendChild(clearButton);
×
231
        header.appendChild(title);
×
NEW
232
        header.appendChild(actions);
×
233

234
        const scrollForm = doc.createElement('div');
×
235
        scrollForm.style.display = 'grid';
×
NEW
236
        scrollForm.style.gridTemplateColumns = '1fr 110px 50px';
×
237
        scrollForm.style.gap = '6px';
×
238
        scrollForm.style.alignItems = 'center';
×
239

240
        const selectorInput = doc.createElement('input');
×
241
        selectorInput.placeholder = '滚动容器 selector';
×
242
        selectorInput.style.padding = '6px 8px';
×
243
        selectorInput.style.borderRadius = '6px';
×
244
        selectorInput.style.border = '1px solid #4b5563';
×
245
        selectorInput.style.background = '#111827';
×
246
        selectorInput.style.color = '#e5e7eb';
×
247
        selectorInput.autocomplete = 'off';
×
248

249
        const distanceInput = doc.createElement('input');
×
250
        distanceInput.placeholder = '滚动距离(px)';
×
251
        distanceInput.type = 'number';
×
252
        distanceInput.style.padding = '6px 8px';
×
253
        distanceInput.style.borderRadius = '6px';
×
254
        distanceInput.style.border = '1px solid #4b5563';
×
255
        distanceInput.style.background = '#111827';
×
256
        distanceInput.style.color = '#e5e7eb';
×
257

258
        const scrollButton = doc.createElement('button');
×
259
        scrollButton.type = 'button';
×
260
        scrollButton.textContent = '滚动';
×
261
        scrollButton.style.background = '#10b981';
×
262
        scrollButton.style.color = '#0b1c15';
×
263
        scrollButton.style.border = 'none';
×
264
        scrollButton.style.borderRadius = '6px';
×
265
        scrollButton.style.padding = '6px 10px';
×
266
        scrollButton.style.cursor = 'pointer';
×
267
        scrollButton.addEventListener('click', () => {
×
268
            const selector = selectorInput.value.trim();
×
269
            const distanceValue = Number(distanceInput.value ?? 0);
×
270
            const distance = Number.isFinite(distanceValue) ? distanceValue : 0;
×
271
            if (!selector) {
×
272
                this.log('warn', '请先填写滚动容器 selector');
×
273
                return;
×
274
            }
275
            const target = doc.querySelector(selector) as HTMLElement | null;
×
276
            if (!target) {
×
277
                this.log('warn', `未找到滚动容器: ${selector}`);
×
278
                return;
×
279
            }
280
            if (typeof target.scrollTo === 'function') {
×
281
                target.scrollTo({ top: distance, behavior: 'auto' });
×
282
            } else if (Object.prototype.hasOwnProperty.call(target, 'scrollTop')) {
×
283
                (target as HTMLElement & { scrollTop: number }).scrollTop = distance;
×
284
            } else {
285
                this.log('warn', '目标元素不支持滚动:', selector);
×
286
                return;
×
287
            }
288
            this.log('log', `已将 ${selector} 滚动到`, distance);
×
289
        });
290

291
        scrollForm.appendChild(selectorInput);
×
292
        scrollForm.appendChild(distanceInput);
×
293
        scrollForm.appendChild(scrollButton);
×
294

295
        const logList = doc.createElement('div');
×
296
        logList.style.height = '260px';
×
297
        logList.style.overflowY = 'auto';
×
298
        logList.style.background = '#0b1220';
×
299
        logList.style.border = '1px solid #1f2937';
×
300
        logList.style.borderRadius = '8px';
×
301
        logList.style.padding = '8px';
×
302
        logList.style.display = 'flex';
×
303
        logList.style.flexDirection = 'column';
×
304
        logList.style.gap = '6px';
×
NEW
305
        logList.style.flex = '1';
×
NEW
306
        logList.style.minHeight = '160px';
×
307

NEW
308
        const bubble = doc.createElement('div');
×
NEW
309
        bubble.textContent = 'VS';
×
NEW
310
        bubble.style.display = 'none';
×
NEW
311
        bubble.style.alignItems = 'center';
×
NEW
312
        bubble.style.justifyContent = 'center';
×
NEW
313
        bubble.style.fontWeight = '700';
×
NEW
314
        bubble.style.fontSize = '14px';
×
NEW
315
        bubble.style.letterSpacing = '0.5px';
×
NEW
316
        bubble.style.width = '100%';
×
NEW
317
        bubble.style.height = '100%';
×
NEW
318
        bubble.style.userSelect = 'none';
×
NEW
319
        bubble.addEventListener('click', () => {
×
NEW
320
            if (this.wasDragged) {
×
NEW
321
                this.wasDragged = false;
×
NEW
322
                return;
×
323
            }
NEW
324
            this.setCollapsed(false);
×
325
        });
326

NEW
327
        const contentWrapper = doc.createElement('div');
×
NEW
328
        contentWrapper.style.display = 'flex';
×
NEW
329
        contentWrapper.style.flexDirection = 'column';
×
NEW
330
        contentWrapper.style.gap = '10px';
×
NEW
331
        contentWrapper.style.width = '100%';
×
NEW
332
        contentWrapper.style.height = '100%';
×
NEW
333
        contentWrapper.appendChild(header);
×
NEW
334
        contentWrapper.appendChild(scrollForm);
×
NEW
335
        contentWrapper.appendChild(logList);
×
336

NEW
337
        container.appendChild(contentWrapper);
×
NEW
338
        container.appendChild(bubble);
×
NEW
339
        const resizeHandle = doc.createElement('div');
×
NEW
340
        resizeHandle.style.position = 'absolute';
×
NEW
341
        resizeHandle.style.right = '6px';
×
NEW
342
        resizeHandle.style.bottom = '6px';
×
NEW
343
        resizeHandle.style.width = '14px';
×
NEW
344
        resizeHandle.style.height = '14px';
×
NEW
345
        resizeHandle.style.cursor = 'nwse-resize';
×
NEW
346
        resizeHandle.style.borderRight = '2px solid #4b5563';
×
NEW
347
        resizeHandle.style.borderBottom = '2px solid #4b5563';
×
NEW
348
        resizeHandle.addEventListener('mousedown', event => {
×
NEW
349
            this.startResize(event);
×
350
        });
NEW
351
        container.appendChild(resizeHandle);
×
UNCOV
352
        doc.body.appendChild(container);
×
353

354
        this.container = container;
×
NEW
355
        this.contentWrapper = contentWrapper;
×
NEW
356
        this.bubble = bubble;
×
357
        this.logList = logList;
×
358
        this.selectorInput = selectorInput;
×
359
        this.distanceInput = distanceInput;
×
NEW
360
        this.collapseToggle = collapseButton;
×
NEW
361
        this.resizeHandle = resizeHandle;
×
NEW
362
        this.applyState();
×
363
    }
364

365
    private startDrag(event: MouseEvent) {
NEW
366
        if (event.button !== 0) {
×
NEW
367
            return;
×
368
        }
NEW
369
        if (!this.container) {
×
NEW
370
            return;
×
371
        }
NEW
372
        const rect = this.container.getBoundingClientRect();
×
NEW
373
        this.isDragging = true;
×
NEW
374
        this.wasDragged = false;
×
NEW
375
        this.dragMoved = false;
×
NEW
376
        this.dragOffsetX = event.clientX - rect.left;
×
NEW
377
        this.dragOffsetY = event.clientY - rect.top;
×
NEW
378
        this.container.style.transition = 'none';
×
NEW
379
        this.doc.addEventListener('mousemove', this.onDragging);
×
NEW
380
        this.doc.addEventListener('mouseup', this.onDragEnd);
×
NEW
381
        if (!this.state.collapsed) {
×
NEW
382
            event.preventDefault();
×
383
        }
384
    }
385

386
    private startResize(event: MouseEvent) {
NEW
387
        if (event.button !== 0 || this.state.collapsed) {
×
NEW
388
            return;
×
389
        }
NEW
390
        if (!this.container) {
×
NEW
391
            return;
×
392
        }
NEW
393
        const rect = this.container.getBoundingClientRect();
×
NEW
394
        this.isResizing = true;
×
NEW
395
        this.resizeStartX = event.clientX;
×
NEW
396
        this.resizeStartY = event.clientY;
×
NEW
397
        this.resizeStartWidth = rect.width;
×
NEW
398
        this.resizeStartHeight = rect.height;
×
NEW
399
        this.doc.addEventListener('mousemove', this.onResizing);
×
NEW
400
        this.doc.addEventListener('mouseup', this.onResizeEnd);
×
NEW
401
        event.preventDefault();
×
NEW
402
        event.stopPropagation();
×
403
    }
404

405
    private applyPosition() {
NEW
406
        if (!this.container) {
×
NEW
407
            return;
×
408
        }
NEW
409
        this.container.style.left = `${this.state.left}px`;
×
NEW
410
        this.container.style.top = `${this.state.top}px`;
×
411
    }
412

413
    private applySize() {
NEW
414
        if (!this.container) {
×
NEW
415
            return;
×
416
        }
NEW
417
        const width = Math.max(VirtualScrollDebugOverlay.minWidth, this.state.width || VirtualScrollDebugOverlay.defaultWidth);
×
NEW
418
        const height = Math.max(VirtualScrollDebugOverlay.minHeight, this.state.height || VirtualScrollDebugOverlay.defaultHeight);
×
NEW
419
        this.state.width = width;
×
NEW
420
        this.state.height = height;
×
NEW
421
        this.container.style.width = `${width}px`;
×
NEW
422
        this.container.style.height = `${height}px`;
×
423
    }
424

425
    private applyCollapsedState() {
NEW
426
        if (!this.container || !this.contentWrapper || !this.bubble || !this.collapseToggle) {
×
NEW
427
            return;
×
428
        }
NEW
429
        if (this.state.collapsed) {
×
NEW
430
            this.container.style.width = '36px';
×
NEW
431
            this.container.style.height = '36px';
×
NEW
432
            this.container.style.minWidth = '';
×
NEW
433
            this.container.style.minHeight = '';
×
NEW
434
            this.container.style.padding = '0';
×
NEW
435
            this.container.style.borderRadius = '50%';
×
NEW
436
            this.container.style.display = 'flex';
×
NEW
437
            this.container.style.flexDirection = 'row';
×
NEW
438
            this.container.style.alignItems = 'center';
×
NEW
439
            this.container.style.justifyContent = 'center';
×
NEW
440
            this.container.style.cursor = 'move';
×
NEW
441
            this.contentWrapper.style.display = 'none';
×
NEW
442
            this.bubble.style.display = 'flex';
×
NEW
443
            this.collapseToggle.textContent = '展开';
×
NEW
444
            this.resizeHandle.style.display = 'none';
×
445
        } else {
NEW
446
            this.applySize();
×
NEW
447
            this.container.style.padding = '12px';
×
NEW
448
            this.container.style.borderRadius = '10px';
×
NEW
449
            this.container.style.display = 'flex';
×
NEW
450
            this.container.style.flexDirection = 'column';
×
NEW
451
            this.container.style.gap = '10px';
×
NEW
452
            this.container.style.cursor = 'default';
×
NEW
453
            this.contentWrapper.style.display = 'flex';
×
NEW
454
            this.bubble.style.display = 'none';
×
NEW
455
            this.collapseToggle.textContent = '折叠';
×
NEW
456
            this.resizeHandle.style.display = 'block';
×
457
        }
458
    }
459

460
    private setCollapsed(collapsed: boolean) {
NEW
461
        this.state.collapsed = collapsed;
×
NEW
462
        this.applyCollapsedState();
×
NEW
463
        this.persistState();
×
464
    }
465

466
    private applyState() {
NEW
467
        this.applyPosition();
×
NEW
468
        this.applyCollapsedState();
×
469
    }
470

471
    private loadState() {
NEW
472
        try {
×
NEW
473
            const raw = this.doc.defaultView?.localStorage?.getItem(VirtualScrollDebugOverlay.storageKey);
×
NEW
474
            if (raw) {
×
NEW
475
                const parsed = JSON.parse(raw) as Partial<OverlayState>;
×
NEW
476
                if (typeof parsed.left === 'number') {
×
NEW
477
                    this.state.left = parsed.left;
×
478
                }
NEW
479
                if (typeof parsed.top === 'number') {
×
NEW
480
                    this.state.top = parsed.top;
×
481
                }
NEW
482
                if (typeof parsed.collapsed === 'boolean') {
×
NEW
483
                    this.state.collapsed = parsed.collapsed;
×
484
                }
NEW
485
                if (typeof parsed.width === 'number') {
×
NEW
486
                    this.state.width = parsed.width;
×
487
                }
NEW
488
                if (typeof parsed.height === 'number') {
×
NEW
489
                    this.state.height = parsed.height;
×
490
                }
491
            }
492
        } catch {
493
            // ignore storage errors
494
        }
495
    }
496

497
    private persistState() {
NEW
498
        try {
×
NEW
499
            this.doc.defaultView?.localStorage?.setItem(VirtualScrollDebugOverlay.storageKey, JSON.stringify(this.state));
×
500
        } catch {
501
            // ignore storage errors
502
        }
503
    }
504

505
    private appendLog(type: 'log' | 'warn', ...args: any[]) {
506
        if (!this.logList) {
×
507
            return;
×
508
        }
509
        const item = this.doc.createElement('div');
×
510
        item.style.display = 'flex';
×
511
        item.style.gap = '6px';
×
512
        item.style.alignItems = 'flex-start';
×
513
        item.style.wordBreak = 'break-all';
×
514
        item.style.color = type === 'warn' ? '#fbbf24' : '#9ca3af';
×
515

516
        const time = this.doc.createElement('span');
×
517
        time.textContent = new Date().toLocaleTimeString();
×
518
        time.style.color = '#6b7280';
×
519
        time.style.flexShrink = '0';
×
520
        time.style.width = '72px';
×
521

522
        const text = this.doc.createElement('span');
×
523
        text.textContent = `[${type}] ${args.map(arg => this.formatValue(arg)).join(' ')}`;
×
524

525
        item.appendChild(time);
×
526
        item.appendChild(text);
×
527
        this.logList.appendChild(item);
×
528
        this.logList.scrollTop = this.logList.scrollHeight;
×
529
    }
530

531
    private formatValue(value: any) {
532
        if (typeof value === 'string') {
×
533
            return value;
×
534
        }
535
        try {
×
536
            return JSON.stringify(value);
×
537
        } catch (error) {
538
            return String(value);
×
539
        }
540
    }
541

542
    private setScrollTopValue(value: number) {
NEW
543
        if (this.distanceInput) {
×
NEW
544
            this.distanceInput.value = String(value ?? 0);
×
545
        }
546
    }
547
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc