sqlite-hub 0.9.0 → 0.9.3
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 +12 -0
- package/frontend/index.html +21 -20
- package/frontend/js/app.js +386 -8
- package/frontend/js/components/modal.js +13 -4
- package/frontend/js/components/queryHistoryDetail.js +14 -2
- package/frontend/js/components/queryHistoryPanel.js +13 -9
- package/frontend/js/components/rowEditorPanel.js +48 -25
- package/frontend/js/lib/queryChartOptions.js +5 -1
- package/frontend/js/lib/queryCharts.js +50 -2
- package/frontend/js/store.js +177 -21
- package/frontend/js/views/charts.js +103 -30
- package/frontend/js/views/data.js +31 -1
- package/frontend/js/views/editor.js +1 -0
- package/frontend/js/views/mediaTagging.js +146 -76
- package/frontend/styles/base.css +57 -13
- package/frontend/styles/components.css +172 -96
- package/frontend/styles/layout.css +1 -1
- package/frontend/styles/structure-graph.css +11 -11
- package/frontend/styles/tokens.css +47 -4
- package/frontend/styles/utilities.css +21 -0
- package/frontend/styles/views.css +218 -96
- package/package.json +1 -1
- package/server/services/storage/appStateStore.js +9 -6
- package/server/services/storage/queryHistoryChartUtils.js +19 -2
- package/server/services/storage/queryHistoryUtils.js +165 -2
- package/shortkeys.md +5 -0
|
@@ -20,10 +20,12 @@ function renderMissingDatabase() {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
function renderChartsList(state) {
|
|
23
|
-
const
|
|
23
|
+
const activeTab = ['recent', 'saved'].includes(state.charts.historyTab) ? state.charts.historyTab : 'recent';
|
|
24
|
+
const allQueries = state.charts.queries ?? [];
|
|
25
|
+
const queries = activeTab === 'saved' ? allQueries.filter(item => item.isSaved) : allQueries;
|
|
24
26
|
const selectedHistoryId = Number(state.charts.selectedHistoryId);
|
|
25
27
|
|
|
26
|
-
if (state.charts.loading && !
|
|
28
|
+
if (state.charts.loading && !allQueries.length) {
|
|
27
29
|
return `
|
|
28
30
|
<div class="flex h-full items-center justify-center px-6 text-center text-on-surface-variant/45">
|
|
29
31
|
<div>
|
|
@@ -34,7 +36,7 @@ function renderChartsList(state) {
|
|
|
34
36
|
`;
|
|
35
37
|
}
|
|
36
38
|
|
|
37
|
-
if (state.charts.error && !
|
|
39
|
+
if (state.charts.error && !allQueries.length) {
|
|
38
40
|
return `
|
|
39
41
|
<div class="p-5">
|
|
40
42
|
<div class="border border-error/30 bg-error-container/20 px-4 py-4 text-sm text-error">
|
|
@@ -45,15 +47,23 @@ function renderChartsList(state) {
|
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
if (!queries.length) {
|
|
50
|
+
const isSavedTab = activeTab === 'saved';
|
|
51
|
+
|
|
48
52
|
return `
|
|
49
53
|
<div class="flex h-full items-center justify-center px-6 text-center">
|
|
50
54
|
<div>
|
|
51
|
-
<span class="material-symbols-outlined mb-3 text-4xl text-on-surface-variant/25"
|
|
55
|
+
<span class="material-symbols-outlined mb-3 text-4xl text-on-surface-variant/25">${
|
|
56
|
+
isSavedTab ? 'bookmark' : 'query_stats'
|
|
57
|
+
}</span>
|
|
52
58
|
<p class="font-headline text-lg font-black uppercase tracking-tight text-on-surface">
|
|
53
|
-
No Chartable Queries
|
|
59
|
+
${isSavedTab ? 'No Saved Charts Queries' : 'No Chartable Queries'}
|
|
54
60
|
</p>
|
|
55
61
|
<p class="mt-2 max-w-xs text-sm leading-6 text-on-surface-variant/60">
|
|
56
|
-
|
|
62
|
+
${
|
|
63
|
+
isSavedTab
|
|
64
|
+
? 'Save chartable queries from this list or from the SQL Editor to keep them here.'
|
|
65
|
+
: 'Run SELECT queries in the SQL Editor first. They will appear here automatically.'
|
|
66
|
+
}
|
|
57
67
|
</p>
|
|
58
68
|
</div>
|
|
59
69
|
</div>
|
|
@@ -66,36 +76,67 @@ function renderChartsList(state) {
|
|
|
66
76
|
${queries
|
|
67
77
|
.map(
|
|
68
78
|
item => `
|
|
69
|
-
<
|
|
70
|
-
class="w-full border
|
|
71
|
-
selectedHistoryId === item.id
|
|
79
|
+
<article
|
|
80
|
+
class="group w-full border transition-colors ${
|
|
81
|
+
selectedHistoryId === Number(item.id)
|
|
72
82
|
? 'border-primary-container/30 bg-surface-container-high'
|
|
73
83
|
: 'border-outline-variant/10 bg-surface-container-lowest hover:bg-surface-container-high'
|
|
74
84
|
}"
|
|
75
|
-
data-
|
|
76
|
-
data-to="/charts/${encodeURIComponent(item.id)}"
|
|
77
|
-
type="button"
|
|
85
|
+
data-charts-history-item
|
|
78
86
|
>
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}"
|
|
83
|
-
|
|
87
|
+
<button
|
|
88
|
+
class="w-full px-4 py-3 text-left transition-colors group-hover:bg-surface-container-high"
|
|
89
|
+
data-action="navigate"
|
|
90
|
+
data-history-id="${escapeHtml(item.id)}"
|
|
91
|
+
data-to="/charts/${encodeURIComponent(item.id)}"
|
|
92
|
+
type="button"
|
|
93
|
+
>
|
|
94
|
+
<div class="flex items-start justify-between gap-3">
|
|
95
|
+
<div class="min-w-0 flex-1 truncate font-mono text-xs ${
|
|
96
|
+
selectedHistoryId === Number(item.id) ? 'text-primary-container' : 'text-on-surface'
|
|
97
|
+
}" data-charts-history-title>
|
|
98
|
+
${escapeHtml(item.displayTitle)}
|
|
99
|
+
</div>
|
|
100
|
+
<div class="flex shrink-0 flex-wrap justify-end gap-1">
|
|
101
|
+
<span class="inline-flex" data-charts-saved-badge ${item.isSaved ? '' : 'hidden'}>
|
|
102
|
+
${renderStatusBadge('saved', 'primary')}
|
|
103
|
+
</span>
|
|
104
|
+
${
|
|
105
|
+
item.chartTypes?.length
|
|
106
|
+
? item.chartTypes
|
|
107
|
+
.map(chartType => renderStatusBadge(getQueryChartTypeLabel(chartType), 'primary'))
|
|
108
|
+
.join('')
|
|
109
|
+
: renderStatusBadge('None', 'muted')
|
|
110
|
+
}
|
|
111
|
+
</div>
|
|
84
112
|
</div>
|
|
85
|
-
<div class="
|
|
86
|
-
${
|
|
87
|
-
item.chartTypes?.length
|
|
88
|
-
? item.chartTypes
|
|
89
|
-
.map(chartType => renderStatusBadge(getQueryChartTypeLabel(chartType), 'primary'))
|
|
90
|
-
.join('')
|
|
91
|
-
: renderStatusBadge('None', 'muted')
|
|
92
|
-
}
|
|
113
|
+
<div class="mt-1 truncate text-[10px] uppercase tracking-[0.16em] text-on-surface-variant/45">
|
|
114
|
+
${escapeHtml(item.previewSql)}
|
|
93
115
|
</div>
|
|
116
|
+
</button>
|
|
117
|
+
<div class="flex items-center justify-end border-t border-outline-variant/10 px-3 py-2 transition-colors group-hover:bg-surface-container-high">
|
|
118
|
+
<button
|
|
119
|
+
aria-label="Open chart query"
|
|
120
|
+
class="min-h-[var(--control-height)] flex-1 self-stretch"
|
|
121
|
+
data-action="navigate"
|
|
122
|
+
data-history-id="${escapeHtml(item.id)}"
|
|
123
|
+
data-to="/charts/${encodeURIComponent(item.id)}"
|
|
124
|
+
type="button"
|
|
125
|
+
></button>
|
|
126
|
+
<button
|
|
127
|
+
class="query-history-icon-button ${item.isSaved ? 'is-active' : ''}"
|
|
128
|
+
data-action="toggle-charts-query-history-saved"
|
|
129
|
+
data-history-id="${escapeHtml(item.id)}"
|
|
130
|
+
data-next-value="${item.isSaved ? 'false' : 'true'}"
|
|
131
|
+
title="${item.isSaved ? 'Remove from saved' : 'Save query'}"
|
|
132
|
+
type="button"
|
|
133
|
+
>
|
|
134
|
+
<span class="material-symbols-outlined text-[18px]">
|
|
135
|
+
${item.isSaved ? 'bookmark' : 'bookmark_add'}
|
|
136
|
+
</span>
|
|
137
|
+
</button>
|
|
94
138
|
</div>
|
|
95
|
-
|
|
96
|
-
${escapeHtml(item.previewSql)}
|
|
97
|
-
</div>
|
|
98
|
-
</button>
|
|
139
|
+
</article>
|
|
99
140
|
`,
|
|
100
141
|
)
|
|
101
142
|
.join('')}
|
|
@@ -104,6 +145,37 @@ function renderChartsList(state) {
|
|
|
104
145
|
`;
|
|
105
146
|
}
|
|
106
147
|
|
|
148
|
+
function renderChartsHistoryTabs(state) {
|
|
149
|
+
const activeTab = ['recent', 'saved'].includes(state.charts.historyTab) ? state.charts.historyTab : 'recent';
|
|
150
|
+
const savedCount = (state.charts.queries ?? []).filter(item => item.isSaved).length;
|
|
151
|
+
const tabs = [
|
|
152
|
+
{ id: 'recent', label: 'Recent', count: state.charts.queries?.length ?? 0 },
|
|
153
|
+
{ id: 'saved', label: 'Saved', count: savedCount },
|
|
154
|
+
];
|
|
155
|
+
|
|
156
|
+
return `
|
|
157
|
+
<div class="mt-4 flex items-center gap-2">
|
|
158
|
+
${tabs
|
|
159
|
+
.map(
|
|
160
|
+
tab => `
|
|
161
|
+
<button
|
|
162
|
+
class="query-history-tab ${activeTab === tab.id ? 'is-active' : ''}"
|
|
163
|
+
data-action="set-charts-history-tab"
|
|
164
|
+
data-tab="${escapeHtml(tab.id)}"
|
|
165
|
+
type="button"
|
|
166
|
+
>
|
|
167
|
+
${escapeHtml(tab.label)}
|
|
168
|
+
</button>
|
|
169
|
+
`,
|
|
170
|
+
)
|
|
171
|
+
.join('')}
|
|
172
|
+
<span class="ml-auto text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/50">
|
|
173
|
+
<span data-charts-history-count>${escapeHtml(String(tabs.find(tab => tab.id === activeTab)?.count ?? 0))}</span>
|
|
174
|
+
</span>
|
|
175
|
+
</div>
|
|
176
|
+
`;
|
|
177
|
+
}
|
|
178
|
+
|
|
107
179
|
function renderEmptyChartDetail() {
|
|
108
180
|
return `
|
|
109
181
|
<div class="flex flex-1 items-center justify-center px-8 py-10 text-center">
|
|
@@ -347,7 +419,7 @@ function renderChartCard(chart, state, analysis) {
|
|
|
347
419
|
`;
|
|
348
420
|
}
|
|
349
421
|
|
|
350
|
-
function renderChartsDetail(state) {
|
|
422
|
+
export function renderChartsDetail(state) {
|
|
351
423
|
const detail = state.charts.detail;
|
|
352
424
|
const selectedHistoryId = state.charts.selectedHistoryId;
|
|
353
425
|
const historyVisible = state.editor.historyPanelVisible !== false;
|
|
@@ -485,6 +557,7 @@ export function renderChartsView(state) {
|
|
|
485
557
|
<h2 class="mt-2 text-[10px] font-mono uppercase tracking-[0.16em] text-on-surface-variant/55">
|
|
486
558
|
Charts
|
|
487
559
|
</h2>
|
|
560
|
+
${renderChartsHistoryTabs(state)}
|
|
488
561
|
</div>
|
|
489
562
|
${renderChartsList(state)}
|
|
490
563
|
</aside>
|
|
@@ -449,7 +449,36 @@ export function renderDataRowEditorPanel(state) {
|
|
|
449
449
|
(foreignKey.mappings ?? []).map(mapping => String(mapping.from ?? '').trim()).filter(Boolean),
|
|
450
450
|
),
|
|
451
451
|
);
|
|
452
|
-
const
|
|
452
|
+
const getColumnTypeBadge = column => String(column.declaredType || column.affinity || 'BLOB').trim().toUpperCase();
|
|
453
|
+
const getColumnNumberInputMeta = column => {
|
|
454
|
+
const affinity = String(column.affinity ?? '').toUpperCase();
|
|
455
|
+
|
|
456
|
+
if (affinity === 'INTEGER') {
|
|
457
|
+
return { inputType: 'number', numberStep: '1' };
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
if (affinity === 'NUMERIC') {
|
|
461
|
+
return { inputType: 'number', numberStep: 'any' };
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
return {};
|
|
465
|
+
};
|
|
466
|
+
const getColumnBadges = column => {
|
|
467
|
+
const badges = [{ label: getColumnTypeBadge(column), tone: 'type' }];
|
|
468
|
+
|
|
469
|
+
if (column.primaryKeyPosition > 0) {
|
|
470
|
+
badges.push({
|
|
471
|
+
label: column.primaryKeyPosition > 1 ? `PK ${column.primaryKeyPosition}` : 'PK',
|
|
472
|
+
tone: 'primary-key',
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (foreignKeyColumnNames.has(column.name)) {
|
|
477
|
+
badges.push({ label: 'FK', tone: 'foreign-key' });
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
return badges;
|
|
481
|
+
};
|
|
453
482
|
|
|
454
483
|
const identityColumns = table.identityStrategy?.type === 'primaryKey' ? (table.identityStrategy.columns ?? []) : [];
|
|
455
484
|
const editableColumns = (table.columnMeta ?? []).filter(column => {
|
|
@@ -502,6 +531,7 @@ export function renderDataRowEditorPanel(state) {
|
|
|
502
531
|
name: column.name,
|
|
503
532
|
label: column.name,
|
|
504
533
|
badges: getColumnBadges(column),
|
|
534
|
+
...getColumnNumberInputMeta(column),
|
|
505
535
|
value: value === null || value === undefined ? '' : String(value),
|
|
506
536
|
};
|
|
507
537
|
}),
|
|
@@ -265,6 +265,7 @@ export function renderEditorView(state, { isResultsRoute = false } = {}) {
|
|
|
265
265
|
error: state.editor.historyError,
|
|
266
266
|
activeTab: state.editor.historyTab,
|
|
267
267
|
search: state.editor.historySearchInput,
|
|
268
|
+
committedSearch: state.editor.historySearch,
|
|
268
269
|
total: state.editor.historyTotal,
|
|
269
270
|
hasMore: state.editor.historyHasMore,
|
|
270
271
|
activeHistoryId: state.editor.historyActiveId,
|
|
@@ -326,7 +326,10 @@ function renderCreatedTagList(tags = [], { canRemove = false, removingTagKey = n
|
|
|
326
326
|
`;
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
-
function renderPreviewMedia(
|
|
329
|
+
function renderPreviewMedia(
|
|
330
|
+
currentItem,
|
|
331
|
+
{ detailsVisible = true, mediaTableName = '', rotationDegrees = 0, status = null } = {},
|
|
332
|
+
) {
|
|
330
333
|
if (!currentItem) {
|
|
331
334
|
return `
|
|
332
335
|
<div class="media-tagging-preview__empty">
|
|
@@ -344,6 +347,15 @@ function renderPreviewMedia(currentItem, { detailsVisible = true, mediaTableName
|
|
|
344
347
|
const pathValue = String(currentItem.path ?? '');
|
|
345
348
|
const fileName = pathValue.split(/[\\/]/).filter(Boolean).pop() || 'Audio file';
|
|
346
349
|
const isAudioPreview = Boolean(currentItem.previewUrl && currentItem.previewKind === 'audio');
|
|
350
|
+
const isRotatablePreview = Boolean(
|
|
351
|
+
currentItem.previewUrl && (currentItem.previewKind === 'image' || currentItem.previewKind === 'video'),
|
|
352
|
+
);
|
|
353
|
+
const numericRotation = Number(rotationDegrees);
|
|
354
|
+
const normalizedRotation = Number.isFinite(numericRotation)
|
|
355
|
+
? ((Math.round(numericRotation / 90) * 90) % 360 + 360) % 360
|
|
356
|
+
: 0;
|
|
357
|
+
const rotationStyle = `--media-tagging-preview-rotation: ${normalizedRotation}deg;`;
|
|
358
|
+
const rotationClass = normalizedRotation === 90 || normalizedRotation === 270 ? ' is-rotated-quarter' : '';
|
|
347
359
|
let assetMarkup = `
|
|
348
360
|
<div class="media-tagging-preview__placeholder">
|
|
349
361
|
<span class="material-symbols-outlined text-5xl">perm_media</span>
|
|
@@ -353,16 +365,27 @@ function renderPreviewMedia(currentItem, { detailsVisible = true, mediaTableName
|
|
|
353
365
|
if (currentItem.previewUrl && currentItem.previewKind === 'image') {
|
|
354
366
|
assetMarkup = `
|
|
355
367
|
<img
|
|
356
|
-
class="media-tagging-preview__asset"
|
|
368
|
+
class="media-tagging-preview__asset${rotationClass}"
|
|
369
|
+
data-media-tagging-rotation-target="true"
|
|
370
|
+
data-rotation-degrees="${escapeHtml(String(normalizedRotation))}"
|
|
371
|
+
style="${escapeHtml(rotationStyle)}"
|
|
357
372
|
src="${escapeHtml(currentItem.previewUrl)}"
|
|
358
373
|
alt="${escapeHtml(pathValue || 'Current media item')}"
|
|
359
374
|
/>
|
|
360
375
|
`;
|
|
361
376
|
} else if (currentItem.previewUrl && currentItem.previewKind === 'video') {
|
|
362
377
|
assetMarkup = `
|
|
363
|
-
<video
|
|
378
|
+
<video
|
|
379
|
+
class="media-tagging-preview__asset${rotationClass}"
|
|
380
|
+
data-media-tagging-rotation-target="true"
|
|
381
|
+
data-rotation-degrees="${escapeHtml(String(normalizedRotation))}"
|
|
382
|
+
style="${escapeHtml(rotationStyle)}"
|
|
383
|
+
controls
|
|
384
|
+
preload="metadata"
|
|
385
|
+
src="${escapeHtml(
|
|
364
386
|
currentItem.previewUrl,
|
|
365
|
-
)}"
|
|
387
|
+
)}"
|
|
388
|
+
></video>
|
|
366
389
|
`;
|
|
367
390
|
} else if (currentItem.previewUrl && currentItem.previewKind === 'audio') {
|
|
368
391
|
assetMarkup = `
|
|
@@ -395,36 +418,100 @@ function renderPreviewMedia(currentItem, { detailsVisible = true, mediaTableName
|
|
|
395
418
|
<div class="media-tagging-preview ${detailsVisible ? '' : 'media-tagging-preview--meta-hidden'}">
|
|
396
419
|
<div class="media-tagging-preview__media${isAudioPreview ? ' media-tagging-preview__media--audio' : ''}">
|
|
397
420
|
<div class="media-tagging-preview__media-toolbar">
|
|
398
|
-
<
|
|
399
|
-
class="
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
421
|
+
<div class="media-tagging-status media-tagging-status--compact media-tagging-status--preview">
|
|
422
|
+
<div class="media-tagging-status__value">${escapeHtml(status?.ratioLabel ?? '0 / 0')}</div>
|
|
423
|
+
<div class="media-tagging-status__label">
|
|
424
|
+
tagged / total
|
|
425
|
+
</div>
|
|
426
|
+
</div>
|
|
427
|
+
<div class="media-tagging-preview__toolbar-actions">
|
|
428
|
+
${
|
|
429
|
+
isRotatablePreview
|
|
430
|
+
? `
|
|
431
|
+
<div class="media-tagging-preview__rotation-controls" aria-label="Rotate media preview">
|
|
432
|
+
<button
|
|
433
|
+
class="standard-button media-tagging-preview__icon-button"
|
|
434
|
+
data-action="rotate-media-tagging-current-media"
|
|
435
|
+
data-rotation-command="left"
|
|
436
|
+
type="button"
|
|
437
|
+
aria-label="Rotate left 90 degrees"
|
|
438
|
+
title="Rotate left 90 degrees"
|
|
439
|
+
>
|
|
440
|
+
<span class="material-symbols-outlined">rotate_left</span>
|
|
441
|
+
</button>
|
|
442
|
+
<button
|
|
443
|
+
class="standard-button media-tagging-preview__icon-button"
|
|
444
|
+
data-action="rotate-media-tagging-current-media"
|
|
445
|
+
data-rotation-command="right"
|
|
446
|
+
type="button"
|
|
447
|
+
aria-label="Rotate right 90 degrees"
|
|
448
|
+
title="Rotate right 90 degrees"
|
|
449
|
+
>
|
|
450
|
+
<span class="material-symbols-outlined">rotate_right</span>
|
|
451
|
+
</button>
|
|
452
|
+
<button
|
|
453
|
+
class="standard-button media-tagging-preview__icon-button"
|
|
454
|
+
data-action="rotate-media-tagging-current-media"
|
|
455
|
+
data-rotation-command="reset"
|
|
456
|
+
type="button"
|
|
457
|
+
aria-label="Reset rotation"
|
|
458
|
+
title="Reset rotation"
|
|
459
|
+
${normalizedRotation === 0 ? 'disabled' : ''}
|
|
460
|
+
>
|
|
461
|
+
<span class="material-symbols-outlined">restart_alt</span>
|
|
462
|
+
</button>
|
|
463
|
+
</div>
|
|
464
|
+
`
|
|
465
|
+
: ''
|
|
466
|
+
}
|
|
467
|
+
<button
|
|
468
|
+
class="standard-button media-tagging-preview__toggle"
|
|
469
|
+
data-action="toggle-media-tagging-current-media"
|
|
470
|
+
data-next-value="${detailsVisible ? 'false' : 'true'}"
|
|
471
|
+
data-expanded-label="Shrink Media Viewer"
|
|
472
|
+
data-collapsed-label="Show Media Viewer"
|
|
473
|
+
aria-expanded="${detailsVisible ? 'true' : 'false'}"
|
|
474
|
+
type="button"
|
|
475
|
+
>
|
|
476
|
+
${toggleLabel}
|
|
477
|
+
</button>
|
|
478
|
+
</div>
|
|
479
|
+
</div>
|
|
480
|
+
<div class="media-tagging-preview__asset-shell">
|
|
481
|
+
${assetMarkup}
|
|
409
482
|
</div>
|
|
410
|
-
${assetMarkup}
|
|
411
483
|
</div>
|
|
412
484
|
<div class="media-tagging-preview__meta">
|
|
413
485
|
<div class="media-tagging-preview__meta-header">
|
|
414
486
|
<div class="media-tagging-preview__eyebrow">Current Media</div>
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
487
|
+
<div class="media-tagging-preview__meta-actions">
|
|
488
|
+
${
|
|
489
|
+
mediaTableName
|
|
490
|
+
? `
|
|
491
|
+
<button
|
|
492
|
+
class="standard-button media-tagging-preview__meta-action"
|
|
493
|
+
data-action="open-media-tagging-current-in-structure"
|
|
494
|
+
type="button"
|
|
495
|
+
>
|
|
496
|
+
Open Structure
|
|
497
|
+
</button>
|
|
498
|
+
`
|
|
499
|
+
: ''
|
|
500
|
+
}
|
|
501
|
+
${
|
|
502
|
+
mediaTableName && currentItem.identity
|
|
503
|
+
? `
|
|
504
|
+
<button
|
|
505
|
+
class="standard-button media-tagging-preview__meta-action"
|
|
506
|
+
data-action="open-media-tagging-current-in-data"
|
|
507
|
+
type="button"
|
|
508
|
+
>
|
|
509
|
+
Open In Data
|
|
510
|
+
</button>
|
|
511
|
+
`
|
|
512
|
+
: ''
|
|
513
|
+
}
|
|
514
|
+
</div>
|
|
428
515
|
</div>
|
|
429
516
|
${
|
|
430
517
|
metadata.length
|
|
@@ -548,7 +635,7 @@ function renderTaggingSection(state) {
|
|
|
548
635
|
const booleanCandidates = state.mediaTagging.booleanCandidates ?? [];
|
|
549
636
|
|
|
550
637
|
return `
|
|
551
|
-
<section class="media-tagging-card shell-section">
|
|
638
|
+
<section class="media-tagging-card media-tagging-card--tagging shell-section">
|
|
552
639
|
<div class="media-tagging-card__header">
|
|
553
640
|
<div>
|
|
554
641
|
<div class="media-tagging-card__eyebrow">2. Tagging</div>
|
|
@@ -739,7 +826,12 @@ function renderWorkflowSection(state) {
|
|
|
739
826
|
`
|
|
740
827
|
: ''
|
|
741
828
|
}
|
|
742
|
-
${renderPreviewMedia(workflow?.currentItem ?? null, {
|
|
829
|
+
${renderPreviewMedia(workflow?.currentItem ?? null, {
|
|
830
|
+
detailsVisible,
|
|
831
|
+
mediaTableName,
|
|
832
|
+
rotationDegrees: state.mediaTagging.workflowMediaRotationDegrees,
|
|
833
|
+
status,
|
|
834
|
+
})}
|
|
743
835
|
<div class="media-tagging-workflow-sidebar">
|
|
744
836
|
<div class="media-tagging-tag-panel">
|
|
745
837
|
<div class="media-tagging-tag-panel__header">
|
|
@@ -750,12 +842,6 @@ function renderWorkflowSection(state) {
|
|
|
750
842
|
${escapeHtml(formatNumber(selectableTags.length))} selectable tag(s)
|
|
751
843
|
</div>
|
|
752
844
|
</div>
|
|
753
|
-
<div class="media-tagging-status media-tagging-status--compact">
|
|
754
|
-
<div class="media-tagging-status__value">${escapeHtml(status.ratioLabel)}</div>
|
|
755
|
-
<div class="media-tagging-status__label">
|
|
756
|
-
tagged / total
|
|
757
|
-
</div>
|
|
758
|
-
</div>
|
|
759
845
|
</div>
|
|
760
846
|
<label class="media-tagging-field mt-4">
|
|
761
847
|
<span class="media-tagging-field__label">Search</span>
|
|
@@ -769,24 +855,29 @@ function renderWorkflowSection(state) {
|
|
|
769
855
|
</div>
|
|
770
856
|
${renderTagList(selectableTags, selectedTagKeys)}
|
|
771
857
|
<div class="media-tagging-tag-panel__footer">
|
|
772
|
-
<div class="
|
|
773
|
-
<
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
858
|
+
<div class="media-tagging-tag-panel__footer-main">
|
|
859
|
+
<div class="media-tagging-tag-panel__remaining">
|
|
860
|
+
${escapeHtml(formatNumber(workflow?.status?.remainingCount ?? 0))} remaining
|
|
861
|
+
</div>
|
|
862
|
+
<div class="media-tagging-tag-panel__actions">
|
|
863
|
+
<button
|
|
864
|
+
class="standard-button"
|
|
865
|
+
data-action="skip-media-tagging-item"
|
|
866
|
+
type="button"
|
|
867
|
+
${workflow?.currentItem && canWrite && !state.mediaTagging.previewLoading && !state.mediaTagging.applying ? '' : 'disabled'}
|
|
868
|
+
>
|
|
869
|
+
${state.mediaTagging.applying ? 'Saving...' : 'Skip'}
|
|
870
|
+
</button>
|
|
871
|
+
<button
|
|
872
|
+
class="signature-button"
|
|
873
|
+
data-action="apply-media-tagging"
|
|
874
|
+
data-can-apply="${workflow?.currentItem && canWrite && !state.mediaTagging.applying ? 'true' : 'false'}"
|
|
875
|
+
type="button"
|
|
876
|
+
${workflow?.currentItem && canWrite && !state.mediaTagging.applying && selectedTagCount > 0 ? '' : 'disabled'}
|
|
877
|
+
>
|
|
878
|
+
${state.mediaTagging.applying ? 'Saving...' : `${selectedTagCount} tagged & next`}
|
|
879
|
+
</button>
|
|
880
|
+
</div>
|
|
790
881
|
</div>
|
|
791
882
|
${
|
|
792
883
|
workflow?.allRemainingSkipped
|
|
@@ -801,9 +892,6 @@ function renderWorkflowSection(state) {
|
|
|
801
892
|
`
|
|
802
893
|
: ''
|
|
803
894
|
}
|
|
804
|
-
<div class="text-[11px] font-mono uppercase tracking-[0.14em] text-on-surface-variant/45">
|
|
805
|
-
${escapeHtml(formatNumber(workflow?.status?.remainingCount ?? 0))} remaining
|
|
806
|
-
</div>
|
|
807
895
|
</div>
|
|
808
896
|
</div>
|
|
809
897
|
</div>
|
|
@@ -820,24 +908,6 @@ export function renderMediaTaggingView(state, { subView = 'setup' } = {}) {
|
|
|
820
908
|
main: `
|
|
821
909
|
<section class="view-surface media-tagging-view">
|
|
822
910
|
<div class="media-tagging-shell">
|
|
823
|
-
<header class="media-tagging-header">
|
|
824
|
-
<div class="media-tagging-header__copy">
|
|
825
|
-
<div class="text-[10px] font-bold uppercase tracking-[0.22em] text-primary-container">
|
|
826
|
-
Media Tagging
|
|
827
|
-
</div>
|
|
828
|
-
<h1 class="mt-3 font-headline text-4xl font-black uppercase tracking-tight text-primary-container">
|
|
829
|
-
${showSetup ? 'Setup' : 'Tagging Queue'}
|
|
830
|
-
</h1>
|
|
831
|
-
<p class="mt-3 max-w-3xl text-sm leading-7 text-on-surface-variant/65">
|
|
832
|
-
${
|
|
833
|
-
showSetup
|
|
834
|
-
? 'Configure tags, media queries, and mapping once per database.'
|
|
835
|
-
: 'Review the next untagged media item, select tags, skip it, or mark it as tagged.'
|
|
836
|
-
}
|
|
837
|
-
</p>
|
|
838
|
-
</div>
|
|
839
|
-
</header>
|
|
840
|
-
|
|
841
911
|
${renderIssueList(state)}
|
|
842
912
|
|
|
843
913
|
${
|