sqlite-hub 0.6.0 → 0.8.7

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.
Files changed (44) hide show
  1. package/README.md +107 -0
  2. package/bin/sqlite-hub.js +285 -4
  3. package/changelog.md +21 -0
  4. package/docs/DESIGN_GUIDELINES.md +36 -0
  5. package/frontend/assets/mockups/sql_editor_croped.png +0 -0
  6. package/frontend/index.html +80 -0
  7. package/frontend/js/api.js +34 -0
  8. package/frontend/js/app.js +188 -10
  9. package/frontend/js/components/connectionCard.js +3 -4
  10. package/frontend/js/components/emptyState.js +4 -4
  11. package/frontend/js/components/modal.js +341 -24
  12. package/frontend/js/components/queryChartRenderer.js +125 -0
  13. package/frontend/js/components/queryEditor.js +33 -6
  14. package/frontend/js/components/queryHistoryDetail.js +16 -7
  15. package/frontend/js/components/queryHistoryPanel.js +18 -7
  16. package/frontend/js/components/rowEditorPanel.js +59 -54
  17. package/frontend/js/components/sidebar.js +32 -32
  18. package/frontend/js/components/structureGraph.js +54 -122
  19. package/frontend/js/components/tableDesignerEditor.js +9 -8
  20. package/frontend/js/components/tableDesignerSidebar.js +52 -48
  21. package/frontend/js/components/tableDesignerSqlPreview.js +60 -28
  22. package/frontend/js/lib/queryChartOptions.js +283 -0
  23. package/frontend/js/lib/queryCharts.js +560 -0
  24. package/frontend/js/router.js +8 -0
  25. package/frontend/js/store.js +641 -0
  26. package/frontend/js/views/charts.js +499 -0
  27. package/frontend/js/views/connections.js +5 -17
  28. package/frontend/js/views/data.js +40 -27
  29. package/frontend/js/views/editor.js +19 -13
  30. package/frontend/js/views/overview.js +149 -10
  31. package/frontend/js/views/settings.js +2 -2
  32. package/frontend/js/views/structure.js +105 -59
  33. package/frontend/js/views/tableDesigner.js +7 -2
  34. package/frontend/styles/base.css +62 -0
  35. package/frontend/styles/components.css +68 -118
  36. package/frontend/styles/structure-graph.css +19 -82
  37. package/frontend/styles/tokens.css +2 -0
  38. package/frontend/styles/views.css +293 -66
  39. package/package.json +2 -1
  40. package/server/routes/charts.js +153 -0
  41. package/server/server.js +6 -0
  42. package/server/services/sqlite/overviewService.js +68 -0
  43. package/server/services/storage/appStateStore.js +400 -1
  44. package/server/services/storage/queryHistoryChartUtils.js +145 -0
@@ -0,0 +1,499 @@
1
+ import { renderQueryResultsPane } from '../components/queryResults.js';
2
+ import { analyzeQueryChartResult, getQueryChartTypeLabel, validateQueryChartConfig } from '../lib/queryCharts.js';
3
+ import { escapeHtml, highlightSql } from '../utils/format.js';
4
+ import { renderStatusBadge } from '../components/badges.js';
5
+
6
+ function renderMissingDatabase() {
7
+ return `
8
+ <section class="flex flex-1 items-center justify-center px-8 text-center">
9
+ <div class="max-w-xl">
10
+ <span class="material-symbols-outlined mb-4 text-5xl text-on-surface-variant/20">database_off</span>
11
+ <h1 class="font-headline text-3xl font-black uppercase tracking-tight text-primary-container">
12
+ No Active SQLite Database
13
+ </h1>
14
+ <p class="mt-3 text-sm leading-7 text-on-surface-variant/65">
15
+ Open a local SQLite database first. The Charts area only works against query-history entries from the active database.
16
+ </p>
17
+ </div>
18
+ </section>
19
+ `;
20
+ }
21
+
22
+ function renderChartsList(state) {
23
+ const queries = state.charts.queries ?? [];
24
+ const selectedHistoryId = Number(state.charts.selectedHistoryId);
25
+
26
+ if (state.charts.loading && !queries.length) {
27
+ return `
28
+ <div class="flex h-full items-center justify-center px-6 text-center text-on-surface-variant/45">
29
+ <div>
30
+ <span class="material-symbols-outlined mb-3 text-4xl">progress_activity</span>
31
+ <p class="font-mono text-[10px] uppercase tracking-[0.18em]">Loading Queries</p>
32
+ </div>
33
+ </div>
34
+ `;
35
+ }
36
+
37
+ if (state.charts.error && !queries.length) {
38
+ return `
39
+ <div class="p-5">
40
+ <div class="border border-error/30 bg-error-container/20 px-4 py-4 text-sm text-error">
41
+ ${escapeHtml(state.charts.error.message)}
42
+ </div>
43
+ </div>
44
+ `;
45
+ }
46
+
47
+ if (!queries.length) {
48
+ return `
49
+ <div class="flex h-full items-center justify-center px-6 text-center">
50
+ <div>
51
+ <span class="material-symbols-outlined mb-3 text-4xl text-on-surface-variant/25">query_stats</span>
52
+ <p class="font-headline text-lg font-black uppercase tracking-tight text-on-surface">
53
+ No Query History
54
+ </p>
55
+ <p class="mt-2 max-w-xs text-sm leading-6 text-on-surface-variant/60">
56
+ Run saved queries in the SQL Editor first. They will appear here automatically.
57
+ </p>
58
+ </div>
59
+ </div>
60
+ `;
61
+ }
62
+
63
+ return `
64
+ <div class="custom-scrollbar flex-1 overflow-auto px-4 py-4">
65
+ <div class="space-y-2">
66
+ ${queries
67
+ .map(
68
+ item => `
69
+ <button
70
+ class="w-full border px-4 py-3 text-left transition-colors ${
71
+ selectedHistoryId === item.id
72
+ ? 'border-primary-container/30 bg-surface-container-high'
73
+ : 'border-outline-variant/10 bg-surface-container-lowest hover:bg-surface-container-high'
74
+ }"
75
+ data-action="navigate"
76
+ data-to="/charts/${encodeURIComponent(item.id)}"
77
+ type="button"
78
+ >
79
+ <div class="flex items-start justify-between gap-3">
80
+ <div class="min-w-0 flex-1 truncate font-mono text-xs ${
81
+ selectedHistoryId === item.id ? 'text-primary-container' : 'text-on-surface'
82
+ }">
83
+ ${escapeHtml(item.displayTitle)}
84
+ </div>
85
+ <div class="flex shrink-0 flex-wrap justify-end gap-1">
86
+ ${
87
+ item.chartTypes?.length
88
+ ? item.chartTypes
89
+ .map(chartType => renderStatusBadge(getQueryChartTypeLabel(chartType), 'primary'))
90
+ .join('')
91
+ : renderStatusBadge('None', 'muted')
92
+ }
93
+ </div>
94
+ </div>
95
+ <div class="mt-1 truncate text-[10px] uppercase tracking-[0.16em] text-on-surface-variant/45">
96
+ ${escapeHtml(item.previewSql)}
97
+ </div>
98
+ </button>
99
+ `,
100
+ )
101
+ .join('')}
102
+ </div>
103
+ </div>
104
+ `;
105
+ }
106
+
107
+ function renderEmptyChartDetail() {
108
+ return `
109
+ <div class="flex flex-1 items-center justify-center px-8 py-10 text-center">
110
+ <div class="max-w-lg">
111
+ <span class="material-symbols-outlined mb-4 text-5xl text-on-surface-variant/20">bar_chart</span>
112
+ <h2 class="font-headline text-2xl font-black uppercase tracking-tight text-primary-container">
113
+ Select A Query
114
+ </h2>
115
+ <p class="mt-3 text-sm leading-7 text-on-surface-variant/65">
116
+ Choose a query-history entry on the left to load its charts, render the live result set, and manage chart definitions.
117
+ </p>
118
+ </div>
119
+ </div>
120
+ `;
121
+ }
122
+
123
+ function renderQueryResultState(state, result) {
124
+ if (state.charts.resultLoading && !result) {
125
+ return `
126
+ <div class="border border-outline-variant/10 bg-surface-container-lowest px-4 py-4 text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">
127
+ Loading live query result...
128
+ </div>
129
+ `;
130
+ }
131
+
132
+ if (state.charts.resultError) {
133
+ return `
134
+ <div class="border border-error/30 bg-error-container/15 px-4 py-4 text-sm text-error">
135
+ <div class="font-mono text-[10px] uppercase tracking-[0.18em]">
136
+ ${escapeHtml(state.charts.resultError.code ?? 'RESULT_ERROR')}
137
+ </div>
138
+ <div class="mt-2">${escapeHtml(state.charts.resultError.message)}</div>
139
+ </div>
140
+ `;
141
+ }
142
+
143
+ if (!result) {
144
+ return `
145
+ <div class="border border-outline-variant/10 bg-surface-container-lowest px-4 py-4 text-sm text-on-surface-variant/60">
146
+ No live result set is available for this query.
147
+ </div>
148
+ `;
149
+ }
150
+
151
+ return '';
152
+ }
153
+
154
+ function renderQuerySqlSection(state, rawSql) {
155
+ const isExpanded = Boolean(state.charts.sqlExpanded);
156
+
157
+ return `
158
+ <section class="mt-5 border border-outline-variant/10 bg-surface-container-lowest">
159
+ <button
160
+ class="standard-button flex w-full justify-between border-b border-outline-variant/10 px-4 text-left"
161
+ data-action="toggle-charts-sql-panel"
162
+ type="button"
163
+ >
164
+ <span class="text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">
165
+ Query SQL
166
+ </span>
167
+ <span class="material-symbols-outlined text-on-surface-variant/55">
168
+ ${isExpanded ? 'expand_less' : 'expand_more'}
169
+ </span>
170
+ </button>
171
+ ${
172
+ isExpanded
173
+ ? `
174
+ <pre class="custom-scrollbar overflow-auto p-4 font-mono text-sm leading-6 text-on-surface"><code>${highlightSql(
175
+ rawSql,
176
+ )}</code></pre>
177
+ `
178
+ : ''
179
+ }
180
+ </section>
181
+ `;
182
+ }
183
+
184
+ function renderQueryResultsSection(state) {
185
+ const isVisible = Boolean(state.charts.resultsVisible);
186
+ const statusMarkup = renderQueryResultState(state, state.charts.result);
187
+
188
+ return `
189
+ <section class="mt-6 border border-outline-variant/10 bg-surface-container-lowest">
190
+ <div class="flex items-center justify-between gap-3 border-b border-outline-variant/10 px-4 py-3">
191
+ <span class="text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">
192
+ Results
193
+ </span>
194
+ <button
195
+ class="standard-button"
196
+ data-action="toggle-charts-results-panel"
197
+ type="button"
198
+ >
199
+ <span class="material-symbols-outlined text-sm">
200
+ ${isVisible ? 'table_rows_narrow' : 'table_rows'}
201
+ </span>
202
+ ${isVisible ? 'Hide Results' : 'Show Results'}
203
+ </button>
204
+ </div>
205
+ ${statusMarkup}
206
+ ${
207
+ isVisible && state.charts.result && !state.charts.resultLoading && !state.charts.resultError
208
+ ? `
209
+ <div class="h-[18rem] overflow-hidden">
210
+ ${renderQueryResultsPane(state.charts.result, {
211
+ selectedRowIndex: null,
212
+ editable: false,
213
+ sortColumn: null,
214
+ sortDirection: null,
215
+ })}
216
+ </div>
217
+ `
218
+ : ''
219
+ }
220
+ </section>
221
+ `;
222
+ }
223
+
224
+ function resolveChartCardSizeClass(state) {
225
+ return `query-chart-card--${String(state.charts.chartHeightPreset ?? 'medium')
226
+ .trim()
227
+ .toLowerCase()}`;
228
+ }
229
+
230
+ function renderChartHeightPresetToggle(state) {
231
+ const activePreset = String(state.charts.chartHeightPreset ?? 'medium')
232
+ .trim()
233
+ .toLowerCase();
234
+ const presets = [
235
+ { value: 'small', label: 'Small', height: '300px' },
236
+ { value: 'medium', label: 'Medium', height: '450px' },
237
+ { value: 'large', label: 'Large', height: '600px' },
238
+ ];
239
+
240
+ return `
241
+ <div class="flex flex-col gap-2">
242
+ <div class="text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">
243
+ Chart Height
244
+ </div>
245
+ <div class="charts-height-toggle" role="group" aria-label="Chart height preset">
246
+ ${presets
247
+ .map(
248
+ preset => `
249
+ <button
250
+ class="standard-button charts-height-toggle__button ${activePreset === preset.value ? 'is-active' : ''}"
251
+ data-action="set-charts-height-preset"
252
+ data-preset="${preset.value}"
253
+ type="button"
254
+ title="${preset.height}"
255
+ >
256
+ ${preset.label}
257
+ </button>
258
+ `,
259
+ )
260
+ .join('')}
261
+ </div>
262
+ </div>
263
+ `;
264
+ }
265
+
266
+ function renderChartSurface(chart, state, analysis) {
267
+ if (state.charts.resultLoading) {
268
+ return `
269
+ <div class="query-chart-surface-state flex h-full items-center justify-center text-[10px] font-mono uppercase tracking-[0.18em] text-on-surface-variant/55">
270
+ Loading chart result...
271
+ </div>
272
+ `;
273
+ }
274
+
275
+ if (state.charts.resultError) {
276
+ return `
277
+ <div class="query-chart-surface-state flex items-center justify-center border border-error/20 bg-error-container/10 px-6 text-center text-sm text-error">
278
+ ${escapeHtml(state.charts.resultError.message)}
279
+ </div>
280
+ `;
281
+ }
282
+
283
+ const validation = validateQueryChartConfig(chart.chartType, chart.config, analysis);
284
+
285
+ if (!validation.valid) {
286
+ return `
287
+ <div class="query-chart-surface-state flex items-center justify-center border border-error/20 bg-error-container/10 px-6 text-center text-sm text-error">
288
+ ${escapeHtml(validation.errors.join(' '))}
289
+ </div>
290
+ `;
291
+ }
292
+
293
+ return `
294
+ <div
295
+ class="query-chart-canvas w-full"
296
+ data-query-chart-id="${escapeHtml(chart.id)}"
297
+ data-chart-export-name="${escapeHtml(chart.name.replace(/[^\w.-]+/g, '_'))}"
298
+ ></div>
299
+ `;
300
+ }
301
+
302
+ function renderChartCard(chart, state, analysis) {
303
+ const sizeClass = resolveChartCardSizeClass(state);
304
+
305
+ return `
306
+ <article class="query-chart-card ${sizeClass}">
307
+ <header class="query-chart-card__header">
308
+ <div class="min-w-0">
309
+ <div class="flex flex-wrap items-center gap-2">
310
+ <h3 class="truncate font-headline text-xl font-black uppercase tracking-tight text-on-surface">
311
+ ${escapeHtml(chart.name)}
312
+ </h3>
313
+ ${renderStatusBadge(getQueryChartTypeLabel(chart.chartType), 'primary')}
314
+ </div>
315
+ </div>
316
+ <div class="flex flex-wrap items-center gap-2">
317
+ <button
318
+ class="standard-button"
319
+ data-action="open-edit-query-chart-modal"
320
+ data-chart-id="${escapeHtml(chart.id)}"
321
+ type="button"
322
+ >
323
+ Edit
324
+ </button>
325
+ <button
326
+ class="delete-button"
327
+ data-action="open-delete-query-chart-modal"
328
+ data-chart-id="${escapeHtml(chart.id)}"
329
+ type="button"
330
+ >
331
+ Delete
332
+ </button>
333
+ <button
334
+ class="standard-button"
335
+ data-action="export-query-chart-png"
336
+ data-chart-id="${escapeHtml(chart.id)}"
337
+ type="button"
338
+ >
339
+ Export PNG
340
+ </button>
341
+ </div>
342
+ </header>
343
+ <div class="query-chart-card__body">
344
+ ${renderChartSurface(chart, state, analysis)}
345
+ </div>
346
+ </article>
347
+ `;
348
+ }
349
+
350
+ function renderChartsDetail(state) {
351
+ const detail = state.charts.detail;
352
+ const selectedHistoryId = state.charts.selectedHistoryId;
353
+ const historyVisible = state.editor.historyPanelVisible !== false;
354
+
355
+ if (!selectedHistoryId) {
356
+ return renderEmptyChartDetail();
357
+ }
358
+
359
+ if (state.charts.detailLoading && !detail) {
360
+ return `
361
+ <div class="flex flex-1 items-center justify-center px-8 text-center text-on-surface-variant/45">
362
+ <div>
363
+ <span class="material-symbols-outlined mb-3 text-4xl">progress_activity</span>
364
+ <p class="font-mono text-[10px] uppercase tracking-[0.18em]">Loading Query Detail</p>
365
+ </div>
366
+ </div>
367
+ `;
368
+ }
369
+
370
+ if (state.charts.detailError && !detail) {
371
+ return `
372
+ <div class="p-8">
373
+ <div class="border border-error/30 bg-error-container/20 px-4 py-4 text-sm text-error">
374
+ ${escapeHtml(state.charts.detailError.message)}
375
+ </div>
376
+ </div>
377
+ `;
378
+ }
379
+
380
+ if (!detail?.item) {
381
+ return renderEmptyChartDetail();
382
+ }
383
+
384
+ const analysis = state.charts.result ? analyzeQueryChartResult(state.charts.result) : null;
385
+ const charts = detail.charts ?? [];
386
+
387
+ return `
388
+ <div class="custom-scrollbar flex-1 overflow-auto">
389
+ <div class="charts-detail-shell">
390
+ <header class="charts-detail-shell__header">
391
+ <div class="text-[10px] font-mono uppercase tracking-[0.18em] text-primary-container/70">
392
+ Charts
393
+ </div>
394
+ <div class="charts-detail-shell__title">
395
+ <h1 class="mt-2 truncate font-headline text-4xl font-black uppercase tracking-tight text-primary-container">
396
+ ${escapeHtml(detail.item.displayTitle)}
397
+ </h1>
398
+ </div>
399
+ <div class="charts-detail-shell__controls">
400
+ <div class="charts-detail-shell__controls-group">
401
+ ${renderChartHeightPresetToggle(state)}
402
+ <button
403
+ class="standard-button"
404
+ data-action="toggle-query-history-panel"
405
+ data-next-value="${historyVisible ? 'false' : 'true'}"
406
+ type="button"
407
+ >
408
+ <span class="material-symbols-outlined text-sm">${
409
+ historyVisible ? 'visibility_off' : 'history'
410
+ }</span>
411
+ ${historyVisible ? 'Hide Query History' : 'Show Query History'}
412
+ </button>
413
+ </div>
414
+ <div class="charts-detail-shell__controls-group charts-detail-shell__controls-group--end">
415
+ <button
416
+ class="standard-button"
417
+ data-action="open-query-history"
418
+ data-history-id="${escapeHtml(detail.item.id)}"
419
+ type="button"
420
+ >
421
+ <span class="material-symbols-outlined text-sm">terminal</span>
422
+ Open In Editor
423
+ </button>
424
+ <button
425
+ class="signature-button"
426
+ data-action="open-create-query-chart-modal"
427
+ type="button"
428
+ ${state.charts.resultError || !state.charts.result ? 'disabled' : ''}
429
+ >
430
+ New Chart
431
+ </button>
432
+ </div>
433
+ </div>
434
+ </header>
435
+
436
+ ${renderQuerySqlSection(state, detail.item.rawSql)}
437
+
438
+ <section class="mt-6 space-y-5">
439
+ ${
440
+ charts.length
441
+ ? charts.map(chart => renderChartCard(chart, state, analysis)).join('')
442
+ : `
443
+ <div class="flex min-h-[240px] items-center justify-center border border-dashed border-outline-variant/20 bg-surface-container-low px-8 text-center">
444
+ <div>
445
+ <span class="material-symbols-outlined mb-3 text-4xl text-on-surface-variant/25">add_chart</span>
446
+ <p class="font-headline text-lg font-black uppercase tracking-tight text-on-surface">
447
+ No Charts Yet
448
+ </p>
449
+ <p class="mt-2 max-w-md text-sm leading-6 text-on-surface-variant/60">
450
+ Create the first chart for this query. The query stays read-only here; only chart definitions are editable.
451
+ </p>
452
+ </div>
453
+ </div>
454
+ `
455
+ }
456
+ </section>
457
+
458
+ ${renderQueryResultsSection(state)}
459
+ </div>
460
+ </div>
461
+ `;
462
+ }
463
+
464
+ export function renderChartsView(state) {
465
+ const historyVisible = state.editor.historyPanelVisible !== false || !state.charts.selectedHistoryId;
466
+
467
+ if (!state.connections.active && state.charts.error?.code === 'ACTIVE_DATABASE_REQUIRED') {
468
+ return {
469
+ main: `<section class="view-surface flex min-h-full">${renderMissingDatabase()}</section>`,
470
+ panel: '',
471
+ };
472
+ }
473
+
474
+ return {
475
+ main: `
476
+ <section class="charts-view">
477
+ ${
478
+ historyVisible
479
+ ? `
480
+ <aside class="charts-view__sidebar">
481
+ <div class="charts-view__sidebar-header">
482
+ <div class="text-[10px] font-bold uppercase tracking-[0.2em] text-primary-container">
483
+ Query History
484
+ </div>
485
+ <h2 class="mt-2 text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">
486
+ Charts
487
+ </h2>
488
+ </div>
489
+ ${renderChartsList(state)}
490
+ </aside>
491
+ `
492
+ : ''
493
+ }
494
+ <div class="charts-view__detail">${renderChartsDetail(state)}</div>
495
+ </section>
496
+ `,
497
+ panel: '',
498
+ };
499
+ }
@@ -8,32 +8,25 @@ function renderConnectionsActionButton({
8
8
  action = "open-modal",
9
9
  modal,
10
10
  tone = "secondary",
11
- className = "",
12
11
  disabled = false,
13
12
  }) {
14
- const clipPath = "polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 0 100%)";
15
13
  const toneClassName =
16
14
  tone === "primary"
17
- ? "clipped-corner border border-primary-container bg-primary-container text-on-primary shadow-[0_0_18px_-10px_rgba(252,227,0,0.65)] hover:bg-primary-fixed"
18
- : "clipped-corner border border-outline-variant/20 bg-surface-container-highest text-primary-container shadow-[inset_2px_0_0_0_rgba(252,227,0,0.95)] hover:bg-surface-bright";
19
- const iconClassName =
20
- tone === "primary"
21
- ? "text-base text-on-primary"
22
- : "text-base text-primary-container/90";
23
- const clipStyle = `style="--clip-path: ${clipPath};"`;
15
+ ? "signature-button"
16
+ : "standard-button";
17
+ const iconClassName = "";
24
18
  const modalAttribute = modal ? `data-modal="${modal}"` : "";
25
19
 
26
20
  return `
27
21
  <button
28
- class="flex h-11 items-center justify-between gap-6 px-5 font-headline text-xs font-bold uppercase tracking-[0.18em] transition-colors disabled:cursor-default disabled:opacity-40 ${toneClassName} ${className}"
22
+ class="${toneClassName}"
29
23
  data-action="${escapeHtml(action)}"
30
24
  ${modalAttribute}
31
- ${clipStyle}
32
25
  type="button"
33
26
  ${disabled ? "disabled" : ""}
34
27
  >
35
28
  <span>${label}</span>
36
- <span class="material-symbols-outlined ${iconClassName}">${icon}</span>
29
+ <span class="material-symbols-outlined${iconClassName ? ` ${iconClassName}` : ""}">${icon}</span>
37
30
  </button>
38
31
  `;
39
32
  }
@@ -77,13 +70,11 @@ function renderConnectionsBody(state) {
77
70
  icon: "folder_open",
78
71
  modal: "open-connection",
79
72
  tone: "primary",
80
- className: "min-w-[17rem] px-8 py-6 text-sm",
81
73
  })}
82
74
  ${renderConnectionsActionButton({
83
75
  label: "Create Database",
84
76
  icon: "note_add",
85
77
  modal: "create-connection",
86
- className: "min-w-[17rem] px-8 py-6 text-sm",
87
78
  })}
88
79
  </div>
89
80
  </div>
@@ -108,13 +99,11 @@ export function renderConnectionsView(state) {
108
99
  icon: "folder_open",
109
100
  modal: "open-connection",
110
101
  tone: "primary",
111
- className: "min-w-[13rem]",
112
102
  })}
113
103
  ${renderConnectionsActionButton({
114
104
  label: "Create Database",
115
105
  icon: "note_add",
116
106
  modal: "create-connection",
117
- className: "min-w-[13rem]",
118
107
  })}
119
108
  ${
120
109
  state.connections.active
@@ -122,7 +111,6 @@ export function renderConnectionsView(state) {
122
111
  label: state.connections.backupLoading ? "Creating Backup..." : "Create Backup",
123
112
  icon: "inventory_2",
124
113
  action: "create-backup",
125
- className: "min-w-[13rem]",
126
114
  disabled: state.connections.backupLoading,
127
115
  })
128
116
  : ""