sqlite-hub 0.7.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 (31) hide show
  1. package/changelog.md +14 -0
  2. package/docs/DESIGN_GUIDELINES.md +36 -0
  3. package/frontend/index.html +79 -0
  4. package/frontend/js/app.js +89 -8
  5. package/frontend/js/components/connectionCard.js +3 -4
  6. package/frontend/js/components/emptyState.js +4 -4
  7. package/frontend/js/components/modal.js +32 -28
  8. package/frontend/js/components/queryEditor.js +2 -4
  9. package/frontend/js/components/queryHistoryDetail.js +16 -7
  10. package/frontend/js/components/queryHistoryPanel.js +2 -2
  11. package/frontend/js/components/rowEditorPanel.js +59 -54
  12. package/frontend/js/components/sidebar.js +32 -33
  13. package/frontend/js/components/structureGraph.js +54 -122
  14. package/frontend/js/components/tableDesignerEditor.js +9 -8
  15. package/frontend/js/components/tableDesignerSidebar.js +11 -10
  16. package/frontend/js/components/tableDesignerSqlPreview.js +60 -28
  17. package/frontend/js/store.js +23 -0
  18. package/frontend/js/views/charts.js +66 -38
  19. package/frontend/js/views/connections.js +5 -17
  20. package/frontend/js/views/data.js +12 -13
  21. package/frontend/js/views/overview.js +149 -10
  22. package/frontend/js/views/settings.js +2 -2
  23. package/frontend/js/views/structure.js +74 -70
  24. package/frontend/js/views/tableDesigner.js +7 -2
  25. package/frontend/styles/base.css +62 -0
  26. package/frontend/styles/components.css +63 -105
  27. package/frontend/styles/structure-graph.css +19 -82
  28. package/frontend/styles/tokens.css +2 -0
  29. package/frontend/styles/views.css +33 -36
  30. package/package.json +1 -1
  31. package/server/services/sqlite/overviewService.js +68 -0
@@ -86,7 +86,7 @@ function renderColumnRow(column, draft, catalogTables) {
86
86
  )
87
87
  .join("")}
88
88
  </select>
89
- <label class="table-designer-check">
89
+ <label class="standard-checkbox table-designer-check table-designer-checkbox-override">
90
90
  <input
91
91
  data-bind="table-designer-column-flag"
92
92
  data-column-id="${escapeHtml(column.id)}"
@@ -96,7 +96,7 @@ function renderColumnRow(column, draft, catalogTables) {
96
96
  />
97
97
  <span>Not null</span>
98
98
  </label>
99
- <label class="table-designer-check">
99
+ <label class="standard-checkbox table-designer-check table-designer-checkbox-override">
100
100
  <input
101
101
  data-bind="table-designer-column-flag"
102
102
  data-column-id="${escapeHtml(column.id)}"
@@ -106,7 +106,7 @@ function renderColumnRow(column, draft, catalogTables) {
106
106
  />
107
107
  <span>Unique</span>
108
108
  </label>
109
- <label class="table-designer-check">
109
+ <label class="standard-checkbox table-designer-check table-designer-checkbox-override">
110
110
  <input
111
111
  data-bind="table-designer-column-flag"
112
112
  data-column-id="${escapeHtml(column.id)}"
@@ -164,12 +164,13 @@ function renderColumnRow(column, draft, catalogTables) {
164
164
  .join("")}
165
165
  </select>
166
166
  <button
167
- class="table-designer-delete"
167
+ class="delete-button"
168
168
  data-action="remove-table-designer-column"
169
169
  data-column-id="${escapeHtml(column.id)}"
170
170
  type="button"
171
171
  >
172
172
  <span class="material-symbols-outlined text-base">delete</span>
173
+ <span>Remove</span>
173
174
  </button>
174
175
  </div>
175
176
  `;
@@ -204,7 +205,7 @@ function renderFillToggle(draft) {
204
205
  const hasImportedRows = (draft.importedCsvRows?.length ?? 0) > 0;
205
206
 
206
207
  return `
207
- <label class="table-designer-fill-toggle ${hasImportedRows ? "" : "is-disabled"}">
208
+ <label class="standard-checkbox table-designer-fill-toggle table-designer-checkbox-override ${hasImportedRows ? "" : "is-disabled"}">
208
209
  <input
209
210
  data-bind="table-designer-field"
210
211
  data-field="fillImportedRows"
@@ -301,14 +302,14 @@ export function renderTableDesignerEditor(state) {
301
302
  <div class="table-designer-main__actions">
302
303
  ${renderFillToggle(draft)}
303
304
  <button
304
- class="table-designer-main__secondary"
305
+ class="standard-button"
305
306
  data-action="refresh-view"
306
307
  type="button"
307
308
  >
308
309
  Reload Schema
309
310
  </button>
310
311
  <button
311
- class="table-designer-main__primary ${draft.canSave ? "" : "is-disabled"}"
312
+ class="standard-button"
312
313
  data-action="save-table-designer"
313
314
  ${draft.canSave ? "" : "disabled"}
314
315
  type="button"
@@ -342,7 +343,7 @@ export function renderTableDesignerEditor(state) {
342
343
  <div class="table-designer-main__section-title">Columns</div>
343
344
  </div>
344
345
  <button
345
- class="table-designer-main__secondary"
346
+ class="standard-button"
346
347
  data-action="add-table-designer-column"
347
348
  type="button"
348
349
  >
@@ -28,15 +28,7 @@ export function renderTableDesignerSidebar(state) {
28
28
  </div>
29
29
  <div class="table-designer-sidebar__header-actions">
30
30
  <button
31
- class="table-designer-sidebar__new-button"
32
- data-action="navigate"
33
- data-to="/table-designer/new"
34
- type="button"
35
- >
36
- + New Table
37
- </button>
38
- <button
39
- class="table-designer-sidebar__import-button"
31
+ class="standard-button"
40
32
  data-action="import-table-designer-csv"
41
33
  type="button"
42
34
  >
@@ -47,7 +39,16 @@ export function renderTableDesignerSidebar(state) {
47
39
  class="table-designer-sidebar__file-input"
48
40
  data-bind="table-designer-import-file"
49
41
  type="file"
50
- />
42
+ />
43
+ <button
44
+ class="signature-button"
45
+ data-action="navigate"
46
+ data-to="/table-designer/new"
47
+ type="button"
48
+ >
49
+ + New Table
50
+ </button>
51
+
51
52
  </div>
52
53
  </div>
53
54
 
@@ -1,40 +1,72 @@
1
1
  import { escapeHtml } from "../utils/format.js";
2
2
 
3
- export function renderTableDesignerSqlPreview(draft) {
3
+ function renderPreviewHeader(draft, isVisible) {
4
+ return `
5
+ <div class="table-designer-preview__header">
6
+ <div>
7
+ <div class="table-designer-preview__eyebrow">SQL Preview</div>
8
+ <div class="table-designer-preview__title">${
9
+ draft ? "Live SQLite Output" : "No Draft Selected"
10
+ }</div>
11
+ </div>
12
+ <div class="table-designer-preview__actions">
13
+ ${
14
+ draft && isVisible
15
+ ? `
16
+ <button
17
+ class="standard-button"
18
+ data-action="copy-table-designer-sql"
19
+ type="button"
20
+ >
21
+ Copy SQL
22
+ </button>
23
+ `
24
+ : ""
25
+ }
26
+ <button
27
+ class="standard-button"
28
+ data-action="toggle-table-designer-sql-preview"
29
+ data-next-value="${isVisible ? "false" : "true"}"
30
+ type="button"
31
+ >
32
+ <span class="material-symbols-outlined">${isVisible ? "visibility_off" : "expand_less"}</span>
33
+ ${isVisible ? "Hide" : "Show"}
34
+ </button>
35
+ </div>
36
+ </div>
37
+ `;
38
+ }
39
+
40
+ export function renderTableDesignerSqlPreview(draft, isVisible = true) {
4
41
  if (!draft) {
5
42
  return `
6
- <section class="table-designer-preview shell-section">
7
- <div class="table-designer-preview__header">
8
- <div>
9
- <div class="table-designer-preview__eyebrow">SQL Preview</div>
10
- <div class="table-designer-preview__title">No Draft Selected</div>
11
- </div>
12
- </div>
13
- <div class="table-designer-preview__empty">
14
- Select a table or create a new one to inspect the generated SQLite statements.
15
- </div>
43
+ <section class="table-designer-preview shell-section${isVisible ? "" : " is-collapsed"}">
44
+ ${renderPreviewHeader(draft, isVisible)}
45
+ ${
46
+ isVisible
47
+ ? `
48
+ <div class="table-designer-preview__empty">
49
+ Select a table or create a new one to inspect the generated SQLite statements.
50
+ </div>
51
+ `
52
+ : ""
53
+ }
16
54
  </section>
17
55
  `;
18
56
  }
19
57
 
20
58
  return `
21
- <section class="table-designer-preview shell-section">
22
- <div class="table-designer-preview__header">
23
- <div>
24
- <div class="table-designer-preview__eyebrow">SQL Preview</div>
25
- <div class="table-designer-preview__title">Live SQLite Output</div>
26
- </div>
27
- <button
28
- class="table-designer-preview__copy"
29
- data-action="copy-table-designer-sql"
30
- type="button"
31
- >
32
- Copy SQL
33
- </button>
34
- </div>
35
- <pre class="table-designer-preview__body custom-scrollbar">${escapeHtml(
36
- draft.sqlPreview || "-- SQL preview unavailable."
37
- )}</pre>
59
+ <section class="table-designer-preview shell-section${isVisible ? "" : " is-collapsed"}">
60
+ ${renderPreviewHeader(draft, isVisible)}
61
+ ${
62
+ isVisible
63
+ ? `
64
+ <pre class="table-designer-preview__body custom-scrollbar">${escapeHtml(
65
+ draft.sqlPreview || "-- SQL preview unavailable."
66
+ )}</pre>
67
+ `
68
+ : ""
69
+ }
38
70
  </section>
39
71
  `;
40
72
  }
@@ -135,6 +135,7 @@ const state = {
135
135
  tables: [],
136
136
  selectedTableName: null,
137
137
  draft: null,
138
+ sqlPreviewVisible: true,
138
139
  pendingImportedDraft: null,
139
140
  loading: false,
140
141
  detailLoading: false,
@@ -148,6 +149,7 @@ const state = {
148
149
  data: null,
149
150
  selectedName: null,
150
151
  detail: null,
152
+ tablesVisible: true,
151
153
  loading: false,
152
154
  detailLoading: false,
153
155
  error: null,
@@ -571,6 +573,7 @@ function setMissingDatabaseState() {
571
573
  state.structure.detailLoading = false;
572
574
  state.structure.data = null;
573
575
  state.structure.detail = null;
576
+ state.structure.tablesVisible = true;
574
577
  state.structure.error = error;
575
578
 
576
579
  state.tableDesigner.loading = false;
@@ -578,6 +581,7 @@ function setMissingDatabaseState() {
578
581
  state.tableDesigner.tables = [];
579
582
  state.tableDesigner.selectedTableName = null;
580
583
  state.tableDesigner.draft = null;
584
+ state.tableDesigner.sqlPreviewVisible = true;
581
585
  state.tableDesigner.pendingImportedDraft = null;
582
586
  state.tableDesigner.saving = false;
583
587
  state.tableDesigner.searchQuery = "";
@@ -1248,6 +1252,7 @@ function invalidateDatabaseCaches() {
1248
1252
  state.tableDesigner.tables = [];
1249
1253
  state.tableDesigner.selectedTableName = null;
1250
1254
  state.tableDesigner.draft = null;
1255
+ state.tableDesigner.sqlPreviewVisible = true;
1251
1256
  state.tableDesigner.pendingImportedDraft = null;
1252
1257
  state.tableDesigner.saving = false;
1253
1258
  state.tableDesigner.searchQuery = "";
@@ -1257,6 +1262,7 @@ function invalidateDatabaseCaches() {
1257
1262
  resetChartsState();
1258
1263
  state.structure.data = null;
1259
1264
  state.structure.detail = null;
1265
+ state.structure.tablesVisible = true;
1260
1266
  }
1261
1267
 
1262
1268
  async function loadRouteData(route) {
@@ -2125,11 +2131,28 @@ export async function selectStructureEntry(name) {
2125
2131
  await loadStructureDetail(++routeLoadVersion);
2126
2132
  }
2127
2133
 
2134
+ export function toggleStructureTablesPanel() {
2135
+ state.structure.tablesVisible = state.structure.tablesVisible === false;
2136
+ emitChange();
2137
+ }
2138
+
2128
2139
  export function setTableDesignerSearchQuery(query) {
2129
2140
  state.tableDesigner.searchQuery = String(query ?? "");
2130
2141
  emitChange();
2131
2142
  }
2132
2143
 
2144
+ export function setTableDesignerSqlPreviewVisibility(visible) {
2145
+ const nextValue =
2146
+ typeof visible === "boolean" ? visible : !Boolean(state.tableDesigner.sqlPreviewVisible);
2147
+
2148
+ if (state.tableDesigner.sqlPreviewVisible === nextValue) {
2149
+ return;
2150
+ }
2151
+
2152
+ state.tableDesigner.sqlPreviewVisible = nextValue;
2153
+ emitChange();
2154
+ }
2155
+
2133
2156
  export function updateCurrentTableDesignerField(field, value) {
2134
2157
  if (!state.tableDesigner.draft) {
2135
2158
  return;
@@ -157,7 +157,7 @@ function renderQuerySqlSection(state, rawSql) {
157
157
  return `
158
158
  <section class="mt-5 border border-outline-variant/10 bg-surface-container-lowest">
159
159
  <button
160
- class="flex w-full items-center justify-between gap-3 border-b border-outline-variant/10 px-4 py-3 text-left"
160
+ class="standard-button flex w-full justify-between border-b border-outline-variant/10 px-4 text-left"
161
161
  data-action="toggle-charts-sql-panel"
162
162
  type="button"
163
163
  >
@@ -192,10 +192,13 @@ function renderQueryResultsSection(state) {
192
192
  Results
193
193
  </span>
194
194
  <button
195
- class="toolbar-button query-chart-card__action"
195
+ class="standard-button"
196
196
  data-action="toggle-charts-results-panel"
197
197
  type="button"
198
198
  >
199
+ <span class="material-symbols-outlined text-sm">
200
+ ${isVisible ? 'table_rows_narrow' : 'table_rows'}
201
+ </span>
199
202
  ${isVisible ? 'Hide Results' : 'Show Results'}
200
203
  </button>
201
204
  </div>
@@ -244,7 +247,7 @@ function renderChartHeightPresetToggle(state) {
244
247
  .map(
245
248
  preset => `
246
249
  <button
247
- class="toolbar-button charts-height-toggle__button ${activePreset === preset.value ? 'is-active' : ''}"
250
+ class="standard-button charts-height-toggle__button ${activePreset === preset.value ? 'is-active' : ''}"
248
251
  data-action="set-charts-height-preset"
249
252
  data-preset="${preset.value}"
250
253
  type="button"
@@ -312,7 +315,7 @@ function renderChartCard(chart, state, analysis) {
312
315
  </div>
313
316
  <div class="flex flex-wrap items-center gap-2">
314
317
  <button
315
- class="toolbar-button query-chart-card__action"
318
+ class="standard-button"
316
319
  data-action="open-edit-query-chart-modal"
317
320
  data-chart-id="${escapeHtml(chart.id)}"
318
321
  type="button"
@@ -320,7 +323,7 @@ function renderChartCard(chart, state, analysis) {
320
323
  Edit
321
324
  </button>
322
325
  <button
323
- class="toolbar-button query-chart-card__action"
326
+ class="delete-button"
324
327
  data-action="open-delete-query-chart-modal"
325
328
  data-chart-id="${escapeHtml(chart.id)}"
326
329
  type="button"
@@ -328,7 +331,7 @@ function renderChartCard(chart, state, analysis) {
328
331
  Delete
329
332
  </button>
330
333
  <button
331
- class="toolbar-button query-chart-card__action"
334
+ class="standard-button"
332
335
  data-action="export-query-chart-png"
333
336
  data-chart-id="${escapeHtml(chart.id)}"
334
337
  type="button"
@@ -347,6 +350,7 @@ function renderChartCard(chart, state, analysis) {
347
350
  function renderChartsDetail(state) {
348
351
  const detail = state.charts.detail;
349
352
  const selectedHistoryId = state.charts.selectedHistoryId;
353
+ const historyVisible = state.editor.historyPanelVisible !== false;
350
354
 
351
355
  if (!selectedHistoryId) {
352
356
  return renderEmptyChartDetail();
@@ -384,32 +388,48 @@ function renderChartsDetail(state) {
384
388
  <div class="custom-scrollbar flex-1 overflow-auto">
385
389
  <div class="charts-detail-shell">
386
390
  <header class="charts-detail-shell__header">
387
- <div class="min-w-0">
388
- <div class="text-[10px] font-mono uppercase tracking-[0.18em] text-primary-container/70">
389
- Charts
390
- </div>
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">
391
395
  <h1 class="mt-2 truncate font-headline text-4xl font-black uppercase tracking-tight text-primary-container">
392
396
  ${escapeHtml(detail.item.displayTitle)}
393
397
  </h1>
394
398
  </div>
395
399
  <div class="charts-detail-shell__controls">
396
- ${renderChartHeightPresetToggle(state)}
397
- <button
398
- class="toolbar-button charts-detail-shell__button"
399
- data-action="open-query-history"
400
- data-history-id="${escapeHtml(detail.item.id)}"
401
- type="button"
402
- >
403
- Open In Editor
404
- </button>
405
- <button
406
- class="toolbar-button charts-detail-shell__button charts-detail-shell__button--primary"
407
- data-action="open-create-query-chart-modal"
408
- type="button"
409
- ${state.charts.resultError || !state.charts.result ? 'disabled' : ''}
410
- >
411
- New Chart
412
- </button>
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>
413
433
  </div>
414
434
  </header>
415
435
 
@@ -442,6 +462,8 @@ function renderChartsDetail(state) {
442
462
  }
443
463
 
444
464
  export function renderChartsView(state) {
465
+ const historyVisible = state.editor.historyPanelVisible !== false || !state.charts.selectedHistoryId;
466
+
445
467
  if (!state.connections.active && state.charts.error?.code === 'ACTIVE_DATABASE_REQUIRED') {
446
468
  return {
447
469
  main: `<section class="view-surface flex min-h-full">${renderMissingDatabase()}</section>`,
@@ -452,17 +474,23 @@ export function renderChartsView(state) {
452
474
  return {
453
475
  main: `
454
476
  <section class="charts-view">
455
- <aside class="charts-view__sidebar">
456
- <div class="charts-view__sidebar-header">
457
- <div class="text-[10px] font-bold uppercase tracking-[0.2em] text-primary-container">
458
- Query History
459
- </div>
460
- <h2 class="mt-2 text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">
461
- Charts
462
- </h2>
463
- </div>
464
- ${renderChartsList(state)}
465
- </aside>
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
+ }
466
494
  <div class="charts-view__detail">${renderChartsDetail(state)}</div>
467
495
  </section>
468
496
  `,
@@ -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
  : ""
@@ -93,14 +93,14 @@ function renderWorkspaceHeader(state) {
93
93
  ${
94
94
  table
95
95
  ? `<button
96
- class="border border-outline-variant/20 px-4 py-3 text-[10px] font-bold uppercase tracking-[0.16em] text-on-surface hover:bg-surface-container-highest"
96
+ class="standard-button"
97
97
  data-action="toggle-data-tables"
98
98
  type="button"
99
99
  >
100
100
  ${tablesVisible ? '<span class="material-symbols-outlined text-sm">visibility_off</span> Hide Tables' : 'Show Tables'}
101
101
  </button>
102
102
  <button
103
- class="border border-outline-variant/20 px-4 py-3 text-[10px] font-bold uppercase tracking-[0.16em] text-on-surface hover:bg-surface-container-highest"
103
+ class="standard-button"
104
104
  data-action="export-data-csv"
105
105
  type="button"
106
106
  >
@@ -109,7 +109,7 @@ function renderWorkspaceHeader(state) {
109
109
  : ''
110
110
  }
111
111
  <button
112
- class="border border-outline-variant/20 px-4 py-3 text-[10px] font-bold uppercase tracking-[0.16em] text-on-surface hover:bg-surface-container-highest"
112
+ class="standard-button"
113
113
  data-action="refresh-view"
114
114
  type="button"
115
115
  >
@@ -119,11 +119,12 @@ function renderWorkspaceHeader(state) {
119
119
  table
120
120
  ? `
121
121
  <button
122
- class="border border-outline-variant/20 px-4 py-3 text-[10px] font-bold uppercase tracking-[0.16em] text-on-surface hover:bg-surface-container-highest"
122
+ class="standard-button"
123
123
  data-action="navigate"
124
124
  data-to="/structure"
125
125
  type="button"
126
126
  >
127
+ <span class="material-symbols-outlined">account_tree</span>
127
128
  Open Structure
128
129
  </button>
129
130
  `
@@ -239,10 +240,10 @@ function renderTableSearchBar(table, state, activeColumn, filteredRowCount) {
239
240
 
240
241
  return `
241
242
  <div class="flex flex-wrap items-center gap-3 border-b border-outline-variant/10 bg-surface-container-low px-6 py-4">
242
- <label class="flex min-w-[18rem] flex-1 items-center gap-3 border border-outline-variant/20 bg-surface-container-lowest px-4 py-3">
243
+ <label class="control-shell flex min-w-[18rem] flex-1 items-center gap-3 border border-outline-variant/20 bg-surface-container-lowest px-3">
243
244
  <span class="material-symbols-outlined text-base text-on-surface-variant/55">search</span>
244
245
  <input
245
- class="min-w-0 flex-1 bg-transparent text-sm text-on-surface outline-none placeholder:text-on-surface-variant/40"
246
+ class="control-input control-input--ghost min-w-0 flex-1 text-sm text-on-surface outline-none placeholder:text-on-surface-variant/40"
246
247
  data-bind="data-search-query"
247
248
  placeholder="Filter current page..."
248
249
  type="search"
@@ -250,7 +251,7 @@ function renderTableSearchBar(table, state, activeColumn, filteredRowCount) {
250
251
  />
251
252
  </label>
252
253
  <select
253
- class="min-w-[14rem] border border-outline-variant/20 bg-surface-container-lowest px-4 py-3 font-mono text-xs tracking-[0.04em] text-on-surface outline-none"
254
+ class="control-select min-w-[14rem] border border-outline-variant/20 bg-surface-container-lowest font-mono text-xs tracking-[0.04em] text-on-surface outline-none"
254
255
  data-bind="data-search-column"
255
256
  >
256
257
  ${columns
@@ -390,10 +391,8 @@ function renderTableSurface(state) {
390
391
  .map(
391
392
  pageSize => `
392
393
  <button
393
- class="border px-3 py-2 text-[10px] font-bold uppercase tracking-[0.16em] transition-colors ${
394
- pageSize === (table.limit ?? state.dataBrowser.pageSize)
395
- ? 'border-primary-container/30 bg-surface-container-high text-primary-container'
396
- : 'border-outline-variant/20 text-on-surface hover:bg-surface-container-highest'
394
+ class="standard-button ${
395
+ pageSize === (table.limit ?? state.dataBrowser.pageSize) ? 'is-active' : ''
397
396
  }"
398
397
  data-action="set-data-page-size"
399
398
  data-page-size="${pageSize}"
@@ -408,7 +407,7 @@ function renderTableSurface(state) {
408
407
  </div>
409
408
  <div class="flex items-center gap-2">
410
409
  <button
411
- class="border border-outline-variant/20 px-3 py-2 text-[10px] font-bold uppercase tracking-[0.16em] text-on-surface transition-colors hover:bg-surface-container-highest disabled:cursor-default disabled:opacity-30"
410
+ class="standard-button"
412
411
  data-action="set-data-page"
413
412
  data-page="${page - 1}"
414
413
  type="button"
@@ -420,7 +419,7 @@ function renderTableSurface(state) {
420
419
  page ${escapeHtml(formatNumber(page))} / ${escapeHtml(formatNumber(pageCount))}
421
420
  </div>
422
421
  <button
423
- class="border border-outline-variant/20 px-3 py-2 text-[10px] font-bold uppercase tracking-[0.16em] text-on-surface transition-colors hover:bg-surface-container-highest disabled:cursor-default disabled:opacity-30"
422
+ class="standard-button"
424
423
  data-action="set-data-page"
425
424
  data-page="${page + 1}"
426
425
  type="button"