admindb 2.1.1 → 2.3.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 (119) hide show
  1. package/README.md +368 -335
  2. package/dist/app.js +227 -1
  3. package/dist/auth/config.d.ts +1 -1
  4. package/dist/auth/config.js +109 -1
  5. package/dist/auth/crypto.js +98 -1
  6. package/dist/auth/index.js +21 -1
  7. package/dist/auth/middleware.js +78 -1
  8. package/dist/auth/routes.js +64 -1
  9. package/dist/auth/types.js +7 -1
  10. package/dist/cli/args.d.ts +1 -1
  11. package/dist/cli/args.js +309 -1
  12. package/dist/cli/config.js +118 -1
  13. package/dist/cli/index.js +20 -1
  14. package/dist/cli/runner.js +208 -1
  15. package/dist/cli.js +4 -1
  16. package/dist/data/chain.d.ts +1 -0
  17. package/dist/data/chain.js +130 -0
  18. package/dist/data/datasets.d.ts +1 -1
  19. package/dist/data/datasets.js +171 -1
  20. package/dist/data/detector.d.ts +1 -1
  21. package/dist/data/detector.js +535 -1
  22. package/dist/data/engine.d.ts +1 -1
  23. package/dist/data/engine.js +586 -1
  24. package/dist/data/index.d.ts +1 -1
  25. package/dist/data/index.js +30 -1
  26. package/dist/data/prng.d.ts +1 -0
  27. package/dist/data/prng.js +73 -0
  28. package/dist/data/registry.d.ts +1 -0
  29. package/dist/data/registry.js +805 -0
  30. package/dist/data/schema-graph.d.ts +1 -0
  31. package/dist/data/schema-graph.js +210 -0
  32. package/dist/data/strategies.d.ts +1 -1
  33. package/dist/data/strategies.js +100 -1
  34. package/dist/data/templates.d.ts +1 -0
  35. package/dist/data/templates.js +134 -0
  36. package/dist/data/types.d.ts +1 -1
  37. package/dist/data/types.js +5 -1
  38. package/dist/data/validator.d.ts +1 -0
  39. package/dist/data/validator.js +160 -0
  40. package/dist/db/database.d.ts +1 -1
  41. package/dist/db/database.js +472 -1
  42. package/dist/db/export.d.ts +1 -1
  43. package/dist/db/export.js +67 -1
  44. package/dist/db/filters.d.ts +1 -1
  45. package/dist/db/filters.js +119 -1
  46. package/dist/db/index.js +24 -1
  47. package/dist/db/introspection.js +164 -1
  48. package/dist/db/manager.js +327 -1
  49. package/dist/db/migrations.js +100 -1
  50. package/dist/db/postgres.d.ts +1 -1
  51. package/dist/db/postgres.js +687 -1
  52. package/dist/db/types.d.ts +1 -1
  53. package/dist/db/types.js +6 -1
  54. package/dist/index.d.ts +1 -1
  55. package/dist/index.js +52 -1
  56. package/dist/public/css/app.css +1 -1
  57. package/dist/public/js/api.js +1 -1
  58. package/dist/public/js/browse.js +1 -1
  59. package/dist/public/js/common.js +1 -0
  60. package/dist/public/js/databases.js +1 -1
  61. package/dist/public/js/designer.js +1 -1
  62. package/dist/public/js/forms.js +1 -1
  63. package/dist/public/js/home.js +1 -0
  64. package/dist/public/js/inspector.js +1 -1
  65. package/dist/public/js/query.js +1 -1
  66. package/dist/public/js/schema.js +1 -1
  67. package/dist/public/js/seed.js +1 -1
  68. package/dist/routes/api/erd.d.ts +1 -0
  69. package/dist/routes/api/erd.js +45 -0
  70. package/dist/routes/api/helpers.d.ts +1 -1
  71. package/dist/routes/api/helpers.js +213 -1
  72. package/dist/routes/api/import-export.js +82 -1
  73. package/dist/routes/api/index.js +48 -1
  74. package/dist/routes/api/query.js +74 -1
  75. package/dist/routes/api/rows.js +358 -1
  76. package/dist/routes/api/seed.js +167 -1
  77. package/dist/routes/api/tables.js +216 -1
  78. package/dist/routes/databases.js +210 -1
  79. package/dist/routes/index.js +19 -1
  80. package/dist/routes/pages.js +446 -1
  81. package/dist/serverless.js +149 -1
  82. package/dist/sql/classifier.js +24 -1
  83. package/dist/sql/error-analyzer.d.ts +1 -0
  84. package/dist/sql/error-analyzer.js +550 -0
  85. package/dist/sql/generator.d.ts +1 -1
  86. package/dist/sql/generator.js +245 -1
  87. package/dist/sql/index.js +18 -1
  88. package/dist/types/api.d.ts +1 -1
  89. package/dist/types/api.js +2 -1
  90. package/dist/utils/colors.js +54 -1
  91. package/dist/utils/common.d.ts +1 -1
  92. package/dist/utils/common.js +185 -1
  93. package/dist/utils/csv.d.ts +1 -1
  94. package/dist/utils/csv.js +61 -1
  95. package/dist/utils/datatype.js +167 -1
  96. package/dist/utils/icons.js +81 -1
  97. package/dist/utils/index.js +20 -1
  98. package/dist/utils/logger.js +36 -1
  99. package/dist/views/layouts/main.hbs +1 -74
  100. package/dist/views/pages/databases.hbs +1 -233
  101. package/dist/views/pages/designer.hbs +1 -89
  102. package/dist/views/pages/erd.hbs +1 -0
  103. package/dist/views/pages/error.hbs +1 -10
  104. package/dist/views/pages/form.hbs +1 -59
  105. package/dist/views/pages/home.hbs +1 -124
  106. package/dist/views/pages/info.hbs +1 -0
  107. package/dist/views/pages/login.hbs +1 -98
  108. package/dist/views/pages/query.hbs +1 -87
  109. package/dist/views/pages/schema.hbs +1 -298
  110. package/dist/views/pages/seed-select.hbs +251 -0
  111. package/dist/views/pages/seed.hbs +273 -167
  112. package/dist/views/pages/table.hbs +1 -327
  113. package/dist/views/partials/navbar.hbs +1 -83
  114. package/dist/views/partials/sidebar.hbs +1 -117
  115. package/docs/API.md +149 -144
  116. package/docs/EXAMPLES.md +579 -576
  117. package/docs/SECURITY.md +265 -265
  118. package/package.json +79 -78
  119. package/dist/public/js/icons.js +0 -1
@@ -1,98 +1 @@
1
- <div class="flex min-h-[75vh] items-center justify-center py-6 px-4">
2
- <div class="w-full max-w-md">
3
- <div class="mb-8 text-center">
4
- <div class="mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-2xl bg-primary/10 text-primary shadow-soft">
5
- {{{icon "database" class="h-8 w-8"}}}
6
- </div>
7
- <h1 class="text-2xl font-extrabold tracking-tight text-base-content sm:text-3xl">
8
- Admin<span class="text-primary">DB</span>
9
- </h1>
10
- <p class="mt-1 text-sm text-base-content/60">
11
- Sign in to access your database administration console
12
- </p>
13
- </div>
14
- <div class="app-card border border-base-300/80 bg-base-100 p-6 sm:p-8 shadow-soft">
15
- {{#if error}}
16
- <div class="mb-6 flex items-start gap-3 rounded-xl bg-error/10 p-4 text-sm text-error border border-error/20" role="alert">
17
- <span class="shrink-0 mt-0.5">
18
- {{{icon "warning" class="h-5 w-5"}}}
19
- </span>
20
- <div class="font-medium">{{error}}</div>
21
- </div>
22
- {{/if}}
23
- {{#if isDefaultPassword}}
24
- <div class="mb-6 rounded-xl bg-info/10 p-3.5 text-xs text-info border border-info/20">
25
- <div class="font-semibold flex items-center gap-1.5 mb-1">
26
- {{{icon "warning" class="h-4 w-4 shrink-0"}}}
27
- Default Credentials Active
28
- </div>
29
- <p class="text-base-content/70">
30
- Username: <code class="font-mono font-semibold text-primary">admin</code> · Password: <code class="font-mono font-semibold text-primary">admin</code>
31
- </p>
32
- <p class="mt-1 text-[11px] text-base-content/50">
33
- Set <code class="font-mono">ADMINDB_PASSWORD</code> in production to secure your console.
34
- </p>
35
- </div>
36
- {{/if}}
37
- <form method="POST" action="{{basePath}}/login" class="space-y-4">
38
- {{#if next}}
39
- <input type="hidden" name="next" value="{{next}}" />
40
- {{/if}}
41
- <div>
42
- <label class="field-label" for="login-username">
43
- <span>Username</span>
44
- </label>
45
- <div class="relative">
46
- <input
47
- id="login-username"
48
- type="text"
49
- name="username"
50
- value="{{#if username}}{{username}}{{else}}admin{{/if}}"
51
- required
52
- autofocus
53
- placeholder="Username"
54
- class="field-input pl-10"
55
- autocomplete="username"
56
- />
57
- <div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3 text-base-content/40">
58
- {{{icon "user" class="h-5 w-5"}}}
59
- </div>
60
- </div>
61
- </div>
62
- <div>
63
- <label class="field-label" for="login-password">
64
- <span>Password</span>
65
- </label>
66
- <div class="relative">
67
- <input
68
- id="login-password"
69
- type="password"
70
- name="password"
71
- required
72
- placeholder="••••••••"
73
- class="field-input pl-10"
74
- autocomplete="current-password"
75
- />
76
- <div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3 text-base-content/40">
77
- {{{icon "lock" class="h-5 w-5"}}}
78
- </div>
79
- </div>
80
- </div>
81
- <div class="pt-2">
82
- <button type="submit" class="btn btn-primary w-full gap-2 font-semibold shadow-soft">
83
- {{{icon "logout" class="h-4 w-4 rotate-180"}}}
84
- Sign In
85
- </button>
86
- </div>
87
- </form>
88
- </div>
89
- <div class="mt-6 space-y-2 text-center text-xs text-base-content/55">
90
- <p class="leading-relaxed">
91
- <span class="font-semibold text-base-content/80">Security Notice:</span> Built-in auth provides basic access control. For production, protect your admin routes using your own application authentication, VPN, or reverse proxy.
92
- </p>
93
- <p class="text-base-content/40">
94
- Native authentication can be disabled via <code class="font-mono">--no-auth</code> or <code class="font-mono">auth: false</code>.
95
- </p>
96
- </div>
97
- </div>
98
- </div>
1
+ <div class="flex min-h-[75vh] items-center justify-center py-6 px-4">
99
2
  <div class="w-full max-w-md">
100
3
  <div class="mb-8 text-center">
101
4
  <div class="mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-2xl bg-primary/10 text-primary shadow-soft">
102
5
  {{{icon "database" class="h-8 w-8"}}}
103
6
  </div>
104
7
  <h1 class="font-headings text-2xl font-extrabold tracking-tight text-base-content sm:text-3xl">
105
8
  Admin<span class="text-primary">DB</span>
106
9
  </h1>
107
10
  <p class="mt-1 text-sm text-base-content/60">
108
11
  Sign in to access your database administration console
109
12
  </p>
110
13
  </div>
111
14
  <div class="app-card border bg-base-100/90 p-6 sm:p-8 shadow-card backdrop-blur-md">
112
15
  {{#if error}}
113
16
  <div class="mb-6 flex items-start gap-3 rounded-xl bg-error/10 p-4 text-sm text-error border border-error/20" role="alert">
114
17
  <span class="shrink-0 mt-0.5">
115
18
  {{{icon "warning" class="h-5 w-5"}}}
116
19
  </span>
117
20
  <div class="font-medium">{{error}}</div>
118
21
  </div>
119
22
  {{/if}}
120
23
  {{#if isDefaultPassword}}
121
24
  <div class="mb-6 rounded-xl bg-info/10 p-3.5 text-xs text-info border border-info/20">
122
25
  <div class="font-semibold flex items-center gap-1.5 mb-1">
123
26
  {{{icon "warning" class="h-4 w-4 shrink-0"}}}
124
27
  Default Credentials Active
125
28
  </div>
126
29
  <p class="text-base-content/70">
127
30
  Username: <code class="font-mono font-semibold text-primary">admin</code> · Password: <code class="font-mono font-semibold text-primary">admin</code>
128
31
  </p>
129
32
  <p class="mt-1 text-[11px] text-base-content/50">
130
33
  Set <code class="font-mono">ADMINDB_PASSWORD</code> in production to secure your console.
131
34
  </p>
132
35
  </div>
133
36
  {{/if}}
134
37
  <form method="POST" action="{{basePath}}/login" class="space-y-4">
135
38
  {{#if next}}
136
39
  <input type="hidden" name="next" value="{{next}}" />
137
40
  {{/if}}
138
41
  <div>
139
42
  <label class="field-label" for="login-username">
140
43
  <span>Username</span>
141
44
  </label>
142
45
  <div class="relative">
143
46
  <input
144
47
  id="login-username"
145
48
  type="text"
146
49
  name="username"
147
50
  value="{{#if username}}{{username}}{{else}}admin{{/if}}"
148
51
  required
149
52
  autofocus
150
53
  placeholder="Username"
151
54
  class="field-input pl-10"
152
55
  autocomplete="username"
153
56
  />
154
57
  <div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3 text-base-content/40">
155
58
  {{{icon "user" class="h-5 w-5"}}}
156
59
  </div>
157
60
  </div>
158
61
  </div>
159
62
  <div>
160
63
  <label class="field-label" for="login-password">
161
64
  <span>Password</span>
162
65
  </label>
163
66
  <div class="relative">
164
67
  <input
165
68
  id="login-password"
166
69
  type="password"
167
70
  name="password"
168
71
  required
169
72
  placeholder="••••••••"
170
73
  class="field-input pl-10"
171
74
  autocomplete="current-password"
172
75
  />
173
76
  <div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3 text-base-content/40">
174
77
  {{{icon "lock" class="h-5 w-5"}}}
175
78
  </div>
176
79
  </div>
177
80
  </div>
178
81
  <div class="pt-2">
179
82
  <button type="submit" class="btn btn-primary w-full gap-2 font-semibold shadow-soft">
180
83
  {{{icon "logout" class="h-4 w-4 rotate-180"}}}
181
84
  Sign In
182
85
  </button>
183
86
  </div>
184
87
  </form>
185
88
  </div>
186
89
  <div class="mt-6 space-y-2 text-center text-xs text-base-content/55">
187
90
  <p class="leading-relaxed">
188
91
  <span class="font-semibold text-base-content/80">Security Notice:</span> Built-in auth provides basic access control. For production, protect your admin routes using your own application authentication, VPN, or reverse proxy.
189
92
  </p>
190
93
  <p class="text-base-content/40">
191
94
  Native authentication can be disabled via <code class="font-mono">--no-auth</code> or <code class="font-mono">auth: false</code>.
192
95
  </p>
193
96
  </div>
194
97
  </div>
@@ -1,87 +1 @@
1
- <div class="space-y-6">
2
- <div class="flex flex-wrap items-center justify-between gap-3">
3
- <div>
4
- <p class="text-sm font-semibold uppercase tracking-wider text-primary">Workspace</p>
5
- <h1 class="mt-1 text-3xl font-extrabold tracking-tight">Query editor</h1>
6
- <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>
7
- </div>
8
- <div class="flex flex-wrap gap-2">
9
- {{#unless readonly}}
10
- <button id="save-query" type="button" class="btn btn-outline btn-sm gap-1.5">
11
- {{{icon "save" class="h-4 w-4"}}}
12
- Save query
13
- </button>
14
- {{/unless}}
15
- <button id="run-query" type="button" class="btn btn-primary btn-sm gap-1.5">
16
- {{{icon "run" class="h-4 w-4"}}}
17
- Run
18
- </button>
19
- </div>
20
- </div>
21
- <div class="flex flex-wrap items-end gap-3">
22
- <div class="form-control w-full max-w-xs">
23
- <label class="field-label" for="saved-query-select">Saved queries</label>
24
- <select id="saved-query-select" class="field-select">
25
- <option value="">— select a saved query —</option>
26
- {{#each savedQueries}}
27
- <option value="{{this.id}}">{{this.name}}</option>
28
- {{/each}}
29
- </select>
30
- </div>
31
- {{#unless readonly}}
32
- <button id="delete-query" type="button" class="btn btn-ghost btn-sm mb-0.5 text-error" disabled>Delete saved</button>
33
- <div class="form-control w-full max-w-xs">
34
- <label class="field-label" for="save-query-name">Save current SQL as</label>
35
- <div class="join w-full">
36
- <input id="save-query-name" type="text" class="field-input join-item !rounded-r-none" placeholder="query name" autocomplete="off" />
37
- <button id="save-query-inline" type="button" class="btn btn-outline join-item">Save</button>
38
- </div>
39
- </div>
40
- {{/unless}}
41
- </div>
42
- <div class="app-card overflow-hidden">
43
- <div class="flex items-center justify-between border-b border-base-200 bg-base-200/40 px-4 py-2.5">
44
- <span class="flex items-center gap-2 text-sm font-semibold text-base-content/60">
45
- <span class="grid h-5 w-5 place-items-center rounded-md bg-primary/10 text-primary">
46
- {{{icon "query" class="h-3.5 w-3.5"}}}
47
- </span>
48
- query.sql
49
- </span>
50
- <span class="text-[11px] text-base-content/40">Ctrl/⌘ + Enter to run</span>
51
- </div>
52
- <div>
53
- <div class="sql-editor-wrap">
54
- <pre id="query-gutter" class="sql-gutter" aria-hidden="true">1</pre>
55
- <textarea id="query-sql" class="sql-input" rows="10" placeholder="SELECT * FROM users WHERE …" spellcheck="false"></textarea>
56
- </div>
57
- <div id="query-message"></div>
58
- </div>
59
- </div>
60
- <div id="query-result" class="hidden">
61
- <div class="app-card overflow-hidden">
62
- <div class="flex items-center gap-2 border-b border-base-200 px-4 py-3">
63
- <span id="result-kind" class="grid h-6 w-6 place-items-center rounded-md bg-success/10 text-success">
64
- {{{icon "check" class="h-4 w-4"}}}
65
- </span>
66
- <span id="result-summary" class="text-sm font-medium text-base-content/60"></span>
67
- <div class="ml-auto flex items-center gap-1.5">
68
- <button id="export-csv" type="button" class="btn btn-ghost btn-xs gap-1 text-base-content/70 hover:text-primary" disabled>
69
- {{{icon "export" class="h-3.5 w-3.5"}}}
70
- CSV
71
- </button>
72
- <button id="export-json" type="button" class="btn btn-ghost btn-xs gap-1 text-base-content/70 hover:text-primary" disabled>
73
- {{{icon "export" class="h-3.5 w-3.5"}}}
74
- JSON
75
- </button>
76
- </div>
77
- </div>
78
- <div class="overflow-x-auto">
79
- <table class="table table-sm table-pin-rows">
80
- <thead id="query-result-head" class="bg-base-200/70 text-xs uppercase tracking-wide text-base-content/50"></thead>
81
- <tbody id="query-result-body"></tbody>
82
- </table>
83
- </div>
84
- </div>
85
- </div>
86
- </div>
87
- <script src="{{basePath}}/js/query.js" defer></script>
1
+ <div class="space-y-6">
88
2
  <div class="page-header">
89
3
  <div class="page-header-row">
90
4
  <div>
91
5
  <div class="page-pretitle">Workspace</div>
92
6
  <h1 class="page-title">Query editor</h1>
93
7
  <p class="mt-0.5 text-xs text-base-content/60">Run arbitrary SQL — SELECT results render as a table, COUNT gives a summary, and write statements execute.</p>
94
8
  </div>
95
9
  <div class="page-actions">
96
10
  {{#unless readonly}}
97
11
  <button id="save-query" type="button" class="btn btn-outline btn-sm gap-1.5">
98
12
  {{{icon "save" class="h-4 w-4"}}}
99
13
  Save query
100
14
  </button>
101
15
  {{/unless}}
102
16
  <button id="run-query" type="button" class="btn btn-primary btn-sm gap-1.5">
103
17
  {{{icon "run" class="h-4 w-4"}}}
104
18
  Run query
105
19
  </button>
106
20
  </div>
107
21
  </div>
108
22
  </div>
109
23
  <div class="flex flex-wrap items-end gap-3">
110
24
  <div class="form-control w-full max-w-xs">
111
25
  <label class="field-label" for="saved-query-select">Saved queries</label>
112
26
  <select id="saved-query-select" class="field-select">
113
27
  <option value="">— select a saved query —</option>
114
28
  {{#each savedQueries}}
115
29
  <option value="{{this.id}}">{{this.name}}</option>
116
30
  {{/each}}
117
31
  </select>
118
32
  </div>
119
33
  {{#unless readonly}}
120
34
  <button id="delete-query" type="button" class="btn btn-ghost btn-sm mb-0.5 text-error" disabled>Delete saved</button>
121
35
  <div class="form-control w-full max-w-xs">
122
36
  <label class="field-label" for="save-query-name">Save current SQL as</label>
123
37
  <div class="join w-full">
124
38
  <input id="save-query-name" type="text" class="field-input join-item !rounded-r-none" placeholder="query name" autocomplete="off" />
125
39
  <button id="save-query-inline" type="button" class="btn btn-outline join-item">Save</button>
126
40
  </div>
127
41
  </div>
128
42
  {{/unless}}
129
43
  </div>
130
44
  <div class="app-card overflow-hidden">
131
45
  <div class="flex flex-wrap items-center justify-between gap-2 border-b border-base-200 bg-base-200/40 px-4 py-2">
132
46
  <div class="flex items-center gap-2">
133
47
  <span class="flex items-center gap-1.5 text-xs font-semibold text-base-content/70">
134
48
  <span class="grid h-5 w-5 place-items-center rounded bg-primary/10 text-primary">
135
49
  {{{icon "query" class="h-3.5 w-3.5"}}}
136
50
  </span>
137
51
  query.sql
138
52
  </span>
139
53
  <span class="badge badge-sm badge-ghost font-mono text-[10px] uppercase font-bold">{{#if isPostgres}}Postgres{{else}}SQLite{{/if}}</span>
140
54
  </div>
141
55
  <div class="flex flex-wrap items-center gap-1.5">
142
56
  <div class="dropdown dropdown-end">
143
57
  <label tabindex="0" class="btn btn-ghost btn-xs gap-1 text-base-content/70 hover:text-primary">
144
58
  {{{icon "terminal" class="h-3.5 w-3.5"}}}
145
59
  Snippets
146
60
  </label>
147
61
  <ul tabindex="0" class="dropdown-content z-30 menu p-1.5 shadow-2xl bg-base-100 rounded-xl w-56 border border-base-300 text-xs">
148
62
  <li class="menu-title text-[10px] uppercase tracking-wider text-base-content/40">Query Templates</li>
149
63
  <li><a class="js-snippet" data-snippet="SELECT * FROM table_name\nWHERE condition\nORDER BY id DESC\nLIMIT 50;">SELECT rows</a></li>
150
64
  <li><a class="js-snippet" data-snippet="SELECT COUNT(*) AS total\nFROM table_name;">COUNT summary</a></li>
151
65
  <li><a class="js-snippet" data-snippet="SELECT a.*, b.*\nFROM table_a a\nJOIN table_b b ON b.a_id = a.id\nLIMIT 50;">JOIN tables</a></li>
152
66
  <li><a class="js-snippet" data-snippet="INSERT INTO table_name (col1, col2)\nVALUES ('value1', 'value2');">INSERT row</a></li>
153
67
  <li><a class="js-snippet" data-snippet="UPDATE table_name\nSET col1 = 'value1'\nWHERE id = 1;">UPDATE row</a></li>
154
68
  <li><a class="js-snippet" data-snippet="DELETE FROM table_name\nWHERE id = 1;">DELETE row</a></li>
155
69
  <li><a class="js-snippet" data-snippet="CREATE TABLE new_table (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT NOT NULL,\n created_at TEXT DEFAULT (datetime('now'))\n);">CREATE TABLE</a></li>
156
70
  </ul>
157
71
  </div>
158
72
  <button id="format-query" type="button" class="btn btn-ghost btn-xs gap-1 text-base-content/70 hover:text-primary" title="Format SQL (Beautify)">
159
73
  {{{icon "format" class="h-3.5 w-3.5"}}}
160
74
  Format
161
75
  </button>
162
76
  <button id="copy-query" type="button" class="btn btn-ghost btn-xs gap-1 text-base-content/70 hover:text-primary" title="Copy SQL to clipboard">
163
77
  {{{icon "copy" class="h-3.5 w-3.5"}}}
164
78
  Copy
165
79
  </button>
166
80
  <button id="clear-query" type="button" class="btn btn-ghost btn-xs gap-1 text-base-content/70 hover:text-error" title="Clear editor">
167
81
  {{{icon "trash" class="h-3.5 w-3.5"}}}
168
82
  Clear
169
83
  </button>
170
84
  <span class="hidden sm:inline-block text-[11px] text-base-content/35 border-l border-base-300 pl-2">Ctrl/⌘ + Enter to run</span>
171
85
  </div>
172
86
  </div>
173
87
  <div class="sql-editor-wrap">
174
88
  <div id="query-gutter" class="sql-gutter" aria-hidden="true">
175
89
  <div class="sql-gutter-line">1</div>
176
90
  </div>
177
91
  <div class="sql-editor-stage">
178
92
  <pre id="query-highlight" class="sql-code-highlight" aria-hidden="true"><code id="query-code"></code></pre>
179
93
  <textarea id="query-sql" class="sql-code-input" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off" placeholder="-- Write SQL here or choose a snippet/saved query&#10;SELECT * FROM table_name WHERE …"></textarea>
180
94
  </div>
181
95
  </div>
182
96
  <div class="flex items-center justify-between border-t border-base-200 bg-base-200/30 px-3.5 py-1.5 text-[11px] font-mono text-base-content/50">
183
97
  <div class="flex items-center gap-3">
184
98
  <span id="editor-cursor-pos">Ln 1, Col 1</span>
185
99
  <span id="editor-stats">0 chars</span>
186
100
  </div>
187
101
  <div class="flex items-center gap-2">
188
102
  <span class="text-[10px] text-base-content/40">Tab to indent · Ctrl+Z / Ctrl+Y to undo/redo · Ctrl+/ to comment</span>
189
103
  </div>
190
104
  </div>
191
105
  <div id="query-message" class="p-3"></div>
192
106
  </div>
193
107
  <div id="query-result" class="hidden">
194
108
  <div class="app-card overflow-hidden">
195
109
  <div class="flex items-center gap-2 border-b border-base-200 px-4 py-3">
196
110
  <span id="result-kind" class="grid h-6 w-6 place-items-center rounded-md bg-success/10 text-success">
197
111
  {{{icon "check" class="h-4 w-4"}}}
198
112
  </span>
199
113
  <span id="result-summary" class="text-sm font-medium text-base-content/60"></span>
200
114
  <div class="ml-auto flex items-center gap-1.5">
201
115
  <button id="export-csv" type="button" class="btn btn-ghost btn-xs gap-1 text-base-content/70 hover:text-primary" disabled>
202
116
  {{{icon "export" class="h-3.5 w-3.5"}}}
203
117
  CSV
204
118
  </button>
205
119
  <button id="export-json" type="button" class="btn btn-ghost btn-xs gap-1 text-base-content/70 hover:text-primary" disabled>
206
120
  {{{icon "export" class="h-3.5 w-3.5"}}}
207
121
  JSON
208
122
  </button>
209
123
  </div>
210
124
  </div>
211
125
  <div class="overflow-x-auto">
212
126
  <table class="table table-sm table-pin-rows">
213
127
  <thead id="query-result-head" class="bg-base-200/70 text-xs uppercase tracking-wide text-base-content/50"></thead>
214
128
  <tbody id="query-result-body"></tbody>
215
129
  </table>
216
130
  </div>
217
131
  </div>
218
132
  </div>