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.
Files changed (34) hide show
  1. package/changelog.md +16 -0
  2. package/frontend/index.html +1 -1
  3. package/frontend/js/api.js +67 -0
  4. package/frontend/js/app.js +1554 -999
  5. package/frontend/js/components/modal.js +208 -3
  6. package/frontend/js/components/queryEditor.js +9 -4
  7. package/frontend/js/components/queryHistoryDetail.js +34 -12
  8. package/frontend/js/components/queryHistoryPanel.js +8 -11
  9. package/frontend/js/components/rowEditorPanel.js +26 -5
  10. package/frontend/js/components/sidebar.js +49 -5
  11. package/frontend/js/components/structureGraph.js +614 -632
  12. package/frontend/js/components/tableDesignerSqlPreview.js +24 -25
  13. package/frontend/js/lib/mediaTaggingDefaults.js +27 -0
  14. package/frontend/js/router.js +81 -71
  15. package/frontend/js/store.js +3140 -2179
  16. package/frontend/js/views/charts.js +2 -2
  17. package/frontend/js/views/data.js +28 -14
  18. package/frontend/js/views/editor.js +172 -177
  19. package/frontend/js/views/mediaTagging.js +931 -0
  20. package/frontend/styles/base.css +11 -1
  21. package/frontend/styles/components.css +54 -6
  22. package/frontend/styles/views.css +910 -0
  23. package/package.json +1 -1
  24. package/server/routes/charts.js +4 -1
  25. package/server/routes/data.js +14 -0
  26. package/server/routes/mediaTagging.js +166 -0
  27. package/server/routes/sql.js +1 -0
  28. package/server/server.js +4 -0
  29. package/server/services/sqlite/dataBrowserService.js +25 -0
  30. package/server/services/sqlite/exportService.js +31 -1
  31. package/server/services/sqlite/mediaTaggingService.js +1689 -0
  32. package/server/services/storage/appStateStore.js +325 -3
  33. package/server/services/storage/queryHistoryUtils.js +165 -2
  34. package/shortkeys.md +5 -0
@@ -1,36 +1,35 @@
1
- import { escapeHtml } from "../utils/format.js";
1
+ import { escapeHtml } from '../utils/format.js';
2
2
 
3
3
  function renderPreviewHeader(draft, isVisible) {
4
- return `
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
- draft && isVisible
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
- Copy SQL
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 ? "false" : "true"}"
28
+ data-next-value="${isVisible ? 'false' : 'true'}"
30
29
  type="button"
31
30
  >
32
- <span class="material-symbols-outlined">${isVisible ? "visibility_off" : "expand_less"}</span>
33
- ${isVisible ? "Hide" : "Show"}
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
- if (!draft) {
42
- return `
43
- <section class="table-designer-preview shell-section${isVisible ? "" : " is-collapsed"}">
40
+ if (!draft) {
41
+ return `
42
+ <section class="table-designer-preview shell-section${isVisible ? '' : ' is-collapsed'}">
44
43
  ${renderPreviewHeader(draft, isVisible)}
45
44
  ${
46
- isVisible
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
- return `
59
- <section class="table-designer-preview shell-section${isVisible ? "" : " is-collapsed"}">
57
+ return `
58
+ <section class="table-designer-preview shell-section${isVisible ? '' : ' is-collapsed'}">
60
59
  ${renderPreviewHeader(draft, isVisible)}
61
60
  ${
62
- isVisible
63
- ? `
61
+ isVisible
62
+ ? `
64
63
  <pre class="table-designer-preview__body custom-scrollbar">${escapeHtml(
65
- draft.sqlPreview || "-- SQL preview unavailable."
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
+ }
@@ -1,84 +1,94 @@
1
1
  export function parseHash(hash = window.location.hash) {
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);
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
- if (segments.length === 0) {
8
- return { name: "landing", path: "/", params: {} };
9
- }
7
+ if (segments.length === 0) {
8
+ return { name: 'landing', path: '/', params: {} };
9
+ }
10
10
 
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
- }
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
- 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 { name: "structure", path: "/structure", params: {} };
40
- case "table-designer":
41
- return {
42
- name: "tableDesigner",
43
- path: cleanPath,
44
- params: {
45
- isNew: segments[1] === "new",
46
- tableName:
47
- segments[1] && segments[1] !== "new" ? decodeURIComponent(segments[1]) : null,
48
- },
49
- };
50
- case "settings":
51
- return { name: "settings", path: "/settings", params: {} };
52
- default:
53
- return { name: "notFound", path: cleanPath, params: {} };
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
- const handleRouteChange = () => {
59
- onRouteChange(parseHash(window.location.hash));
60
- };
68
+ const handleRouteChange = () => {
69
+ onRouteChange(parseHash(window.location.hash));
70
+ };
61
71
 
62
- return {
63
- start() {
64
- window.addEventListener("hashchange", handleRouteChange);
72
+ return {
73
+ start() {
74
+ window.addEventListener('hashchange', handleRouteChange);
65
75
 
66
- if (!window.location.hash) {
67
- window.location.hash = "#/";
68
- return;
69
- }
76
+ if (!window.location.hash) {
77
+ window.location.hash = '#/';
78
+ return;
79
+ }
70
80
 
71
- handleRouteChange();
72
- },
73
- navigate(path) {
74
- const nextHash = path.startsWith("#") ? path : `#${path}`;
81
+ handleRouteChange();
82
+ },
83
+ navigate(path) {
84
+ const nextHash = path.startsWith('#') ? path : `#${path}`;
75
85
 
76
- if (window.location.hash === nextHash) {
77
- handleRouteChange();
78
- return;
79
- }
86
+ if (window.location.hash === nextHash) {
87
+ handleRouteChange();
88
+ return;
89
+ }
80
90
 
81
- window.location.hash = nextHash;
82
- },
83
- };
91
+ window.location.hash = nextHash;
92
+ },
93
+ };
84
94
  }