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
|
@@ -180,52 +180,39 @@ function renderParentTagFields({
|
|
|
180
180
|
|
|
181
181
|
const isCreatingParentTag = Boolean(parentToggleColumn ? tagFormValues[parentToggleColumn.name] : false);
|
|
182
182
|
const selectedParentTagId = parentSelectColumn ? String(tagFormValues[parentSelectColumn.name] ?? '') : '';
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
data-field="${escapeHtml(parentToggleColumn.name)}"
|
|
217
|
-
type="checkbox"
|
|
218
|
-
${isCreatingParentTag ? 'checked' : ''}
|
|
219
|
-
/>
|
|
220
|
-
<span class="media-tagging-field__meta">
|
|
221
|
-
<span class="media-tagging-field__label">Create Parent Tag</span>
|
|
222
|
-
</span>
|
|
223
|
-
</label>
|
|
224
|
-
</div>
|
|
225
|
-
`
|
|
226
|
-
: ''
|
|
227
|
-
}
|
|
228
|
-
`;
|
|
183
|
+
const parentSelectMarkup = parentSelectColumn
|
|
184
|
+
? [
|
|
185
|
+
'<label class="media-tagging-field"><span class="media-tagging-field__label">Parent Tag</span>',
|
|
186
|
+
'<select 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" data-bind="media-tagging-tag-form-field" data-field="',
|
|
187
|
+
escapeHtml(parentSelectColumn.name),
|
|
188
|
+
'" ',
|
|
189
|
+
isCreatingParentTag ? 'disabled' : '',
|
|
190
|
+
'>',
|
|
191
|
+
renderOptionList(
|
|
192
|
+
parentTagOptions.map(tag => ({
|
|
193
|
+
value: String(tag.identityValue ?? ''),
|
|
194
|
+
label: tag.label,
|
|
195
|
+
})),
|
|
196
|
+
selectedParentTagId,
|
|
197
|
+
parentTagOptions.length ? 'Select a parent tag' : 'No parent tags available',
|
|
198
|
+
),
|
|
199
|
+
'</select></label>',
|
|
200
|
+
].join('')
|
|
201
|
+
: '';
|
|
202
|
+
const parentToggleMarkup = parentToggleColumn
|
|
203
|
+
? [
|
|
204
|
+
'<div class="media-tagging-field"><label class="standard-checkbox table-designer-check table-designer-checkbox-override">',
|
|
205
|
+
'<input data-bind="media-tagging-tag-form-field" data-field="',
|
|
206
|
+
escapeHtml(parentToggleColumn.name),
|
|
207
|
+
'" type="checkbox" ',
|
|
208
|
+
isCreatingParentTag ? 'checked' : '',
|
|
209
|
+
'/>',
|
|
210
|
+
'<span class="media-tagging-field__meta"><span class="media-tagging-field__label">Create Parent Tag</span></span>',
|
|
211
|
+
'</label></div>',
|
|
212
|
+
].join('')
|
|
213
|
+
: '';
|
|
214
|
+
|
|
215
|
+
return [parentSelectMarkup, parentToggleMarkup].join('');
|
|
229
216
|
}
|
|
230
217
|
|
|
231
218
|
function renderTagList(tags = [], selectedTagKeys = []) {
|
|
@@ -285,42 +272,40 @@ function renderCreatedTagList(tags = [], { canRemove = false, removingTagKey = n
|
|
|
285
272
|
return `
|
|
286
273
|
<div class="media-tagging-created-tags custom-scrollbar">
|
|
287
274
|
${tags
|
|
288
|
-
.map(
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
`,
|
|
323
|
-
)
|
|
275
|
+
.map(tag => {
|
|
276
|
+
const parentMetaMarkup =
|
|
277
|
+
tag.isParentTag || tag.parentTagLabel
|
|
278
|
+
? [
|
|
279
|
+
'<div class="media-tagging-created-tag__meta">',
|
|
280
|
+
tag.isParentTag ? '<span class="media-tagging-created-tag__badge">Parent</span>' : '',
|
|
281
|
+
tag.parentTagLabel
|
|
282
|
+
? [
|
|
283
|
+
'<span class="media-tagging-created-tag__badge">',
|
|
284
|
+
escapeHtml(tag.parentTagLabel),
|
|
285
|
+
'</span>',
|
|
286
|
+
].join('')
|
|
287
|
+
: '',
|
|
288
|
+
'</div>',
|
|
289
|
+
].join('')
|
|
290
|
+
: '';
|
|
291
|
+
|
|
292
|
+
return [
|
|
293
|
+
'<article class="media-tagging-created-tag">',
|
|
294
|
+
'<div class="media-tagging-created-tag__content">',
|
|
295
|
+
'<div class="media-tagging-created-tag__label">',
|
|
296
|
+
escapeHtml(tag.label),
|
|
297
|
+
'</div>',
|
|
298
|
+
parentMetaMarkup,
|
|
299
|
+
'</div>',
|
|
300
|
+
'<button aria-label="Remove tag" class="delete-button media-tagging-created-tag__remove" data-action="remove-media-tag" data-tag-key="',
|
|
301
|
+
escapeHtml(tag.key),
|
|
302
|
+
'" type="button" title="Remove tag" ',
|
|
303
|
+
canRemove && removingTagKey !== tag.key ? '' : 'disabled',
|
|
304
|
+
'><span class="material-symbols-outlined text-[18px]">',
|
|
305
|
+
removingTagKey === tag.key ? 'hourglass_top' : 'delete',
|
|
306
|
+
'</span></button></article>',
|
|
307
|
+
].join('');
|
|
308
|
+
})
|
|
324
309
|
.join('')}
|
|
325
310
|
</div>
|
|
326
311
|
`;
|
|
@@ -633,89 +618,71 @@ function renderTaggingSection(state) {
|
|
|
633
618
|
const mediaTableColumns = state.mediaTagging.mediaTableColumns ?? [];
|
|
634
619
|
const pathCandidates = state.mediaTagging.pathCandidates ?? [];
|
|
635
620
|
const booleanCandidates = state.mediaTagging.booleanCandidates ?? [];
|
|
621
|
+
const tableOptions = renderOptionList(
|
|
622
|
+
tables
|
|
623
|
+
.map(table => ({ value: table.name, label: table.name }))
|
|
624
|
+
.sort((a, b) => a.label.localeCompare(b.label)),
|
|
625
|
+
draft.mediaTable,
|
|
626
|
+
'Select a media table',
|
|
627
|
+
);
|
|
628
|
+
const pathOptions = renderOptionList(
|
|
629
|
+
mediaTableColumns
|
|
630
|
+
.map(column => ({
|
|
631
|
+
value: column.name,
|
|
632
|
+
label: pathCandidates.includes(column.name)
|
|
633
|
+
? [column.name, ' // suggested'].join('')
|
|
634
|
+
: column.name,
|
|
635
|
+
}))
|
|
636
|
+
.sort((a, b) => a.label.localeCompare(b.label)),
|
|
637
|
+
draft.pathColumn,
|
|
638
|
+
'Select the media path column',
|
|
639
|
+
);
|
|
640
|
+
const taggedOptions = renderOptionList(
|
|
641
|
+
mediaTableColumns
|
|
642
|
+
.map(column => ({
|
|
643
|
+
value: column.name,
|
|
644
|
+
label: booleanCandidates.includes(column.name)
|
|
645
|
+
? [column.name, ' // suggested'].join('')
|
|
646
|
+
: column.name,
|
|
647
|
+
}))
|
|
648
|
+
.sort((a, b) => a.label.localeCompare(b.label)),
|
|
649
|
+
draft.taggedColumn,
|
|
650
|
+
mediaTableColumns.length ? 'Select the tagged flag column' : 'No columns available',
|
|
651
|
+
);
|
|
636
652
|
|
|
637
|
-
return
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
<div>
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
<
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
draft.pathColumn,
|
|
671
|
-
'Select the media path column',
|
|
672
|
-
)}
|
|
673
|
-
</select>
|
|
674
|
-
</label>
|
|
675
|
-
<label class="media-tagging-field">
|
|
676
|
-
<span class="media-tagging-field__label">Tagged Boolean Column</span>
|
|
677
|
-
<select 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" data-bind="media-tagging-field" data-field="taggedColumn">
|
|
678
|
-
${renderOptionList(
|
|
679
|
-
mediaTableColumns
|
|
680
|
-
.map(column => ({
|
|
681
|
-
value: column.name,
|
|
682
|
-
label: booleanCandidates.includes(column.name) ? `${column.name} // suggested` : column.name,
|
|
683
|
-
}))
|
|
684
|
-
.sort((a, b) => a.label.localeCompare(b.label)),
|
|
685
|
-
draft.taggedColumn,
|
|
686
|
-
mediaTableColumns.length ? 'Select the tagged flag column' : 'No columns available',
|
|
687
|
-
)}
|
|
688
|
-
</select>
|
|
689
|
-
</label>
|
|
690
|
-
</div>
|
|
691
|
-
|
|
692
|
-
<div class="media-tagging-query-grid">
|
|
693
|
-
${renderSqlTextarea({
|
|
694
|
-
label: 'Untagged Query',
|
|
695
|
-
value: draft.untaggedQuery,
|
|
696
|
-
dataBind: 'media-tagging-field',
|
|
697
|
-
dataField: 'untaggedQuery',
|
|
698
|
-
})}
|
|
699
|
-
${renderSqlTextarea({
|
|
700
|
-
label: 'Tagged Query',
|
|
701
|
-
value: draft.taggedQuery,
|
|
702
|
-
dataBind: 'media-tagging-field',
|
|
703
|
-
dataField: 'taggedQuery',
|
|
704
|
-
})}
|
|
705
|
-
</div>
|
|
706
|
-
|
|
707
|
-
<div class="flex flex-wrap items-center gap-3">
|
|
708
|
-
<button
|
|
709
|
-
class="standard-button"
|
|
710
|
-
data-action="reset-media-tagging-queries"
|
|
711
|
-
type="button"
|
|
712
|
-
>
|
|
713
|
-
Reset Queries to Defaults
|
|
714
|
-
</button>
|
|
715
|
-
</div>
|
|
716
|
-
</div>
|
|
717
|
-
</section>
|
|
718
|
-
`;
|
|
653
|
+
return [
|
|
654
|
+
'<section class="media-tagging-card media-tagging-card--tagging shell-section">',
|
|
655
|
+
'<div class="media-tagging-card__header"><div><div class="media-tagging-card__eyebrow">2. Tagging</div><h2 class="media-tagging-card__title">Media Source</h2></div></div>',
|
|
656
|
+
'<div class="media-tagging-card__body"><div class="media-tagging-form-grid">',
|
|
657
|
+
'<label class="media-tagging-field"><span class="media-tagging-field__label">Media Table</span>',
|
|
658
|
+
'<select 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" data-bind="media-tagging-field" data-field="mediaTable">',
|
|
659
|
+
tableOptions,
|
|
660
|
+
'</select></label><div></div>',
|
|
661
|
+
'<label class="media-tagging-field"><span class="media-tagging-field__label">Path Column</span>',
|
|
662
|
+
'<select 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" data-bind="media-tagging-field" data-field="pathColumn">',
|
|
663
|
+
pathOptions,
|
|
664
|
+
'</select></label>',
|
|
665
|
+
'<label class="media-tagging-field"><span class="media-tagging-field__label">Tagged Boolean Column</span>',
|
|
666
|
+
'<select 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" data-bind="media-tagging-field" data-field="taggedColumn">',
|
|
667
|
+
taggedOptions,
|
|
668
|
+
'</select></label></div>',
|
|
669
|
+
'<div class="media-tagging-query-grid">',
|
|
670
|
+
renderSqlTextarea({
|
|
671
|
+
label: 'Untagged Query',
|
|
672
|
+
value: draft.untaggedQuery,
|
|
673
|
+
dataBind: 'media-tagging-field',
|
|
674
|
+
dataField: 'untaggedQuery',
|
|
675
|
+
}),
|
|
676
|
+
renderSqlTextarea({
|
|
677
|
+
label: 'Tagged Query',
|
|
678
|
+
value: draft.taggedQuery,
|
|
679
|
+
dataBind: 'media-tagging-field',
|
|
680
|
+
dataField: 'taggedQuery',
|
|
681
|
+
}),
|
|
682
|
+
'</div>',
|
|
683
|
+
'<div class="flex flex-wrap items-center gap-3"><button class="standard-button" data-action="reset-media-tagging-queries" type="button">Reset Queries to Defaults</button></div>',
|
|
684
|
+
'</div></section>',
|
|
685
|
+
].join('');
|
|
719
686
|
}
|
|
720
687
|
|
|
721
688
|
function renderMappingSection(state) {
|
|
@@ -3,60 +3,64 @@ import { escapeHtml, formatNumber } from "../utils/format.js";
|
|
|
3
3
|
|
|
4
4
|
function renderEntryGroup(title, entries, activeName, options = {}) {
|
|
5
5
|
const { compact = false, showMeta = true } = options;
|
|
6
|
+
const entriesMarkup = entries.length
|
|
7
|
+
? [
|
|
8
|
+
'<div class="space-y-2">',
|
|
9
|
+
entries
|
|
10
|
+
.map((entry) => {
|
|
11
|
+
const isActive = entry.name === activeName;
|
|
12
|
+
const entryAttributes =
|
|
13
|
+
entry.type === "table"
|
|
14
|
+
? [
|
|
15
|
+
'data-structure-entry-name="',
|
|
16
|
+
escapeHtml(entry.name),
|
|
17
|
+
'"',
|
|
18
|
+
].join("")
|
|
19
|
+
: "";
|
|
20
|
+
const metaMarkup = showMeta
|
|
21
|
+
? [
|
|
22
|
+
'<div class="mt-1 text-[10px] uppercase tracking-[0.16em] text-on-surface-variant/45">',
|
|
23
|
+
escapeHtml(entry.tableName || entry.type),
|
|
24
|
+
"</div>",
|
|
25
|
+
].join("")
|
|
26
|
+
: "";
|
|
27
|
+
|
|
28
|
+
return [
|
|
29
|
+
'<button class="w-full border px-3 ',
|
|
30
|
+
compact ? "py-2.5" : "py-3",
|
|
31
|
+
" text-left transition-colors ",
|
|
32
|
+
isActive
|
|
33
|
+
? "border-primary-container/30 bg-surface-container-high"
|
|
34
|
+
: "border-outline-variant/10 bg-surface-container-lowest hover:bg-surface-container-high",
|
|
35
|
+
'" data-action="select-structure-entry" data-entry-name="',
|
|
36
|
+
escapeHtml(entry.name),
|
|
37
|
+
'" ',
|
|
38
|
+
entryAttributes,
|
|
39
|
+
' type="button">',
|
|
40
|
+
'<div class="font-mono text-xs ',
|
|
41
|
+
isActive ? "text-primary-container" : "text-on-surface",
|
|
42
|
+
'">',
|
|
43
|
+
escapeHtml(entry.name),
|
|
44
|
+
"</div>",
|
|
45
|
+
metaMarkup,
|
|
46
|
+
"</button>",
|
|
47
|
+
].join("");
|
|
48
|
+
})
|
|
49
|
+
.join(""),
|
|
50
|
+
"</div>",
|
|
51
|
+
].join("")
|
|
52
|
+
: [
|
|
53
|
+
'<div class="text-sm text-on-surface-variant/45">No ',
|
|
54
|
+
escapeHtml(title.toLowerCase()),
|
|
55
|
+
" found.</div>",
|
|
56
|
+
].join("");
|
|
6
57
|
|
|
7
58
|
return `
|
|
8
59
|
<section class="structure-sidebar__section">
|
|
9
60
|
<div class="mb-4 shrink-0 text-[10px] font-bold uppercase tracking-[0.25em] text-primary-container">
|
|
10
61
|
${escapeHtml(title)}
|
|
11
62
|
</div>
|
|
12
|
-
${
|
|
13
|
-
entries.length
|
|
14
|
-
? `
|
|
15
|
-
<div class="space-y-2">
|
|
16
|
-
${entries
|
|
17
|
-
.map(
|
|
18
|
-
(entry) => `
|
|
19
|
-
<button
|
|
20
|
-
class="w-full border px-3 ${compact ? "py-2.5" : "py-3"} text-left transition-colors ${
|
|
21
|
-
entry.name === activeName
|
|
22
|
-
? "border-primary-container/30 bg-surface-container-high"
|
|
23
|
-
: "border-outline-variant/10 bg-surface-container-lowest hover:bg-surface-container-high"
|
|
24
|
-
}"
|
|
25
|
-
data-action="select-structure-entry"
|
|
26
|
-
data-entry-name="${escapeHtml(entry.name)}"
|
|
27
|
-
${
|
|
28
|
-
entry.type === "table"
|
|
29
|
-
? `data-structure-entry-name="${escapeHtml(entry.name)}"`
|
|
30
|
-
: ""
|
|
31
|
-
}
|
|
32
|
-
type="button"
|
|
33
|
-
>
|
|
34
|
-
<div class="font-mono text-xs ${
|
|
35
|
-
entry.name === activeName
|
|
36
|
-
? "text-primary-container"
|
|
37
|
-
: "text-on-surface"
|
|
38
|
-
}">
|
|
39
|
-
${escapeHtml(entry.name)}
|
|
40
|
-
</div>
|
|
41
|
-
${
|
|
42
|
-
showMeta
|
|
43
|
-
? `
|
|
44
|
-
<div class="mt-1 text-[10px] uppercase tracking-[0.16em] text-on-surface-variant/45">
|
|
45
|
-
${escapeHtml(entry.tableName || entry.type)}
|
|
46
|
-
</div>
|
|
47
|
-
`
|
|
48
|
-
: ""
|
|
49
|
-
}
|
|
50
|
-
</button>
|
|
51
|
-
`
|
|
52
|
-
)
|
|
53
|
-
.join("")}
|
|
54
|
-
</div>
|
|
55
|
-
`
|
|
56
|
-
: `<div class="text-sm text-on-surface-variant/45">No ${escapeHtml(
|
|
57
|
-
title.toLowerCase()
|
|
58
|
-
)} found.</div>`
|
|
59
|
-
}
|
|
63
|
+
${entriesMarkup}
|
|
60
64
|
</section>
|
|
61
65
|
`;
|
|
62
66
|
}
|
package/frontend/styles/base.css
CHANGED
|
@@ -28,6 +28,26 @@ button {
|
|
|
28
28
|
cursor: pointer;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
button:disabled,
|
|
32
|
+
button[aria-disabled='true'],
|
|
33
|
+
input:disabled,
|
|
34
|
+
select:disabled,
|
|
35
|
+
textarea:disabled {
|
|
36
|
+
cursor: not-allowed;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
:where(a, button, input, select, textarea, [tabindex]:not([tabindex='-1'])):focus-visible {
|
|
40
|
+
outline: 2px solid var(--color-primary-container);
|
|
41
|
+
outline-offset: 2px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.control-shell:focus-within,
|
|
45
|
+
.standard-checkbox:focus-within,
|
|
46
|
+
.sql-highlight-shell:focus-within {
|
|
47
|
+
border-color: var(--primary-alpha-35);
|
|
48
|
+
box-shadow: var(--focus-ring-inset);
|
|
49
|
+
}
|
|
50
|
+
|
|
31
51
|
.control-button {
|
|
32
52
|
align-items: center;
|
|
33
53
|
box-sizing: border-box;
|
|
@@ -52,22 +72,22 @@ button {
|
|
|
52
72
|
|
|
53
73
|
input.control-input--ghost {
|
|
54
74
|
appearance: none;
|
|
55
|
-
background: transparent
|
|
56
|
-
background-color: transparent
|
|
57
|
-
background-image: none
|
|
58
|
-
border: 0
|
|
59
|
-
border-color: transparent
|
|
60
|
-
box-shadow: none
|
|
75
|
+
background: transparent;
|
|
76
|
+
background-color: transparent;
|
|
77
|
+
background-image: none;
|
|
78
|
+
border: 0;
|
|
79
|
+
border-color: transparent;
|
|
80
|
+
box-shadow: none;
|
|
61
81
|
-webkit-appearance: none;
|
|
62
82
|
padding: 0;
|
|
63
83
|
}
|
|
64
84
|
|
|
65
85
|
input.control-input--ghost:focus,
|
|
66
86
|
input.control-input--ghost:not(:focus) {
|
|
67
|
-
background: transparent
|
|
68
|
-
background-color: transparent
|
|
69
|
-
border-color: transparent
|
|
70
|
-
box-shadow: none
|
|
87
|
+
background: transparent;
|
|
88
|
+
background-color: transparent;
|
|
89
|
+
border-color: transparent;
|
|
90
|
+
box-shadow: none;
|
|
71
91
|
outline: none;
|
|
72
92
|
}
|
|
73
93
|
|
|
@@ -105,6 +125,7 @@ img {
|
|
|
105
125
|
}
|
|
106
126
|
|
|
107
127
|
[hidden] {
|
|
128
|
+
/* Hidden must beat utility display classes generated by Tailwind CDN. */
|
|
108
129
|
display: none !important;
|
|
109
130
|
}
|
|
110
131
|
|
|
@@ -118,9 +139,6 @@ img {
|
|
|
118
139
|
|
|
119
140
|
.font-mono {
|
|
120
141
|
font-family: var(--font-family-mono);
|
|
121
|
-
overflow: hidden;
|
|
122
|
-
white-space: nowrap;
|
|
123
|
-
text-overflow: ellipsis;
|
|
124
142
|
}
|
|
125
143
|
|
|
126
144
|
.material-symbols-outlined {
|
|
@@ -218,12 +236,14 @@ img {
|
|
|
218
236
|
width: 100%;
|
|
219
237
|
word-break: normal;
|
|
220
238
|
overflow-wrap: anywhere;
|
|
239
|
+
font-variant-ligatures: none;
|
|
221
240
|
}
|
|
222
241
|
|
|
223
242
|
.query-editor-highlight {
|
|
224
243
|
color: var(--color-on-surface);
|
|
225
244
|
overflow: visible;
|
|
226
245
|
pointer-events: none;
|
|
246
|
+
user-select: none;
|
|
227
247
|
}
|
|
228
248
|
|
|
229
249
|
.query-editor-input {
|
|
@@ -236,6 +256,14 @@ img {
|
|
|
236
256
|
overflow: auto;
|
|
237
257
|
}
|
|
238
258
|
|
|
259
|
+
.query-editor-input:focus-visible {
|
|
260
|
+
outline: none;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.query-editor-layer:focus-within {
|
|
264
|
+
box-shadow: inset 0 0 0 1px var(--primary-alpha-30);
|
|
265
|
+
}
|
|
266
|
+
|
|
239
267
|
.query-editor-gutter-track {
|
|
240
268
|
min-height: 100%;
|
|
241
269
|
will-change: transform;
|
|
@@ -260,3 +288,19 @@ img {
|
|
|
260
288
|
border-color: var(--color-primary-container);
|
|
261
289
|
}
|
|
262
290
|
}
|
|
291
|
+
|
|
292
|
+
@media (prefers-reduced-motion: reduce) {
|
|
293
|
+
*,
|
|
294
|
+
*::before,
|
|
295
|
+
*::after {
|
|
296
|
+
animation-duration: 0.001ms;
|
|
297
|
+
animation-iteration-count: 1;
|
|
298
|
+
scroll-behavior: auto;
|
|
299
|
+
transition-duration: 0.001ms;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.cursor-blink {
|
|
303
|
+
animation: none;
|
|
304
|
+
border-right-color: var(--color-primary-container);
|
|
305
|
+
}
|
|
306
|
+
}
|