@geekmidas/studio 0.0.1 → 0.1.0

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.
@@ -18,96 +18,102 @@ export function RowDetail({ row, columns, onClose }: RowDetailProps) {
18
18
  const getValueClass = (value: unknown): string => {
19
19
  if (value === null) return 'text-slate-500 italic';
20
20
  if (typeof value === 'boolean')
21
- return value ? 'text-green-400' : 'text-red-400';
21
+ return value ? 'text-emerald-400' : 'text-red-400';
22
22
  if (typeof value === 'number') return 'text-blue-400';
23
- if (typeof value === 'string' && value.length > 100)
24
- return 'text-slate-300 text-xs';
25
23
  return 'text-slate-300';
26
24
  };
27
25
 
28
26
  return (
29
- <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
30
- <div className="bg-bg-secondary border border-border rounded-lg shadow-xl max-w-2xl w-full max-h-[80vh] flex flex-col">
31
- {/* Header */}
32
- <div className="flex items-center justify-between px-4 py-3 border-b border-border">
33
- <h2 className="text-lg font-semibold text-slate-100">Row Details</h2>
34
- <button
35
- onClick={onClose}
36
- className="text-slate-400 hover:text-slate-100 transition-colors text-xl"
27
+ <aside className="w-96 bg-studio-surface border-l border-studio-border flex flex-col shrink-0 overflow-hidden">
28
+ {/* Header */}
29
+ <div className="flex items-center justify-between px-4 py-3 border-b border-studio-border shrink-0">
30
+ <h2 className="text-sm font-medium text-slate-200">Row Details</h2>
31
+ <button
32
+ onClick={onClose}
33
+ className="p-1 hover:bg-studio-hover rounded transition-colors"
34
+ >
35
+ <svg
36
+ className="w-4 h-4 text-slate-400"
37
+ fill="none"
38
+ stroke="currentColor"
39
+ viewBox="0 0 24 24"
37
40
  >
38
- &times;
39
- </button>
40
- </div>
41
-
42
- {/* Content */}
43
- <div className="flex-1 overflow-y-auto p-4">
44
- <div className="space-y-4">
45
- {columns.map((col) => {
46
- const value = row[col.name];
47
- const isLongValue =
48
- typeof value === 'string' && value.length > 100;
49
- const isJson = typeof value === 'object' && value !== null;
41
+ <path
42
+ strokeLinecap="round"
43
+ strokeLinejoin="round"
44
+ strokeWidth={2}
45
+ d="M6 18L18 6M6 6l12 12"
46
+ />
47
+ </svg>
48
+ </button>
49
+ </div>
50
50
 
51
- return (
52
- <div key={col.name} className="border-b border-border pb-3">
53
- {/* Column name and metadata */}
54
- <div className="flex items-center gap-2 mb-1">
55
- <span className="font-medium text-slate-200">
56
- {col.name}
57
- </span>
58
- <span className="text-xs text-slate-500">
59
- {col.rawType}
60
- </span>
61
- {col.isPrimaryKey && (
62
- <span className="text-xs text-amber-400 bg-amber-400/10 px-1.5 py-0.5 rounded">
63
- PK
64
- </span>
65
- )}
66
- {col.isForeignKey && (
67
- <span className="text-xs text-blue-400 bg-blue-400/10 px-1.5 py-0.5 rounded">
68
- FK
69
- </span>
70
- )}
71
- {col.nullable && (
72
- <span className="text-xs text-slate-500">nullable</span>
73
- )}
74
- </div>
51
+ {/* Content */}
52
+ <div className="flex-1 overflow-y-auto">
53
+ {columns.map((col) => {
54
+ const value = row[col.name];
55
+ const isLongValue = typeof value === 'string' && value.length > 50;
56
+ const isJson = typeof value === 'object' && value !== null;
75
57
 
76
- {/* Value */}
77
- {isLongValue || isJson ? (
78
- <pre
79
- className={`${getValueClass(value)} bg-bg-tertiary p-2 rounded overflow-x-auto whitespace-pre-wrap break-words`}
80
- >
81
- {formatValue(value)}
82
- </pre>
83
- ) : (
84
- <div className={getValueClass(value)}>
85
- {formatValue(value)}
86
- </div>
87
- )}
58
+ return (
59
+ <div
60
+ key={col.name}
61
+ className="px-4 py-3 border-b border-studio-border"
62
+ >
63
+ {/* Column name and metadata */}
64
+ <div className="flex items-center gap-2 mb-1.5">
65
+ <span className="text-sm font-medium text-slate-300">
66
+ {col.name}
67
+ </span>
68
+ <span className="text-xs text-slate-600">{col.rawType}</span>
69
+ {col.isPrimaryKey && (
70
+ <span className="text-[10px] px-1 py-0.5 rounded bg-amber-500/20 text-amber-400">
71
+ PK
72
+ </span>
73
+ )}
74
+ {col.isForeignKey && (
75
+ <span className="text-[10px] px-1 py-0.5 rounded bg-blue-500/20 text-blue-400">
76
+ FK
77
+ </span>
78
+ )}
79
+ </div>
88
80
 
89
- {/* Foreign key reference */}
90
- {col.isForeignKey && col.foreignKeyTable && (
91
- <div className="mt-1 text-xs text-blue-400">
92
- References: {col.foreignKeyTable}.{col.foreignKeyColumn}
93
- </div>
94
- )}
81
+ {/* Value */}
82
+ {isLongValue || isJson ? (
83
+ <pre
84
+ className={`${getValueClass(value)} bg-studio-bg text-xs p-2 rounded overflow-x-auto whitespace-pre-wrap break-words max-h-48`}
85
+ >
86
+ {formatValue(value)}
87
+ </pre>
88
+ ) : (
89
+ <div className={`text-sm ${getValueClass(value)}`}>
90
+ {formatValue(value)}
95
91
  </div>
96
- );
97
- })}
98
- </div>
99
- </div>
92
+ )}
100
93
 
101
- {/* Footer */}
102
- <div className="px-4 py-3 border-t border-border flex justify-end">
103
- <button
104
- onClick={onClose}
105
- className="px-4 py-2 bg-bg-tertiary hover:bg-slate-600 rounded transition-colors text-sm"
106
- >
107
- Close
108
- </button>
109
- </div>
94
+ {/* Foreign key reference */}
95
+ {col.isForeignKey && col.foreignKeyTable && (
96
+ <div className="mt-1.5 text-xs text-blue-400 flex items-center gap-1">
97
+ <svg
98
+ className="w-3 h-3"
99
+ fill="none"
100
+ stroke="currentColor"
101
+ viewBox="0 0 24 24"
102
+ >
103
+ <path
104
+ strokeLinecap="round"
105
+ strokeLinejoin="round"
106
+ strokeWidth={2}
107
+ d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"
108
+ />
109
+ </svg>
110
+ {col.foreignKeyTable}.{col.foreignKeyColumn}
111
+ </div>
112
+ )}
113
+ </div>
114
+ );
115
+ })}
110
116
  </div>
111
- </div>
117
+ </aside>
112
118
  );
113
119
  }
@@ -2,50 +2,57 @@ import type { TableSummary } from '../types';
2
2
 
3
3
  interface TableListProps {
4
4
  tables: TableSummary[];
5
+ selectedTable: string | null;
5
6
  onSelect: (tableName: string) => void;
6
7
  }
7
8
 
8
- export function TableList({ tables, onSelect }: TableListProps) {
9
+ export function TableList({ tables, selectedTable, onSelect }: TableListProps) {
9
10
  if (tables.length === 0) {
10
11
  return (
11
- <div className="flex-1 flex flex-col items-center justify-center text-slate-500">
12
- <h3 className="text-lg mb-2">No tables found</h3>
13
- <p className="text-sm">
14
- Make sure your database has tables in the public schema.
15
- </p>
12
+ <div className="p-4 text-center text-slate-500 text-sm">
13
+ <p>No tables found</p>
14
+ <p className="mt-1 text-xs">Check your database schema.</p>
16
15
  </div>
17
16
  );
18
17
  }
19
18
 
20
19
  return (
21
- <div className="flex-1 p-4 overflow-y-auto">
22
- <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
23
- {tables.map((table) => (
24
- <div
25
- key={table.name}
26
- onClick={() => onSelect(table.name)}
27
- className="bg-bg-secondary border border-border rounded-lg p-4 cursor-pointer transition-colors hover:border-purple-500 hover:bg-bg-tertiary"
20
+ <div className="py-1">
21
+ {tables.map((table) => (
22
+ <button
23
+ key={table.name}
24
+ onClick={() => onSelect(table.name)}
25
+ className={`w-full text-left px-3 py-2 flex items-center gap-3 transition-colors ${
26
+ selectedTable === table.name
27
+ ? 'bg-studio-hover text-white'
28
+ : 'text-slate-400 hover:bg-studio-hover hover:text-slate-200'
29
+ }`}
30
+ >
31
+ {/* Table icon */}
32
+ <svg
33
+ className={`w-4 h-4 shrink-0 ${selectedTable === table.name ? 'text-emerald-400' : 'text-slate-500'}`}
34
+ fill="none"
35
+ stroke="currentColor"
36
+ viewBox="0 0 24 24"
28
37
  >
29
- <div className="flex items-center justify-between mb-2">
30
- <h3 className="font-semibold text-slate-100 truncate">
31
- {table.name}
32
- </h3>
33
- <span className="text-xs text-slate-500">{table.schema}</span>
34
- </div>
35
- <div className="flex items-center gap-4 text-sm text-slate-400">
36
- <span>{table.columnCount} columns</span>
37
- {table.estimatedRowCount !== undefined && (
38
- <span>~{table.estimatedRowCount.toLocaleString()} rows</span>
39
- )}
40
- </div>
41
- {table.primaryKey.length > 0 && (
42
- <div className="mt-2 text-xs text-slate-500">
43
- PK: {table.primaryKey.join(', ')}
38
+ <path
39
+ strokeLinecap="round"
40
+ strokeLinejoin="round"
41
+ strokeWidth={1.5}
42
+ d="M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z"
43
+ />
44
+ </svg>
45
+
46
+ <div className="flex-1 min-w-0">
47
+ <div className="truncate text-sm">{table.name}</div>
48
+ {table.estimatedRowCount !== undefined && (
49
+ <div className="text-xs text-slate-500">
50
+ {table.estimatedRowCount.toLocaleString()} rows
44
51
  </div>
45
52
  )}
46
53
  </div>
47
- ))}
48
- </div>
54
+ </button>
55
+ ))}
49
56
  </div>
50
57
  );
51
58
  }