sqlite-hub 0.8.7 → 0.9.1
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 +16 -0
- package/frontend/index.html +1 -1
- package/frontend/js/api.js +67 -0
- package/frontend/js/app.js +1554 -999
- package/frontend/js/components/modal.js +208 -3
- package/frontend/js/components/queryEditor.js +9 -4
- package/frontend/js/components/queryHistoryDetail.js +34 -12
- package/frontend/js/components/queryHistoryPanel.js +8 -11
- package/frontend/js/components/rowEditorPanel.js +26 -5
- package/frontend/js/components/sidebar.js +49 -5
- package/frontend/js/components/structureGraph.js +614 -632
- package/frontend/js/components/tableDesignerSqlPreview.js +24 -25
- package/frontend/js/lib/mediaTaggingDefaults.js +27 -0
- package/frontend/js/router.js +81 -71
- package/frontend/js/store.js +3140 -2179
- package/frontend/js/views/charts.js +2 -2
- package/frontend/js/views/data.js +28 -14
- package/frontend/js/views/editor.js +172 -177
- package/frontend/js/views/mediaTagging.js +931 -0
- package/frontend/styles/base.css +11 -1
- package/frontend/styles/components.css +54 -6
- package/frontend/styles/views.css +910 -0
- package/package.json +1 -1
- package/server/routes/charts.js +4 -1
- package/server/routes/data.js +14 -0
- package/server/routes/mediaTagging.js +166 -0
- package/server/routes/sql.js +1 -0
- package/server/server.js +4 -0
- package/server/services/sqlite/dataBrowserService.js +25 -0
- package/server/services/sqlite/exportService.js +31 -1
- package/server/services/sqlite/mediaTaggingService.js +1689 -0
- package/server/services/storage/appStateStore.js +325 -3
- package/server/services/storage/queryHistoryUtils.js +165 -2
- package/shortkeys.md +5 -0
|
@@ -1,36 +1,35 @@
|
|
|
1
|
-
import { escapeHtml } from
|
|
1
|
+
import { escapeHtml } from '../utils/format.js';
|
|
2
2
|
|
|
3
3
|
function renderPreviewHeader(draft, isVisible) {
|
|
4
|
-
|
|
4
|
+
return `
|
|
5
5
|
<div class="table-designer-preview__header">
|
|
6
6
|
<div>
|
|
7
7
|
<div class="table-designer-preview__eyebrow">SQL Preview</div>
|
|
8
|
-
<div class="table-designer-preview__title">${
|
|
9
|
-
draft ? "Live SQLite Output" : "No Draft Selected"
|
|
10
|
-
}</div>
|
|
8
|
+
<div class="table-designer-preview__title">${draft ? 'Live SQLite Output' : 'No Draft Selected'}</div>
|
|
11
9
|
</div>
|
|
12
10
|
<div class="table-designer-preview__actions">
|
|
13
11
|
${
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
draft && isVisible
|
|
13
|
+
? `
|
|
16
14
|
<button
|
|
17
15
|
class="standard-button"
|
|
18
16
|
data-action="copy-table-designer-sql"
|
|
19
17
|
type="button"
|
|
20
18
|
>
|
|
21
|
-
|
|
19
|
+
<span class="material-symbols-outlined">content_copy</span>
|
|
20
|
+
Copy SQL to clipboard
|
|
22
21
|
</button>
|
|
23
22
|
`
|
|
24
|
-
|
|
23
|
+
: ''
|
|
25
24
|
}
|
|
26
25
|
<button
|
|
27
26
|
class="standard-button"
|
|
28
27
|
data-action="toggle-table-designer-sql-preview"
|
|
29
|
-
data-next-value="${isVisible ?
|
|
28
|
+
data-next-value="${isVisible ? 'false' : 'true'}"
|
|
30
29
|
type="button"
|
|
31
30
|
>
|
|
32
|
-
<span class="material-symbols-outlined">${isVisible ?
|
|
33
|
-
${isVisible ?
|
|
31
|
+
<span class="material-symbols-outlined">${isVisible ? 'visibility_off' : 'expand_less'}</span>
|
|
32
|
+
${isVisible ? 'Hide' : 'Show'}
|
|
34
33
|
</button>
|
|
35
34
|
</div>
|
|
36
35
|
</div>
|
|
@@ -38,34 +37,34 @@ function renderPreviewHeader(draft, isVisible) {
|
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
export function renderTableDesignerSqlPreview(draft, isVisible = true) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
<section class="table-designer-preview shell-section${isVisible ?
|
|
40
|
+
if (!draft) {
|
|
41
|
+
return `
|
|
42
|
+
<section class="table-designer-preview shell-section${isVisible ? '' : ' is-collapsed'}">
|
|
44
43
|
${renderPreviewHeader(draft, isVisible)}
|
|
45
44
|
${
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
isVisible
|
|
46
|
+
? `
|
|
48
47
|
<div class="table-designer-preview__empty">
|
|
49
48
|
Select a table or create a new one to inspect the generated SQLite statements.
|
|
50
49
|
</div>
|
|
51
50
|
`
|
|
52
|
-
|
|
51
|
+
: ''
|
|
53
52
|
}
|
|
54
53
|
</section>
|
|
55
54
|
`;
|
|
56
|
-
|
|
55
|
+
}
|
|
57
56
|
|
|
58
|
-
|
|
59
|
-
<section class="table-designer-preview shell-section${isVisible ?
|
|
57
|
+
return `
|
|
58
|
+
<section class="table-designer-preview shell-section${isVisible ? '' : ' is-collapsed'}">
|
|
60
59
|
${renderPreviewHeader(draft, isVisible)}
|
|
61
60
|
${
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
isVisible
|
|
62
|
+
? `
|
|
64
63
|
<pre class="table-designer-preview__body custom-scrollbar">${escapeHtml(
|
|
65
|
-
|
|
64
|
+
draft.sqlPreview || '-- SQL preview unavailable.',
|
|
66
65
|
)}</pre>
|
|
67
66
|
`
|
|
68
|
-
|
|
67
|
+
: ''
|
|
69
68
|
}
|
|
70
69
|
</section>
|
|
71
70
|
`;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export const MEDIA_TAGGING_DEFAULT_TAG_TABLE = 'media_tags';
|
|
2
|
+
export const MEDIA_TAGGING_DEFAULT_MAPPING_TABLE = 'media_asset_tags';
|
|
3
|
+
export const MEDIA_TAGGING_DEFAULT_TAG_TABLE_SQL = [
|
|
4
|
+
'CREATE TABLE "media_tags" (',
|
|
5
|
+
' id INTEGER PRIMARY KEY,',
|
|
6
|
+
' name TEXT NOT NULL UNIQUE,',
|
|
7
|
+
' isParentTag INTEGER NOT NULL DEFAULT 0,',
|
|
8
|
+
' parentTagId INTEGER REFERENCES media_tags(id) ON DELETE SET NULL',
|
|
9
|
+
')',
|
|
10
|
+
].join('\n');
|
|
11
|
+
export const MEDIA_TAGGING_DEFAULT_MAPPING_TABLE_SQL = [
|
|
12
|
+
'CREATE TABLE media_asset_tags (',
|
|
13
|
+
' media_asset_id INTEGER NOT NULL,',
|
|
14
|
+
' media_tag_id INTEGER NOT NULL,',
|
|
15
|
+
' PRIMARY KEY (media_asset_id, media_tag_id),',
|
|
16
|
+
' FOREIGN KEY (media_asset_id) REFERENCES media_assets(id) ON DELETE CASCADE,',
|
|
17
|
+
' FOREIGN KEY (media_tag_id) REFERENCES media_tags(id) ON DELETE CASCADE',
|
|
18
|
+
')',
|
|
19
|
+
].join('\n');
|
|
20
|
+
|
|
21
|
+
export function hasDefaultMediaTaggingTagTable(schemaTables = []) {
|
|
22
|
+
return schemaTables.some(table => String(table?.name ?? '').trim() === MEDIA_TAGGING_DEFAULT_TAG_TABLE);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function hasDefaultMediaTaggingMappingTable(schemaTables = []) {
|
|
26
|
+
return schemaTables.some(table => String(table?.name ?? '').trim() === MEDIA_TAGGING_DEFAULT_MAPPING_TABLE);
|
|
27
|
+
}
|
package/frontend/js/router.js
CHANGED
|
@@ -1,84 +1,94 @@
|
|
|
1
1
|
export function parseHash(hash = window.location.hash) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
const normalized = hash.startsWith('#') ? hash.slice(1) : hash;
|
|
3
|
+
const [pathname = '/'] = normalized.split('?');
|
|
4
|
+
const cleanPath = pathname || '/';
|
|
5
|
+
const segments = cleanPath.split('/').filter(Boolean);
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
if (segments.length === 0) {
|
|
8
|
+
return { name: 'landing', path: '/', params: {} };
|
|
9
|
+
}
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
11
|
+
switch (segments[0]) {
|
|
12
|
+
case 'connections':
|
|
13
|
+
return { name: 'connections', path: '/connections', params: {} };
|
|
14
|
+
case 'overview':
|
|
15
|
+
return { name: 'overview', path: '/overview', params: {} };
|
|
16
|
+
case 'charts':
|
|
17
|
+
return {
|
|
18
|
+
name: 'charts',
|
|
19
|
+
path: cleanPath,
|
|
20
|
+
params: {
|
|
21
|
+
historyId: segments[1] ? decodeURIComponent(segments[1]) : null,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
case 'editor':
|
|
25
|
+
if (segments[1] === 'results') {
|
|
26
|
+
return { name: 'editorResults', path: '/editor/results', params: {} };
|
|
27
|
+
}
|
|
28
28
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
29
|
+
return { name: 'editor', path: '/editor', params: {} };
|
|
30
|
+
case 'data':
|
|
31
|
+
return {
|
|
32
|
+
name: 'data',
|
|
33
|
+
path: cleanPath,
|
|
34
|
+
params: {
|
|
35
|
+
tableName: segments[1] ? decodeURIComponent(segments[1]) : null,
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
case 'structure':
|
|
39
|
+
return {
|
|
40
|
+
name: 'structure',
|
|
41
|
+
path: cleanPath,
|
|
42
|
+
params: {
|
|
43
|
+
tableName: segments[1] ? decodeURIComponent(segments[1]) : null,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
case 'table-designer':
|
|
47
|
+
return {
|
|
48
|
+
name: 'tableDesigner',
|
|
49
|
+
path: cleanPath,
|
|
50
|
+
params: {
|
|
51
|
+
isNew: segments[1] === 'new',
|
|
52
|
+
tableName: segments[1] && segments[1] !== 'new' ? decodeURIComponent(segments[1]) : null,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
case 'media-tagging':
|
|
56
|
+
if (segments[1] === 'queue') {
|
|
57
|
+
return { name: 'mediaTaggingQueue', path: '/media-tagging/queue', params: {} };
|
|
58
|
+
}
|
|
59
|
+
return { name: 'mediaTaggingSetup', path: '/media-tagging', params: {} };
|
|
60
|
+
case 'settings':
|
|
61
|
+
return { name: 'settings', path: '/settings', params: {} };
|
|
62
|
+
default:
|
|
63
|
+
return { name: 'notFound', path: cleanPath, params: {} };
|
|
64
|
+
}
|
|
55
65
|
}
|
|
56
66
|
|
|
57
67
|
export function createRouter(onRouteChange) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
68
|
+
const handleRouteChange = () => {
|
|
69
|
+
onRouteChange(parseHash(window.location.hash));
|
|
70
|
+
};
|
|
61
71
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
72
|
+
return {
|
|
73
|
+
start() {
|
|
74
|
+
window.addEventListener('hashchange', handleRouteChange);
|
|
65
75
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
76
|
+
if (!window.location.hash) {
|
|
77
|
+
window.location.hash = '#/';
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
70
80
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
81
|
+
handleRouteChange();
|
|
82
|
+
},
|
|
83
|
+
navigate(path) {
|
|
84
|
+
const nextHash = path.startsWith('#') ? path : `#${path}`;
|
|
75
85
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
86
|
+
if (window.location.hash === nextHash) {
|
|
87
|
+
handleRouteChange();
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
80
90
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
91
|
+
window.location.hash = nextHash;
|
|
92
|
+
},
|
|
93
|
+
};
|
|
84
94
|
}
|