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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dbn-cli",
3
- "version": "0.5.2",
3
+ "version": "0.5.3",
4
4
  "description": "A lightweight terminal-based database browser",
5
5
  "repository": "https://github.com/amio/dbn-cli",
6
6
  "type": "module",
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.screen.on('resize', () => {
77
- this.render();
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();
@@ -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
  }
@@ -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
  },
@@ -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
- // Clear and render
42
- this.screen.clear();
43
- this.screen.write(lines.join('\n'));
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
@@ -17,7 +17,7 @@ export const THEME = {
17
17
  text: '#FFFFFF',
18
18
  textDim: '#8E8E93',
19
19
  headerBg: '#242426',
20
- footerBg: '#1C1C1E',
20
+ footerBg: '',
21
21
  selectionBg: '#3A3A3C',
22
22
  success: '#34C759',
23
23
  warning: '#FF9500',
@@ -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
+ }