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.
- package/changelog.md +14 -0
- package/docs/DESIGN_GUIDELINES.md +36 -0
- package/frontend/index.html +79 -0
- package/frontend/js/app.js +89 -8
- package/frontend/js/components/connectionCard.js +3 -4
- package/frontend/js/components/emptyState.js +4 -4
- package/frontend/js/components/modal.js +32 -28
- package/frontend/js/components/queryEditor.js +2 -4
- package/frontend/js/components/queryHistoryDetail.js +16 -7
- package/frontend/js/components/queryHistoryPanel.js +2 -2
- package/frontend/js/components/rowEditorPanel.js +59 -54
- package/frontend/js/components/sidebar.js +32 -33
- package/frontend/js/components/structureGraph.js +54 -122
- package/frontend/js/components/tableDesignerEditor.js +9 -8
- package/frontend/js/components/tableDesignerSidebar.js +11 -10
- package/frontend/js/components/tableDesignerSqlPreview.js +60 -28
- package/frontend/js/store.js +23 -0
- package/frontend/js/views/charts.js +66 -38
- package/frontend/js/views/connections.js +5 -17
- package/frontend/js/views/data.js +12 -13
- package/frontend/js/views/overview.js +149 -10
- package/frontend/js/views/settings.js +2 -2
- package/frontend/js/views/structure.js +74 -70
- package/frontend/js/views/tableDesigner.js +7 -2
- package/frontend/styles/base.css +62 -0
- package/frontend/styles/components.css +63 -105
- package/frontend/styles/structure-graph.css +19 -82
- package/frontend/styles/tokens.css +2 -0
- package/frontend/styles/views.css +33 -36
- package/package.json +1 -1
- package/server/services/sqlite/overviewService.js +68 -0
package/changelog.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# v0.8.7
|
|
2
|
+
|
|
3
|
+
- UX fixes
|
|
4
|
+
- fixing a lot of the vibe slop
|
|
5
|
+
- trying to build reuseable components
|
|
6
|
+
|
|
7
|
+
# v0.8.0
|
|
8
|
+
|
|
9
|
+
- DDL copy button
|
|
10
|
+
- open in charts button in query details
|
|
11
|
+
- clear makes editor active
|
|
12
|
+
- UI fixes
|
|
13
|
+
- Overview improvement
|
|
14
|
+
|
|
1
15
|
# v0.7.0
|
|
2
16
|
|
|
3
17
|
- hide query history, hide editor
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# SQLite Hub UI Rules for Codex
|
|
2
|
+
|
|
3
|
+
Follow these rules exactly when creating or modifying UI controls.
|
|
4
|
+
|
|
5
|
+
1. Reuse existing shared components before creating anything new.
|
|
6
|
+
2. Use exactly one semantic main class per control.
|
|
7
|
+
3. Never combine multiple button-style classes on the same element.
|
|
8
|
+
4. View-specific classes may change layout, width, spacing, or position only.
|
|
9
|
+
5. View-specific classes must not redefine the base style of shared components.
|
|
10
|
+
6. The standard height for interactive controls is `36px`.
|
|
11
|
+
7. The source of truth for control height is `--control-height` in `frontend/styles/tokens.css`.
|
|
12
|
+
8. Controls in the same row must have the same height.
|
|
13
|
+
9. All regular buttons must use one of these classes only: `standard-button`, `signature-button`, `delete-button`.
|
|
14
|
+
10. Use `standard-button` for normal actions.
|
|
15
|
+
11. Use `signature-button` only for the primary yellow CTA in a context.
|
|
16
|
+
12. Use `delete-button` only for destructive actions.
|
|
17
|
+
13. `signature-button` must keep its hover state and chamfered corner.
|
|
18
|
+
14. All visible clickable buttons must have a hover state.
|
|
19
|
+
15. Disabled states must come from the shared component, not from local view CSS.
|
|
20
|
+
16. All reusable checkboxes must use `standard-checkbox`.
|
|
21
|
+
17. Do not build feature-specific checkbox shells or checkbox base styles.
|
|
22
|
+
18. Inputs and selects must follow the shared base rules in `frontend/styles/base.css`.
|
|
23
|
+
19. Ghost or transparent inputs must not use a white background, including unfocused state.
|
|
24
|
+
20. When a new control pattern appears in multiple places, promote it to a shared component first, then migrate existing usages.
|
|
25
|
+
|
|
26
|
+
## Source of Truth
|
|
27
|
+
|
|
28
|
+
- `frontend/styles/tokens.css`
|
|
29
|
+
- `frontend/styles/base.css`
|
|
30
|
+
- `frontend/index.html`
|
|
31
|
+
|
|
32
|
+
## Default Decision Rules
|
|
33
|
+
|
|
34
|
+
- Prefer reuse over invention.
|
|
35
|
+
- Prefer migration over local override.
|
|
36
|
+
- Prefer shared component updates over per-view fixes.
|
package/frontend/index.html
CHANGED
|
@@ -85,6 +85,85 @@
|
|
|
85
85
|
},
|
|
86
86
|
};
|
|
87
87
|
</script>
|
|
88
|
+
<style type="text/tailwindcss">
|
|
89
|
+
@layer components {
|
|
90
|
+
.signature-button {
|
|
91
|
+
@apply inline-flex items-center justify-center gap-2 h-[var(--control-height)] min-h-[var(--control-height)] border border-primary-container bg-primary-container px-[var(--control-padding-inline)] font-headline text-xs font-bold uppercase tracking-[0.16em] text-on-primary transition-all;
|
|
92
|
+
box-shadow: 0 0 18px -10px rgba(252, 227, 0, 0.7);
|
|
93
|
+
clip-path: polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 0 100%);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.signature-button:hover {
|
|
97
|
+
@apply -translate-y-px bg-primary-fixed text-on-primary;
|
|
98
|
+
border-color: rgba(252, 227, 0, 0.55);
|
|
99
|
+
box-shadow: 0 0 24px -8px rgba(252, 227, 0, 0.92);
|
|
100
|
+
filter: brightness(1.03);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.signature-button:disabled,
|
|
104
|
+
.signature-button[aria-disabled="true"] {
|
|
105
|
+
@apply cursor-not-allowed opacity-45;
|
|
106
|
+
box-shadow: none;
|
|
107
|
+
filter: none;
|
|
108
|
+
transform: none;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.signature-button .material-symbols-outlined {
|
|
112
|
+
@apply text-base;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.standard-button {
|
|
116
|
+
@apply inline-flex items-center justify-center gap-2 h-[var(--control-height)] min-h-[var(--control-height)] border border-outline-variant/20 bg-surface-container px-[var(--control-padding-inline)] font-mono text-[10px] font-bold uppercase tracking-[0.16em] text-on-surface transition-colors;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.standard-button:hover {
|
|
120
|
+
@apply bg-surface-container-highest text-primary-container;
|
|
121
|
+
border-color: rgba(252, 227, 0, 0.24);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.standard-button.is-active {
|
|
125
|
+
@apply bg-surface-container-high text-primary-container;
|
|
126
|
+
border-color: rgba(252, 227, 0, 0.3);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.standard-button:disabled,
|
|
130
|
+
.standard-button[aria-disabled="true"] {
|
|
131
|
+
@apply cursor-not-allowed opacity-40;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.standard-button .material-symbols-outlined {
|
|
135
|
+
@apply text-base;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.standard-checkbox {
|
|
139
|
+
@apply inline-flex items-center gap-3 min-h-[var(--control-height)] border border-outline-variant/10 bg-surface-container-lowest px-[var(--control-padding-inline)] text-sm text-on-surface;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.standard-checkbox input[type="checkbox"] {
|
|
143
|
+
@apply m-0 rounded-none border-outline bg-surface-container-lowest text-primary-container focus:ring-primary-container;
|
|
144
|
+
accent-color: var(--color-primary-container);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.standard-checkbox.is-disabled,
|
|
148
|
+
.standard-checkbox:has(input:disabled) {
|
|
149
|
+
@apply opacity-45;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.delete-button {
|
|
153
|
+
@apply inline-flex items-center justify-center gap-2 h-[var(--control-height)] min-h-[var(--control-height)] border border-error/25 bg-error-container/10 px-[var(--control-padding-inline)] font-mono text-[10px] font-bold uppercase tracking-[0.16em] text-error transition-all;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.delete-button:hover {
|
|
157
|
+
@apply -translate-y-px border-error bg-error-container/20 text-error;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.delete-button:disabled,
|
|
161
|
+
.delete-button[aria-disabled="true"] {
|
|
162
|
+
@apply cursor-not-allowed opacity-45;
|
|
163
|
+
transform: none;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
</style>
|
|
88
167
|
<link href="styles/tokens.css" rel="stylesheet" />
|
|
89
168
|
<link href="styles/base.css" rel="stylesheet" />
|
|
90
169
|
<link href="styles/layout.css" rel="stylesheet" />
|
package/frontend/js/app.js
CHANGED
|
@@ -50,6 +50,8 @@ import {
|
|
|
50
50
|
selectQueryHistoryItem,
|
|
51
51
|
selectStructureEntry,
|
|
52
52
|
setTableDesignerSearchQuery,
|
|
53
|
+
setTableDesignerSqlPreviewVisibility,
|
|
54
|
+
toggleStructureTablesPanel,
|
|
53
55
|
setDataPage,
|
|
54
56
|
setDataPageSize,
|
|
55
57
|
setDataSearchColumn,
|
|
@@ -115,7 +117,21 @@ const shellRefs = {
|
|
|
115
117
|
toast: document.querySelector("#toast-root"),
|
|
116
118
|
};
|
|
117
119
|
let lastRenderedRoutePath = null;
|
|
120
|
+
let lastRenderedTopNavMarkup = "";
|
|
121
|
+
let lastRenderedSidebarMarkup = "";
|
|
122
|
+
let lastRenderedStatusBarMarkup = "";
|
|
123
|
+
let lastRenderedMainMarkup = "";
|
|
124
|
+
let lastRenderedPanelMarkup = "";
|
|
125
|
+
let lastRenderedModalMarkup = "";
|
|
126
|
+
let lastRenderedToastMarkup = "";
|
|
127
|
+
let lastRenderedPanelOpen = false;
|
|
128
|
+
let lastRenderedLockedRoute = false;
|
|
118
129
|
let pendingNewTableDesignerAutofocus = false;
|
|
130
|
+
let pendingQueryEditorFocus = false;
|
|
131
|
+
|
|
132
|
+
function invalidateMainRenderCache() {
|
|
133
|
+
lastRenderedMainMarkup = null;
|
|
134
|
+
}
|
|
119
135
|
|
|
120
136
|
function resetStructureGraphForDatabaseChange() {
|
|
121
137
|
resetPersistedStructureGraphState();
|
|
@@ -215,7 +231,7 @@ function renderNotFoundView() {
|
|
|
215
231
|
404_SIGNAL
|
|
216
232
|
</h1>
|
|
217
233
|
<button
|
|
218
|
-
class="mt-8
|
|
234
|
+
class="standard-button mt-8 px-6 font-headline text-sm"
|
|
219
235
|
data-action="navigate"
|
|
220
236
|
data-to="/"
|
|
221
237
|
type="button"
|
|
@@ -389,6 +405,18 @@ function focusTableDesignerColumnNameField(columnId) {
|
|
|
389
405
|
return true;
|
|
390
406
|
}
|
|
391
407
|
|
|
408
|
+
function focusQueryEditorInput() {
|
|
409
|
+
const input = document.querySelector('[data-bind="current-query"]');
|
|
410
|
+
|
|
411
|
+
if (!(input instanceof HTMLTextAreaElement)) {
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
input.focus({ preventScroll: true });
|
|
416
|
+
input.setSelectionRange(input.value.length, input.value.length);
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
|
|
392
420
|
async function handleTableDesignerCsvImport(fileInput) {
|
|
393
421
|
if (!(fileInput instanceof HTMLInputElement)) {
|
|
394
422
|
return;
|
|
@@ -429,12 +457,39 @@ async function handleTableDesignerCsvImport(fileInput) {
|
|
|
429
457
|
}
|
|
430
458
|
|
|
431
459
|
function renderApp(state) {
|
|
432
|
-
const focusedInput = captureFocusedInputState();
|
|
433
460
|
const previousRoutePath = lastRenderedRoutePath;
|
|
434
461
|
const { main, panel } = resolveView(state);
|
|
462
|
+
const topNavMarkup = renderTopNav(state);
|
|
463
|
+
const sidebarMarkup = renderSidebar(state);
|
|
464
|
+
const statusBarMarkup = renderStatusBar(state);
|
|
465
|
+
const modalMarkup = renderModal(state);
|
|
466
|
+
const toastMarkup = renderToasts(state.toasts);
|
|
435
467
|
const isLockedRoute = ["editor", "editorResults", "data", "charts", "structure", "tableDesigner"].includes(
|
|
436
468
|
state.route.name
|
|
437
469
|
);
|
|
470
|
+
const panelOpen = Boolean(panel);
|
|
471
|
+
const shellMarkupUnchanged =
|
|
472
|
+
state.route.path === lastRenderedRoutePath &&
|
|
473
|
+
topNavMarkup === lastRenderedTopNavMarkup &&
|
|
474
|
+
sidebarMarkup === lastRenderedSidebarMarkup &&
|
|
475
|
+
statusBarMarkup === lastRenderedStatusBarMarkup &&
|
|
476
|
+
main === lastRenderedMainMarkup &&
|
|
477
|
+
panel === lastRenderedPanelMarkup &&
|
|
478
|
+
modalMarkup === lastRenderedModalMarkup &&
|
|
479
|
+
panelOpen === lastRenderedPanelOpen &&
|
|
480
|
+
isLockedRoute === lastRenderedLockedRoute;
|
|
481
|
+
|
|
482
|
+
if (shellMarkupUnchanged && toastMarkup !== lastRenderedToastMarkup) {
|
|
483
|
+
shellRefs.toast.innerHTML = toastMarkup;
|
|
484
|
+
lastRenderedToastMarkup = toastMarkup;
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (shellMarkupUnchanged && toastMarkup === lastRenderedToastMarkup) {
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const focusedInput = captureFocusedInputState();
|
|
438
493
|
const isEnteringNewTableDesignerRoute =
|
|
439
494
|
state.route.name === "tableDesigner" &&
|
|
440
495
|
state.route.params?.isNew &&
|
|
@@ -448,17 +503,24 @@ function renderApp(state) {
|
|
|
448
503
|
|
|
449
504
|
teardownStructureGraph();
|
|
450
505
|
teardownQueryChartRenderer();
|
|
451
|
-
shellRefs.topNav.innerHTML =
|
|
452
|
-
shellRefs.sidebar.innerHTML =
|
|
453
|
-
shellRefs.statusBar.innerHTML =
|
|
506
|
+
shellRefs.topNav.innerHTML = topNavMarkup;
|
|
507
|
+
shellRefs.sidebar.innerHTML = sidebarMarkup;
|
|
508
|
+
shellRefs.statusBar.innerHTML = statusBarMarkup;
|
|
454
509
|
shellRefs.view.innerHTML = main;
|
|
455
510
|
shellRefs.view.classList.toggle("app-main-scroll--locked", isLockedRoute);
|
|
456
511
|
shellRefs.panel.innerHTML = panel;
|
|
457
|
-
shellRefs.modal.innerHTML =
|
|
458
|
-
shellRefs.toast.innerHTML =
|
|
459
|
-
shellRefs.shell.classList.toggle("panel-open",
|
|
512
|
+
shellRefs.modal.innerHTML = modalMarkup;
|
|
513
|
+
shellRefs.toast.innerHTML = toastMarkup;
|
|
514
|
+
shellRefs.shell.classList.toggle("panel-open", panelOpen);
|
|
460
515
|
|
|
461
516
|
if (
|
|
517
|
+
pendingQueryEditorFocus &&
|
|
518
|
+
(state.route.name === "editor" || state.route.name === "editorResults")
|
|
519
|
+
) {
|
|
520
|
+
if (focusQueryEditorInput()) {
|
|
521
|
+
pendingQueryEditorFocus = false;
|
|
522
|
+
}
|
|
523
|
+
} else if (
|
|
462
524
|
pendingNewTableDesignerAutofocus &&
|
|
463
525
|
state.route.name === "tableDesigner" &&
|
|
464
526
|
state.route.params?.isNew &&
|
|
@@ -472,6 +534,15 @@ function renderApp(state) {
|
|
|
472
534
|
}
|
|
473
535
|
|
|
474
536
|
lastRenderedRoutePath = state.route.path;
|
|
537
|
+
lastRenderedTopNavMarkup = topNavMarkup;
|
|
538
|
+
lastRenderedSidebarMarkup = sidebarMarkup;
|
|
539
|
+
lastRenderedStatusBarMarkup = statusBarMarkup;
|
|
540
|
+
lastRenderedMainMarkup = main;
|
|
541
|
+
lastRenderedPanelMarkup = panel;
|
|
542
|
+
lastRenderedModalMarkup = modalMarkup;
|
|
543
|
+
lastRenderedToastMarkup = toastMarkup;
|
|
544
|
+
lastRenderedPanelOpen = panelOpen;
|
|
545
|
+
lastRenderedLockedRoute = isLockedRoute;
|
|
475
546
|
|
|
476
547
|
if (state.route.name === "structure") {
|
|
477
548
|
mountStructureGraph(state).catch((error) => {
|
|
@@ -567,6 +638,7 @@ async function handleAction(actionNode) {
|
|
|
567
638
|
openDeleteEditorRowModal(actionNode.dataset.rowIndex);
|
|
568
639
|
return;
|
|
569
640
|
case "clear-query":
|
|
641
|
+
pendingQueryEditorFocus = true;
|
|
570
642
|
clearCurrentQuery();
|
|
571
643
|
if (getState().route.name === "editorResults") {
|
|
572
644
|
router.navigate("/editor");
|
|
@@ -663,6 +735,9 @@ async function handleAction(actionNode) {
|
|
|
663
735
|
case "toggle-data-tables":
|
|
664
736
|
toggleDataTablesPanel();
|
|
665
737
|
return;
|
|
738
|
+
case "toggle-structure-tables":
|
|
739
|
+
toggleStructureTablesPanel();
|
|
740
|
+
return;
|
|
666
741
|
case "select-structure-entry":
|
|
667
742
|
if (actionNode.dataset.entryName) {
|
|
668
743
|
await selectStructureEntry(actionNode.dataset.entryName);
|
|
@@ -704,6 +779,11 @@ async function handleAction(actionNode) {
|
|
|
704
779
|
}
|
|
705
780
|
return;
|
|
706
781
|
}
|
|
782
|
+
case "toggle-table-designer-sql-preview":
|
|
783
|
+
setTableDesignerSqlPreviewVisibility(
|
|
784
|
+
actionNode.dataset.nextValue ? actionNode.dataset.nextValue === "true" : undefined
|
|
785
|
+
);
|
|
786
|
+
return;
|
|
707
787
|
case "import-table-designer-csv": {
|
|
708
788
|
const fileInput = document.querySelector('[data-bind="table-designer-import-file"]');
|
|
709
789
|
|
|
@@ -822,6 +902,7 @@ document.addEventListener("input", (event) => {
|
|
|
822
902
|
}
|
|
823
903
|
|
|
824
904
|
if (bindNode.dataset.bind === "current-query") {
|
|
905
|
+
invalidateMainRenderCache();
|
|
825
906
|
syncQueryEditorHighlight(bindNode);
|
|
826
907
|
syncQueryEditorScroll(bindNode);
|
|
827
908
|
setCurrentQuery(bindNode.value);
|
|
@@ -73,17 +73,16 @@ export function renderConnectionCard(connection, activeConnectionId) {
|
|
|
73
73
|
<div class="border-t border-outline-variant/10 bg-surface-container-low px-4 py-3">
|
|
74
74
|
<div class="grid grid-cols-[minmax(0,1fr)_5.1rem_5.8rem] gap-2">
|
|
75
75
|
<button
|
|
76
|
-
class="
|
|
76
|
+
class="signature-button"
|
|
77
77
|
data-action="select-connection"
|
|
78
78
|
data-connection-id="${escapeHtml(connection.id)}"
|
|
79
|
-
style="--clip-path: ${clipPath};"
|
|
80
79
|
type="button"
|
|
81
80
|
title="${primaryActionLabel}"
|
|
82
81
|
>
|
|
83
82
|
${primaryActionLabel}
|
|
84
83
|
</button>
|
|
85
84
|
<button
|
|
86
|
-
class="
|
|
85
|
+
class="standard-button"
|
|
87
86
|
data-action="edit-connection"
|
|
88
87
|
data-connection-id="${escapeHtml(connection.id)}"
|
|
89
88
|
type="button"
|
|
@@ -91,7 +90,7 @@ export function renderConnectionCard(connection, activeConnectionId) {
|
|
|
91
90
|
Edit
|
|
92
91
|
</button>
|
|
93
92
|
<button
|
|
94
|
-
class="
|
|
93
|
+
class="delete-button"
|
|
95
94
|
data-action="remove-connection"
|
|
96
95
|
data-connection-id="${escapeHtml(connection.id)}"
|
|
97
96
|
type="button"
|
|
@@ -22,7 +22,7 @@ function renderRecentConnections(recentConnections = []) {
|
|
|
22
22
|
.map(
|
|
23
23
|
(connection) => `
|
|
24
24
|
<button
|
|
25
|
-
class="flex items-center gap-2 border border-outline-variant/15 bg-surface-container-low px-4
|
|
25
|
+
class="control-button flex items-center gap-2 border border-outline-variant/15 bg-surface-container-low px-4 text-left text-on-surface transition-colors hover:border-primary-container/30 hover:bg-surface-container-high"
|
|
26
26
|
data-action="select-connection"
|
|
27
27
|
data-connection-id="${escapeHtml(connection.id)}"
|
|
28
28
|
type="button"
|
|
@@ -135,7 +135,7 @@ export function renderEmptyState({ activeConnection, recentConnections = [] }) {
|
|
|
135
135
|
? `
|
|
136
136
|
<div class="mx-auto mt-8 grid w-full max-w-3xl grid-cols-1 gap-4 px-6 md:grid-cols-3">
|
|
137
137
|
<button
|
|
138
|
-
class="
|
|
138
|
+
class="standard-button"
|
|
139
139
|
data-action="navigate"
|
|
140
140
|
data-to="/overview"
|
|
141
141
|
type="button"
|
|
@@ -143,7 +143,7 @@ export function renderEmptyState({ activeConnection, recentConnections = [] }) {
|
|
|
143
143
|
Overview
|
|
144
144
|
</button>
|
|
145
145
|
<button
|
|
146
|
-
class="
|
|
146
|
+
class="standard-button"
|
|
147
147
|
data-action="navigate"
|
|
148
148
|
data-to="/structure"
|
|
149
149
|
type="button"
|
|
@@ -151,7 +151,7 @@ export function renderEmptyState({ activeConnection, recentConnections = [] }) {
|
|
|
151
151
|
Structure
|
|
152
152
|
</button>
|
|
153
153
|
<button
|
|
154
|
-
class="
|
|
154
|
+
class="standard-button"
|
|
155
155
|
data-action="navigate"
|
|
156
156
|
data-to="/editor"
|
|
157
157
|
type="button"
|
|
@@ -13,7 +13,7 @@ function renderField({ label, name, type = "text", placeholder = "", value = ""
|
|
|
13
13
|
${escapeHtml(label)}
|
|
14
14
|
</span>
|
|
15
15
|
<input
|
|
16
|
-
class="w-full border border-outline-variant/20 bg-surface-container-lowest
|
|
16
|
+
class="control-input w-full border border-outline-variant/20 bg-surface-container-lowest text-sm text-on-surface outline-none transition-colors focus:border-primary-container"
|
|
17
17
|
name="${escapeHtml(name)}"
|
|
18
18
|
placeholder="${escapeHtml(placeholder)}"
|
|
19
19
|
type="${escapeHtml(type)}"
|
|
@@ -25,14 +25,18 @@ function renderField({ label, name, type = "text", placeholder = "", value = ""
|
|
|
25
25
|
|
|
26
26
|
function renderCheckboxField({ label, name, checked = false, text }) {
|
|
27
27
|
return `
|
|
28
|
-
<label class="
|
|
29
|
-
<
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
28
|
+
<label class="block space-y-2">
|
|
29
|
+
<span class="text-[10px] font-mono uppercase tracking-[0.22em] text-on-surface-variant/60">
|
|
30
|
+
${escapeHtml(label)}
|
|
31
|
+
</span>
|
|
32
|
+
<span class="standard-checkbox">
|
|
33
|
+
<input
|
|
34
|
+
${checked ? "checked" : ""}
|
|
35
|
+
name="${escapeHtml(name)}"
|
|
36
|
+
type="checkbox"
|
|
37
|
+
/>
|
|
38
|
+
<span>${escapeHtml(text || label)}</span>
|
|
39
|
+
</span>
|
|
36
40
|
</label>
|
|
37
41
|
`;
|
|
38
42
|
}
|
|
@@ -50,7 +54,7 @@ function renderFileField({
|
|
|
50
54
|
</span>
|
|
51
55
|
<input
|
|
52
56
|
accept="${escapeHtml(accept)}"
|
|
53
|
-
class="block w-full border border-outline-variant/20 bg-surface-container-lowest
|
|
57
|
+
class="control-input block w-full border border-outline-variant/20 bg-surface-container-lowest text-sm text-on-surface file:mr-4 file:border-0 file:bg-primary-container file:px-3 file:py-2 file:text-xs file:font-bold file:text-on-primary"
|
|
54
58
|
name="${escapeHtml(name)}"
|
|
55
59
|
type="file"
|
|
56
60
|
/>
|
|
@@ -70,7 +74,7 @@ function renderSelectField({ label, name, value = "", options = [], bind = "" })
|
|
|
70
74
|
${escapeHtml(label)}
|
|
71
75
|
</span>
|
|
72
76
|
<select
|
|
73
|
-
class="w-full border border-outline-variant/20 bg-surface-container-lowest
|
|
77
|
+
class="control-select w-full border border-outline-variant/20 bg-surface-container-lowest text-sm text-on-surface outline-none transition-colors focus:border-primary-container"
|
|
74
78
|
${bind ? `data-bind="${escapeHtml(bind)}"` : ""}
|
|
75
79
|
${name ? `name="${escapeHtml(name)}"` : ""}
|
|
76
80
|
>
|
|
@@ -124,14 +128,14 @@ function renderOpenConnectionForm(modal) {
|
|
|
124
128
|
${renderError(modal.error)}
|
|
125
129
|
<div class="flex items-center justify-end gap-3 pt-2">
|
|
126
130
|
<button
|
|
127
|
-
class="
|
|
131
|
+
class="standard-button"
|
|
128
132
|
data-action="close-modal"
|
|
129
133
|
type="button"
|
|
130
134
|
>
|
|
131
135
|
Cancel
|
|
132
136
|
</button>
|
|
133
137
|
<button
|
|
134
|
-
class="
|
|
138
|
+
class="standard-button"
|
|
135
139
|
type="submit"
|
|
136
140
|
>
|
|
137
141
|
${modal.submitting ? "Opening..." : "Open Database"}
|
|
@@ -198,14 +202,14 @@ function renderEditConnectionForm(modal) {
|
|
|
198
202
|
${renderError(modal.error)}
|
|
199
203
|
<div class="flex items-center justify-end gap-3 pt-2">
|
|
200
204
|
<button
|
|
201
|
-
class="
|
|
205
|
+
class="standard-button"
|
|
202
206
|
data-action="close-modal"
|
|
203
207
|
type="button"
|
|
204
208
|
>
|
|
205
209
|
Cancel
|
|
206
210
|
</button>
|
|
207
211
|
<button
|
|
208
|
-
class="
|
|
212
|
+
class="standard-button"
|
|
209
213
|
type="submit"
|
|
210
214
|
>
|
|
211
215
|
${modal.submitting ? "Saving..." : "Save Changes"}
|
|
@@ -231,14 +235,14 @@ function renderCreateDatabaseForm(modal) {
|
|
|
231
235
|
${renderError(modal.error)}
|
|
232
236
|
<div class="flex items-center justify-end gap-3 pt-2">
|
|
233
237
|
<button
|
|
234
|
-
class="
|
|
238
|
+
class="standard-button"
|
|
235
239
|
data-action="close-modal"
|
|
236
240
|
type="button"
|
|
237
241
|
>
|
|
238
242
|
Cancel
|
|
239
243
|
</button>
|
|
240
244
|
<button
|
|
241
|
-
class="
|
|
245
|
+
class="standard-button"
|
|
242
246
|
type="submit"
|
|
243
247
|
>
|
|
244
248
|
${modal.submitting ? "Creating..." : "Create Database"}
|
|
@@ -265,7 +269,7 @@ function renderImportTargetOptions(state) {
|
|
|
265
269
|
Import Target
|
|
266
270
|
</span>
|
|
267
271
|
<select
|
|
268
|
-
class="w-full border border-outline-variant/20 bg-surface-container-lowest
|
|
272
|
+
class="control-select w-full border border-outline-variant/20 bg-surface-container-lowest text-sm text-on-surface outline-none transition-colors focus:border-primary-container"
|
|
269
273
|
name="targetMode"
|
|
270
274
|
>
|
|
271
275
|
${
|
|
@@ -286,7 +290,7 @@ function renderImportTargetOptions(state) {
|
|
|
286
290
|
Recent Connection
|
|
287
291
|
</span>
|
|
288
292
|
<select
|
|
289
|
-
class="w-full border border-outline-variant/20 bg-surface-container-lowest
|
|
293
|
+
class="control-select w-full border border-outline-variant/20 bg-surface-container-lowest text-sm text-on-surface outline-none transition-colors focus:border-primary-container"
|
|
290
294
|
name="targetConnectionId"
|
|
291
295
|
>
|
|
292
296
|
${recentOptions}
|
|
@@ -324,14 +328,14 @@ function renderImportSqlForm(modal, state) {
|
|
|
324
328
|
${renderError(modal.error)}
|
|
325
329
|
<div class="flex items-center justify-end gap-3 pt-2">
|
|
326
330
|
<button
|
|
327
|
-
class="
|
|
331
|
+
class="standard-button"
|
|
328
332
|
data-action="close-modal"
|
|
329
333
|
type="button"
|
|
330
334
|
>
|
|
331
335
|
Cancel
|
|
332
336
|
</button>
|
|
333
337
|
<button
|
|
334
|
-
class="
|
|
338
|
+
class="standard-button"
|
|
335
339
|
type="submit"
|
|
336
340
|
>
|
|
337
341
|
${modal.submitting ? "Importing..." : "Import SQL Dump"}
|
|
@@ -393,14 +397,14 @@ function renderDeleteRowConfirmForm(modal) {
|
|
|
393
397
|
${renderError(modal.error)}
|
|
394
398
|
<div class="flex items-center justify-end gap-3 pt-2">
|
|
395
399
|
<button
|
|
396
|
-
class="
|
|
400
|
+
class="standard-button"
|
|
397
401
|
data-action="close-modal"
|
|
398
402
|
type="button"
|
|
399
403
|
>
|
|
400
404
|
Cancel
|
|
401
405
|
</button>
|
|
402
406
|
<button
|
|
403
|
-
class="
|
|
407
|
+
class="delete-button"
|
|
404
408
|
type="submit"
|
|
405
409
|
>
|
|
406
410
|
${modal.submitting ? "Deleting..." : "Delete Row"}
|
|
@@ -633,14 +637,14 @@ function renderChartEditorForm(modal, state) {
|
|
|
633
637
|
${renderError(modal.error)}
|
|
634
638
|
<div class="flex items-center justify-end gap-3 pt-2">
|
|
635
639
|
<button
|
|
636
|
-
class="
|
|
640
|
+
class="standard-button"
|
|
637
641
|
data-action="close-modal"
|
|
638
642
|
type="button"
|
|
639
643
|
>
|
|
640
644
|
Cancel
|
|
641
645
|
</button>
|
|
642
646
|
<button
|
|
643
|
-
class="
|
|
647
|
+
class="standard-button"
|
|
644
648
|
type="submit"
|
|
645
649
|
>
|
|
646
650
|
${modal.submitting ? "Saving..." : draft.mode === "edit" ? "Save Chart" : "Create Chart"}
|
|
@@ -666,14 +670,14 @@ function renderDeleteChartForm(modal) {
|
|
|
666
670
|
${renderError(modal.error)}
|
|
667
671
|
<div class="flex items-center justify-end gap-3 pt-2">
|
|
668
672
|
<button
|
|
669
|
-
class="
|
|
673
|
+
class="standard-button"
|
|
670
674
|
data-action="close-modal"
|
|
671
675
|
type="button"
|
|
672
676
|
>
|
|
673
677
|
Cancel
|
|
674
678
|
</button>
|
|
675
679
|
<button
|
|
676
|
-
class="
|
|
680
|
+
class="delete-button"
|
|
677
681
|
type="submit"
|
|
678
682
|
>
|
|
679
683
|
${modal.submitting ? "Deleting..." : "Delete Chart"}
|
|
@@ -747,7 +751,7 @@ export function renderModal(state) {
|
|
|
747
751
|
</h2>
|
|
748
752
|
</div>
|
|
749
753
|
<button
|
|
750
|
-
class="
|
|
754
|
+
class="control-icon-button border border-outline-variant/20 text-on-surface-variant hover:bg-surface-container-highest hover:text-primary-container"
|
|
751
755
|
data-action="close-modal"
|
|
752
756
|
type="button"
|
|
753
757
|
>
|
|
@@ -55,8 +55,7 @@ export function renderQueryEditor({
|
|
|
55
55
|
editorVisible = true,
|
|
56
56
|
historyVisible = true,
|
|
57
57
|
}) {
|
|
58
|
-
const secondaryButtonClass =
|
|
59
|
-
"toolbar-button border border-outline-variant/20 bg-surface-container px-4 py-2 text-[10px] font-bold uppercase tracking-widest text-on-surface transition-colors hover:border-primary-container hover:text-primary-container";
|
|
58
|
+
const secondaryButtonClass = "standard-button";
|
|
60
59
|
const left = `
|
|
61
60
|
<div class="flex items-center gap-2 bg-surface-container-lowest px-3 py-1">
|
|
62
61
|
<span class="material-symbols-outlined text-xs text-[#FCE300]">database</span>
|
|
@@ -104,9 +103,8 @@ export function renderQueryEditor({
|
|
|
104
103
|
${exporting ? "Exporting..." : "Export CSV"}
|
|
105
104
|
</button>
|
|
106
105
|
<button
|
|
107
|
-
class="
|
|
106
|
+
class="signature-button"
|
|
108
107
|
data-action="execute-query"
|
|
109
|
-
style="--clip-path: polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 0 100%);"
|
|
110
108
|
type="button"
|
|
111
109
|
>
|
|
112
110
|
${executing ? "RUNNING..." : "EXECUTE"}
|