sqlite-hub 0.9.1 → 0.9.4
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/.github/workflows/ci.yml +36 -0
- package/README.md +2 -2
- package/bin/sqlite-hub.js +1 -1
- package/frontend/index.html +3 -158
- package/frontend/js/app.js +231 -5
- package/frontend/js/components/connectionCard.js +62 -87
- package/frontend/js/components/emptyState.js +20 -23
- package/frontend/js/components/modal.js +158 -199
- package/frontend/js/components/pageHeader.js +1 -1
- package/frontend/js/components/queryEditor.js +16 -30
- package/frontend/js/components/queryHistoryDetail.js +93 -164
- package/frontend/js/components/queryHistoryPanel.js +90 -100
- package/frontend/js/components/queryResults.js +3 -1
- package/frontend/js/components/rowEditorPanel.js +76 -56
- package/frontend/js/components/tableDesignerEditor.js +91 -116
- package/frontend/js/lib/queryChartOptions.js +5 -1
- package/frontend/js/lib/queryCharts.js +50 -2
- package/frontend/js/store.js +161 -23
- package/frontend/js/utils/tableDesigner.js +8 -2
- package/frontend/js/views/charts.js +126 -73
- package/frontend/js/views/data.js +147 -133
- package/frontend/js/views/editor.js +1 -0
- package/frontend/js/views/mediaTagging.js +131 -164
- package/frontend/js/views/structure.js +52 -48
- package/frontend/styles/base.css +57 -13
- package/frontend/styles/components.css +166 -96
- package/frontend/styles/layout.css +1 -1
- package/frontend/styles/structure-graph.css +11 -11
- package/frontend/styles/tailwind.css +81 -0
- package/frontend/styles/tailwind.generated.css +1 -0
- package/frontend/styles/tokens.css +50 -7
- package/frontend/styles/utilities.css +21 -0
- package/frontend/styles/views.css +94 -86
- package/package.json +16 -3
- package/server/routes/mediaTagging.js +2 -10
- package/server/routes/sql.js +35 -10
- package/server/server.js +24 -0
- package/server/services/sqlite/dataBrowserService.js +25 -5
- package/server/services/sqlite/exportService.js +4 -2
- package/server/services/sqlite/introspection.js +2 -2
- package/server/services/sqlite/mediaTaggingService.js +166 -53
- package/server/services/sqlite/structureService.js +2 -2
- package/server/services/sqlite/tableDesigner/sql.js +19 -3
- package/server/services/storage/appStateStore.js +227 -87
- package/server/services/storage/queryHistoryChartUtils.js +19 -2
- package/server/utils/appPaths.js +55 -19
- package/server/utils/fileValidation.js +94 -8
- package/tailwind.config.cjs +73 -0
- package/tests/security-paths.test.js +84 -0
- package/tests/sql-identifier-safety.test.js +66 -0
- package/.npmingnore +0 -4
- package/changelog.md +0 -77
- package/docs/DESIGN_GUIDELINES.md +0 -36
- package/scripts/publish_brew.sh +0 -466
- package/scripts/publish_npm.sh +0 -241
- package/shortkeys.md +0 -5
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
.landing-accent {
|
|
93
|
-
background:
|
|
93
|
+
background: rgb(var(--rgb-outline) / 0.2);
|
|
94
94
|
height: 1px;
|
|
95
95
|
position: absolute;
|
|
96
96
|
}
|
|
@@ -110,8 +110,8 @@
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
.landing-accent--c {
|
|
113
|
-
border-bottom: 1px solid
|
|
114
|
-
border-right: 1px solid
|
|
113
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
114
|
+
border-right: 1px solid var(--border-subtle);
|
|
115
115
|
height: 4rem;
|
|
116
116
|
left: 0;
|
|
117
117
|
top: 50%;
|
|
@@ -126,7 +126,7 @@
|
|
|
126
126
|
|
|
127
127
|
.system-placeholder-card {
|
|
128
128
|
background: var(--color-surface-low);
|
|
129
|
-
|
|
129
|
+
box-shadow: inset 0 -2px 0 rgb(49 48 48);
|
|
130
130
|
display: flex;
|
|
131
131
|
flex-direction: column;
|
|
132
132
|
gap: var(--spacing-2);
|
|
@@ -148,16 +148,16 @@
|
|
|
148
148
|
|
|
149
149
|
.charts-view__sidebar {
|
|
150
150
|
background: var(--color-surface-low);
|
|
151
|
-
border-right: 1px solid
|
|
151
|
+
border-right: 1px solid var(--border-medium);
|
|
152
152
|
display: flex;
|
|
153
|
-
flex: 0 0 23rem;
|
|
153
|
+
flex: 0 0 clamp(20rem, 28vw, 23rem);
|
|
154
154
|
flex-direction: column;
|
|
155
155
|
min-height: 0;
|
|
156
|
-
width: 23rem;
|
|
156
|
+
width: clamp(20rem, 28vw, 23rem);
|
|
157
157
|
}
|
|
158
158
|
|
|
159
159
|
.charts-view__sidebar-header {
|
|
160
|
-
border-bottom: 1px solid
|
|
160
|
+
border-bottom: 1px solid var(--border-medium);
|
|
161
161
|
padding: var(--spacing-5);
|
|
162
162
|
}
|
|
163
163
|
|
|
@@ -171,16 +171,16 @@
|
|
|
171
171
|
|
|
172
172
|
.structure-view__sidebar {
|
|
173
173
|
background: var(--color-surface-low);
|
|
174
|
-
border-right: 1px solid
|
|
174
|
+
border-right: 1px solid var(--border-medium);
|
|
175
175
|
display: flex;
|
|
176
|
-
flex: 0 0 18.5rem;
|
|
176
|
+
flex: 0 0 clamp(16rem, 22vw, 18.5rem);
|
|
177
177
|
flex-direction: column;
|
|
178
178
|
min-height: 0;
|
|
179
|
-
width: 18.5rem;
|
|
179
|
+
width: clamp(16rem, 22vw, 18.5rem);
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
.structure-view__sidebar-header {
|
|
183
|
-
border-bottom: 1px solid
|
|
183
|
+
border-bottom: 1px solid var(--border-medium);
|
|
184
184
|
padding: var(--spacing-5);
|
|
185
185
|
}
|
|
186
186
|
|
|
@@ -200,7 +200,7 @@
|
|
|
200
200
|
}
|
|
201
201
|
|
|
202
202
|
.structure-view__header {
|
|
203
|
-
border-bottom: 1px solid
|
|
203
|
+
border-bottom: 1px solid var(--border-medium);
|
|
204
204
|
padding: 1.25rem 1.5rem;
|
|
205
205
|
}
|
|
206
206
|
|
|
@@ -262,7 +262,7 @@
|
|
|
262
262
|
|
|
263
263
|
.charts-height-toggle {
|
|
264
264
|
background: var(--color-surface-container-low);
|
|
265
|
-
border: 1px solid
|
|
265
|
+
border: 1px solid var(--border-medium);
|
|
266
266
|
display: inline-flex;
|
|
267
267
|
gap: 0;
|
|
268
268
|
height: var(--control-height);
|
|
@@ -287,7 +287,7 @@
|
|
|
287
287
|
}
|
|
288
288
|
|
|
289
289
|
.charts-height-toggle__button + .charts-height-toggle__button {
|
|
290
|
-
border-left: 1px solid
|
|
290
|
+
border-left: 1px solid var(--border-medium);
|
|
291
291
|
}
|
|
292
292
|
|
|
293
293
|
.charts-height-toggle__button.is-active {
|
|
@@ -299,7 +299,7 @@
|
|
|
299
299
|
.query-chart-card {
|
|
300
300
|
--query-chart-height: 450px;
|
|
301
301
|
background: var(--color-surface-low);
|
|
302
|
-
border: 1px solid
|
|
302
|
+
border: 1px solid var(--border-subtle);
|
|
303
303
|
overflow: hidden;
|
|
304
304
|
}
|
|
305
305
|
|
|
@@ -327,7 +327,7 @@
|
|
|
327
327
|
|
|
328
328
|
.query-chart-card__body {
|
|
329
329
|
background:
|
|
330
|
-
radial-gradient(circle at top right,
|
|
330
|
+
radial-gradient(circle at top right, var(--primary-alpha-08), transparent 28%),
|
|
331
331
|
linear-gradient(180deg, rgba(32, 31, 31, 0.92), rgba(14, 14, 14, 1));
|
|
332
332
|
min-height: var(--query-chart-height);
|
|
333
333
|
padding: 1rem;
|
|
@@ -360,25 +360,6 @@
|
|
|
360
360
|
padding-top: var(--spacing-4);
|
|
361
361
|
}
|
|
362
362
|
|
|
363
|
-
.media-tagging-created-tags-shell {
|
|
364
|
-
display: flex;
|
|
365
|
-
flex: 1;
|
|
366
|
-
flex-direction: column;
|
|
367
|
-
min-height: 0;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
.media-tagging-created-tags {
|
|
371
|
-
flex: 1;
|
|
372
|
-
max-height: none;
|
|
373
|
-
min-height: 0;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
.media-tagging-mapping-list {
|
|
377
|
-
flex: 0 0 auto;
|
|
378
|
-
min-height: auto;
|
|
379
|
-
overflow-y: auto;
|
|
380
|
-
}
|
|
381
|
-
|
|
382
363
|
.media-tagging-shell {
|
|
383
364
|
display: flex;
|
|
384
365
|
flex: 1;
|
|
@@ -416,7 +397,7 @@
|
|
|
416
397
|
|
|
417
398
|
.media-tagging-card__header {
|
|
418
399
|
align-items: start;
|
|
419
|
-
border-bottom: 1px solid
|
|
400
|
+
border-bottom: 1px solid rgb(var(--rgb-outline) / 0.14);
|
|
420
401
|
display: flex;
|
|
421
402
|
flex-wrap: wrap;
|
|
422
403
|
gap: var(--spacing-4);
|
|
@@ -501,18 +482,23 @@
|
|
|
501
482
|
}
|
|
502
483
|
|
|
503
484
|
.media-tagging-field__hint {
|
|
504
|
-
color:
|
|
485
|
+
color: var(--text-subtle);
|
|
505
486
|
font-family: var(--font-family-mono);
|
|
506
487
|
font-size: 0.65rem;
|
|
507
488
|
letter-spacing: 0.06em;
|
|
508
489
|
text-transform: uppercase;
|
|
509
490
|
}
|
|
510
491
|
|
|
511
|
-
.media-tagging-static-field
|
|
492
|
+
.media-tagging-static-field,
|
|
493
|
+
.media-tagging-card .control-input,
|
|
494
|
+
.media-tagging-card .control-select {
|
|
512
495
|
align-items: center;
|
|
513
|
-
background: var(--color-surface-container
|
|
514
|
-
border: 1px solid
|
|
496
|
+
background: var(--color-surface-container);
|
|
497
|
+
border: 1px solid var(--border-medium);
|
|
515
498
|
color: var(--color-on-surface);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.media-tagging-static-field {
|
|
516
502
|
display: flex;
|
|
517
503
|
font-family: var(--font-family-mono);
|
|
518
504
|
font-size: 0.8rem;
|
|
@@ -521,9 +507,12 @@
|
|
|
521
507
|
}
|
|
522
508
|
|
|
523
509
|
.media-tagging-textarea {
|
|
524
|
-
background: var(--color-surface-container
|
|
525
|
-
border: 1px solid
|
|
510
|
+
background: var(--color-surface-container);
|
|
511
|
+
border: 1px solid var(--border-medium);
|
|
526
512
|
color: var(--color-on-surface);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
.media-tagging-textarea {
|
|
527
516
|
font-family: var(--font-family-mono);
|
|
528
517
|
font-size: 0.8rem;
|
|
529
518
|
min-height: 13rem;
|
|
@@ -531,9 +520,16 @@
|
|
|
531
520
|
resize: vertical;
|
|
532
521
|
}
|
|
533
522
|
|
|
523
|
+
.media-tagging-textarea:focus-visible,
|
|
524
|
+
.media-tagging-static-field:focus-within {
|
|
525
|
+
border-color: var(--primary-alpha-35);
|
|
526
|
+
box-shadow: var(--focus-ring-inset);
|
|
527
|
+
outline: none;
|
|
528
|
+
}
|
|
529
|
+
|
|
534
530
|
.sql-highlight-shell {
|
|
535
531
|
background: var(--color-surface-container-lowest);
|
|
536
|
-
border: 1px solid
|
|
532
|
+
border: 1px solid var(--border-medium);
|
|
537
533
|
min-height: 13rem;
|
|
538
534
|
overflow: hidden;
|
|
539
535
|
}
|
|
@@ -582,7 +578,7 @@
|
|
|
582
578
|
}
|
|
583
579
|
|
|
584
580
|
.media-tagging-status__label {
|
|
585
|
-
color:
|
|
581
|
+
color: rgb(var(--rgb-on-surface) / 0.5);
|
|
586
582
|
font-family: var(--font-family-mono);
|
|
587
583
|
font-size: 0.625rem;
|
|
588
584
|
letter-spacing: 0.14em;
|
|
@@ -605,18 +601,21 @@
|
|
|
605
601
|
|
|
606
602
|
.media-tagging-mapping-list {
|
|
607
603
|
display: grid;
|
|
604
|
+
flex: 0 0 auto;
|
|
608
605
|
gap: var(--spacing-3);
|
|
609
606
|
max-height: 18rem;
|
|
607
|
+
min-height: auto;
|
|
608
|
+
overflow-y: auto;
|
|
610
609
|
}
|
|
611
610
|
|
|
612
611
|
.media-tagging-mapping-card {
|
|
613
|
-
background: var(--color-surface-container
|
|
614
|
-
border: 1px solid
|
|
612
|
+
background: var(--color-surface-container);
|
|
613
|
+
border: 1px solid rgb(var(--rgb-outline) / 0.16);
|
|
615
614
|
padding: var(--spacing-4);
|
|
616
615
|
}
|
|
617
616
|
|
|
618
617
|
.media-tagging-mapping-card.is-selected {
|
|
619
|
-
border-color:
|
|
618
|
+
border-color: rgb(var(--rgb-primary) / 0.45);
|
|
620
619
|
}
|
|
621
620
|
|
|
622
621
|
.media-tagging-preview {
|
|
@@ -637,9 +636,9 @@
|
|
|
637
636
|
.media-tagging-preview__media {
|
|
638
637
|
align-items: stretch;
|
|
639
638
|
background:
|
|
640
|
-
radial-gradient(circle at top right,
|
|
639
|
+
radial-gradient(circle at top right, var(--primary-alpha-10), transparent 30%),
|
|
641
640
|
linear-gradient(180deg, rgba(32, 31, 31, 0.95), rgba(14, 14, 14, 1));
|
|
642
|
-
border: 1px solid
|
|
641
|
+
border: 1px solid rgb(var(--rgb-outline) / 0.16);
|
|
643
642
|
display: flex;
|
|
644
643
|
flex-direction: column;
|
|
645
644
|
justify-content: flex-start;
|
|
@@ -659,7 +658,7 @@
|
|
|
659
658
|
|
|
660
659
|
.media-tagging-preview__media-toolbar {
|
|
661
660
|
align-items: center;
|
|
662
|
-
border-bottom: 1px solid
|
|
661
|
+
border-bottom: 1px solid var(--border-medium);
|
|
663
662
|
display: flex;
|
|
664
663
|
flex: 0 0 auto;
|
|
665
664
|
flex-wrap: wrap;
|
|
@@ -728,8 +727,8 @@
|
|
|
728
727
|
align-self: center;
|
|
729
728
|
background:
|
|
730
729
|
linear-gradient(180deg, rgba(32, 31, 31, 0.96), rgba(14, 14, 14, 0.98)),
|
|
731
|
-
radial-gradient(circle at top left,
|
|
732
|
-
border: 1px solid
|
|
730
|
+
radial-gradient(circle at top left, var(--primary-alpha-10), transparent 35%);
|
|
731
|
+
border: 1px solid rgb(var(--rgb-outline) / 0.2);
|
|
733
732
|
box-shadow: var(--shadow-subtle-glow);
|
|
734
733
|
display: grid;
|
|
735
734
|
gap: var(--spacing-5);
|
|
@@ -742,8 +741,8 @@
|
|
|
742
741
|
|
|
743
742
|
.media-tagging-audio-preview__icon {
|
|
744
743
|
align-items: center;
|
|
745
|
-
background:
|
|
746
|
-
border: 1px solid
|
|
744
|
+
background: var(--primary-alpha-08);
|
|
745
|
+
border: 1px solid var(--primary-alpha-22);
|
|
747
746
|
color: var(--color-primary-container);
|
|
748
747
|
display: flex;
|
|
749
748
|
height: 5.5rem;
|
|
@@ -763,7 +762,7 @@
|
|
|
763
762
|
}
|
|
764
763
|
|
|
765
764
|
.media-tagging-audio-preview__eyebrow {
|
|
766
|
-
color:
|
|
765
|
+
color: rgb(var(--rgb-on-surface) / 0.48);
|
|
767
766
|
font-family: var(--font-family-mono);
|
|
768
767
|
font-size: 0.625rem;
|
|
769
768
|
font-weight: 700;
|
|
@@ -789,7 +788,7 @@
|
|
|
789
788
|
.media-tagging-preview__placeholder,
|
|
790
789
|
.media-tagging-preview__empty {
|
|
791
790
|
align-items: center;
|
|
792
|
-
color:
|
|
791
|
+
color: rgb(var(--rgb-on-surface) / 0.4);
|
|
793
792
|
display: flex;
|
|
794
793
|
flex: 1;
|
|
795
794
|
flex-direction: column;
|
|
@@ -799,7 +798,7 @@
|
|
|
799
798
|
|
|
800
799
|
.media-tagging-preview__meta {
|
|
801
800
|
background: var(--color-surface-container-lowest);
|
|
802
|
-
border: 1px solid
|
|
801
|
+
border: 1px solid rgb(var(--rgb-outline) / 0.16);
|
|
803
802
|
display: flex;
|
|
804
803
|
flex-direction: column;
|
|
805
804
|
min-height: 0;
|
|
@@ -816,7 +815,7 @@
|
|
|
816
815
|
}
|
|
817
816
|
|
|
818
817
|
.media-tagging-preview__eyebrow {
|
|
819
|
-
color:
|
|
818
|
+
color: rgb(var(--rgb-on-surface) / 0.48);
|
|
820
819
|
font-family: var(--font-family-mono);
|
|
821
820
|
font-size: 0.625rem;
|
|
822
821
|
font-weight: 700;
|
|
@@ -836,7 +835,7 @@
|
|
|
836
835
|
}
|
|
837
836
|
|
|
838
837
|
.media-tagging-preview__metadata {
|
|
839
|
-
border-top: 1px solid
|
|
838
|
+
border-top: 1px solid rgb(var(--rgb-outline) / 0.14);
|
|
840
839
|
display: flex;
|
|
841
840
|
flex: 1;
|
|
842
841
|
flex-direction: column;
|
|
@@ -855,7 +854,7 @@
|
|
|
855
854
|
}
|
|
856
855
|
|
|
857
856
|
.media-tagging-preview__metadata-row span:first-child {
|
|
858
|
-
color:
|
|
857
|
+
color: rgb(var(--rgb-on-surface) / 0.5);
|
|
859
858
|
font-family: var(--font-family-mono);
|
|
860
859
|
font-size: 0.65rem;
|
|
861
860
|
letter-spacing: 0.08em;
|
|
@@ -879,8 +878,8 @@
|
|
|
879
878
|
}
|
|
880
879
|
|
|
881
880
|
.media-tagging-tag-panel {
|
|
882
|
-
background: var(--color-surface-container
|
|
883
|
-
border: 1px solid
|
|
881
|
+
background: var(--color-surface-container);
|
|
882
|
+
border: 1px solid rgb(var(--rgb-outline) / 0.16);
|
|
884
883
|
display: flex;
|
|
885
884
|
flex: 1;
|
|
886
885
|
flex-direction: column;
|
|
@@ -894,7 +893,7 @@
|
|
|
894
893
|
}
|
|
895
894
|
|
|
896
895
|
.media-tagging-tag-panel__header {
|
|
897
|
-
border-bottom: 1px solid
|
|
896
|
+
border-bottom: 1px solid rgb(var(--rgb-outline) / 0.14);
|
|
898
897
|
}
|
|
899
898
|
|
|
900
899
|
.media-tagging-tag-panel__header-row {
|
|
@@ -905,7 +904,7 @@
|
|
|
905
904
|
}
|
|
906
905
|
|
|
907
906
|
.media-tagging-tag-panel__footer {
|
|
908
|
-
border-top: 1px solid
|
|
907
|
+
border-top: 1px solid rgb(var(--rgb-outline) / 0.14);
|
|
909
908
|
display: flex;
|
|
910
909
|
flex-direction: column;
|
|
911
910
|
gap: var(--spacing-3);
|
|
@@ -919,7 +918,7 @@
|
|
|
919
918
|
}
|
|
920
919
|
|
|
921
920
|
.media-tagging-tag-panel__remaining {
|
|
922
|
-
color:
|
|
921
|
+
color: var(--text-variant-subtle);
|
|
923
922
|
flex: 1 1 auto;
|
|
924
923
|
font-family: var(--font-family-mono);
|
|
925
924
|
font-size: 0.6875rem;
|
|
@@ -949,10 +948,14 @@
|
|
|
949
948
|
}
|
|
950
949
|
|
|
951
950
|
.media-tagging-created-tags-shell {
|
|
952
|
-
border-top: 1px solid
|
|
951
|
+
border-top: 1px solid rgb(var(--rgb-outline) / 0.14);
|
|
952
|
+
display: flex;
|
|
953
|
+
flex: 1;
|
|
954
|
+
flex-direction: column;
|
|
953
955
|
margin-top: var(--spacing-2);
|
|
954
|
-
|
|
956
|
+
min-height: 0;
|
|
955
957
|
overflow: hidden;
|
|
958
|
+
padding-top: var(--spacing-4);
|
|
956
959
|
}
|
|
957
960
|
|
|
958
961
|
.media-tagging-created-tags-frame {
|
|
@@ -963,8 +966,8 @@
|
|
|
963
966
|
}
|
|
964
967
|
|
|
965
968
|
.media-tagging-created-tags {
|
|
966
|
-
background: var(--color-surface-container
|
|
967
|
-
border: 1px solid
|
|
969
|
+
background: var(--color-surface-container);
|
|
970
|
+
border: 1px solid rgb(var(--rgb-outline) / 0.16);
|
|
968
971
|
display: flex;
|
|
969
972
|
flex: 1;
|
|
970
973
|
flex-direction: column;
|
|
@@ -977,8 +980,8 @@
|
|
|
977
980
|
|
|
978
981
|
.media-tagging-created-tag {
|
|
979
982
|
align-items: center;
|
|
980
|
-
background:
|
|
981
|
-
border: 1px solid
|
|
983
|
+
background: var(--primary-alpha-08);
|
|
984
|
+
border: 1px solid var(--primary-alpha-12);
|
|
982
985
|
display: flex;
|
|
983
986
|
gap: var(--spacing-3);
|
|
984
987
|
justify-content: space-between;
|
|
@@ -1007,8 +1010,8 @@
|
|
|
1007
1010
|
.media-tagging-created-tag__badge,
|
|
1008
1011
|
.media-tagging-tag-option__badge {
|
|
1009
1012
|
align-items: center;
|
|
1010
|
-
background:
|
|
1011
|
-
border: 1px solid
|
|
1013
|
+
background: var(--primary-alpha-10);
|
|
1014
|
+
border: 1px solid var(--primary-alpha-18);
|
|
1012
1015
|
color: var(--color-primary-container);
|
|
1013
1016
|
display: inline-flex;
|
|
1014
1017
|
font-family: var(--font-family-mono);
|
|
@@ -1033,8 +1036,8 @@
|
|
|
1033
1036
|
|
|
1034
1037
|
.media-tagging-tag-option {
|
|
1035
1038
|
align-items: start;
|
|
1036
|
-
background:
|
|
1037
|
-
border: 1px solid
|
|
1039
|
+
background: var(--primary-alpha-05);
|
|
1040
|
+
border: 1px solid var(--border-medium);
|
|
1038
1041
|
border-radius: 0.25rem;
|
|
1039
1042
|
cursor: pointer;
|
|
1040
1043
|
display: grid;
|
|
@@ -1049,15 +1052,20 @@
|
|
|
1049
1052
|
}
|
|
1050
1053
|
|
|
1051
1054
|
.media-tagging-tag-option:hover {
|
|
1052
|
-
border-color:
|
|
1053
|
-
background:
|
|
1055
|
+
border-color: rgb(var(--rgb-primary) / 0.28);
|
|
1056
|
+
background: var(--primary-alpha-08);
|
|
1054
1057
|
transform: translateY(-1px);
|
|
1055
1058
|
}
|
|
1056
1059
|
|
|
1060
|
+
.media-tagging-tag-option:focus-within {
|
|
1061
|
+
border-color: var(--primary-alpha-35);
|
|
1062
|
+
box-shadow: var(--focus-ring-inset);
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1057
1065
|
.media-tagging-tag-option.is-selected {
|
|
1058
|
-
background:
|
|
1059
|
-
border-color:
|
|
1060
|
-
box-shadow: inset 0 0 0 1px
|
|
1066
|
+
background: rgb(var(--rgb-primary) / 0.09);
|
|
1067
|
+
border-color: rgb(var(--rgb-primary) / 0.34);
|
|
1068
|
+
box-shadow: inset 0 0 0 1px rgb(var(--rgb-primary) / 0.14);
|
|
1061
1069
|
}
|
|
1062
1070
|
|
|
1063
1071
|
.media-tagging-tag-option__checkbox {
|
|
@@ -1097,8 +1105,8 @@
|
|
|
1097
1105
|
}
|
|
1098
1106
|
|
|
1099
1107
|
.media-tagging-issue {
|
|
1100
|
-
background:
|
|
1101
|
-
border: 1px solid
|
|
1108
|
+
background: var(--primary-alpha-06);
|
|
1109
|
+
border: 1px solid var(--primary-alpha-15);
|
|
1102
1110
|
color: var(--color-on-surface);
|
|
1103
1111
|
font-size: 0.875rem;
|
|
1104
1112
|
line-height: 1.65;
|
|
@@ -1130,7 +1138,7 @@
|
|
|
1130
1138
|
align-items: center;
|
|
1131
1139
|
background: transparent;
|
|
1132
1140
|
border: 0;
|
|
1133
|
-
color:
|
|
1141
|
+
color: var(--text-subtle);
|
|
1134
1142
|
cursor: pointer;
|
|
1135
1143
|
display: inline-flex;
|
|
1136
1144
|
font-size: 1rem;
|
|
@@ -1155,8 +1163,8 @@
|
|
|
1155
1163
|
}
|
|
1156
1164
|
|
|
1157
1165
|
.media-tagging-inline-warning {
|
|
1158
|
-
background:
|
|
1159
|
-
border: 1px solid
|
|
1166
|
+
background: var(--primary-alpha-08);
|
|
1167
|
+
border: 1px solid var(--primary-alpha-20);
|
|
1160
1168
|
color: var(--color-on-surface);
|
|
1161
1169
|
font-size: 0.85rem;
|
|
1162
1170
|
line-height: 1.6;
|
package/package.json
CHANGED
|
@@ -1,22 +1,35 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sqlite-hub",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.4",
|
|
4
4
|
"description": "SQLite-only local management app backend and SPA shell",
|
|
5
5
|
"main": "server/server.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"sqlite-hub": "bin/sqlite-hub.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
+
"build": "npm run build:css",
|
|
11
|
+
"build:css": "tailwindcss -c tailwind.config.cjs -i ./frontend/styles/tailwind.css -o ./frontend/styles/tailwind.generated.css --minify",
|
|
12
|
+
"audit": "npm audit --audit-level=high",
|
|
13
|
+
"test": "node --test tests/**/*.test.js",
|
|
10
14
|
"start": "node bin/sqlite-hub.js",
|
|
11
15
|
"dev": "node --watch server/server.js --port:4180",
|
|
16
|
+
"prepack": "npm run build:css",
|
|
12
17
|
"publish": "bash scripts/publish_brew.sh && sleep 10 && npm login --auth-type=web && bash scripts/publish_npm.sh"
|
|
13
18
|
},
|
|
14
19
|
"dependencies": {
|
|
15
|
-
"better-sqlite3": "^
|
|
20
|
+
"better-sqlite3": "^12.10.0",
|
|
16
21
|
"cytoscape": "^3.33.2",
|
|
17
22
|
"cytoscape-elk": "^2.3.0",
|
|
18
23
|
"echarts": "^5.6.0",
|
|
19
24
|
"elkjs": "^0.11.1",
|
|
20
|
-
"express": "^4.21.2"
|
|
25
|
+
"express": "^4.21.2",
|
|
26
|
+
"express-rate-limit": "^8.5.1",
|
|
27
|
+
"helmet": "^8.1.0",
|
|
28
|
+
"material-symbols": "^0.44.6"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@tailwindcss/container-queries": "^0.1.1",
|
|
32
|
+
"@tailwindcss/forms": "^0.5.10",
|
|
33
|
+
"tailwindcss": "^3.4.17"
|
|
21
34
|
}
|
|
22
35
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
const fs = require("node:fs");
|
|
2
1
|
const express = require("express");
|
|
3
|
-
const {
|
|
2
|
+
const { route, successResponse } = require("../utils/errors");
|
|
4
3
|
|
|
5
4
|
function createMediaTaggingRouter({ mediaTaggingService }) {
|
|
6
5
|
const router = express.Router();
|
|
@@ -147,14 +146,7 @@ function createMediaTaggingRouter({ mediaTaggingService }) {
|
|
|
147
146
|
router.get(
|
|
148
147
|
"/media-file",
|
|
149
148
|
route((req, res) => {
|
|
150
|
-
|
|
151
|
-
const resolvedPath = mediaTaggingService.resolveMediaFilePath(rawPath);
|
|
152
|
-
|
|
153
|
-
if (!resolvedPath || !fs.existsSync(resolvedPath)) {
|
|
154
|
-
throw new NotFoundError(`Media file not found: ${rawPath}`);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
res.sendFile(resolvedPath);
|
|
149
|
+
mediaTaggingService.sendMediaFile(req.query.path, res);
|
|
158
150
|
})
|
|
159
151
|
);
|
|
160
152
|
|
package/server/routes/sql.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const express = require("express");
|
|
2
|
-
const { route, successResponse } = require("../utils/errors");
|
|
2
|
+
const { DatabaseRequiredError, route, successResponse } = require("../utils/errors");
|
|
3
3
|
|
|
4
4
|
function parseBooleanFlag(value) {
|
|
5
5
|
if (typeof value === "boolean") {
|
|
@@ -37,6 +37,16 @@ function getActiveDatabaseKey(connectionManager) {
|
|
|
37
37
|
return connectionManager.getActiveConnection()?.id ?? null;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
function requireActiveDatabaseKey(connectionManager) {
|
|
41
|
+
const databaseKey = getActiveDatabaseKey(connectionManager);
|
|
42
|
+
|
|
43
|
+
if (!databaseKey) {
|
|
44
|
+
throw new DatabaseRequiredError();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return databaseKey;
|
|
48
|
+
}
|
|
49
|
+
|
|
40
50
|
function createSqlRouter({ appStateStore, connectionManager, sqlExecutor }) {
|
|
41
51
|
const router = express.Router();
|
|
42
52
|
|
|
@@ -57,7 +67,7 @@ function createSqlRouter({ appStateStore, connectionManager, sqlExecutor }) {
|
|
|
57
67
|
router.get(
|
|
58
68
|
"/history",
|
|
59
69
|
route((req, res) => {
|
|
60
|
-
const databaseKey =
|
|
70
|
+
const databaseKey = requireActiveDatabaseKey(connectionManager);
|
|
61
71
|
const tab = String(req.query.tab ?? "recent").trim().toLowerCase();
|
|
62
72
|
const options = {
|
|
63
73
|
databaseKey,
|
|
@@ -92,7 +102,7 @@ function createSqlRouter({ appStateStore, connectionManager, sqlExecutor }) {
|
|
|
92
102
|
router.delete(
|
|
93
103
|
"/history",
|
|
94
104
|
route((req, res) => {
|
|
95
|
-
const databaseKey =
|
|
105
|
+
const databaseKey = requireActiveDatabaseKey(connectionManager);
|
|
96
106
|
const deletedCount = appStateStore.clearQueryHistoryForDatabase(databaseKey);
|
|
97
107
|
|
|
98
108
|
res.json(
|
|
@@ -114,12 +124,15 @@ function createSqlRouter({ appStateStore, connectionManager, sqlExecutor }) {
|
|
|
114
124
|
router.get(
|
|
115
125
|
"/history/:historyId/runs",
|
|
116
126
|
route((req, res) => {
|
|
127
|
+
const databaseKey = requireActiveDatabaseKey(connectionManager);
|
|
117
128
|
res.json(
|
|
118
129
|
successResponse({
|
|
119
130
|
data: appStateStore.getQueryRunsByHistoryId(
|
|
120
131
|
req.params.historyId,
|
|
121
|
-
parseListLimit(req.query.limit, 8, 50)
|
|
132
|
+
parseListLimit(req.query.limit, 8, 50),
|
|
133
|
+
databaseKey
|
|
122
134
|
),
|
|
135
|
+
metadata: { databaseKey },
|
|
123
136
|
})
|
|
124
137
|
);
|
|
125
138
|
})
|
|
@@ -129,10 +142,12 @@ function createSqlRouter({ appStateStore, connectionManager, sqlExecutor }) {
|
|
|
129
142
|
"/history/:historyId/favorite",
|
|
130
143
|
route((req, res) => {
|
|
131
144
|
const nextValue = parseBooleanFlag(req.body?.value);
|
|
145
|
+
const databaseKey = requireActiveDatabaseKey(connectionManager);
|
|
132
146
|
res.json(
|
|
133
147
|
successResponse({
|
|
134
148
|
message: nextValue ? "Query favorited." : "Query removed from favorites.",
|
|
135
|
-
data: appStateStore.toggleFavorite(req.params.historyId, nextValue),
|
|
149
|
+
data: appStateStore.toggleFavorite(req.params.historyId, nextValue, databaseKey),
|
|
150
|
+
metadata: { databaseKey },
|
|
136
151
|
})
|
|
137
152
|
);
|
|
138
153
|
})
|
|
@@ -142,10 +157,12 @@ function createSqlRouter({ appStateStore, connectionManager, sqlExecutor }) {
|
|
|
142
157
|
"/history/:historyId/saved",
|
|
143
158
|
route((req, res) => {
|
|
144
159
|
const nextValue = parseBooleanFlag(req.body?.value);
|
|
160
|
+
const databaseKey = requireActiveDatabaseKey(connectionManager);
|
|
145
161
|
res.json(
|
|
146
162
|
successResponse({
|
|
147
163
|
message: nextValue ? "Query saved." : "Query removed from saved queries.",
|
|
148
|
-
data: appStateStore.toggleSaved(req.params.historyId, nextValue),
|
|
164
|
+
data: appStateStore.toggleSaved(req.params.historyId, nextValue, databaseKey),
|
|
165
|
+
metadata: { databaseKey },
|
|
149
166
|
})
|
|
150
167
|
);
|
|
151
168
|
})
|
|
@@ -154,10 +171,12 @@ function createSqlRouter({ appStateStore, connectionManager, sqlExecutor }) {
|
|
|
154
171
|
router.patch(
|
|
155
172
|
"/history/:historyId/title",
|
|
156
173
|
route((req, res) => {
|
|
174
|
+
const databaseKey = requireActiveDatabaseKey(connectionManager);
|
|
157
175
|
res.json(
|
|
158
176
|
successResponse({
|
|
159
177
|
message: "Query title updated.",
|
|
160
|
-
data: appStateStore.renameQuery(req.params.historyId, req.body?.title),
|
|
178
|
+
data: appStateStore.renameQuery(req.params.historyId, req.body?.title, databaseKey),
|
|
179
|
+
metadata: { databaseKey },
|
|
161
180
|
})
|
|
162
181
|
);
|
|
163
182
|
})
|
|
@@ -166,10 +185,12 @@ function createSqlRouter({ appStateStore, connectionManager, sqlExecutor }) {
|
|
|
166
185
|
router.patch(
|
|
167
186
|
"/history/:historyId/notes",
|
|
168
187
|
route((req, res) => {
|
|
188
|
+
const databaseKey = requireActiveDatabaseKey(connectionManager);
|
|
169
189
|
res.json(
|
|
170
190
|
successResponse({
|
|
171
191
|
message: "Query notes updated.",
|
|
172
|
-
data: appStateStore.updateQueryNotes(req.params.historyId, req.body?.notes),
|
|
192
|
+
data: appStateStore.updateQueryNotes(req.params.historyId, req.body?.notes, databaseKey),
|
|
193
|
+
metadata: { databaseKey },
|
|
173
194
|
})
|
|
174
195
|
);
|
|
175
196
|
})
|
|
@@ -178,13 +199,15 @@ function createSqlRouter({ appStateStore, connectionManager, sqlExecutor }) {
|
|
|
178
199
|
router.delete(
|
|
179
200
|
"/history/:historyId",
|
|
180
201
|
route((req, res) => {
|
|
181
|
-
|
|
202
|
+
const databaseKey = requireActiveDatabaseKey(connectionManager);
|
|
203
|
+
appStateStore.deleteQueryHistoryItem(req.params.historyId, databaseKey);
|
|
182
204
|
res.json(
|
|
183
205
|
successResponse({
|
|
184
206
|
message: "Query history item deleted.",
|
|
185
207
|
data: {
|
|
186
208
|
id: Number(req.params.historyId),
|
|
187
209
|
},
|
|
210
|
+
metadata: { databaseKey },
|
|
188
211
|
})
|
|
189
212
|
);
|
|
190
213
|
})
|
|
@@ -193,9 +216,11 @@ function createSqlRouter({ appStateStore, connectionManager, sqlExecutor }) {
|
|
|
193
216
|
router.get(
|
|
194
217
|
"/history/:historyId",
|
|
195
218
|
route((req, res) => {
|
|
219
|
+
const databaseKey = requireActiveDatabaseKey(connectionManager);
|
|
196
220
|
res.json(
|
|
197
221
|
successResponse({
|
|
198
|
-
data: appStateStore.getQueryHistoryItemById(req.params.historyId),
|
|
222
|
+
data: appStateStore.getQueryHistoryItemById(req.params.historyId, databaseKey),
|
|
223
|
+
metadata: { databaseKey },
|
|
199
224
|
})
|
|
200
225
|
);
|
|
201
226
|
})
|