sqlite-hub 0.6.0 → 0.7.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.
@@ -1,4 +1,3 @@
1
- import { renderPageHeader } from "../components/pageHeader.js";
2
1
  import { clearInspector, renderInspector } from "../components/structureGraph.js";
3
2
  import { escapeHtml, formatNumber } from "../utils/format.js";
4
3
 
@@ -6,7 +5,7 @@ function renderEntryGroup(title, entries, activeName, options = {}) {
6
5
  const { compact = false, showMeta = true } = options;
7
6
 
8
7
  return `
9
- <section class="shell-section flex flex-col p-5">
8
+ <section class="structure-sidebar__section">
10
9
  <div class="mb-4 shrink-0 text-[10px] font-bold uppercase tracking-[0.25em] text-primary-container">
11
10
  ${escapeHtml(title)}
12
11
  </div>
@@ -282,6 +281,31 @@ function renderGraphSurface(structure, selectedName, detail, detailLoading) {
282
281
  `;
283
282
  }
284
283
 
284
+ function renderStructureWorkspaceHeader(structure, selectedName) {
285
+ const tableCount = structure?.grouped?.tables?.length ?? 0;
286
+ const viewCount = structure?.grouped?.views?.length ?? 0;
287
+
288
+ return `
289
+ <header class="structure-view__header">
290
+ <div class="structure-headline-container">
291
+ <div class="text-[10px] font-bold uppercase tracking-[0.2em] text-primary-container">
292
+ Schema Graph
293
+ </div>
294
+ <h1 class="mt-2 font-headline text-4xl font-black uppercase tracking-tight text-primary-container">
295
+ Structure
296
+ </h1>
297
+ <div class="mt-2 text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">
298
+ ${
299
+ selectedName
300
+ ? `selected ${escapeHtml(selectedName)} // tables ${escapeHtml(formatNumber(tableCount))} // views ${escapeHtml(formatNumber(viewCount))}`
301
+ : `tables ${escapeHtml(formatNumber(tableCount))} // views ${escapeHtml(formatNumber(viewCount))}`
302
+ }
303
+ </div>
304
+ </div>
305
+ </header>
306
+ `;
307
+ }
308
+
285
309
  export function renderStructureView(state) {
286
310
  const structure = state.structure.data;
287
311
  const detail =
@@ -289,17 +313,61 @@ export function renderStructureView(state) {
289
313
 
290
314
  return {
291
315
  main: `
292
- <section class="view-surface flex h-full min-h-0 flex-col bg-surface-container">
293
- <div class="view-frame flex h-full min-h-0 flex-col">
294
- ${renderPageHeader({
295
- title: "Structure",
296
- subtitle: "Schema graph, foreign-key paths, raw DDL, and object metadata",
297
- })}
316
+ <section class="view-surface structure-view">
317
+ <aside class="structure-view__sidebar">
318
+ <div class="structure-view__sidebar-header">
319
+ <div class="text-[10px] font-bold uppercase tracking-[0.2em] text-primary-container">
320
+ Objects
321
+ </div>
322
+ <div class="mt-2 text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">
323
+ total ${escapeHtml(
324
+ formatNumber(
325
+ (structure?.grouped?.tables?.length ?? 0) +
326
+ (structure?.grouped?.views?.length ?? 0) +
327
+ (structure?.grouped?.indexes?.length ?? 0) +
328
+ (structure?.grouped?.triggers?.length ?? 0)
329
+ )
330
+ )}
331
+ </div>
332
+ </div>
333
+ <div class="structure-view__sidebar-body custom-scrollbar">
334
+ ${
335
+ structure
336
+ ? `
337
+ ${renderEntryGroup(
338
+ "Tables",
339
+ structure.grouped.tables,
340
+ state.structure.selectedName,
341
+ { compact: true, showMeta: false }
342
+ )}
343
+ ${renderEntryGroup(
344
+ "Views",
345
+ structure.grouped.views,
346
+ state.structure.selectedName
347
+ )}
348
+ ${renderEntryGroup(
349
+ "Indexes",
350
+ structure.grouped.indexes,
351
+ state.structure.selectedName,
352
+ { compact: true, showMeta: false }
353
+ )}
354
+ ${renderEntryGroup(
355
+ "Triggers",
356
+ structure.grouped.triggers,
357
+ state.structure.selectedName
358
+ )}
359
+ `
360
+ : ""
361
+ }
362
+ </div>
363
+ </aside>
298
364
 
365
+ <section class="structure-view__detail">
366
+ ${renderStructureWorkspaceHeader(structure, state.structure.selectedName)}
299
367
  ${
300
368
  state.structure.loading && !structure
301
369
  ? `
302
- <div class="flex min-h-0 flex-1 items-center justify-center border border-outline-variant/10 bg-surface-container-low">
370
+ <div class="flex min-h-0 flex-1 items-center justify-center border-t border-outline-variant/10 bg-surface-container-low">
303
371
  <div class="text-center text-on-surface-variant/40">
304
372
  <span class="material-symbols-outlined mb-3 text-4xl">progress_activity</span>
305
373
  <p class="font-mono text-[10px] uppercase tracking-[0.22em]">LOADING_STRUCTURE</p>
@@ -308,7 +376,7 @@ export function renderStructureView(state) {
308
376
  `
309
377
  : state.structure.error
310
378
  ? `
311
- <div class="min-h-0 flex-1 border border-error/20 bg-error-container/10 px-6 py-5 text-sm text-on-surface">
379
+ <div class="min-h-0 flex-1 border-t border-error/20 bg-error-container/10 px-6 py-5 text-sm text-on-surface">
312
380
  <div class="font-headline text-xs font-bold uppercase tracking-[0.18em] text-error">
313
381
  ${escapeHtml(state.structure.error.code)}
314
382
  </div>
@@ -317,44 +385,18 @@ export function renderStructureView(state) {
317
385
  `
318
386
  : structure
319
387
  ? `
320
- <section class="grid min-h-0 flex-1 grid-cols-1 gap-6 xl:grid-cols-[18.5rem_minmax(0,1fr)] 2xl:grid-cols-[19.5rem_minmax(0,1fr)]">
321
- <div class="custom-scrollbar min-h-0 overflow-y-auto pr-1">
322
- <div class="space-y-6">
323
- ${renderEntryGroup(
324
- "Tables",
325
- structure.grouped.tables,
326
- state.structure.selectedName,
327
- { compact: true, showMeta: false }
328
- )}
329
- ${renderEntryGroup(
330
- "Views",
331
- structure.grouped.views,
332
- state.structure.selectedName
333
- )}
334
- ${renderEntryGroup(
335
- "Indexes",
336
- structure.grouped.indexes,
337
- state.structure.selectedName,
338
- { compact: true, showMeta: false }
339
- )}
340
- ${renderEntryGroup(
341
- "Triggers",
342
- structure.grouped.triggers,
343
- state.structure.selectedName
344
- )}
345
- </div>
346
- </div>
388
+ <div class="structure-view__graph-shell">
347
389
  ${renderGraphSurface(
348
390
  structure,
349
391
  state.structure.selectedName,
350
392
  detail,
351
393
  state.structure.detailLoading
352
394
  )}
353
- </section>
395
+ </div>
354
396
  `
355
397
  : ""
356
398
  }
357
- </div>
399
+ </section>
358
400
  </section>
359
401
  `,
360
402
  panel: "",
@@ -371,7 +371,6 @@
371
371
  text-transform: uppercase;
372
372
  }
373
373
 
374
- .table-designer-sidebar__title,
375
374
  .table-designer-preview__title,
376
375
  .table-designer-main__section-title {
377
376
  color: var(--color-on-surface);
@@ -457,7 +456,7 @@
457
456
  align-items: center;
458
457
  background: var(--color-surface-container-lowest);
459
458
  border: 1px solid rgba(75, 71, 50, 0.18);
460
- margin: var(--spacing-4) var(--spacing-5) 0;
459
+ margin: var(--spacing-4) var(--spacing-3) 0;
461
460
  border-bottom: 1px solid rgba(75, 71, 50, 0.18);
462
461
  display: flex;
463
462
  gap: var(--spacing-3);
@@ -514,26 +513,19 @@ select.table-designer-field {
514
513
  }
515
514
 
516
515
  .table-designer-sidebar__item {
517
- background: var(--color-surface-container-lowest);
518
- border: 1px solid rgba(75, 71, 50, 0.14);
519
516
  display: block;
520
517
  margin-bottom: var(--spacing-2);
521
- padding: var(--spacing-4);
522
- text-align: left;
523
518
  transition:
524
519
  background-color var(--transition-fast),
525
520
  border-color var(--transition-fast);
526
- width: 100%;
527
521
  }
528
522
 
529
523
  .table-designer-sidebar__item:hover {
530
- background: var(--color-surface-container);
531
- border-color: rgba(252, 227, 0, 0.22);
524
+ border-color: rgba(75, 71, 50, 0.1);
532
525
  }
533
526
 
534
527
  .table-designer-sidebar__item.is-active {
535
- background: var(--color-surface-container-high);
536
- border-color: rgba(252, 227, 0, 0.32);
528
+ border-color: rgba(252, 227, 0, 0.3);
537
529
  }
538
530
 
539
531
  .table-designer-sidebar__item-name {
@@ -545,12 +537,12 @@ select.table-designer-field {
545
537
  text-overflow: ellipsis;
546
538
  }
547
539
 
548
- .table-designer-sidebar__item.is-active .table-designer-sidebar__item-name {
540
+ .table-designer-sidebar__item-name.is-active {
549
541
  color: var(--color-primary-container);
550
542
  }
551
543
 
552
544
  .table-designer-sidebar__item-meta {
553
- color: rgba(205, 199, 171, 0.48);
545
+ color: rgba(229, 226, 225, 0.45);
554
546
  font-family: var(--font-family-mono);
555
547
  font-size: var(--font-size-status);
556
548
  letter-spacing: 0.12em;
@@ -1,124 +1,354 @@
1
1
  .machined-grid {
2
- background-image: radial-gradient(var(--color-grid-dot) 1px, transparent 1px);
3
- background-size: 32px 32px;
2
+ background-image: radial-gradient(var(--color-grid-dot) 1px, transparent 1px);
3
+ background-size: 32px 32px;
4
4
  }
5
5
 
6
6
  .data-grid-texture {
7
- background-image: radial-gradient(var(--color-primary-container) 1px, transparent 1px);
8
- background-size: 24px 24px;
9
- opacity: 0.03;
7
+ background-image: radial-gradient(var(--color-primary-container) 1px, transparent 1px);
8
+ background-size: 24px 24px;
9
+ opacity: 0.03;
10
10
  }
11
11
 
12
12
  .view-surface {
13
- background: var(--color-surface);
14
- min-height: 100%;
13
+ background: var(--color-surface);
14
+ min-height: 100%;
15
15
  }
16
16
 
17
17
  .view-frame {
18
- min-height: 100%;
19
- padding: var(--spacing-8);
18
+ min-height: 100%;
19
+ padding: var(--spacing-8);
20
20
  }
21
21
 
22
22
  .view-stack {
23
- display: flex;
24
- flex-direction: column;
25
- gap: var(--spacing-8);
23
+ display: flex;
24
+ flex-direction: column;
25
+ gap: var(--spacing-8);
26
26
  }
27
27
 
28
28
  .table-designer-view {
29
- display: flex;
30
- height: 100%;
31
- min-height: 0;
32
- overflow: hidden;
29
+ display: flex;
30
+ height: 100%;
31
+ min-height: 0;
32
+ overflow: hidden;
33
33
  }
34
34
 
35
35
  .table-designer-workspace {
36
- display: flex;
37
- flex: 1;
38
- flex-direction: column;
39
- gap: var(--spacing-4);
40
- min-height: 0;
41
- min-width: 0;
36
+ display: flex;
37
+ flex: 1;
38
+ flex-direction: column;
39
+ gap: var(--spacing-4);
40
+ min-height: 0;
41
+ min-width: 0;
42
42
  }
43
43
 
44
44
  .table-designer-workspace__top,
45
45
  .table-designer-workspace__bottom {
46
- min-height: 0;
47
- min-width: 0;
46
+ min-height: 0;
47
+ min-width: 0;
48
48
  }
49
49
 
50
50
  .table-designer-workspace__top {
51
- flex: 1;
51
+ flex: 1;
52
52
  }
53
53
 
54
54
  .table-designer-workspace__bottom {
55
- flex: 0 0 34%;
55
+ flex: 0 0 34%;
56
56
  }
57
57
 
58
58
  .landing-view {
59
- align-items: center;
60
- display: flex;
61
- justify-content: center;
62
- min-height: 100%;
63
- overflow: hidden;
64
- position: relative;
59
+ align-items: center;
60
+ display: flex;
61
+ justify-content: center;
62
+ min-height: 100%;
63
+ overflow: hidden;
64
+ position: relative;
65
65
  }
66
66
 
67
67
  .landing-accent {
68
- background: rgba(75, 71, 50, 0.2);
69
- height: 1px;
70
- position: absolute;
68
+ background: rgba(75, 71, 50, 0.2);
69
+ height: 1px;
70
+ position: absolute;
71
71
  }
72
72
 
73
73
  .landing-accent--a {
74
- left: 3rem;
75
- top: 2.5rem;
76
- transform: rotate(45deg);
77
- width: 8rem;
74
+ left: 3rem;
75
+ top: 2.5rem;
76
+ transform: rotate(45deg);
77
+ width: 8rem;
78
78
  }
79
79
 
80
80
  .landing-accent--b {
81
- bottom: 2.5rem;
82
- right: 3rem;
83
- transform: rotate(-12deg);
84
- width: 16rem;
81
+ bottom: 2.5rem;
82
+ right: 3rem;
83
+ transform: rotate(-12deg);
84
+ width: 16rem;
85
85
  }
86
86
 
87
87
  .landing-accent--c {
88
- border-bottom: 1px solid rgba(75, 71, 50, 0.12);
89
- border-right: 1px solid rgba(75, 71, 50, 0.12);
90
- height: 4rem;
91
- left: 0;
92
- top: 50%;
93
- width: 4rem;
88
+ border-bottom: 1px solid rgba(75, 71, 50, 0.12);
89
+ border-right: 1px solid rgba(75, 71, 50, 0.12);
90
+ height: 4rem;
91
+ left: 0;
92
+ top: 50%;
93
+ width: 4rem;
94
94
  }
95
95
 
96
96
  .system-placeholder-grid {
97
- display: grid;
98
- gap: var(--spacing-4);
99
- grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
97
+ display: grid;
98
+ gap: var(--spacing-4);
99
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
100
100
  }
101
101
 
102
102
  .system-placeholder-card {
103
- background: var(--color-surface-low);
104
- border-bottom: 2px solid rgba(49, 48, 48, 1);
105
- display: flex;
106
- flex-direction: column;
107
- gap: var(--spacing-2);
108
- min-height: 160px;
109
- padding: var(--spacing-6);
103
+ background: var(--color-surface-low);
104
+ border-bottom: 2px solid rgba(49, 48, 48, 1);
105
+ display: flex;
106
+ flex-direction: column;
107
+ gap: var(--spacing-2);
108
+ min-height: 160px;
109
+ padding: var(--spacing-6);
110
110
  }
111
111
 
112
112
  .system-placeholder-card.is-primary {
113
- border-bottom-color: var(--color-primary-container);
113
+ border-bottom-color: var(--color-primary-container);
114
114
  }
115
115
 
116
- @media (max-width: 1023px) {
117
- .table-designer-view {
116
+ .charts-view {
117
+ background: var(--color-surface);
118
+ display: flex;
119
+ height: 100%;
120
+ min-height: 0;
121
+ overflow: hidden;
122
+ }
123
+
124
+ .charts-view__sidebar {
125
+ background: var(--color-surface-low);
126
+ border-right: 1px solid rgba(75, 71, 50, 0.18);
127
+ display: flex;
128
+ flex: 0 0 23rem;
118
129
  flex-direction: column;
119
- }
130
+ min-height: 0;
131
+ width: 23rem;
132
+ }
133
+
134
+ .charts-view__sidebar-header {
135
+ border-bottom: 1px solid rgba(75, 71, 50, 0.18);
136
+ padding: var(--spacing-5);
137
+ }
138
+
139
+ .structure-view {
140
+ background: var(--color-surface);
141
+ display: flex;
142
+ height: 100%;
143
+ min-height: 0;
144
+ overflow: hidden;
145
+ }
146
+
147
+ .structure-view__sidebar {
148
+ background: var(--color-surface-low);
149
+ border-right: 1px solid rgba(75, 71, 50, 0.18);
150
+ display: flex;
151
+ flex: 0 0 18.5rem;
152
+ flex-direction: column;
153
+ min-height: 0;
154
+ width: 18.5rem;
155
+ }
156
+
157
+ .structure-view__sidebar-header {
158
+ border-bottom: 1px solid rgba(75, 71, 50, 0.18);
159
+ padding: var(--spacing-5);
160
+ }
161
+
162
+ .structure-view__sidebar-body {
163
+ flex: 1;
164
+ min-height: 0;
165
+ overflow-y: auto;
166
+ padding: var(--spacing-4);
167
+ }
168
+
169
+ .structure-view__detail {
170
+ display: flex;
171
+ flex: 1;
172
+ flex-direction: column;
173
+ min-height: 0;
174
+ min-width: 0;
175
+ }
176
+
177
+ .structure-view__header {
178
+ border-bottom: 1px solid rgba(75, 71, 50, 0.18);
179
+ padding: 1.25rem 1.5rem;
180
+ }
181
+
182
+ .structure-headline-container {
183
+ max-width: min(64rem, 100%);
184
+ }
185
+
186
+ .structure-view__graph-shell {
187
+ flex: 1;
188
+ min-height: 0;
189
+ }
190
+
191
+ .structure-sidebar__section + .structure-sidebar__section {
192
+ margin-top: 1.5rem;
193
+ }
194
+
195
+ .charts-view__detail {
196
+ display: flex;
197
+ flex: 1;
198
+ flex-direction: column;
199
+ min-height: 0;
200
+ min-width: 0;
201
+ }
202
+
203
+ .charts-detail-shell {
204
+ min-height: 100%;
205
+ padding: var(--spacing-6);
206
+ }
207
+
208
+ .charts-detail-shell__header {
209
+ align-items: end;
210
+ display: flex;
211
+ flex-wrap: wrap;
212
+ gap: var(--spacing-4);
213
+ justify-content: space-between;
214
+ }
215
+
216
+ .charts-detail-shell__controls {
217
+ align-items: end;
218
+ display: flex;
219
+ flex-wrap: wrap;
220
+ gap: var(--spacing-3);
221
+ }
222
+
223
+ .charts-detail-shell__button {
224
+ background: var(--color-surface-container);
225
+ border: 1px solid rgba(75, 71, 50, 0.2);
226
+ color: var(--color-on-surface);
227
+ font-family: var(--font-family-mono);
228
+ font-size: 0.625rem;
229
+ font-weight: 700;
230
+ letter-spacing: 0.16em;
231
+ padding: 0.75rem 1rem;
232
+ }
233
+
234
+ .charts-detail-shell__button--primary {
235
+ background: var(--color-primary-container);
236
+ border-color: transparent;
237
+ color: var(--color-on-primary);
238
+ }
239
+
240
+ .charts-detail-shell__button[disabled] {
241
+ cursor: not-allowed;
242
+ opacity: 0.5;
243
+ }
244
+
245
+ .charts-height-toggle {
246
+ background: var(--color-surface-container-low);
247
+ border: 1px solid rgba(75, 71, 50, 0.18);
248
+ display: inline-flex;
249
+ gap: 0.25rem;
250
+ padding: 0.25rem;
251
+ }
252
+
253
+ .charts-height-toggle__button {
254
+ background: transparent;
255
+ border: 1px solid transparent;
256
+ color: var(--color-on-surface-variant);
257
+ font-family: var(--font-family-mono);
258
+ font-size: 0.625rem;
259
+ font-weight: 700;
260
+ letter-spacing: 0.16em;
261
+ padding: 0.55rem 0.8rem;
262
+ }
263
+
264
+ .charts-height-toggle__button.is-active {
265
+ background: var(--color-primary-container);
266
+ border-color: transparent;
267
+ color: var(--color-on-primary);
268
+ }
269
+
270
+ .query-chart-card {
271
+ --query-chart-height: 450px;
272
+ background: var(--color-surface-low);
273
+ border: 1px solid rgba(75, 71, 50, 0.12);
274
+ overflow: hidden;
275
+ }
276
+
277
+ .query-chart-card--small {
278
+ --query-chart-height: 300px;
279
+ }
280
+
281
+ .query-chart-card--medium {
282
+ --query-chart-height: 450px;
283
+ }
284
+
285
+ .query-chart-card--large {
286
+ --query-chart-height: 600px;
287
+ }
288
+
289
+ .query-chart-card__header {
290
+ align-items: center;
291
+ background: var(--color-surface-container-highest);
292
+ display: flex;
293
+ flex-wrap: wrap;
294
+ gap: var(--spacing-4);
295
+ justify-content: space-between;
296
+ padding: 1rem 1.25rem;
297
+ }
298
+
299
+ .query-chart-card__body {
300
+ background:
301
+ radial-gradient(circle at top right, rgba(252, 227, 0, 0.08), transparent 28%),
302
+ linear-gradient(180deg, rgba(32, 31, 31, 0.92), rgba(14, 14, 14, 1));
303
+ min-height: var(--query-chart-height);
304
+ padding: 1rem;
305
+ }
306
+
307
+ .query-chart-canvas,
308
+ .query-chart-surface-state {
309
+ height: var(--query-chart-height);
310
+ min-height: var(--query-chart-height);
311
+ }
312
+
313
+ .query-chart-card__action {
314
+ background: var(--color-surface-container);
315
+ border: 1px solid rgba(75, 71, 50, 0.18);
316
+ color: var(--color-on-surface);
317
+ font-family: var(--font-family-mono);
318
+ font-size: 0.625rem;
319
+ font-weight: 700;
320
+ letter-spacing: 0.14em;
321
+ padding: 0.55rem 0.85rem;
322
+ }
323
+
324
+ .query-chart-card__results {
325
+ background: var(--color-surface-container-lowest);
326
+ }
327
+
328
+ @media (max-width: 1023px) {
329
+ .charts-view {
330
+ flex-direction: column;
331
+ }
332
+
333
+ .charts-view__sidebar {
334
+ flex-basis: 18rem;
335
+ width: 100%;
336
+ }
337
+
338
+ .structure-view {
339
+ flex-direction: column;
340
+ }
341
+
342
+ .structure-view__sidebar {
343
+ flex-basis: 18rem;
344
+ width: 100%;
345
+ }
346
+
347
+ .table-designer-view {
348
+ flex-direction: column;
349
+ }
120
350
 
121
- .table-designer-workspace__bottom {
122
- flex-basis: 280px;
123
- }
351
+ .table-designer-workspace__bottom {
352
+ flex-basis: 280px;
353
+ }
124
354
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sqlite-hub",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "SQLite-only local management app backend and SPA shell",
5
5
  "main": "server/server.js",
6
6
  "bin": {
@@ -15,6 +15,7 @@
15
15
  "better-sqlite3": "^11.8.1",
16
16
  "cytoscape": "^3.33.2",
17
17
  "cytoscape-elk": "^2.3.0",
18
+ "echarts": "^5.6.0",
18
19
  "elkjs": "^0.11.1",
19
20
  "express": "^4.21.2"
20
21
  }