dbn-cli 0.5.2 → 0.5.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/package.json +1 -1
- package/src/index.ts +4 -4
- package/src/ui/grit/index.ts +6 -0
- package/src/ui/grit/utils.ts +2 -0
- package/src/ui/renderer.ts +26 -3
- package/src/ui/theme.ts +1 -1
- package/src/utils/debounce.ts +16 -0
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { SQLiteAdapter } from './adapter/sqlite.ts';
|
|
|
5
5
|
import { Screen } from './ui/screen.ts';
|
|
6
6
|
import { Renderer } from './ui/renderer.ts';
|
|
7
7
|
import { Navigator } from './ui/navigator.ts';
|
|
8
|
+
import { debounce } from './utils/debounce.ts';
|
|
8
9
|
import type { KeyPress } from './types.ts';
|
|
9
10
|
|
|
10
11
|
/**
|
|
@@ -72,10 +73,9 @@ export class DBPeek {
|
|
|
72
73
|
// Set up keyboard input
|
|
73
74
|
this.setupInput();
|
|
74
75
|
|
|
75
|
-
// Handle screen resize
|
|
76
|
-
this.
|
|
77
|
-
|
|
78
|
-
});
|
|
76
|
+
// Handle screen resize with debounce to avoid flickering
|
|
77
|
+
const debouncedRender = debounce(() => this.render(), 50);
|
|
78
|
+
this.screen.on('resize', debouncedRender);
|
|
79
79
|
|
|
80
80
|
// Initial render
|
|
81
81
|
this.render();
|
package/src/ui/grit/index.ts
CHANGED
|
@@ -52,10 +52,16 @@ export class Box {
|
|
|
52
52
|
|
|
53
53
|
export class Transition {
|
|
54
54
|
static draw(width: number, topBg: Color, bottomBg: Color): string {
|
|
55
|
+
if (topBg === bottomBg) {
|
|
56
|
+
return `${ANSI.bg(bottomBg)}${' '.repeat(width)}${ANSI.reset}`;
|
|
57
|
+
}
|
|
55
58
|
return `${ANSI.fg(topBg)}${ANSI.bg(bottomBg)}${ANSI.blockUpper.repeat(width)}${ANSI.reset}`;
|
|
56
59
|
}
|
|
57
60
|
|
|
58
61
|
static drawInverted(width: number, topBg: Color, bottomBg: Color): string {
|
|
62
|
+
if (topBg === bottomBg) {
|
|
63
|
+
return `${ANSI.bg(topBg)}${' '.repeat(width)}${ANSI.reset}`;
|
|
64
|
+
}
|
|
59
65
|
return `${ANSI.fg(bottomBg)}${ANSI.bg(topBg)}${ANSI.blockLower.repeat(width)}${ANSI.reset}`;
|
|
60
66
|
}
|
|
61
67
|
}
|
package/src/ui/grit/utils.ts
CHANGED
|
@@ -5,11 +5,13 @@ export const ANSI = {
|
|
|
5
5
|
inverse: '\x1b[7m',
|
|
6
6
|
// TrueColor (24-bit) foreground
|
|
7
7
|
fg: (hex: string) => {
|
|
8
|
+
if (!hex) return '\x1b[39m';
|
|
8
9
|
const { r, g, b } = hexToRgb(hex);
|
|
9
10
|
return `\x1b[38;2;${r};${g};${b}m`;
|
|
10
11
|
},
|
|
11
12
|
// TrueColor (24-bit) background
|
|
12
13
|
bg: (hex: string) => {
|
|
14
|
+
if (!hex) return '\x1b[49m';
|
|
13
15
|
const { r, g, b } = hexToRgb(hex);
|
|
14
16
|
return `\x1b[48;2;${r};${g};${b}m`;
|
|
15
17
|
},
|
package/src/ui/renderer.ts
CHANGED
|
@@ -10,6 +10,9 @@ import type { ViewState, TablesViewState, TableDetailViewState, SchemaViewState,
|
|
|
10
10
|
*/
|
|
11
11
|
export class Renderer {
|
|
12
12
|
private screen: Screen;
|
|
13
|
+
private lastLines: string[] = [];
|
|
14
|
+
private lastWidth: number = 0;
|
|
15
|
+
private lastHeight: number = 0;
|
|
13
16
|
|
|
14
17
|
constructor(screen: Screen) {
|
|
15
18
|
this.screen = screen;
|
|
@@ -38,9 +41,26 @@ export class Renderer {
|
|
|
38
41
|
lines.push(Transition.draw(width, THEME.background, THEME.footerBg));
|
|
39
42
|
lines.push(this.buildHelpBar(state, width));
|
|
40
43
|
|
|
41
|
-
//
|
|
42
|
-
this.
|
|
43
|
-
|
|
44
|
+
// Incremental Render Logic
|
|
45
|
+
if (width !== this.lastWidth || height !== this.lastHeight) {
|
|
46
|
+
// Screen resized: Full redraw from top, then clear remainder of screen
|
|
47
|
+
this.screen.moveCursor(1, 1);
|
|
48
|
+
this.screen.write(lines.join('\n'));
|
|
49
|
+
this.screen.write('\x1b[J'); // Clear remaining lines if new height is smaller
|
|
50
|
+
} else {
|
|
51
|
+
// Incremental update: Only write changed lines
|
|
52
|
+
for (let i = 0; i < lines.length; i++) {
|
|
53
|
+
if (lines[i] !== this.lastLines[i]) {
|
|
54
|
+
this.screen.moveCursor(i + 1, 1);
|
|
55
|
+
// Write line and clear to end of line to prevent ghosting
|
|
56
|
+
this.screen.write(lines[i] + '\x1b[K');
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
this.lastLines = lines;
|
|
62
|
+
this.lastWidth = width;
|
|
63
|
+
this.lastHeight = height;
|
|
44
64
|
}
|
|
45
65
|
|
|
46
66
|
private buildTitleBar(state: ViewState, dbPath: string, width: number): string {
|
|
@@ -291,6 +311,7 @@ export class Renderer {
|
|
|
291
311
|
case 'tables':
|
|
292
312
|
helpItems = [
|
|
293
313
|
{ key: 'j/k', label: 'select' },
|
|
314
|
+
{ key: 'g/G', label: 'first/last' },
|
|
294
315
|
{ key: 'Enter/l', label: 'open' },
|
|
295
316
|
{ key: 'i', label: 'info' },
|
|
296
317
|
{ key: 'q', label: 'quit' }
|
|
@@ -299,6 +320,7 @@ export class Renderer {
|
|
|
299
320
|
case 'table-detail':
|
|
300
321
|
helpItems = [
|
|
301
322
|
{ key: 'j/k', label: 'scroll' },
|
|
323
|
+
{ key: 'g/G', label: 'first/last' },
|
|
302
324
|
{ key: 'Enter/l', label: 'row' },
|
|
303
325
|
{ key: 's', label: 'schema' },
|
|
304
326
|
{ key: 'h', label: 'back' },
|
|
@@ -308,6 +330,7 @@ export class Renderer {
|
|
|
308
330
|
case 'schema-view':
|
|
309
331
|
helpItems = [
|
|
310
332
|
{ key: 'j/k', label: 'scroll' },
|
|
333
|
+
{ key: 'g/G', label: 'first/last' },
|
|
311
334
|
{ key: 's/h', label: 'back' },
|
|
312
335
|
{ key: 'q', label: 'quit' }
|
|
313
336
|
];
|
package/src/ui/theme.ts
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debounce a function
|
|
3
|
+
*/
|
|
4
|
+
export function debounce<T extends (...args: any[]) => any>(
|
|
5
|
+
fn: T,
|
|
6
|
+
ms: number
|
|
7
|
+
): (...args: Parameters<T>) => void {
|
|
8
|
+
let timeoutId: NodeJS.Timeout | null = null;
|
|
9
|
+
return (...args: Parameters<T>) => {
|
|
10
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
11
|
+
timeoutId = setTimeout(() => {
|
|
12
|
+
fn(...args);
|
|
13
|
+
timeoutId = null;
|
|
14
|
+
}, ms);
|
|
15
|
+
};
|
|
16
|
+
}
|