admindb 1.0.1 → 1.1.0

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 (48) hide show
  1. package/LICENSE +20 -20
  2. package/README.md +354 -325
  3. package/dist/db/database.d.ts +61 -39
  4. package/dist/db/database.js +251 -51
  5. package/dist/db/database.js.map +1 -1
  6. package/dist/db/manager.d.ts +2 -2
  7. package/dist/db/manager.js +3 -2
  8. package/dist/db/manager.js.map +1 -1
  9. package/dist/public/css/app.css +1 -1
  10. package/dist/public/css/input.css +232 -179
  11. package/dist/public/js/api.js +47 -47
  12. package/dist/public/js/app.js +120 -120
  13. package/dist/public/js/browse.js +877 -123
  14. package/dist/public/js/databases.js +45 -45
  15. package/dist/public/js/designer.js +205 -205
  16. package/dist/public/js/forms.js +244 -244
  17. package/dist/public/js/query.js +232 -232
  18. package/dist/public/js/schema.js +314 -314
  19. package/dist/routes/api.js +261 -97
  20. package/dist/routes/api.js.map +1 -1
  21. package/dist/routes/pages.js +50 -30
  22. package/dist/routes/pages.js.map +1 -1
  23. package/dist/sql/generator.d.ts +0 -22
  24. package/dist/sql/generator.js +0 -48
  25. package/dist/sql/generator.js.map +1 -1
  26. package/dist/test/database.test.js +241 -63
  27. package/dist/test/database.test.js.map +1 -1
  28. package/dist/test/generator.test.js +0 -32
  29. package/dist/test/generator.test.js.map +1 -1
  30. package/dist/util.d.ts +13 -1
  31. package/dist/util.js +24 -2
  32. package/dist/util.js.map +1 -1
  33. package/dist/views/layouts/main.hbs +49 -49
  34. package/dist/views/pages/databases.hbs +102 -102
  35. package/dist/views/pages/designer.hbs +81 -81
  36. package/dist/views/pages/error.hbs +12 -12
  37. package/dist/views/pages/form.hbs +62 -62
  38. package/dist/views/pages/home.hbs +123 -123
  39. package/dist/views/pages/query.hbs +94 -94
  40. package/dist/views/pages/schema.hbs +290 -290
  41. package/dist/views/pages/table.hbs +256 -203
  42. package/dist/views/partials/navbar.hbs +50 -50
  43. package/dist/views/partials/sidebar.hbs +116 -128
  44. package/package.json +66 -62
  45. package/dist/public/js/triggers.js +0 -155
  46. package/dist/public/js/views.js +0 -176
  47. package/dist/views/pages/triggers.hbs +0 -162
  48. package/dist/views/pages/views.hbs +0 -134
@@ -1,62 +1,62 @@
1
- <div class="mx-auto max-w-3xl space-y-5">
2
- <!-- Header -->
3
- <div class="flex items-center gap-3">
4
- <a href="{{basePath}}/tables/{{table}}" class="btn btn-circle btn-ghost btn-sm" aria-label="Back to table">
5
- <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M10 19l-7-7m0 0l7-7m-7 7h18" /></svg>
6
- </a>
7
- <div>
8
- <h1 class="text-2xl font-bold tracking-tight">
9
- {{#if (eq mode 'edit')}}Edit row{{else}}New row{{/if}}
10
- <span class="text-base-content/40">in</span> <span class="font-mono text-primary">{{table}}</span>
11
- </h1>
12
- <p class="text-sm text-base-content/55">{{infoSummary}}</p>
13
- </div>
14
- </div>
15
-
16
- <div class="app-card">
17
- <div class="border-b border-base-200 px-6 py-4">
18
- <h2 class="text-sm font-semibold uppercase tracking-wide text-base-content/45">Row fields</h2>
19
- </div>
20
- <div class="p-6">
21
- <div id="row-fields" class="grid grid-cols-1 gap-x-6 gap-y-5 md:grid-cols-2"></div>
22
- <div id="form-error" class="hidden"></div>
23
- <div class="mt-6 flex flex-wrap items-center gap-2 border-t border-base-200 pt-5">
24
- {{#unless readonly}}
25
- <button id="row-submit" type="button" class="btn btn-primary">
26
- {{#if (eq mode 'edit')}}Update row{{else}}Insert row{{/if}}
27
- </button>
28
- <button id="row-generate" type="button" class="btn btn-outline gap-1.5">
29
- <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" /></svg>
30
- Get query
31
- </button>
32
- {{else}}
33
- <span class="text-sm font-medium text-warning">This database is read-only — rows cannot be {{#if (eq mode 'edit')}}updated{{else}}inserted{{/if}}.</span>
34
- {{/unless}}
35
- <a href="{{basePath}}/tables/{{table}}" class="btn btn-ghost">Cancel</a>
36
- </div>
37
- <p class="mt-3 text-xs text-base-content/40">
38
- Columns with defaults are pre-filled from the schema. Empty fields are treated as "not set" (DB defaults
39
- apply). <code class="font-mono">0</code> is a valid value. "Get query" only previews the SQL — it never executes.
40
- </p>
41
- </div>
42
- </div>
43
-
44
- <div id="generated-sql" class="hidden">
45
- <div class="app-card">
46
- <div class="flex items-center justify-between border-b border-base-200 px-6 py-4">
47
- <h2 class="flex items-center gap-2 text-sm font-semibold uppercase tracking-wide text-base-content/45">
48
- <span class="grid h-6 w-6 place-items-center rounded-md bg-primary/10 text-primary">
49
- <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" /></svg>
50
- </span>
51
- Generated SQL
52
- </h2>
53
- <button id="copy-sql" type="button" class="btn btn-ghost btn-xs gap-1">Copy</button>
54
- </div>
55
- <div class="p-4">
56
- <pre class="code-panel"><code id="generated-sql-code"></code></pre>
57
- </div>
58
- </div>
59
- </div>
60
- </div>
61
- <script id="form-config" type="application/json">{{{json config}}}</script>
62
- <script src="{{basePath}}/js/forms.js" defer></script>
1
+ <div class="mx-auto max-w-3xl space-y-5">
2
+ <!-- Header -->
3
+ <div class="flex items-center gap-3">
4
+ <a href="{{basePath}}/tables/{{table}}" class="btn btn-circle btn-ghost btn-sm" aria-label="Back to table">
5
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M10 19l-7-7m0 0l7-7m-7 7h18" /></svg>
6
+ </a>
7
+ <div>
8
+ <h1 class="text-2xl font-bold tracking-tight">
9
+ {{#if (eq mode 'edit')}}Edit row{{else}}New row{{/if}}
10
+ <span class="text-base-content/40">in</span> <span class="font-mono text-primary">{{table}}</span>
11
+ </h1>
12
+ <p class="text-sm text-base-content/55">{{infoSummary}}</p>
13
+ </div>
14
+ </div>
15
+
16
+ <div class="app-card">
17
+ <div class="border-b border-base-200 px-6 py-4">
18
+ <h2 class="text-sm font-semibold uppercase tracking-wide text-base-content/45">Row fields</h2>
19
+ </div>
20
+ <div class="p-6">
21
+ <div id="row-fields" class="grid grid-cols-1 gap-x-6 gap-y-5 md:grid-cols-2"></div>
22
+ <div id="form-error" class="hidden"></div>
23
+ <div class="mt-6 flex flex-wrap items-center gap-2 border-t border-base-200 pt-5">
24
+ {{#unless readonly}}
25
+ <button id="row-submit" type="button" class="btn btn-primary">
26
+ {{#if (eq mode 'edit')}}Update row{{else}}Insert row{{/if}}
27
+ </button>
28
+ <button id="row-generate" type="button" class="btn btn-outline gap-1.5">
29
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" /></svg>
30
+ Get query
31
+ </button>
32
+ {{else}}
33
+ <span class="text-sm font-medium text-warning">This database is read-only — rows cannot be {{#if (eq mode 'edit')}}updated{{else}}inserted{{/if}}.</span>
34
+ {{/unless}}
35
+ <a href="{{basePath}}/tables/{{table}}" class="btn btn-ghost">Cancel</a>
36
+ </div>
37
+ <p class="mt-3 text-xs text-base-content/40">
38
+ Columns with defaults are pre-filled from the schema. Empty fields are treated as "not set" (DB defaults
39
+ apply). <code class="font-mono">0</code> is a valid value. "Get query" only previews the SQL — it never executes.
40
+ </p>
41
+ </div>
42
+ </div>
43
+
44
+ <div id="generated-sql" class="hidden">
45
+ <div class="app-card">
46
+ <div class="flex items-center justify-between border-b border-base-200 px-6 py-4">
47
+ <h2 class="flex items-center gap-2 text-sm font-semibold uppercase tracking-wide text-base-content/45">
48
+ <span class="grid h-6 w-6 place-items-center rounded-md bg-primary/10 text-primary">
49
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" /></svg>
50
+ </span>
51
+ Generated SQL
52
+ </h2>
53
+ <button id="copy-sql" type="button" class="btn btn-ghost btn-xs gap-1">Copy</button>
54
+ </div>
55
+ <div class="p-4">
56
+ <pre class="code-panel"><code id="generated-sql-code"></code></pre>
57
+ </div>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ <script id="form-config" type="application/json">{{{json config}}}</script>
62
+ <script src="{{basePath}}/js/forms.js" defer></script>
@@ -1,123 +1,123 @@
1
- <div class="space-y-8">
2
- <!-- Header -->
3
- <div class="flex flex-wrap items-end justify-between gap-4">
4
- <div>
5
- <p class="text-sm font-semibold uppercase tracking-wider text-primary">Overview</p>
6
- <h1 class="mt-1 text-3xl font-extrabold tracking-tight">Database</h1>
7
- <p class="mt-1.5 text-sm text-base-content/55">Browse, edit, and query the connected SQLite database from your browser.</p>
8
- </div>
9
- <div class="flex gap-2">
10
- <a href="{{basePath}}/query" class="btn btn-ghost btn-sm gap-1.5">
11
- <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
12
- Query
13
- </a>
14
- {{#unless readonly}}
15
- <a href="{{basePath}}/designer" class="btn btn-primary btn-sm gap-1.5">
16
- <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4v16m8-8H4" /></svg>
17
- New table
18
- </a>
19
- {{/unless}}
20
- </div>
21
- </div>
22
-
23
- <!-- Stat cards -->
24
- <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4">
25
- <div class="stat-card">
26
- <span class="stat-icon bg-primary/10 text-primary">
27
- <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" /></svg>
28
- </span>
29
- <div>
30
- <div class="text-2xl font-extrabold tabular-nums">{{tables.length}}</div>
31
- <div class="text-[13px] text-base-content/50">Tables</div>
32
- </div>
33
- </div>
34
- <div class="stat-card">
35
- <span class="stat-icon bg-secondary/10 text-secondary">
36
- <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /></svg>
37
- </span>
38
- <div>
39
- <div class="text-2xl font-extrabold tabular-nums">{{totalRows}}</div>
40
- <div class="text-[13px] text-base-content/50">Total rows</div>
41
- </div>
42
- </div>
43
- <div class="stat-card">
44
- <span class="stat-icon bg-accent/10 text-accent">
45
- <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z" /></svg>
46
- </span>
47
- <div>
48
- <div class="text-2xl font-extrabold tabular-nums">{{savedQueries}}</div>
49
- <div class="text-[13px] text-base-content/50">Saved queries</div>
50
- </div>
51
- </div>
52
- <div class="stat-card">
53
- <span class="stat-icon bg-warning/10 text-warning">
54
- <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4 7v10c0 1.1.9 2 2 2h12a2 2 0 002-2V7M4 7c0-1.1.9-2 2-2h12a2 2 0 012 2M4 7c0 1.1.9 2 2 2h12a2 2 0 002-2" /></svg>
55
- </span>
56
- <div>
57
- <div class="truncate font-mono text-lg font-bold" title="{{dbPath}}">{{dbPath}}</div>
58
- <div class="text-[13px] text-base-content/50">Database file</div>
59
- </div>
60
- </div>
61
- </div>
62
-
63
- <!-- Tables -->
64
- <div>
65
- <div class="mb-3 flex items-center justify-between">
66
- <h2 class="text-lg font-bold">Tables</h2>
67
- <span class="text-sm text-base-content/45">{{tables.length}} total</span>
68
- </div>
69
-
70
- {{#if tables.length}}
71
- <div class="app-card overflow-hidden">
72
- <div class="overflow-x-auto">
73
- <table class="table table-sm">
74
- <thead>
75
- <tr class="bg-base-200/70 text-xs uppercase tracking-wide text-base-content/50">
76
- <th class="font-semibold">Table</th>
77
- <th class="text-right font-semibold">Columns</th>
78
- <th class="text-right font-semibold">Rows</th>
79
- <th class="text-right font-semibold">Actions</th>
80
- </tr>
81
- </thead>
82
- <tbody>
83
- {{#each tables}}
84
- <tr class="group hover:bg-base-200/50">
85
- <td>
86
- <a href="{{../basePath}}/tables/{{this}}" class="inline-flex items-center gap-2.5 font-medium">
87
- <span class="grid h-8 w-8 place-items-center rounded-lg bg-primary/10 text-primary transition-colors group-hover:bg-primary/20">
88
- <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" /></svg>
89
- </span>
90
- <span class="font-mono text-[13.5px]">{{this}}</span>
91
- </a>
92
- </td>
93
- <td class="text-right tabular-nums text-base-content/60">{{lookup ../cols this}}</td>
94
- <td class="text-right tabular-nums text-base-content/60">{{lookup ../counts this}}</td>
95
- <td class="text-right">
96
- <a href="{{../basePath}}/tables/{{this}}" class="btn btn-ghost btn-xs gap-1 text-base-content/70 hover:text-primary">
97
- Browse
98
- <svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M13 7l5 5m0 0l-5 5m5-5H6" /></svg>
99
- </a>
100
- </td>
101
- </tr>
102
- {{/each}}
103
- </tbody>
104
- </table>
105
- </div>
106
- </div>
107
- {{else}}
108
- <div class="empty-state">
109
- <span class="empty-icon">
110
- <svg xmlns="http://www.w3.org/2000/svg" class="h-7 w-7" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" /></svg>
111
- </span>
112
- <p class="text-sm font-medium text-base-content/70">No tables yet</p>
113
- <p class="max-w-sm text-sm text-base-content/45">Create one with the table designer, or run a SQL statement in the query editor to get started.</p>
114
- <div class="mt-2 flex gap-2">
115
- {{#unless readonly}}
116
- <a href="{{basePath}}/designer" class="btn btn-primary btn-sm">New table</a>
117
- {{/unless}}
118
- <a href="{{basePath}}/query" class="btn btn-outline btn-sm">Query editor</a>
119
- </div>
120
- </div>
121
- {{/if}}
122
- </div>
123
- </div>
1
+ <div class="space-y-8">
2
+ <!-- Header -->
3
+ <div class="flex flex-wrap items-end justify-between gap-4">
4
+ <div>
5
+ <p class="text-sm font-semibold uppercase tracking-wider text-primary">Overview</p>
6
+ <h1 class="mt-1 text-3xl font-extrabold tracking-tight">Database</h1>
7
+ <p class="mt-1.5 text-sm text-base-content/55">Browse, edit, and query the connected SQLite database from your browser.</p>
8
+ </div>
9
+ <div class="flex gap-2">
10
+ <a href="{{basePath}}/query" class="btn btn-ghost btn-sm gap-1.5">
11
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
12
+ Query
13
+ </a>
14
+ {{#unless readonly}}
15
+ <a href="{{basePath}}/designer" class="btn btn-primary btn-sm gap-1.5">
16
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M12 4v16m8-8H4" /></svg>
17
+ New table
18
+ </a>
19
+ {{/unless}}
20
+ </div>
21
+ </div>
22
+
23
+ <!-- Stat cards -->
24
+ <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4">
25
+ <div class="stat-card">
26
+ <span class="stat-icon bg-primary/10 text-primary">
27
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" /></svg>
28
+ </span>
29
+ <div>
30
+ <div class="text-2xl font-extrabold tabular-nums">{{tables.length}}</div>
31
+ <div class="text-[13px] text-base-content/50">Tables</div>
32
+ </div>
33
+ </div>
34
+ <div class="stat-card">
35
+ <span class="stat-icon bg-secondary/10 text-secondary">
36
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M9 17v-2m3 2v-4m3 4v-6m2 10H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" /></svg>
37
+ </span>
38
+ <div>
39
+ <div class="text-2xl font-extrabold tabular-nums">{{totalRows}}</div>
40
+ <div class="text-[13px] text-base-content/50">Total rows</div>
41
+ </div>
42
+ </div>
43
+ <div class="stat-card">
44
+ <span class="stat-icon bg-accent/10 text-accent">
45
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z" /></svg>
46
+ </span>
47
+ <div>
48
+ <div class="text-2xl font-extrabold tabular-nums">{{savedQueries}}</div>
49
+ <div class="text-[13px] text-base-content/50">Saved queries</div>
50
+ </div>
51
+ </div>
52
+ <div class="stat-card">
53
+ <span class="stat-icon bg-warning/10 text-warning">
54
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4 7v10c0 1.1.9 2 2 2h12a2 2 0 002-2V7M4 7c0-1.1.9-2 2-2h12a2 2 0 012 2M4 7c0 1.1.9 2 2 2h12a2 2 0 002-2" /></svg>
55
+ </span>
56
+ <div>
57
+ <div class="truncate font-mono text-lg font-bold" title="{{dbPath}}">{{dbPath}}</div>
58
+ <div class="text-[13px] text-base-content/50">Database file</div>
59
+ </div>
60
+ </div>
61
+ </div>
62
+
63
+ <!-- Tables -->
64
+ <div>
65
+ <div class="mb-3 flex items-center justify-between">
66
+ <h2 class="text-lg font-bold">Tables</h2>
67
+ <span class="text-sm text-base-content/45">{{tables.length}} total</span>
68
+ </div>
69
+
70
+ {{#if tables.length}}
71
+ <div class="app-card overflow-hidden">
72
+ <div class="overflow-x-auto">
73
+ <table class="table table-sm">
74
+ <thead>
75
+ <tr class="bg-base-200/70 text-xs uppercase tracking-wide text-base-content/50">
76
+ <th class="font-semibold">Table</th>
77
+ <th class="text-right font-semibold">Columns</th>
78
+ <th class="text-right font-semibold">Rows</th>
79
+ <th class="text-right font-semibold">Actions</th>
80
+ </tr>
81
+ </thead>
82
+ <tbody>
83
+ {{#each tables}}
84
+ <tr class="group hover:bg-base-200/50">
85
+ <td>
86
+ <a href="{{../basePath}}/tables/{{this}}" class="inline-flex items-center gap-2.5 font-medium">
87
+ <span class="grid h-8 w-8 place-items-center rounded-lg bg-primary/10 text-primary transition-colors group-hover:bg-primary/20">
88
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" /></svg>
89
+ </span>
90
+ <span class="font-mono text-[13.5px]">{{this}}</span>
91
+ </a>
92
+ </td>
93
+ <td class="text-right tabular-nums text-base-content/60">{{lookup ../cols this}}</td>
94
+ <td class="text-right tabular-nums text-base-content/60">{{lookup ../counts this}}</td>
95
+ <td class="text-right">
96
+ <a href="{{../basePath}}/tables/{{this}}" class="btn btn-ghost btn-xs gap-1 text-base-content/70 hover:text-primary">
97
+ Browse
98
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M13 7l5 5m0 0l-5 5m5-5H6" /></svg>
99
+ </a>
100
+ </td>
101
+ </tr>
102
+ {{/each}}
103
+ </tbody>
104
+ </table>
105
+ </div>
106
+ </div>
107
+ {{else}}
108
+ <div class="empty-state">
109
+ <span class="empty-icon">
110
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-7 w-7" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M3 10h18M3 14h18m-9-4v8m-7 0h14a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z" /></svg>
111
+ </span>
112
+ <p class="text-sm font-medium text-base-content/70">No tables yet</p>
113
+ <p class="max-w-sm text-sm text-base-content/45">Create one with the table designer, or run a SQL statement in the query editor to get started.</p>
114
+ <div class="mt-2 flex gap-2">
115
+ {{#unless readonly}}
116
+ <a href="{{basePath}}/designer" class="btn btn-primary btn-sm">New table</a>
117
+ {{/unless}}
118
+ <a href="{{basePath}}/query" class="btn btn-outline btn-sm">Query editor</a>
119
+ </div>
120
+ </div>
121
+ {{/if}}
122
+ </div>
123
+ </div>
@@ -1,94 +1,94 @@
1
- <div class="space-y-6">
2
- <!-- Header -->
3
- <div class="flex flex-wrap items-center justify-between gap-3">
4
- <div>
5
- <p class="text-sm font-semibold uppercase tracking-wider text-primary">Workspace</p>
6
- <h1 class="mt-1 text-3xl font-extrabold tracking-tight">Query editor</h1>
7
- <p class="mt-1.5 text-sm text-base-content/55">Run arbitrary SQL — SELECT results render as a table, COUNT gives a summary, and write statements execute.</p>
8
- </div>
9
- <div class="flex flex-wrap gap-2">
10
- {{#unless readonly}}
11
- <button id="save-query" type="button" class="btn btn-outline btn-sm gap-1.5">
12
- <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4" /></svg>
13
- Save query
14
- </button>
15
- {{/unless}}
16
- <button id="run-query" type="button" class="btn btn-primary btn-sm gap-1.5">
17
- <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" /><path stroke-linecap="round" stroke-linejoin="round" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
18
- Run
19
- </button>
20
- </div>
21
- </div>
22
-
23
- <!-- Saved queries row -->
24
- <div class="flex flex-wrap items-end gap-3">
25
- <div class="form-control w-full max-w-xs">
26
- <label class="field-label" for="saved-query-select">Saved queries</label>
27
- <select id="saved-query-select" class="field-select">
28
- <option value="">— select a saved query —</option>
29
- {{#each savedQueries}}
30
- <option value="{{this.id}}">{{this.name}}</option>
31
- {{/each}}
32
- </select>
33
- </div>
34
- {{#unless readonly}}
35
- <button id="delete-query" type="button" class="btn btn-ghost btn-sm mb-0.5 text-error" disabled>Delete saved</button>
36
- <div class="form-control w-full max-w-xs">
37
- <label class="field-label" for="save-query-name">Save current SQL as</label>
38
- <div class="join w-full">
39
- <input id="save-query-name" type="text" class="field-input join-item !rounded-r-none" placeholder="query name" autocomplete="off" />
40
- <button id="save-query-inline" type="button" class="btn btn-outline join-item">Save</button>
41
- </div>
42
- </div>
43
- {{/unless}}
44
- </div>
45
-
46
- <!-- Editor -->
47
- <div class="app-card overflow-hidden">
48
- <div class="flex items-center justify-between border-b border-base-200 bg-base-200/40 px-4 py-2.5">
49
- <span class="flex items-center gap-2 text-sm font-semibold text-base-content/60">
50
- <span class="grid h-5 w-5 place-items-center rounded-md bg-primary/10 text-primary">
51
- <svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
52
- </span>
53
- query.sql
54
- </span>
55
- <span class="text-[11px] text-base-content/40">Ctrl/⌘ + Enter to run</span>
56
- </div>
57
- <div class="p-3">
58
- <div class="sql-editor-wrap">
59
- <pre id="query-gutter" class="sql-gutter" aria-hidden="true">1</pre>
60
- <textarea id="query-sql" class="sql-input" rows="10" placeholder="SELECT * FROM users WHERE …" spellcheck="false"></textarea>
61
- </div>
62
- <div id="query-message" class="mt-3"></div>
63
- </div>
64
- </div>
65
-
66
- <!-- Results -->
67
- <div id="query-result" class="hidden">
68
- <div class="app-card overflow-hidden">
69
- <div class="flex items-center gap-2 border-b border-base-200 px-4 py-3">
70
- <span id="result-kind" class="grid h-6 w-6 place-items-center rounded-md bg-success/10 text-success">
71
- <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg>
72
- </span>
73
- <span id="result-summary" class="text-sm font-medium text-base-content/60"></span>
74
- <div class="ml-auto flex items-center gap-1.5">
75
- <button id="export-csv" type="button" class="btn btn-ghost btn-xs gap-1 text-base-content/70 hover:text-primary" disabled>
76
- <svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" /></svg>
77
- CSV
78
- </button>
79
- <button id="export-json" type="button" class="btn btn-ghost btn-xs gap-1 text-base-content/70 hover:text-primary" disabled>
80
- <svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" /></svg>
81
- JSON
82
- </button>
83
- </div>
84
- </div>
85
- <div class="overflow-x-auto">
86
- <table class="table table-sm table-pin-rows">
87
- <thead id="query-result-head" class="bg-base-200/70 text-xs uppercase tracking-wide text-base-content/50"></thead>
88
- <tbody id="query-result-body"></tbody>
89
- </table>
90
- </div>
91
- </div>
92
- </div>
93
- </div>
94
- <script src="{{basePath}}/js/query.js" defer></script>
1
+ <div class="space-y-6">
2
+ <!-- Header -->
3
+ <div class="flex flex-wrap items-center justify-between gap-3">
4
+ <div>
5
+ <p class="text-sm font-semibold uppercase tracking-wider text-primary">Workspace</p>
6
+ <h1 class="mt-1 text-3xl font-extrabold tracking-tight">Query editor</h1>
7
+ <p class="mt-1.5 text-sm text-base-content/55">Run arbitrary SQL — SELECT results render as a table, COUNT gives a summary, and write statements execute.</p>
8
+ </div>
9
+ <div class="flex flex-wrap gap-2">
10
+ {{#unless readonly}}
11
+ <button id="save-query" type="button" class="btn btn-outline btn-sm gap-1.5">
12
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M8 7H5a2 2 0 00-2 2v9a2 2 0 002 2h14a2 2 0 002-2V9a2 2 0 00-2-2h-3m-1 4l-3 3m0 0l-3-3m3 3V4" /></svg>
13
+ Save query
14
+ </button>
15
+ {{/unless}}
16
+ <button id="run-query" type="button" class="btn btn-primary btn-sm gap-1.5">
17
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z" /><path stroke-linecap="round" stroke-linejoin="round" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
18
+ Run
19
+ </button>
20
+ </div>
21
+ </div>
22
+
23
+ <!-- Saved queries row -->
24
+ <div class="flex flex-wrap items-end gap-3">
25
+ <div class="form-control w-full max-w-xs">
26
+ <label class="field-label" for="saved-query-select">Saved queries</label>
27
+ <select id="saved-query-select" class="field-select">
28
+ <option value="">— select a saved query —</option>
29
+ {{#each savedQueries}}
30
+ <option value="{{this.id}}">{{this.name}}</option>
31
+ {{/each}}
32
+ </select>
33
+ </div>
34
+ {{#unless readonly}}
35
+ <button id="delete-query" type="button" class="btn btn-ghost btn-sm mb-0.5 text-error" disabled>Delete saved</button>
36
+ <div class="form-control w-full max-w-xs">
37
+ <label class="field-label" for="save-query-name">Save current SQL as</label>
38
+ <div class="join w-full">
39
+ <input id="save-query-name" type="text" class="field-input join-item !rounded-r-none" placeholder="query name" autocomplete="off" />
40
+ <button id="save-query-inline" type="button" class="btn btn-outline join-item">Save</button>
41
+ </div>
42
+ </div>
43
+ {{/unless}}
44
+ </div>
45
+
46
+ <!-- Editor -->
47
+ <div class="app-card overflow-hidden">
48
+ <div class="flex items-center justify-between border-b border-base-200 bg-base-200/40 px-4 py-2.5">
49
+ <span class="flex items-center gap-2 text-sm font-semibold text-base-content/60">
50
+ <span class="grid h-5 w-5 place-items-center rounded-md bg-primary/10 text-primary">
51
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" /></svg>
52
+ </span>
53
+ query.sql
54
+ </span>
55
+ <span class="text-[11px] text-base-content/40">Ctrl/⌘ + Enter to run</span>
56
+ </div>
57
+ <div class="p-3">
58
+ <div class="sql-editor-wrap">
59
+ <pre id="query-gutter" class="sql-gutter" aria-hidden="true">1</pre>
60
+ <textarea id="query-sql" class="sql-input" rows="10" placeholder="SELECT * FROM users WHERE …" spellcheck="false"></textarea>
61
+ </div>
62
+ <div id="query-message" class="mt-3"></div>
63
+ </div>
64
+ </div>
65
+
66
+ <!-- Results -->
67
+ <div id="query-result" class="hidden">
68
+ <div class="app-card overflow-hidden">
69
+ <div class="flex items-center gap-2 border-b border-base-200 px-4 py-3">
70
+ <span id="result-kind" class="grid h-6 w-6 place-items-center rounded-md bg-success/10 text-success">
71
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M5 13l4 4L19 7" /></svg>
72
+ </span>
73
+ <span id="result-summary" class="text-sm font-medium text-base-content/60"></span>
74
+ <div class="ml-auto flex items-center gap-1.5">
75
+ <button id="export-csv" type="button" class="btn btn-ghost btn-xs gap-1 text-base-content/70 hover:text-primary" disabled>
76
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" /></svg>
77
+ CSV
78
+ </button>
79
+ <button id="export-json" type="button" class="btn btn-ghost btn-xs gap-1 text-base-content/70 hover:text-primary" disabled>
80
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" /></svg>
81
+ JSON
82
+ </button>
83
+ </div>
84
+ </div>
85
+ <div class="overflow-x-auto">
86
+ <table class="table table-sm table-pin-rows">
87
+ <thead id="query-result-head" class="bg-base-200/70 text-xs uppercase tracking-wide text-base-content/50"></thead>
88
+ <tbody id="query-result-body"></tbody>
89
+ </table>
90
+ </div>
91
+ </div>
92
+ </div>
93
+ </div>
94
+ <script src="{{basePath}}/js/query.js" defer></script>