drixio 1.1.10 → 1.2.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.
- package/README.md +1 -0
- package/dist/cli.js +999 -458
- package/dist/studio/assets/index-DOTfHOMW.js +3244 -0
- package/dist/studio/assets/index-DPNeFArq.css +1 -0
- package/dist/studio/index.html +38 -34
- package/package.json +2 -1
- package/dist/studio/assets/index-CgrvFEsQ.js +0 -2324
- package/dist/studio/assets/index-ru5pIVFY.css +0 -1
|
@@ -0,0 +1,3244 @@
|
|
|
1
|
+
(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e=`
|
|
2
|
+
<header>
|
|
3
|
+
<div class="header-left">
|
|
4
|
+
<div class="header-brand">
|
|
5
|
+
<div class="header-brand-icon">
|
|
6
|
+
<span class="material-symbols-outlined">database</span>
|
|
7
|
+
</div>
|
|
8
|
+
<div class="header-brand-info">
|
|
9
|
+
<div class="header-brand-text">
|
|
10
|
+
<span class="header-brand-title">DRIXIO</span>
|
|
11
|
+
<span class="header-brand-badge">STUDIO</span>
|
|
12
|
+
</div>
|
|
13
|
+
<span class="header-brand-by">By Horizora</span>
|
|
14
|
+
</div>
|
|
15
|
+
</div>
|
|
16
|
+
<span class="header-brand-divider">/</span>
|
|
17
|
+
<div id="current-path" aria-label="Breadcrumbs">
|
|
18
|
+
<div class="header-dropdown-wrap" style="position: relative;">
|
|
19
|
+
<div class="breadcrumb-item db-crumb" id="breadcrumb-db-crumb" title="Switch Database" role="button" tabindex="0">
|
|
20
|
+
<span class="material-symbols-outlined breadcrumb-icon">dns</span>
|
|
21
|
+
<span id="db-name">database</span>
|
|
22
|
+
<span class="material-symbols-outlined breadcrumb-caret" id="db-switcher-caret">unfold_more</span>
|
|
23
|
+
</div>
|
|
24
|
+
<div id="db-switcher-dropdown" class="db-switcher-dropdown hidden" role="menu"></div>
|
|
25
|
+
</div>
|
|
26
|
+
<span id="slash" class="material-symbols-outlined breadcrumb-separator">chevron_right</span>
|
|
27
|
+
<div id="current-table" class="breadcrumb-item table-crumb" title="Current Table">
|
|
28
|
+
<span class="material-symbols-outlined" id="table-icon">table_chart</span>
|
|
29
|
+
<span id="table-name">users</span>
|
|
30
|
+
</div>
|
|
31
|
+
<div id="env-badge-wrap" class="breadcrumb-item">
|
|
32
|
+
<span id="env-badge" class="env-badge local hidden">LOCAL</span>
|
|
33
|
+
</div>
|
|
34
|
+
<button id="header-connect-btn" class="header-connect-pill" title="Database Connection Status & Switcher" type="button">
|
|
35
|
+
<span class="material-symbols-outlined connect-icon">settings_ethernet</span>
|
|
36
|
+
<span class="connect-label">Connect</span>
|
|
37
|
+
</button>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<div class="header-right">
|
|
42
|
+
<!-- Quick Search Pill (Supabase Style) -->
|
|
43
|
+
<div id="header-search-pill" class="header-search-pill" title="Quick search tables (Ctrl+K)">
|
|
44
|
+
<span class="material-symbols-outlined search-icon">search</span>
|
|
45
|
+
<span class="search-placeholder">Search...</span>
|
|
46
|
+
<kbd class="search-kbd">Ctrl K</kbd>
|
|
47
|
+
</div>
|
|
48
|
+
|
|
49
|
+
<div class="header-actions">
|
|
50
|
+
<!-- Import Pure Icon Button (Opens Import Modal) -->
|
|
51
|
+
<button id="import-btn" class="header-icon-btn" title="Import Data / Restore Database" aria-label="Import Data" type="button">
|
|
52
|
+
<span id="import-icon" class="material-symbols-outlined">upload</span>
|
|
53
|
+
</button>
|
|
54
|
+
|
|
55
|
+
<!-- Export Pure Icon Button (Dropdown Popover) -->
|
|
56
|
+
<div class="header-dropdown-wrap" style="position: relative;">
|
|
57
|
+
<button id="export-btn" class="header-icon-btn" title="Export Data / Schema" aria-label="Export Data" type="button">
|
|
58
|
+
<span id="export-icon" class="material-symbols-outlined">download</span>
|
|
59
|
+
</button>
|
|
60
|
+
|
|
61
|
+
<ul id="export-dropdown" class="dropdown-menu hidden" role="menu">
|
|
62
|
+
<li class="dropdown-submenu">
|
|
63
|
+
<a class="dropdown-item">
|
|
64
|
+
<span class="material-symbols-outlined">table_chart</span>
|
|
65
|
+
<span class="dropdown-item-label">Data</span>
|
|
66
|
+
<span class="caret material-symbols-outlined">chevron_right</span>
|
|
67
|
+
</a>
|
|
68
|
+
<ul class="dropdown-menu sub-menu">
|
|
69
|
+
<li>
|
|
70
|
+
<button id="export-data-csv-btn" class="dropdown-item">
|
|
71
|
+
<span class="material-symbols-outlined">description</span>
|
|
72
|
+
<span class="dropdown-item-label">Table Records</span>
|
|
73
|
+
<span class="dropdown-badge">CSV</span>
|
|
74
|
+
</button>
|
|
75
|
+
</li>
|
|
76
|
+
<li>
|
|
77
|
+
<button id="export-data-json-btn" class="dropdown-item">
|
|
78
|
+
<span class="material-symbols-outlined">data_object</span>
|
|
79
|
+
<span class="dropdown-item-label">Table Records</span>
|
|
80
|
+
<span class="dropdown-badge">JSON</span>
|
|
81
|
+
</button>
|
|
82
|
+
</li>
|
|
83
|
+
<li class="data-tab-only" style="display: none;">
|
|
84
|
+
<button id="export-data-filtered-csv-btn" class="dropdown-item" style="color: var(--color-primary);">
|
|
85
|
+
<span class="material-symbols-outlined" style="color: var(--color-primary);">filter_alt</span>
|
|
86
|
+
<span class="dropdown-item-label">Filtered Records</span>
|
|
87
|
+
<span class="dropdown-badge" style="color: var(--color-primary); border-color: rgba(59, 130, 246, 0.3);">CSV</span>
|
|
88
|
+
</button>
|
|
89
|
+
</li>
|
|
90
|
+
<li class="data-tab-only" style="display: none;">
|
|
91
|
+
<button id="export-data-filtered-json-btn" class="dropdown-item" style="color: var(--color-primary);">
|
|
92
|
+
<span class="material-symbols-outlined" style="color: var(--color-primary);">filter_alt</span>
|
|
93
|
+
<span class="dropdown-item-label">Filtered Records</span>
|
|
94
|
+
<span class="dropdown-badge" style="color: var(--color-primary); border-color: rgba(59, 130, 246, 0.3);">JSON</span>
|
|
95
|
+
</button>
|
|
96
|
+
</li>
|
|
97
|
+
</ul>
|
|
98
|
+
</li>
|
|
99
|
+
|
|
100
|
+
<li class="dropdown-submenu">
|
|
101
|
+
<a class="dropdown-item">
|
|
102
|
+
<span class="material-symbols-outlined">schema</span>
|
|
103
|
+
<span class="dropdown-item-label">Schema</span>
|
|
104
|
+
<span class="caret material-symbols-outlined">chevron_right</span>
|
|
105
|
+
</a>
|
|
106
|
+
<ul class="dropdown-menu sub-menu">
|
|
107
|
+
<li>
|
|
108
|
+
<button id="export-schema-dict-btn" class="dropdown-item">
|
|
109
|
+
<span class="material-symbols-outlined">menu_book</span>
|
|
110
|
+
<span class="dropdown-item-label">Data Dictionary</span>
|
|
111
|
+
<span class="dropdown-badge">MD</span>
|
|
112
|
+
</button>
|
|
113
|
+
</li>
|
|
114
|
+
<li>
|
|
115
|
+
<button id="export-schema-sql-btn" class="dropdown-item">
|
|
116
|
+
<span class="material-symbols-outlined">code</span>
|
|
117
|
+
<span class="dropdown-item-label">DDL Schema Only</span>
|
|
118
|
+
<span class="dropdown-badge">SQL</span>
|
|
119
|
+
</button>
|
|
120
|
+
</li>
|
|
121
|
+
<li>
|
|
122
|
+
<button id="export-schema-snapshot-btn" class="dropdown-item">
|
|
123
|
+
<span class="material-symbols-outlined">data_object</span>
|
|
124
|
+
<span class="dropdown-item-label">Schema Snapshot</span>
|
|
125
|
+
<span class="dropdown-badge">JSON</span>
|
|
126
|
+
</button>
|
|
127
|
+
</li>
|
|
128
|
+
</ul>
|
|
129
|
+
</li>
|
|
130
|
+
|
|
131
|
+
<li class="dropdown-submenu console-tab-only" style="display: none;">
|
|
132
|
+
<a class="dropdown-item">
|
|
133
|
+
<span class="material-symbols-outlined">terminal</span>
|
|
134
|
+
<span class="dropdown-item-label">SQL Console</span>
|
|
135
|
+
<span class="caret material-symbols-outlined">chevron_right</span>
|
|
136
|
+
</a>
|
|
137
|
+
<ul class="dropdown-menu sub-menu">
|
|
138
|
+
<li>
|
|
139
|
+
<button id="export-console-csv-btn" class="dropdown-item">
|
|
140
|
+
<span class="material-symbols-outlined">description</span>
|
|
141
|
+
<span class="dropdown-item-label">Query Result</span>
|
|
142
|
+
<span class="dropdown-badge">CSV</span>
|
|
143
|
+
</button>
|
|
144
|
+
</li>
|
|
145
|
+
<li>
|
|
146
|
+
<button id="export-console-json-btn" class="dropdown-item">
|
|
147
|
+
<span class="material-symbols-outlined">data_object</span>
|
|
148
|
+
<span class="dropdown-item-label">Query Result</span>
|
|
149
|
+
<span class="dropdown-badge">JSON</span>
|
|
150
|
+
</button>
|
|
151
|
+
</li>
|
|
152
|
+
</ul>
|
|
153
|
+
</li>
|
|
154
|
+
|
|
155
|
+
<li class="dropdown-submenu">
|
|
156
|
+
<a class="dropdown-item">
|
|
157
|
+
<span class="material-symbols-outlined">account_tree</span>
|
|
158
|
+
<span class="dropdown-item-label">ERD</span>
|
|
159
|
+
<span class="caret material-symbols-outlined">chevron_right</span>
|
|
160
|
+
</a>
|
|
161
|
+
<ul class="dropdown-menu sub-menu">
|
|
162
|
+
<li>
|
|
163
|
+
<button id="export-erd-json-btn" class="dropdown-item">
|
|
164
|
+
<span class="material-symbols-outlined">layers</span>
|
|
165
|
+
<span class="dropdown-item-label">Canvas Layout</span>
|
|
166
|
+
<span class="dropdown-badge">JSON</span>
|
|
167
|
+
</button>
|
|
168
|
+
</li>
|
|
169
|
+
</ul>
|
|
170
|
+
</li>
|
|
171
|
+
|
|
172
|
+
<li class="dropdown-divider"></li>
|
|
173
|
+
|
|
174
|
+
<li>
|
|
175
|
+
<button id="export-db-sql-btn" class="dropdown-item">
|
|
176
|
+
<span class="material-symbols-outlined">database</span>
|
|
177
|
+
<span class="dropdown-item-label">Full Database</span>
|
|
178
|
+
<span class="dropdown-badge">SQL</span>
|
|
179
|
+
</button>
|
|
180
|
+
</li>
|
|
181
|
+
</ul>
|
|
182
|
+
</div>
|
|
183
|
+
|
|
184
|
+
<!-- Help / Shortcuts Icon Button -->
|
|
185
|
+
<button id="header-help-btn" class="header-icon-btn" title="Shortcuts & Documentation" aria-label="Help" type="button">
|
|
186
|
+
<span class="material-symbols-outlined">help</span>
|
|
187
|
+
</button>
|
|
188
|
+
|
|
189
|
+
<!-- GitHub Link Icon Button -->
|
|
190
|
+
<a href="https://github.com/TerKSDev/drixio" target="_blank" rel="noopener noreferrer" class="header-icon-btn" title="GitHub Repository" aria-label="GitHub">
|
|
191
|
+
<span class="material-symbols-outlined">code</span>
|
|
192
|
+
</a>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
</header>
|
|
196
|
+
`,t=`
|
|
197
|
+
<aside id="sidebar-panel">
|
|
198
|
+
<div id="sidebar-header">
|
|
199
|
+
<div class="sidebar-header-title">
|
|
200
|
+
<span class="sidebar-header-text">Table Editor</span>
|
|
201
|
+
</div>
|
|
202
|
+
<button id="sidebar-collapse-btn" class="sidebar-icon-btn" title="Toggle Sidebar" aria-label="Toggle Sidebar">
|
|
203
|
+
<span class="material-symbols-outlined" id="sidebar-collapse-icon">menu_open</span>
|
|
204
|
+
</button>
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
<div id="sidebar-content">
|
|
208
|
+
<div id="schema-selector-wrap" class="hidden">
|
|
209
|
+
<div class="schema-select-box" id="schema-select-box" title="Switch Schema" style="cursor: pointer;">
|
|
210
|
+
<div class="schema-select-text">
|
|
211
|
+
<span class="schema-prefix">schema</span>
|
|
212
|
+
<span class="schema-current-name" id="schema-current-display">public</span>
|
|
213
|
+
</div>
|
|
214
|
+
<span class="material-symbols-outlined schema-chevron">expand_more</span>
|
|
215
|
+
</div>
|
|
216
|
+
</div>
|
|
217
|
+
|
|
218
|
+
<button type="button" id="add-table-btn" class="sidebar-new-table-btn" title="Create New Table">
|
|
219
|
+
<span class="material-symbols-outlined">add</span>
|
|
220
|
+
<span>New table</span>
|
|
221
|
+
</button>
|
|
222
|
+
|
|
223
|
+
<div id="sidebar-search-row">
|
|
224
|
+
<div id="search-bar" role="search">
|
|
225
|
+
<span class="material-symbols-outlined" id="search-icon">search</span>
|
|
226
|
+
<input type="text" placeholder="Search tables..." id="search-input" aria-label="Search tables" autocomplete="off" spellcheck="false" />
|
|
227
|
+
<button id="search-clear-btn" class="search-clear-btn hidden" title="Clear filter" aria-label="Clear filter">✕</button>
|
|
228
|
+
</div>
|
|
229
|
+
<button type="button" id="refresh-tables-btn" class="sidebar-refresh-btn" title="Refresh tables" aria-label="Refresh tables">
|
|
230
|
+
<span class="material-symbols-outlined">refresh</span>
|
|
231
|
+
</button>
|
|
232
|
+
</div>
|
|
233
|
+
|
|
234
|
+
<nav id="table-nav" aria-label="Database Tables">
|
|
235
|
+
<!-- Dynamically populated tables -->
|
|
236
|
+
</nav>
|
|
237
|
+
</div>
|
|
238
|
+
|
|
239
|
+
<div id="sidebar-footer">
|
|
240
|
+
<div class="sidebar-db-status" title="Database Connection Status">
|
|
241
|
+
<span class="status-indicator-dot" id="sidebar-db-status-dot"></span>
|
|
242
|
+
<span id="brand-db-type">CONNECTING...</span>
|
|
243
|
+
</div>
|
|
244
|
+
|
|
245
|
+
<div class="sidebar-footer-actions">
|
|
246
|
+
<span class="sidebar-version-tag">v1.2.0</span>
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
249
|
+
</aside>
|
|
250
|
+
`,n=`
|
|
251
|
+
<nav id="tab-nav" role="navigation" aria-label="Views and tools">
|
|
252
|
+
<!-- Top Navigation Items -->
|
|
253
|
+
<div class="rail-top">
|
|
254
|
+
<button
|
|
255
|
+
type="button"
|
|
256
|
+
class="tab-btn rail-item isCurrentTab"
|
|
257
|
+
onclick="handleSwitchTab('data-btn')"
|
|
258
|
+
id="data-btn"
|
|
259
|
+
title="Table Editor"
|
|
260
|
+
aria-label="Table Editor"
|
|
261
|
+
>
|
|
262
|
+
<span class="material-symbols-outlined rail-icon" id="data-icon">table_chart</span>
|
|
263
|
+
<span class="rail-label">Table Editor</span>
|
|
264
|
+
</button>
|
|
265
|
+
|
|
266
|
+
<button
|
|
267
|
+
type="button"
|
|
268
|
+
class="tab-btn rail-item"
|
|
269
|
+
onclick="handleSwitchTab('schema-btn')"
|
|
270
|
+
id="schema-btn"
|
|
271
|
+
title="Schema"
|
|
272
|
+
aria-label="Schema"
|
|
273
|
+
>
|
|
274
|
+
<span class="material-symbols-outlined rail-icon" id="schema-icon">schema</span>
|
|
275
|
+
<span class="rail-label">Schema</span>
|
|
276
|
+
</button>
|
|
277
|
+
|
|
278
|
+
<button
|
|
279
|
+
type="button"
|
|
280
|
+
class="tab-btn rail-item"
|
|
281
|
+
onclick="handleSwitchTab('sql-btn')"
|
|
282
|
+
id="sql-btn"
|
|
283
|
+
title="SQL Editor"
|
|
284
|
+
aria-label="SQL Editor"
|
|
285
|
+
>
|
|
286
|
+
<span class="material-symbols-outlined rail-icon" id="sql-icon">terminal</span>
|
|
287
|
+
<span class="rail-label">SQL Editor</span>
|
|
288
|
+
</button>
|
|
289
|
+
|
|
290
|
+
<div class="rail-divider"></div>
|
|
291
|
+
|
|
292
|
+
<button
|
|
293
|
+
type="button"
|
|
294
|
+
class="tab-btn rail-item"
|
|
295
|
+
onclick="handleSwitchTab('erd-btn')"
|
|
296
|
+
id="erd-btn"
|
|
297
|
+
title="ERD Diagram"
|
|
298
|
+
aria-label="ERD Diagram"
|
|
299
|
+
>
|
|
300
|
+
<span class="material-symbols-outlined rail-icon" id="erd-icon">account_tree</span>
|
|
301
|
+
<span class="rail-label">ERD Diagram</span>
|
|
302
|
+
</button>
|
|
303
|
+
|
|
304
|
+
<button
|
|
305
|
+
type="button"
|
|
306
|
+
class="tab-btn rail-item"
|
|
307
|
+
onclick="handleSwitchTab('status-btn')"
|
|
308
|
+
id="status-btn"
|
|
309
|
+
title="Database Status"
|
|
310
|
+
aria-label="Database Status"
|
|
311
|
+
>
|
|
312
|
+
<span class="material-symbols-outlined rail-icon" id="status-icon">monitoring</span>
|
|
313
|
+
<span class="rail-label">Status & Metrics</span>
|
|
314
|
+
</button>
|
|
315
|
+
|
|
316
|
+
<button
|
|
317
|
+
type="button"
|
|
318
|
+
class="tab-btn rail-item hidden"
|
|
319
|
+
onclick="handleSwitchTab('connect-btn')"
|
|
320
|
+
id="connect-btn"
|
|
321
|
+
title="Connection"
|
|
322
|
+
aria-label="Connection"
|
|
323
|
+
>
|
|
324
|
+
<span class="material-symbols-outlined rail-icon" id="connect-icon">cable</span>
|
|
325
|
+
<span class="rail-label">Connection</span>
|
|
326
|
+
</button>
|
|
327
|
+
</div>
|
|
328
|
+
|
|
329
|
+
<!-- Bottom: Theme Mode Toggle Button -->
|
|
330
|
+
<div class="rail-bottom">
|
|
331
|
+
<div class="rail-divider"></div>
|
|
332
|
+
<button
|
|
333
|
+
type="button"
|
|
334
|
+
class="rail-item rail-theme-toggle"
|
|
335
|
+
id="theme-toggle"
|
|
336
|
+
title="Toggle Theme (Light / Dark)"
|
|
337
|
+
aria-label="Toggle Theme"
|
|
338
|
+
>
|
|
339
|
+
<span class="material-symbols-outlined rail-icon" id="theme-icon">dark_mode</span>
|
|
340
|
+
<span class="rail-label" id="theme-text">Dark Mode</span>
|
|
341
|
+
</button>
|
|
342
|
+
</div>
|
|
343
|
+
</nav>
|
|
344
|
+
`;async function r(){return await(await fetch(`/api/tables`)).json()}async function i(e,t){return await(await fetch(`/api/tables`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({tableName:e,columns:t})})).json()}async function a(){return await(await fetch(`/api/config`)).json()}async function o(){return await(await fetch(`/api/tables/stats`)).json()}async function s(e,t={}){let{where:n=``,limit:r=50,offset:i=0,orderCol:a,orderAsc:o}=t,s=new URLSearchParams;n&&s.append(`where`,n),s.append(`limit`,r.toString()),s.append(`offset`,i.toString()),a&&(s.append(`orderCol`,a),s.append(`orderAsc`,o===!1?`false`:`true`));let c=`/api/tables/${e}/data?${s.toString()}`;return await(await fetch(c)).json()}async function c(e){return await(await fetch(`/api/tables/${e}/schema`)).json()}async function l(e){return await(await fetch(`/api/tables/${e}/indexes`)).json()}async function u(e){return await(await fetch(`/api/query`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({sql:e})})).json()}async function d(e,t){return await(await fetch(`/api/tables/${encodeURIComponent(e)}/records`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(t)})).json()}async function f(e,t){return await(await fetch(`/api/tables/${encodeURIComponent(e)}/schema`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(t)})).json()}async function p(e){return await(await fetch(`/api/tables/${encodeURIComponent(e)}/truncate`,{method:`POST`})).json()}async function m(e,t=`csv`){let n=await(await fetch(`/api/query/export?format=${t}`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e)})).blob(),r=URL.createObjectURL(n),i=document.createElement(`a`);i.href=r,i.download=`query_result_${new Date().getTime()}.${t}`,document.body.appendChild(i),i.click(),document.body.removeChild(i),URL.revokeObjectURL(r)}async function h(e){return await(await fetch(`/api/query/explain`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({sql:e})})).json()}async function g(e){return await(await fetch(`/api/schema/diff`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e)})).json()}async function _(e){return await(await fetch(`/api/schema/apply-migration`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({sql:e})})).json()}function v(){window.location.href=`/api/schema/snapshot`}async function y(){return await(await fetch(`/api/snippets`)).json()}async function b(e){return await(await fetch(`/api/snippets`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(e)})).json()}async function x(e,t){return await(await fetch(`/api/snippets/${encodeURIComponent(e)}`,{method:`PUT`,headers:{"Content-Type":`application/json`},body:JSON.stringify(t)})).json()}async function S(e){return await(await fetch(`/api/snippets/${encodeURIComponent(e)}`,{method:`DELETE`})).json()}async function C(){return await(await fetch(`/api/schemas`)).json()}async function w(e){return await(await fetch(`/api/schemas/switch`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({schema:e})})).json()}async function T(){return await(await fetch(`/api/database/enums`)).json()}var E=null,D=-1;function O(){E||(E=document.createElement(`div`),E.className=`drixio-context-menu`,document.body.appendChild(E),document.addEventListener(`pointerdown`,e=>{E.classList.contains(`visible`)&&!E.contains(e.target)&&j()}),document.addEventListener(`keydown`,e=>{if(!E.classList.contains(`visible`))return;if(e.key===`Escape`){e.preventDefault(),e.stopPropagation(),j();return}let t=Array.from(E.querySelectorAll(`.drixio-context-menu-item:not(.disabled)`));t.length!==0&&(e.key===`ArrowDown`?(e.preventDefault(),e.stopPropagation(),D=(D+1)%t.length,k(t)):e.key===`ArrowUp`?(e.preventDefault(),e.stopPropagation(),D=(D-1+t.length)%t.length,k(t)):e.key===`Enter`&&D>=0&&D<t.length&&(e.preventDefault(),e.stopPropagation(),t[D].click()))}),window.addEventListener(`scroll`,e=>{E.classList.contains(`visible`)&&(E.contains(e.target)||j())},!0))}function k(e){e.forEach((e,t)=>{t===D?(e.classList.add(`focused`),e.scrollIntoView({block:`nearest`})):e.classList.remove(`focused`)})}function A(e,t){e.preventDefault&&e.preventDefault(),e.stopPropagation&&e.stopPropagation(),O(),E.innerHTML=``,E.classList.remove(`visible`),D=-1,t.forEach(e=>{if(e.type===`divider`||e===`divider`){let e=document.createElement(`div`);e.className=`drixio-context-menu-divider`,E.appendChild(e);return}let t=document.createElement(`div`);t.className=`drixio-context-menu-item`,e.danger&&t.classList.add(`danger`),e.disabled&&t.classList.add(`disabled`);let n=e.icon?`<span class="material-symbols-outlined drixio-menu-icon">${e.icon}</span>`:``,r=e.checked?`<span class="material-symbols-outlined drixio-menu-check">check</span>`:``,i=e.shortcut?`<span class="drixio-menu-shortcut">${e.shortcut}</span>`:``,a=e.desc?`<span class="drixio-menu-desc">${e.desc}</span>`:``;t.innerHTML=`
|
|
345
|
+
${n}
|
|
346
|
+
<div class="drixio-menu-content">
|
|
347
|
+
<span class="drixio-menu-label">${e.label}</span>
|
|
348
|
+
${a}
|
|
349
|
+
</div>
|
|
350
|
+
${i}
|
|
351
|
+
${r}
|
|
352
|
+
`,t.addEventListener(`mouseenter`,()=>{let e=Array.from(E.querySelectorAll(`.drixio-context-menu-item:not(.disabled)`));D=e.indexOf(t),k(e)}),t.addEventListener(`click`,t=>{e.disabled||(t.stopPropagation(),j(),e.action&&e.action())}),E.appendChild(t)}),requestAnimationFrame(()=>{let t=E.getBoundingClientRect(),n=e.clientX??0,r=e.clientY??0,i=`left`,a=`top`;n+t.width>window.innerWidth-8&&(n=Math.max(8,window.innerWidth-t.width-8),i=`right`),r+t.height>window.innerHeight-8&&(r=Math.max(8,window.innerHeight-t.height-8),a=`bottom`),E.style.transformOrigin=`${a} ${i}`,E.style.left=`${n}px`,E.style.top=`${r}px`,E.classList.add(`visible`)})}function j(){E&&(E.classList.remove(`visible`),D=-1)}async function M(e,t,n){let r=document.getElementById(`mock-data-modal-overlay`);r||(r=document.createElement(`div`),r.id=`mock-data-modal-overlay`,r.className=`mock-modal-overlay`,document.body.appendChild(r)),r.innerHTML=`
|
|
353
|
+
<div class="mock-modal-container">
|
|
354
|
+
<div class="mock-modal-header">
|
|
355
|
+
<div class="mock-modal-title">
|
|
356
|
+
<span class="material-symbols-outlined" style="color: var(--color-primary);">auto_fix_high</span>
|
|
357
|
+
<h3>Generate Mock Data</h3>
|
|
358
|
+
<span class="mock-target-badge">${e}</span>
|
|
359
|
+
</div>
|
|
360
|
+
<button type="button" class="mock-modal-close" id="btn-mock-close">
|
|
361
|
+
<span class="material-symbols-outlined">close</span>
|
|
362
|
+
</button>
|
|
363
|
+
</div>
|
|
364
|
+
<div class="mock-modal-body" style="padding: 40px; text-align: center;">
|
|
365
|
+
<div style="display: inline-flex; align-items: center; gap: 8px; color: var(--color-text-soft);">
|
|
366
|
+
<span class="material-symbols-outlined animate-spin icon-18">progress_activity</span>
|
|
367
|
+
<span>Analyzing schema and preparing preview...</span>
|
|
368
|
+
</div>
|
|
369
|
+
</div>
|
|
370
|
+
</div>
|
|
371
|
+
`,r.querySelector(`#btn-mock-close`)?.addEventListener(`click`,i),r.classList.add(`visible`);function i(){r.classList.remove(`visible`)}let a=10,o=null;try{let t=await(await fetch(`/api/tables/${encodeURIComponent(e)}/mock/preview`)).json();if(!t.success||!t.data)throw Error(t.error||`Failed to load preview`);o=t.data}catch(e){r.innerHTML=`
|
|
372
|
+
<div class="mock-modal-container">
|
|
373
|
+
<div class="mock-modal-header">
|
|
374
|
+
<div class="mock-modal-title">
|
|
375
|
+
<span class="material-symbols-outlined" style="color: #ef4444;">error</span>
|
|
376
|
+
<h3>Failed to Load Mock Generator</h3>
|
|
377
|
+
</div>
|
|
378
|
+
<button type="button" class="mock-modal-close" id="btn-mock-close">
|
|
379
|
+
<span class="material-symbols-outlined">close</span>
|
|
380
|
+
</button>
|
|
381
|
+
</div>
|
|
382
|
+
<div class="mock-modal-body" style="padding: 24px;">
|
|
383
|
+
<p style="color: var(--color-text-soft); font-size: 13px;">${e.message}</p>
|
|
384
|
+
</div>
|
|
385
|
+
</div>
|
|
386
|
+
`,r.querySelector(`#btn-mock-close`)?.addEventListener(`click`,i);return}function s(){let{rules:t,previewRows:c}=o,l=t.filter(e=>e.strategy.type!==`pk_auto`),u=l.map(e=>`<th>${e.column.name}</th>`).join(``),d=c.map(e=>`<tr>${l.map(t=>`<td title="${String(e[t.column.name]??``)}">${String(e[t.column.name]??``)}</td>`).join(``)}</tr>`).join(``),f=t.map(({column:e,strategy:t})=>`
|
|
387
|
+
<div class="mock-rule-item">
|
|
388
|
+
<div class="mock-rule-col">
|
|
389
|
+
<span class="mock-col-name">${e.name}</span>
|
|
390
|
+
<span class="mock-col-type">${e.type||`TEXT`}</span>
|
|
391
|
+
</div>
|
|
392
|
+
<span class="mock-rule-strategy ${t.type===`fk`?`is-fk`:``}">
|
|
393
|
+
${t.label}
|
|
394
|
+
</span>
|
|
395
|
+
</div>
|
|
396
|
+
`).join(``);r.innerHTML=`
|
|
397
|
+
<div class="mock-modal-container">
|
|
398
|
+
<div class="mock-modal-header">
|
|
399
|
+
<div class="mock-modal-title">
|
|
400
|
+
<span class="material-symbols-outlined" style="color: var(--color-primary);">auto_fix_high</span>
|
|
401
|
+
<h3>Generate Mock Data</h3>
|
|
402
|
+
<span class="mock-target-badge">${e}</span>
|
|
403
|
+
</div>
|
|
404
|
+
<button type="button" class="mock-modal-close" id="btn-mock-close">
|
|
405
|
+
<span class="material-symbols-outlined">close</span>
|
|
406
|
+
</button>
|
|
407
|
+
</div>
|
|
408
|
+
|
|
409
|
+
<div class="mock-modal-body">
|
|
410
|
+
<!-- Row Count Selector -->
|
|
411
|
+
<div class="mock-section">
|
|
412
|
+
<label class="mock-section-label">Number of Rows</label>
|
|
413
|
+
<div class="mock-count-pills">
|
|
414
|
+
<button type="button" class="mock-pill ${a===10?`active`:``}" data-count="10">10 Rows</button>
|
|
415
|
+
<button type="button" class="mock-pill ${a===50?`active`:``}" data-count="50">50 Rows</button>
|
|
416
|
+
<button type="button" class="mock-pill ${a===100?`active`:``}" data-count="100">100 Rows</button>
|
|
417
|
+
<div class="mock-custom-input-wrap">
|
|
418
|
+
<input type="number" id="mock-custom-count" min="1" max="500" placeholder="Custom" value="${[10,50,100].includes(a)?``:a}" />
|
|
419
|
+
</div>
|
|
420
|
+
</div>
|
|
421
|
+
</div>
|
|
422
|
+
|
|
423
|
+
<!-- Column Strategies -->
|
|
424
|
+
<div class="mock-section">
|
|
425
|
+
<label class="mock-section-label">Inferred Column Rules (${t.length} columns)</label>
|
|
426
|
+
<div class="mock-rules-list">
|
|
427
|
+
${f}
|
|
428
|
+
</div>
|
|
429
|
+
</div>
|
|
430
|
+
|
|
431
|
+
<!-- Sample Preview -->
|
|
432
|
+
<div class="mock-section">
|
|
433
|
+
<label class="mock-section-label">Sample Preview (First 3 Rows)</label>
|
|
434
|
+
<div class="mock-preview-table-wrap">
|
|
435
|
+
<table class="mock-preview-table">
|
|
436
|
+
<thead><tr>${u}</tr></thead>
|
|
437
|
+
<tbody>${d}</tbody>
|
|
438
|
+
</table>
|
|
439
|
+
</div>
|
|
440
|
+
</div>
|
|
441
|
+
</div>
|
|
442
|
+
|
|
443
|
+
<div class="mock-modal-footer">
|
|
444
|
+
<button type="button" class="mock-btn-secondary" id="btn-mock-cancel">Cancel</button>
|
|
445
|
+
<button type="button" class="mock-btn-primary" id="btn-mock-generate">
|
|
446
|
+
<span class="material-symbols-outlined icon-16">auto_fix_high</span>
|
|
447
|
+
<span>Generate & Insert ${a} Rows</span>
|
|
448
|
+
</button>
|
|
449
|
+
</div>
|
|
450
|
+
</div>
|
|
451
|
+
`,r.querySelector(`#btn-mock-close`)?.addEventListener(`click`,i),r.querySelector(`#btn-mock-cancel`)?.addEventListener(`click`,i),r.querySelectorAll(`.mock-pill`).forEach(e=>{e.addEventListener(`click`,()=>{a=parseInt(e.dataset.count,10),s()})});let p=r.querySelector(`#mock-custom-count`);p&&p.addEventListener(`change`,e=>{let t=parseInt(e.target.value,10);t&&t>0&&t<=500&&(a=t,s())}),r.querySelector(`#btn-mock-generate`)?.addEventListener(`click`,async()=>{let t=r.querySelector(`#btn-mock-generate`);if(t){t.disabled=!0,t.innerHTML=`<span class="material-symbols-outlined animate-spin icon-16">progress_activity</span> Inserting...`;try{let t=await(await fetch(`/api/tables/${encodeURIComponent(e)}/mock`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({count:a})})).json();if(!t.success)throw Error(t.error||`Failed to insert mock data`);i(),window.loadTableStats?.(),window.updateSidebarActiveTable?.(e),window.showToast&&window.showToast(`Successfully generated and inserted ${t.count||a} rows into "${e}"!`,`success`),n&&n()}catch(e){alert(`Failed to insert mock data: ${e.message}`),t.disabled=!1,t.innerHTML=`<span>Try Again</span>`}}})}s()}var N=[`INTEGER`,`VARCHAR(255)`,`TEXT`,`BOOLEAN`,`TIMESTAMP`,`DATETIME`,`DATE`,`NUMERIC`,`DECIMAL(10,2)`,`REAL`,`JSON`,`BLOB`,`UUID`,`ENUM`];function P(e){if(e==null)return null;let t=String(e).trim();if(!t||t.toLowerCase()===`null`||t===`-`)return null;for(;t.startsWith(`''`)&&t.endsWith(`''`)&&t.length>4;)t=t.slice(1,-1);if(/^-?\d+(\.\d+)?$/.test(t))return t;let n=t.toLowerCase();if(n===`true`||n===`false`)return n.toUpperCase();let r=t.toUpperCase();if(r===`CURRENT_TIMESTAMP`||r===`CURRENT_DATE`||r===`CURRENT_TIME`||r===`NOW()`||r===`TIMESTAMP`||r.startsWith(`CURRENT_TIMESTAMP`)||r.startsWith(`NOW()`)||r.startsWith(`GEN_RANDOM_UUID(`)||r.startsWith(`UUID_GENERATE_`)||r.startsWith(`UUID(`)||t.startsWith(`(`)&&t.endsWith(`)`))return r===`TIMESTAMP`?`CURRENT_TIMESTAMP`:t;let i=t.match(/^('[\s\S]*')(?:::[\w\s()]+)$/);return i?i[1]:t.startsWith(`'`)&&t.endsWith(`'`)&&t.length>=2?t:t.startsWith(`"`)&&t.endsWith(`"`)&&t.length>=2?`'${t.slice(1,-1).replace(/'/g,`''`)}'`:`'${t.replace(/'/g,`''`)}'`}var F=null;function I({anchorEl:e,title:t=``,placeholder:n=`Search...`,items:r=[],initialValue:i=``,onSelect:a,onCancel:o,width:s=320,maxHeight:c=260,searchable:l=!0,allowCustom:u=!1,emptyText:d=`No matching options found`}){L();let f=r.map(e=>typeof e==`string`||typeof e==`number`?{name:String(e),value:String(e),desc:``,icon:``}:{name:String(e.name??e.value??``),value:e.value===void 0?String(e.name??``):String(e.value),desc:e.desc?String(e.desc):``,icon:e.icon||``,badge:e.badge||``,badgeClass:e.badgeClass||``,group:e.group||``}),p=document.createElement(`div`);p.className=`dropdown-picker-popover`,s&&(p.style.width=typeof s==`number`?`${s}px`:s),p.innerHTML=`
|
|
452
|
+
${l?`
|
|
453
|
+
<div class="dropdown-picker-search-wrap">
|
|
454
|
+
<span class="material-symbols-outlined dropdown-picker-search-icon">search</span>
|
|
455
|
+
<input
|
|
456
|
+
type="text"
|
|
457
|
+
class="dropdown-picker-search-input"
|
|
458
|
+
placeholder="${n}"
|
|
459
|
+
autocomplete="off"
|
|
460
|
+
spellcheck="false"
|
|
461
|
+
/>
|
|
462
|
+
</div>`:``}
|
|
463
|
+
<div class="dropdown-picker-list-wrap" style="max-height: ${c}px;"></div>
|
|
464
|
+
`,document.body.appendChild(p);let m=p.querySelector(`.dropdown-picker-search-input`),h=p.querySelector(`.dropdown-picker-list-wrap`),g=-1,_=[],v=e=>String(e).replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`),y=(e,t)=>{if(!t)return v(e);let n=e.toLowerCase().indexOf(t.toLowerCase());return n===-1?v(e):`${v(e.slice(0,n))}<mark class="dropdown-picker-mark">${v(e.slice(n,n+t.length))}</mark>${v(e.slice(n+t.length))}`},b=(e=``)=>{let n=e.trim().toLowerCase();if(_=f.filter(e=>e.name.toLowerCase().includes(n)||e.value.toLowerCase().includes(n)||e.desc.toLowerCase().includes(n)),_.length===0)if(u&&n)_=[{name:e.trim(),value:e.trim(),desc:`Use custom value`,icon:`add`}];else{h.innerHTML=`
|
|
465
|
+
<div class="dropdown-picker-empty">
|
|
466
|
+
<span>${d}</span>
|
|
467
|
+
</div>
|
|
468
|
+
`,g=-1;return}if(g===-1&&i!=null){let e=String(i).trim().toLowerCase(),t=_.findIndex(t=>t.value.toLowerCase()===e||t.name.toLowerCase()===e);g=t===-1?0:t}else(g===-1||g>=_.length)&&(g=0);let r=``;t&&(r+=`<div class="dropdown-picker-section-header">${v(t)}</div>`);let a=``;_.forEach((e,t)=>{e.group&&e.group!==a&&(a=e.group,r+=`<div class="dropdown-picker-section-header">${v(e.group)}</div>`);let i=t===g,o=``;e.icon&&(o=e.icon.length<=3?`<span class="dropdown-picker-code-icon">${v(e.icon)}</span>`:`<span class="material-symbols-outlined dropdown-picker-material-icon">${v(e.icon)}</span>`);let s=``;e.badge&&(s=`<span class="dropdown-picker-item-badge ${e.badgeClass||``}">${v(e.badge)}</span>`);let c=``;e.desc&&(c=`<span class="dropdown-picker-item-desc">${y(e.desc,n)}</span>`),r+=`
|
|
469
|
+
<div class="dropdown-picker-item ${i?`active`:``}" data-index="${t}">
|
|
470
|
+
<div class="dropdown-picker-item-left">
|
|
471
|
+
${o}
|
|
472
|
+
<span class="dropdown-picker-item-name">${y(e.name,n)}</span>
|
|
473
|
+
</div>
|
|
474
|
+
<div class="dropdown-picker-item-right">
|
|
475
|
+
${c}
|
|
476
|
+
${s}
|
|
477
|
+
</div>
|
|
478
|
+
</div>
|
|
479
|
+
`}),h.innerHTML=r,h.querySelectorAll(`.dropdown-picker-item`).forEach(e=>{e.addEventListener(`mouseenter`,()=>{g=parseInt(e.dataset.index,10),x()}),e.addEventListener(`click`,t=>{t.stopPropagation();let n=parseInt(e.dataset.index,10);_[n]&&C(_[n])})}),S()},x=()=>{h.querySelectorAll(`.dropdown-picker-item`).forEach((e,t)=>{t===g?e.classList.add(`active`):e.classList.remove(`active`)})},S=()=>{let e=h.querySelector(`.dropdown-picker-item.active`);e&&e.scrollIntoView({block:`nearest`})},C=e=>{L(),a&&a(e.value,e)};(()=>{if(!e)return;let n=e.getBoundingClientRect(),r=s&&typeof s==`number`?s:320,i=c+(l?50:0)+(t?30:0),a=n.bottom+4,o=n.left;a+i>window.innerHeight-8&&(a=Math.max(8,n.top-i-4)),o+r>window.innerWidth-8&&(o=Math.max(8,window.innerWidth-r-8)),p.style.top=`${a}px`,p.style.left=`${o}px`})(),b(),m?(m.focus(),m.addEventListener(`input`,e=>{b(e.target.value)}),m.addEventListener(`keydown`,e=>{e.key===`ArrowDown`?(e.preventDefault(),_.length>0&&(g=(g+1)%_.length,x(),S())):e.key===`ArrowUp`?(e.preventDefault(),_.length>0&&(g=(g-1+_.length)%_.length,x(),S())):e.key===`Enter`?(e.preventDefault(),g>=0&&_[g]?C(_[g]):u&&m.value.trim()&&C({value:m.value.trim(),name:m.value.trim()})):e.key===`Escape`&&(e.preventDefault(),L(),o&&o())})):(p.tabIndex=-1,p.focus(),p.addEventListener(`keydown`,e=>{e.key===`ArrowDown`?(e.preventDefault(),_.length>0&&(g=(g+1)%_.length,x(),S())):e.key===`ArrowUp`?(e.preventDefault(),_.length>0&&(g=(g-1+_.length)%_.length,x(),S())):e.key===`Enter`?(e.preventDefault(),g>=0&&_[g]&&C(_[g])):e.key===`Escape`&&(e.preventDefault(),L(),o&&o())}));let w=t=>{!p.contains(t.target)&&!e.contains(t.target)&&(L(),o&&o())};setTimeout(()=>{document.addEventListener(`click`,w)},50),F={popover:p,cleanup:()=>{document.removeEventListener(`click`,w),p.parentNode&&p.remove(),F=null}}}function L(){F&&F.cleanup()}var R={sqlite:{category:`SQLITE DATA TYPES`,types:[{name:`INTEGER`,value:`INTEGER`,desc:`Signed integer (1 to 8 bytes, auto affinity)`,icon:`#`},{name:`TEXT`,value:`TEXT`,desc:`UTF-8 encoded text string`,icon:`abc`},{name:`VARCHAR(255)`,value:`VARCHAR(255)`,desc:`Variable-length character string`,icon:`abc`},{name:`BOOLEAN`,value:`BOOLEAN`,desc:`Logical boolean (stored as 0 or 1)`,icon:`toggle_on`},{name:`REAL`,value:`REAL`,desc:`Floating point number (8-byte IEEE 754)`,icon:`#`},{name:`NUMERIC`,value:`NUMERIC`,desc:`Exact / flexible numeric representation`,icon:`#`},{name:`DATETIME`,value:`DATETIME`,desc:`Date and time string (YYYY-MM-DD HH:MM:SS)`,icon:`schedule`},{name:`DATE`,value:`DATE`,desc:`Calendar date (YYYY-MM-DD)`,icon:`schedule`},{name:`TIME`,value:`TIME`,desc:`Time of day (HH:MM:SS)`,icon:`schedule`},{name:`JSON`,value:`JSON`,desc:`Textual JSON data (supports JSON functions)`,icon:`{}`},{name:`BLOB`,value:`BLOB`,desc:`Binary Large Object (raw binary data)`,icon:`data_array`},{name:`UUID`,value:`UUID`,desc:`Universally unique identifier (stored as TEXT)`,icon:`key`},{name:`ENUM`,value:`ENUM`,desc:`User-defined enumeration (CHECK constraint)`,icon:`list`}]},mysql:{category:`MYSQL DATA TYPES`,types:[{name:`INT`,value:`INT`,desc:`Standard 4-byte integer (-2.1B to 2.1B)`,icon:`#`},{name:`BIGINT`,value:`BIGINT`,desc:`Large 8-byte integer`,icon:`#`},{name:`TINYINT`,value:`TINYINT`,desc:`1-byte small integer (-128 to 127)`,icon:`#`},{name:`BOOLEAN`,value:`BOOLEAN`,desc:`Logical boolean (alias for TINYINT(1))`,icon:`toggle_on`},{name:`VARCHAR(255)`,value:`VARCHAR(255)`,desc:`Variable-length string with max limit`,icon:`abc`},{name:`TEXT`,value:`TEXT`,desc:`Standard text up to 65,535 bytes`,icon:`abc`},{name:`DECIMAL(10,2)`,value:`DECIMAL(10,2)`,desc:`Exact fixed-point numeric`,icon:`#`},{name:`FLOAT`,value:`FLOAT`,desc:`Single-precision floating-point number`,icon:`#`},{name:`DOUBLE`,value:`DOUBLE`,desc:`Double-precision floating-point number`,icon:`#`},{name:`DATETIME`,value:`DATETIME`,desc:`Date and time (1000-01-01 to 9999-12-31)`,icon:`schedule`},{name:`TIMESTAMP`,value:`TIMESTAMP`,desc:`UTC timestamp with automatic timezone conversion`,icon:`schedule`},{name:`DATE`,value:`DATE`,desc:`Calendar date (YYYY-MM-DD)`,icon:`schedule`},{name:`TIME`,value:`TIME`,desc:`Time value (HH:MM:SS)`,icon:`schedule`},{name:`JSON`,value:`JSON`,desc:`Native binary JSON document type`,icon:`{ }`},{name:`BLOB`,value:`BLOB`,desc:`Binary large object for raw data/files`,icon:`data_array`},{name:`ENUM`,value:`ENUM`,desc:`Enumeration with predefined string list`,icon:`list`}]},postgres:{category:`POSTGRES DATA TYPES`,types:[{name:`int4`,value:`int4`,desc:`Signed four-byte integer (integer)`,icon:`#`},{name:`int8`,value:`int8`,desc:`Signed eight-byte integer (bigint)`,icon:`#`},{name:`int2`,value:`int2`,desc:`Signed two-byte integer (smallint)`,icon:`#`},{name:`numeric`,value:`numeric`,desc:`Exact numeric of selectable precision`,icon:`#`},{name:`float8`,value:`float8`,desc:`Double precision floating-point number (8 bytes)`,icon:`#`},{name:`float4`,value:`float4`,desc:`Single precision floating-point number (4 bytes)`,icon:`#`},{name:`bool`,value:`boolean`,desc:`Logical boolean (true/false)`,icon:`toggle_on`},{name:`varchar(255)`,value:`varchar(255)`,desc:`Variable-length character string with limit`,icon:`abc`},{name:`text`,value:`text`,desc:`Variable-length character string`,icon:`abc`},{name:`uuid`,value:`uuid`,desc:`Universally unique identifier`,icon:`key`},{name:`timestamptz`,value:`timestamptz`,desc:`Date and time with time zone`,icon:`schedule`},{name:`timestamp`,value:`timestamp`,desc:`Date and time without time zone`,icon:`schedule`},{name:`date`,value:`date`,desc:`Calendar date (year, month, day)`,icon:`schedule`},{name:`time`,value:`time`,desc:`Time of day without time zone`,icon:`schedule`},{name:`timetz`,value:`timetz`,desc:`Time of day including time zone`,icon:`schedule`},{name:`jsonb`,value:`jsonb`,desc:`Binary JSON data, decomposed & indexable`,icon:`{ }`},{name:`json`,value:`json`,desc:`Textual JSON data`,icon:`{ }`},{name:`bytea`,value:`bytea`,desc:`Binary data ("byte array")`,icon:`data_array`},{name:`ENUM`,value:`ENUM`,desc:`User-defined enumeration values`,icon:`list`}]}};function ee(e){let t=(e||window.AppState?.dbType||`sqlite`).toLowerCase();return t.includes(`postgres`)||t.includes(`pg`)?`postgres`:t.includes(`mysql`)?`mysql`:`sqlite`}function te({anchorEl:e,initialValue:t=``,dialect:n,extraItems:r=[],onSelect:i,onCancel:a}){let o=R[ee(n)]||R.sqlite,s=[...o.types];Array.isArray(r)&&r.length>0&&s.unshift(...r),t&&!s.some(e=>e.value.toUpperCase()===t.trim().toUpperCase()||e.name.toUpperCase()===t.trim().toUpperCase())&&s.unshift({name:t.trim(),value:t.trim(),desc:`Current column type`,icon:`tune`}),I({anchorEl:e,title:o.category,placeholder:`Search types...`,items:s,initialValue:t,width:380,maxHeight:280,onSelect:i,onCancel:a})}function ne(e){let t=document.getElementById(`create-table-modal`);t&&t.remove();let n=(window.AppState?.dbType||`sqlite`).toLowerCase(),a=N,o=[{name:`id`,type:`INTEGER`,primaryKey:!0,nullable:!1,isUnique:!1,defaultValue:``,fkTarget:null},{name:`name`,type:`VARCHAR(255)`,primaryKey:!1,nullable:!0,isUnique:!1,defaultValue:``,fkTarget:null}],s=[],l=[],u={},d=null;r().then(e=>{Array.isArray(e)&&(s=e)}).catch(e=>console.warn(`Failed to preload tables for FK:`,e)),T().then(e=>{e?.success&&Array.isArray(e.data)&&(l=e.data,E(),w())}).catch(e=>console.warn(`Failed to preload enums:`,e)),t=document.createElement(`div`),t.id=`create-table-modal`,t.className=`modal-overlay`,t.innerHTML=`
|
|
480
|
+
<div class="modal-container create-table-modal-container">
|
|
481
|
+
<div class="modal-header">
|
|
482
|
+
<div class="modal-title-wrap">
|
|
483
|
+
<div class="modal-icon-badge">
|
|
484
|
+
<span class="material-symbols-outlined">add_circle</span>
|
|
485
|
+
</div>
|
|
486
|
+
<div class="modal-title-col">
|
|
487
|
+
<div class="modal-title-row">
|
|
488
|
+
<h3 class="modal-title-text">Create New Table</h3>
|
|
489
|
+
<span class="modal-title-badge">${n}</span>
|
|
490
|
+
</div>
|
|
491
|
+
<p class="modal-subtitle-text">Define table name, columns, and foreign key constraints</p>
|
|
492
|
+
</div>
|
|
493
|
+
</div>
|
|
494
|
+
<button type="button" id="close-create-table-modal-btn" class="modal-close-btn" title="Close (Esc)">
|
|
495
|
+
<span class="material-symbols-outlined">close</span>
|
|
496
|
+
</button>
|
|
497
|
+
</div>
|
|
498
|
+
|
|
499
|
+
<div class="create-table-modal-body" id="create-table-modal-body">
|
|
500
|
+
<!-- Table Name Field -->
|
|
501
|
+
<div class="table-form-group">
|
|
502
|
+
<label class="table-form-label">
|
|
503
|
+
<span>Table Name <span style="color: var(--color-error);">*</span></span>
|
|
504
|
+
</label>
|
|
505
|
+
<input type="text" id="new-table-name-input" class="table-name-input" placeholder="e.g. users, products, orders" autofocus />
|
|
506
|
+
<span id="table-name-error" style="display: none; font-size: 11px; color: var(--color-error); margin-top: 4px;"></span>
|
|
507
|
+
</div>
|
|
508
|
+
|
|
509
|
+
<!-- Quick Presets -->
|
|
510
|
+
<div class="table-presets-bar">
|
|
511
|
+
<span class="table-preset-label">Quick Add:</span>
|
|
512
|
+
<button type="button" id="preset-add-timestamps" class="table-preset-btn" title="Add created_at and updated_at timestamp columns">
|
|
513
|
+
<span class="material-symbols-outlined" style="font-size: 13px;">schedule</span>
|
|
514
|
+
<span>+ Timestamps</span>
|
|
515
|
+
</button>
|
|
516
|
+
<button type="button" id="preset-add-softdelete" class="table-preset-btn" title="Add deleted_at nullable timestamp column">
|
|
517
|
+
<span class="material-symbols-outlined" style="font-size: 13px;">delete_sweep</span>
|
|
518
|
+
<span>+ Soft Delete</span>
|
|
519
|
+
</button>
|
|
520
|
+
<button type="button" id="preset-add-uuid-pk" class="table-preset-btn" title="Change primary key to UUID">
|
|
521
|
+
<span class="material-symbols-outlined" style="font-size: 13px;">fingerprint</span>
|
|
522
|
+
<span>+ UUID PK</span>
|
|
523
|
+
</button>
|
|
524
|
+
</div>
|
|
525
|
+
|
|
526
|
+
<!-- Columns Section -->
|
|
527
|
+
<div class="table-form-group">
|
|
528
|
+
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 2px;">
|
|
529
|
+
<label class="table-form-label">
|
|
530
|
+
<span>Columns Definition (<span id="column-count-label">${o.length}</span>)</span>
|
|
531
|
+
</label>
|
|
532
|
+
<button type="button" id="btn-add-column-row" class="btn-secondary" style="display: inline-flex; align-items: center; gap: 4px; padding: 4px 10px; font-size: 11.5px; border-radius: 6px; cursor: pointer;">
|
|
533
|
+
<span class="material-symbols-outlined" style="font-size: 14px;">add</span>
|
|
534
|
+
<span>Add Column</span>
|
|
535
|
+
</button>
|
|
536
|
+
</div>
|
|
537
|
+
|
|
538
|
+
<!-- Column Header Grid -->
|
|
539
|
+
<div class="table-columns-header-grid">
|
|
540
|
+
<div>Column Name</div>
|
|
541
|
+
<div>Type</div>
|
|
542
|
+
<div style="text-align: center;">PK</div>
|
|
543
|
+
<div style="text-align: center;">Null</div>
|
|
544
|
+
<div style="text-align: center;">Unique</div>
|
|
545
|
+
<div>Default</div>
|
|
546
|
+
<div>Foreign Key</div>
|
|
547
|
+
<div style="text-align: center;">Actions</div>
|
|
548
|
+
</div>
|
|
549
|
+
|
|
550
|
+
<!-- Column Rows Container -->
|
|
551
|
+
<div id="create-table-columns-container" class="table-columns-body-container">
|
|
552
|
+
<!-- Rendered dynamically -->
|
|
553
|
+
</div>
|
|
554
|
+
</div>
|
|
555
|
+
|
|
556
|
+
<!-- Live SQL Preview Section -->
|
|
557
|
+
<div class="table-sql-preview-section" id="table-sql-preview-section">
|
|
558
|
+
<div class="table-sql-preview-header" id="table-sql-preview-toggle">
|
|
559
|
+
<div class="table-sql-preview-title">
|
|
560
|
+
<span class="material-symbols-outlined text-primary">terminal</span>
|
|
561
|
+
<span>SQL Preview (DDL)</span>
|
|
562
|
+
</div>
|
|
563
|
+
<div style="display: flex; align-items: center; gap: 6px;">
|
|
564
|
+
<span class="material-symbols-outlined" id="table-sql-preview-arrow" style="font-size: 16px; color: var(--color-text-soft);">expand_less</span>
|
|
565
|
+
</div>
|
|
566
|
+
</div>
|
|
567
|
+
<div class="table-sql-code-container">
|
|
568
|
+
<button type="button" id="copy-table-sql-btn" class="table-sql-copy-btn">
|
|
569
|
+
<span class="material-symbols-outlined" style="font-size: 13px;">content_copy</span>
|
|
570
|
+
<span>Copy</span>
|
|
571
|
+
</button>
|
|
572
|
+
<pre id="table-sql-preview-code" class="table-sql-code-block"></pre>
|
|
573
|
+
</div>
|
|
574
|
+
</div>
|
|
575
|
+
</div>
|
|
576
|
+
|
|
577
|
+
<div class="modal-footer">
|
|
578
|
+
<button type="button" id="cancel-create-table-btn" class="btn-secondary">Cancel</button>
|
|
579
|
+
<button type="button" id="submit-create-table-btn" class="btn-primary" style="display: inline-flex; align-items: center; gap: 6px;">
|
|
580
|
+
<span class="material-symbols-outlined" style="font-size: 16px;">check</span>
|
|
581
|
+
<span id="submit-create-table-text">Create Table</span>
|
|
582
|
+
</button>
|
|
583
|
+
</div>
|
|
584
|
+
</div>
|
|
585
|
+
`,document.body.appendChild(t);let f=document.getElementById(`create-table-columns-container`),p=document.getElementById(`new-table-name-input`),m=document.getElementById(`column-count-label`),h=document.getElementById(`submit-create-table-btn`),g=document.getElementById(`submit-create-table-text`),_=document.getElementById(`table-name-error`),v=document.getElementById(`table-sql-preview-code`),y=document.getElementById(`table-sql-preview-section`),b=document.getElementById(`table-sql-preview-toggle`),x=document.getElementById(`table-sql-preview-arrow`),S=document.getElementById(`copy-table-sql-btn`);b.onclick=()=>{let e=y.classList.toggle(`collapsed`);x.textContent=e?`expand_more`:`expand_less`},S.onclick=()=>{v.textContent&&(navigator.clipboard.writeText(v.textContent),window.showToast?.(`SQL copied to clipboard!`,`success`))};function C(e){return n===`mysql`?`\`${e}\``:`"${e}"`}function w(){let e=(p.value.trim()||`table_name`).trim(),t=[],r=[];n===`postgres`&&o.forEach(e=>{if(e.isNewEnum&&e.enumValues&&e.enumValues.length>0){let t=(e.newEnumName||e.type||`CustomEnum`).trim(),n=e.enumValues.map(e=>`'${e.replace(/'/g,`''`)}'`).join(`, `);r.push(`CREATE TYPE ${C(t)} AS ENUM (${n});`)}}),o.forEach(e=>{let r=e.name?C(e.name):`"column"`,i=(e.type||`VARCHAR(255)`).toLowerCase(),a=e.type||`VARCHAR(255)`,o=e.enumValues&&e.enumValues.length>0;if(n===`sqlite`?(o?a=`TEXT CHECK(${r} IN (${e.enumValues.map(e=>`'${e.replace(/'/g,`''`)}'`).join(`, `)}))`:i===`integer`||i===`int`?a=`INTEGER`:i===`text`||i===`string`?a=`TEXT`:i.startsWith(`varchar`)?a=e.type.toUpperCase():i===`boolean`||i===`bool`?a=`BOOLEAN`:[`decimal`,`numeric`,`float`,`double`,`real`].includes(i)||i.startsWith(`decimal`)?a=`REAL`:i===`date`?a=`DATE`:i===`datetime`||i===`timestamp`?a=`DATETIME`:i===`blob`?a=`BLOB`:(i===`json`||i===`uuid`)&&(a=`TEXT`),e.primaryKey&&(i.includes(`int`)||a===`INTEGER`)?a=`INTEGER PRIMARY KEY AUTOINCREMENT`:e.primaryKey?a+=` PRIMARY KEY`:(e.nullable||(a+=` NOT NULL`),e.isUnique&&(a+=` UNIQUE`))):n===`postgres`?e.primaryKey&&(i===`integer`||i===`int`)?a=`SERIAL PRIMARY KEY`:(o||e.isExistingEnum||e.isNewEnum?a=C(e.isNewEnum?e.newEnumName||e.type||`CustomEnum`:e.type):i===`integer`||i===`int`?a=`INTEGER`:i===`text`||i===`string`?a=`TEXT`:i.startsWith(`varchar`)?a=e.type.toUpperCase():i===`boolean`||i===`bool`?a=`BOOLEAN`:i===`decimal`||i===`numeric`||i.startsWith(`decimal`)?a=e.type.toUpperCase().includes(`(`)?e.type.toUpperCase():`NUMERIC`:i===`datetime`||i===`timestamp`?a=`TIMESTAMP`:i===`date`?a=`DATE`:i===`real`?a=`REAL`:i===`json`?a=`JSONB`:i===`blob`?a=`BYTEA`:i===`uuid`&&(a=`UUID`),e.primaryKey&&(a+=` PRIMARY KEY`),!e.nullable&&!e.primaryKey&&(a+=` NOT NULL`),e.isUnique&&!e.primaryKey&&(a+=` UNIQUE`)):(o?a=`ENUM(${e.enumValues.map(e=>`'${e.replace(/'/g,`''`)}'`).join(`, `)})`:i===`integer`||i===`int`?a=`INT`:i===`text`||i===`string`?a=`TEXT`:i.startsWith(`varchar`)?a=e.type.toUpperCase():i===`boolean`||i===`bool`?a=`BOOLEAN`:i===`decimal`||i===`numeric`||i.startsWith(`decimal`)?a=e.type.toUpperCase().includes(`(`)?e.type.toUpperCase():`DECIMAL(10,2)`:i===`datetime`||i===`timestamp`?a=`DATETIME`:i===`date`?a=`DATE`:i===`real`?a=`FLOAT`:i===`json`?a=`JSON`:i===`blob`?a=`BLOB`:i===`uuid`&&(a=`VARCHAR(36)`),e.primaryKey&&(i.includes(`int`)||a===`INT`)?a+=` AUTO_INCREMENT PRIMARY KEY`:e.primaryKey&&(a+=` PRIMARY KEY`),!e.nullable&&!e.primaryKey&&(a+=` NOT NULL`),e.isUnique&&!e.primaryKey&&(a+=` UNIQUE`)),e.defaultValue&&!e.defaultValue.startsWith(`FK ->`)){let t=P(e.defaultValue);t!==null&&(a+=` DEFAULT ${t}`)}t.push(` ${r} ${a}`)}),o.forEach(e=>{if(e.fkTarget&&e.fkTarget.table&&e.fkTarget.column){let n=` FOREIGN KEY (${C(e.name||`col`)}) REFERENCES ${C(e.fkTarget.table)}(${C(e.fkTarget.column)})`;e.fkTarget.onDelete&&e.fkTarget.onDelete!==`NO ACTION`&&(n+=` ON DELETE ${e.fkTarget.onDelete}`),e.fkTarget.onUpdate&&e.fkTarget.onUpdate!==`NO ACTION`&&(n+=` ON UPDATE ${e.fkTarget.onUpdate}`),t.push(n)}});let i=``;r.length>0&&(i+=r.join(`
|
|
586
|
+
`)+`
|
|
587
|
+
|
|
588
|
+
`),i+=`CREATE TABLE ${C(e)} (\n${t.join(`,
|
|
589
|
+
`)}\n);`,v.textContent=i}function E(){f.innerHTML=``,m.textContent=o.length.toString(),o.forEach((e,t)=>{let r=document.createElement(`div`);r.className=`table-column-row`;let i=!!(e.fkTarget&&e.fkTarget.table&&e.fkTarget.column),p=i&&e.fkTarget.onDelete&&e.fkTarget.onDelete!==`NO ACTION`?` (${e.fkTarget.onDelete})`:``,m=i?`${e.fkTarget.table}.${e.fkTarget.column}${p}`:`+ FK`,h=e.type||a[0]||`INTEGER`;e.isNewEnum?h=`ENUM (${e.newEnumName||`Custom`})`:e.isExistingEnum&&(h=`${e.type} (Enum)`);let g=``;e.isNewEnum&&(g=`
|
|
590
|
+
<div class="col-new-enum-panel" data-index="${t}">
|
|
591
|
+
<div class="col-new-enum-field">
|
|
592
|
+
<span class="col-new-enum-label">
|
|
593
|
+
<span class="material-symbols-outlined" style="font-size: 14px;">label</span>
|
|
594
|
+
<span>Enum Name:</span>
|
|
595
|
+
</span>
|
|
596
|
+
<input type="text" class="table-col-input new-enum-name-input" data-index="${t}" placeholder="e.g. OrderStatus" value="${e.newEnumName||e.type||``}" />
|
|
597
|
+
</div>
|
|
598
|
+
<div class="col-new-enum-field" style="flex: 1;">
|
|
599
|
+
<span class="col-new-enum-label">
|
|
600
|
+
<span class="material-symbols-outlined" style="font-size: 14px;">list</span>
|
|
601
|
+
<span>Values:</span>
|
|
602
|
+
</span>
|
|
603
|
+
<input type="text" class="table-col-input new-enum-values-input" data-index="${t}" placeholder="e.g. PENDING, PAID, CANCELLED" value="${e.newEnumValuesStr||(e.enumValues?e.enumValues.join(`, `):``)}" />
|
|
604
|
+
</div>
|
|
605
|
+
</div>`),r.innerHTML=`
|
|
606
|
+
<div>
|
|
607
|
+
<input type="text" class="table-col-input col-name-input" data-index="${t}" value="${e.name}" placeholder="column_name" />
|
|
608
|
+
</div>
|
|
609
|
+
<div>
|
|
610
|
+
<div class="table-col-select-wrap">
|
|
611
|
+
<button type="button" class="table-col-select col-type-trigger" data-index="${t}" style="text-align: left; display: flex; align-items: center; justify-content: space-between; cursor: pointer; padding-right: 22px;">
|
|
612
|
+
<span class="col-type-label" style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${h}</span>
|
|
613
|
+
<span class="material-symbols-outlined table-col-select-arrow" style="pointer-events: none;">expand_more</span>
|
|
614
|
+
</button>
|
|
615
|
+
</div>
|
|
616
|
+
</div>
|
|
617
|
+
<div style="display: flex; justify-content: center; align-items: center;">
|
|
618
|
+
<input type="checkbox" class="table-col-checkbox col-pk-check" data-index="${t}" ${e.primaryKey?`checked`:``} title="Primary Key" />
|
|
619
|
+
</div>
|
|
620
|
+
<div style="display: flex; justify-content: center; align-items: center;">
|
|
621
|
+
<input type="checkbox" class="table-col-checkbox col-null-check" data-index="${t}" ${e.nullable?`checked`:``} title="Nullable" ${e.primaryKey?`disabled`:``} />
|
|
622
|
+
</div>
|
|
623
|
+
<div style="display: flex; justify-content: center; align-items: center;">
|
|
624
|
+
<input type="checkbox" class="table-col-checkbox col-unique-check" data-index="${t}" ${e.isUnique?`checked`:``} title="Unique" ${e.primaryKey?`disabled`:``} />
|
|
625
|
+
</div>
|
|
626
|
+
<div>
|
|
627
|
+
<input type="text" class="table-col-input col-default-input" data-index="${t}" value="${e.defaultValue||``}" placeholder="NULL, 'val'..." />
|
|
628
|
+
</div>
|
|
629
|
+
<div class="table-col-fk-wrap" data-index="${t}">
|
|
630
|
+
<button type="button" class="table-col-fk-btn col-fk-btn ${i?`is-active`:``}" data-index="${t}" title="${i?`Foreign Key: ${m}`:`Configure Foreign Key`}">
|
|
631
|
+
<span class="material-symbols-outlined">${i?`link`:`add_link`}</span>
|
|
632
|
+
<span style="overflow: hidden; text-overflow: ellipsis;">${m}</span>
|
|
633
|
+
</button>
|
|
634
|
+
<div class="table-fk-popover" id="fk-popover-${t}" style="display: ${d===t?`flex`:`none`};">
|
|
635
|
+
<div class="table-fk-popover-title">
|
|
636
|
+
<span class="flex items-center gap-1">
|
|
637
|
+
<span class="material-symbols-outlined text-primary" style="font-size: 15px;">link</span>
|
|
638
|
+
<span>Foreign Key Target</span>
|
|
639
|
+
</span>
|
|
640
|
+
<button type="button" class="modal-close-btn fk-popover-close" style="width: 20px; height: 20px;" title="Close">
|
|
641
|
+
<span class="material-symbols-outlined" style="font-size: 14px;">close</span>
|
|
642
|
+
</button>
|
|
643
|
+
</div>
|
|
644
|
+
<div class="table-fk-popover-field">
|
|
645
|
+
<label>Target Table</label>
|
|
646
|
+
<button type="button" class="table-col-select fk-target-table-select" style="display: flex; justify-content: space-between; align-items: center; width: 100%; height: 30px; cursor: pointer; text-align: left; padding: 0 8px;">
|
|
647
|
+
<span class="fk-table-display" style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${e.fkTarget?.table||`-- Select Table --`}</span>
|
|
648
|
+
<span class="material-symbols-outlined table-col-select-arrow" style="font-size: 16px; color: var(--color-text-soft);">expand_more</span>
|
|
649
|
+
</button>
|
|
650
|
+
</div>
|
|
651
|
+
<div class="table-fk-popover-field">
|
|
652
|
+
<label>Target Column</label>
|
|
653
|
+
<button type="button" class="table-col-select fk-target-column-select" ${e.fkTarget?.table?``:`disabled`} style="display: flex; justify-content: space-between; align-items: center; width: 100%; height: 30px; cursor: pointer; text-align: left; padding: 0 8px;">
|
|
654
|
+
<span class="fk-col-display" style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${e.fkTarget?.column||`-- Select Column --`}</span>
|
|
655
|
+
<span class="material-symbols-outlined table-col-select-arrow" style="font-size: 16px; color: var(--color-text-soft);">expand_more</span>
|
|
656
|
+
</button>
|
|
657
|
+
</div>
|
|
658
|
+
<div class="table-fk-popover-grid-2">
|
|
659
|
+
<div class="table-fk-popover-field">
|
|
660
|
+
<label>On Delete</label>
|
|
661
|
+
<button type="button" class="table-col-select fk-on-delete-select" style="display: flex; justify-content: space-between; align-items: center; width: 100%; height: 30px; cursor: pointer; text-align: left; padding: 0 8px;">
|
|
662
|
+
<span class="fk-on-delete-display" style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${e.fkTarget?.onDelete||`NO ACTION`}</span>
|
|
663
|
+
<span class="material-symbols-outlined table-col-select-arrow" style="font-size: 16px; color: var(--color-text-soft);">expand_more</span>
|
|
664
|
+
</button>
|
|
665
|
+
</div>
|
|
666
|
+
<div class="table-fk-popover-field">
|
|
667
|
+
<label>On Update</label>
|
|
668
|
+
<button type="button" class="table-col-select fk-on-update-select" style="display: flex; justify-content: space-between; align-items: center; width: 100%; height: 30px; cursor: pointer; text-align: left; padding: 0 8px;">
|
|
669
|
+
<span class="fk-on-update-display" style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${e.fkTarget?.onUpdate||`NO ACTION`}</span>
|
|
670
|
+
<span class="material-symbols-outlined table-col-select-arrow" style="font-size: 16px; color: var(--color-text-soft);">expand_more</span>
|
|
671
|
+
</button>
|
|
672
|
+
</div>
|
|
673
|
+
</div>
|
|
674
|
+
<div style="display: flex; justify-content: space-between; align-items: center; margin-top: 4px;">
|
|
675
|
+
<button type="button" class="btn-secondary fk-clear-btn" style="padding: 3px 8px; font-size: 11px; color: var(--color-error);" ${i?``:`disabled`}>Clear FK</button>
|
|
676
|
+
<button type="button" class="btn-primary fk-done-btn" style="padding: 3px 10px; font-size: 11px;">Done</button>
|
|
677
|
+
</div>
|
|
678
|
+
</div>
|
|
679
|
+
</div>
|
|
680
|
+
<div style="display: flex; justify-content: center; align-items: center; gap: 3px;">
|
|
681
|
+
<button type="button" class="table-col-action-btn btn-move-up" data-index="${t}" title="Move Column Up" ${t===0?`disabled`:``}>
|
|
682
|
+
<span class="material-symbols-outlined">arrow_upward</span>
|
|
683
|
+
</button>
|
|
684
|
+
<button type="button" class="table-col-action-btn btn-move-down" data-index="${t}" title="Move Column Down" ${t===o.length-1?`disabled`:``}>
|
|
685
|
+
<span class="material-symbols-outlined">arrow_downward</span>
|
|
686
|
+
</button>
|
|
687
|
+
<button type="button" class="table-col-action-btn btn-del-col" data-index="${t}" title="Remove Column" ${o.length<=1?`disabled`:``}>
|
|
688
|
+
<span class="material-symbols-outlined">delete</span>
|
|
689
|
+
</button>
|
|
690
|
+
</div>
|
|
691
|
+
${g}
|
|
692
|
+
`;let _=r.querySelector(`.col-name-input`);_.oninput=e=>{o[t].name=e.target.value.trim(),w()};let v=r.querySelector(`.col-type-trigger`);v.onclick=r=>{r.stopPropagation();let i=l.map(e=>({name:e.name,value:`__ENUM_EXISTING__:${e.name}`,desc:`Existing Enum (${e.values.slice(0,3).join(`, `)}${e.values.length>3?`...`:``})`,icon:`list`,group:`EXISTING DATABASE ENUMS`}));te({anchorEl:v,initialValue:e.isNewEnum?`ENUM`:e.isExistingEnum?`__ENUM_EXISTING__:${e.type}`:e.type,dialect:n,extraItems:i,onSelect:e=>{if(e===`ENUM`||e===`__NEW_ENUM__`){if(o[t].isNewEnum=!0,o[t].isExistingEnum=!1,!o[t].newEnumName){let e=o[t].name?o[t].name.charAt(0).toUpperCase()+o[t].name.slice(1)+`Enum`:`CustomEnum`;o[t].newEnumName=e,o[t].type=e}o[t].enumValues||(o[t].enumValues=[])}else if(e.startsWith(`__ENUM_EXISTING__:`)){let n=e.slice(18);o[t].isNewEnum=!1,o[t].isExistingEnum=!0,o[t].type=n;let r=l.find(e=>e.name===n);o[t].enumValues=r?[...r.values]:[]}else o[t].isNewEnum=!1,o[t].isExistingEnum=!1,o[t].type=e,o[t].enumValues=void 0;E(),w()}})};let y=r.querySelector(`.new-enum-name-input`);y&&(y.oninput=e=>{let n=e.target.value.trim();o[t].newEnumName=n,o[t].type=n||`CustomEnum`,w()});let b=r.querySelector(`.new-enum-values-input`);b&&(b.oninput=e=>{let n=e.target.value;o[t].newEnumValuesStr=n,o[t].enumValues=n.split(`,`).map(e=>e.trim().replace(/^['"]|['"]$/g,``)).filter(Boolean),w()});let x=r.querySelector(`.col-pk-check`);x.onchange=e=>{let n=e.target.checked;o[t].primaryKey=n,n&&(o[t].nullable=!1,o[t].isUnique=!1),E(),w()};let S=r.querySelector(`.col-null-check`);S.onchange=e=>{o[t].nullable=e.target.checked,w()};let C=r.querySelector(`.col-unique-check`);C.onchange=e=>{o[t].isUnique=e.target.checked,w()};let T=r.querySelector(`.col-default-input`);T.oninput=e=>{o[t].defaultValue=e.target.value.trim(),w()};let D=r.querySelector(`.col-fk-btn`),O=r.querySelector(`.table-fk-popover`),k=r.querySelector(`.fk-target-table-select`),A=r.querySelector(`.fk-target-column-select`),j=r.querySelector(`.fk-on-delete-select`),M=r.querySelector(`.fk-on-update-select`),N=r.querySelector(`.fk-popover-close`),P=r.querySelector(`.fk-clear-btn`),F=r.querySelector(`.fk-done-btn`),L=e.fkTarget?.table||``,R=e.fkTarget?.column||``,ee=e.fkTarget?.onDelete||`NO ACTION`,ne=e.fkTarget?.onUpdate||`NO ACTION`;async function re(e){if(!e){if(A){let e=A.querySelector(`.fk-col-display`);e&&(e.textContent=`-- Select Column --`),A.disabled=!0}return}if(A){A.disabled=!1;let e=A.querySelector(`.fk-col-display`);e&&(e.textContent=`Loading...`)}if(!u[e])try{let t=await c(e);Array.isArray(t)?u[e]=t.map(e=>e.name):t&&Array.isArray(t.columns)?u[e]=t.columns.map(e=>e.name):u[e]=[`id`]}catch{u[e]=[`id`]}let t=u[e]||[];if((!R||!t.includes(R))&&(R=t.find(e=>e.toLowerCase()===`id`)||t[0]||``),A){let e=A.querySelector(`.fk-col-display`);e&&(e.textContent=R||`-- Select Column --`)}}k.onclick=e=>{e.stopPropagation(),I({anchorEl:k,title:`Select Target Table`,searchable:!0,placeholder:`Search tables...`,initialValue:L,items:s.map(e=>({name:e,value:e,icon:`table_chart`})),onSelect:async e=>{L=e;let t=k.querySelector(`.fk-table-display`);t&&(t.textContent=e),R=``,await re(e)}})},A.onclick=e=>{if(e.stopPropagation(),!L)return;let t=u[L]||[];I({anchorEl:A,title:`Columns in ${L}`,searchable:!0,placeholder:`Search columns...`,initialValue:R,items:t.map(e=>({name:e,value:e,icon:`tag`})),onSelect:e=>{R=e;let t=A.querySelector(`.fk-col-display`);t&&(t.textContent=e)}})},j.onclick=e=>{e.stopPropagation(),I({anchorEl:j,title:`ON DELETE Action`,searchable:!1,initialValue:ee,items:[`NO ACTION`,`CASCADE`,`SET NULL`,`RESTRICT`],onSelect:e=>{ee=e;let t=j.querySelector(`.fk-on-delete-display`);t&&(t.textContent=e)}})},M.onclick=e=>{e.stopPropagation(),I({anchorEl:M,title:`ON UPDATE Action`,searchable:!1,initialValue:ne,items:[`NO ACTION`,`CASCADE`,`RESTRICT`,`SET NULL`],onSelect:e=>{ne=e;let t=M.querySelector(`.fk-on-update-display`);t&&(t.textContent=e)}})},D.onclick=n=>{if(n.stopPropagation(),d===t)d=null,O.style.display=`none`;else{d=t,E();let n=document.getElementById(`fk-popover-${t}`);if(n){let r=e.fkTarget?.table||``;r&&re(r);let i=document.querySelector(`.col-fk-btn[data-index="${t}"]`);if(i){let e=i.getBoundingClientRect();n.style.top=`${e.bottom+4}px`,n.style.left=`${Math.max(10,Math.min(e.right-270,window.innerWidth-280))}px`}}}},N.onclick=e=>{e.stopPropagation(),d=null,O.style.display=`none`},P.onclick=e=>{e.stopPropagation(),o[t].fkTarget=null,d=null,E(),w()},F.onclick=e=>{e.stopPropagation(),L&&R?o[t].fkTarget={table:L,column:R,onDelete:ee===`NO ACTION`?void 0:ee,onUpdate:ne===`NO ACTION`?void 0:ne}:o[t].fkTarget=null,d=null,E(),w()};let ie=r.querySelector(`.btn-move-up`);ie&&t>0&&(ie.onclick=e=>{e.stopPropagation();let n=o[t];o[t]=o[t-1],o[t-1]=n,d!==null&&(d=null),E(),w()});let ae=r.querySelector(`.btn-move-down`);ae&&t<o.length-1&&(ae.onclick=e=>{e.stopPropagation();let n=o[t];o[t]=o[t+1],o[t+1]=n,d!==null&&(d=null),E(),w()});let oe=r.querySelector(`.btn-del-col`);oe&&o.length>1&&(oe.onclick=e=>{e.stopPropagation(),o.splice(t,1),d===t&&(d=null),E(),w()}),f.appendChild(r)})}E(),w(),p.oninput=()=>{w()},f.onscroll=()=>{d!==null&&(d=null,E())},document.getElementById(`btn-add-column-row`).onclick=()=>{o.push({name:`column_${o.length+1}`,type:`VARCHAR(255)`,primaryKey:!1,nullable:!0,isUnique:!1,defaultValue:``,fkTarget:null}),E(),w(),setTimeout(()=>{f.scrollTop=f.scrollHeight},20)},document.getElementById(`preset-add-timestamps`).onclick=()=>{let e=o.map(e=>e.name.toLowerCase());e.includes(`created_at`)||o.push({name:`created_at`,type:`TIMESTAMP`,primaryKey:!1,nullable:!1,isUnique:!1,defaultValue:`CURRENT_TIMESTAMP`,fkTarget:null}),e.includes(`updated_at`)||o.push({name:`updated_at`,type:`TIMESTAMP`,primaryKey:!1,nullable:!1,isUnique:!1,defaultValue:`CURRENT_TIMESTAMP`,fkTarget:null}),E(),w(),setTimeout(()=>{f.scrollTop=f.scrollHeight},20)},document.getElementById(`preset-add-softdelete`).onclick=()=>{o.map(e=>e.name.toLowerCase()).includes(`deleted_at`)||o.push({name:`deleted_at`,type:`TIMESTAMP`,primaryKey:!1,nullable:!0,isUnique:!1,defaultValue:``,fkTarget:null}),E(),w(),setTimeout(()=>{f.scrollTop=f.scrollHeight},20)},document.getElementById(`preset-add-uuid-pk`).onclick=()=>{let e=o.findIndex(e=>e.primaryKey);e===-1?o.unshift({name:`id`,type:`VARCHAR(36)`,primaryKey:!0,nullable:!1,isUnique:!1,defaultValue:``,fkTarget:null}):(o[e].name=`id`,o[e].type=`VARCHAR(36)`),E(),w()};let D=e=>{d!==null&&!e.target.closest(`.table-col-fk-wrap`)&&!e.target.closest(`.dropdown-picker-popover`)&&(d=null,E())};document.addEventListener(`click`,D);let O=()=>{document.removeEventListener(`keydown`,k),document.removeEventListener(`click`,D),t.remove()},k=e=>{e.key===`Escape`&&O()};document.addEventListener(`keydown`,k),document.getElementById(`close-create-table-modal-btn`).onclick=O,document.getElementById(`cancel-create-table-btn`).onclick=O,h.onclick=async()=>{_.style.display=`none`;let t=p.value.trim();if(!t){_.textContent=`Table name cannot be empty.`,_.style.display=`block`,p.focus();return}if(!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(t)){_.textContent=`Table name must start with a letter/underscore and contain only alphanumeric characters.`,_.style.display=`block`,p.focus();return}let n=new Set;for(let e of o){if(!e.name){alert(`All column names must be non-empty.`);return}let t=e.name.toLowerCase();if(n.has(t)){alert(`Duplicate column name "${e.name}".`);return}n.add(t)}for(let e of o)if(e.isNewEnum&&(!e.enumValues||e.enumValues.length===0)){alert(`Please specify at least one enum value for column "${e.name||`column`}".`);return}h.disabled=!0,g.textContent=`Creating...`;try{let n=await i(t,o.map(e=>({name:e.name,type:e.isNewEnum?(e.newEnumName||e.type||`CustomEnum`).trim():e.type,isPk:!!e.primaryKey,primaryKey:!!e.primaryKey,nullable:!e.primaryKey&&e.nullable!==!1,isUnique:!e.primaryKey&&!!e.isUnique,defaultValue:e.defaultValue||void 0,enumValues:e.enumValues&&e.enumValues.length>0?e.enumValues:void 0,isNewEnum:!!e.isNewEnum,fkTarget:e.fkTarget&&e.fkTarget.table&&e.fkTarget.column?{table:e.fkTarget.table,column:e.fkTarget.column,onDelete:e.fkTarget.onDelete,onUpdate:e.fkTarget.onUpdate}:void 0})));n.success?(O(),window.showToast?.(`Table "${t}" created successfully!`,`success`),window.refreshTableList&&(await window.refreshTableList(!0),setTimeout(()=>{let e=document.querySelector(`.table-btn[data-table="${t}"]`);e?e.click():(window.AppState.currentTable=t,window.handleSwitchTab(`schema-btn`))},100)),e&&e(t)):alert(`Failed to create table: ${n.error||`Unknown error`}`)}catch(e){alert(`Error: ${e.message}`)}finally{h.disabled=!1,g.textContent=`Create Table`}}}window.openCreateTableModal=ne,window.refreshTableList=ie;var re=!1;async function ie(e=!1){try{let t=document.getElementById(`table-nav`);if(!t)return;e&&(t.innerHTML=`
|
|
693
|
+
<div class="sidebar-loading-state">
|
|
694
|
+
<span class="material-symbols-outlined animate-spin icon-18">progress_activity</span>
|
|
695
|
+
<span>Refreshing tables...</span>
|
|
696
|
+
</div>
|
|
697
|
+
`);let n=await r();if(t.innerHTML=``,await se(),await de(),n.success&&n.data&&n.data.length>0){let r=n.data,i=document.getElementById(`table-count-badge`);i&&(i.textContent=r.length.toString()),r.forEach(e=>{let n=document.createElement(`button`);n.type=`button`,n.className=`table-btn`,n.dataset.table=e,n.title=e,n.innerHTML=`
|
|
698
|
+
<div class="table-btn-label">
|
|
699
|
+
<i class="material-symbols-outlined table-item-icon">table</i>
|
|
700
|
+
<span class="table-item-initial">${(e[0]||`T`).toUpperCase()}</span>
|
|
701
|
+
<span class="table-name-text">${e}</span>
|
|
702
|
+
<span class="dirty-indicator-dot hidden" title="Unsaved changes"></span>
|
|
703
|
+
</div>
|
|
704
|
+
<span class="table-btn-badge hidden" id="badge-${e}"></span>
|
|
705
|
+
`,n.onclick=()=>{window.fkNavHistory&&window.fkNavHistory.toTable!==e&&(window.fkNavHistory=null),window.AppState.currentTable=e,window.AppState.currentTableBtnElement=n,document.querySelectorAll(`.table-btn`).forEach(e=>e.classList.remove(`active`)),n.classList.add(`active`),window.AppState.currentTab===`erd-btn`||window.AppState.currentTab===`sql-btn`||window.AppState.currentTab===`status-btn`?window.handleSwitchTab(`data-btn`):(window.updateUrlHash?.(window.AppState.currentTab,e),window.renderCurrentView())},n.oncontextmenu=t=>{A(t,[{icon:`content_copy`,label:`Copy Table Name`,action:async()=>{try{await navigator.clipboard.writeText(e)}catch{let t=document.createElement(`textarea`);t.value=e,document.body.appendChild(t),t.select(),document.execCommand(`copy`),t.remove()}window.showToast&&window.showToast(`Copied "${e}"`,`success`)}},{icon:`auto_fix_high`,label:`Generate Mock Data...`,action:async()=>{let t=await c(e);M(e,t.success?t.data:[],()=>{window.AppState.currentTable===e&&window.renderCurrentView()})}},{icon:`table_chart`,label:`View Data`,action:()=>{n.click(),window.handleSwitchTab(`data-btn`)}},{icon:`schema`,label:`View Schema`,action:()=>{n.click(),window.handleSwitchTab(`schema-btn`)}},{type:`divider`},{icon:`download`,label:`Export as CSV`,action:()=>{window.open(`/api/tables/${e}/export?format=csv`,`_blank`)}},{icon:`download`,label:`Export as JSON`,action:()=>{window.open(`/api/tables/${e}/export?format=json`,`_blank`)}},{type:`divider`},{icon:`mop`,label:`Truncate Table (Clear Rows)`,danger:!0,action:async()=>{if(confirm(`Are you sure you want to delete ALL data from table "${e}"? This cannot be undone!`)){let t=await p(e);t.success?(window.showToast&&window.showToast(`Cleared all rows from "${e}"`,`info`),window.AppState.currentTable===e&&window.renderCurrentView(),ae()):alert(`Failed to clear table: ${t.error}`)}}}])},t.appendChild(n)});let a=(window.parseHash?window.parseHash():{route:null,table:null}).table||window.AppState.currentTable;if(a){let e=document.querySelector(`.table-btn[data-table="${a}"]`);e&&(e.classList.add(`active`),window.AppState.currentTable=a,window.AppState.currentTableBtnElement=e)}if(!e){let e=window.getInitialRouteTab?window.getInitialRouteTab():null;window.handleSwitchTab(e||`data-btn`)}ae();let o=document.getElementById(`search-input`);o&&o.value.trim()&&fe(o.value.trim())}else{let n=document.getElementById(`table-count-badge`);n&&(n.textContent=`0`),t.innerHTML=`
|
|
706
|
+
<div id="not-found-msg">
|
|
707
|
+
<span class="material-symbols-outlined icon-20 text-soft">inventory_2</span>
|
|
708
|
+
<span>No Tables Found</span>
|
|
709
|
+
</div>
|
|
710
|
+
`,e||(window.AppState.dbType===`none`?window.handleSwitchTab(`connect-btn`):window.handleSwitchTab(`sql-btn`))}re||=(pe(),!0)}catch(e){console.error(`Failed to fetch tables:`,e);let t=document.getElementById(`table-nav`);t&&(t.innerHTML=`
|
|
711
|
+
<div id="not-found-msg" class="text-error">
|
|
712
|
+
<span class="material-symbols-outlined icon-20">warning</span>
|
|
713
|
+
<span>Failed to load tables</span>
|
|
714
|
+
</div>
|
|
715
|
+
`);let n=document.getElementById(`sidebar-db-status-dot`),r=document.getElementById(`brand-db-type`);n&&(n.classList.remove(`status-connected`),n.classList.add(`status-error`)),r&&(r.textContent=`ERROR`)}}async function ae(){try{let e=await o();e.success&&e.data&&Object.entries(e.data).forEach(([e,t])=>{let n=document.getElementById(`badge-${e}`);n&&(n.textContent=Number(t).toLocaleString(),n.classList.remove(`hidden`),n.style.display=`inline-flex`)})}catch(e){console.error(`Failed to load table stats:`,e)}}window.loadTableStats=ae;function oe(e){let t=e||window.AppState?.currentTable;if(!t)return;document.querySelectorAll(`.table-btn`).forEach(e=>e.classList.remove(`active`));let n=document.querySelector(`.table-btn[data-table="${t}"]`);n&&(n.classList.add(`active`),window.AppState&&(window.AppState.currentTableBtnElement=n))}window.updateSidebarActiveTable=oe,window.initSidebar=ie;async function se(){let e=document.getElementById(`sidebar-db-status-dot`),t=document.getElementById(`brand-db-type`);try{if(!t)return;let n=await a();n?.success&&n.data?.connected&&n.data?.dbType&&n.data.dbType!==`none`?(window.AppState.dbType=n.data.dbType,t.textContent=n.data.dbType.toUpperCase(),e&&(e.classList.remove(`status-error`,`status-warning`),e.classList.add(`status-connected`)),window.setStudioConnectionMode?.(!0)):(window.AppState.dbType=`none`,t.textContent=`NO DATABASE`,e&&(e.classList.remove(`status-connected`,`status-error`),e.classList.add(`status-warning`)),window.setStudioConnectionMode?.(!1))}catch{e&&(e.classList.remove(`status-connected`,`status-warning`),e.classList.add(`status-error`)),t&&(t.textContent=`DISCONNECTED`),window.setStudioConnectionMode?.(!1)}}var ce=!1,le=`public`,ue=[];async function de(){let e=document.getElementById(`schema-selector-wrap`),t=document.getElementById(`schema-select-box`);if(!(!e||!t))try{let n=await C();if(!n?.success||!n.data?.supported){e.classList.add(`hidden`);return}let{schemas:r,currentSchema:i}=n.data;ue=r||[],le=i||`public`;let a=document.getElementById(`schema-current-display`);a&&(a.textContent=le),e.classList.remove(`hidden`),ce||(ce=!0,t.addEventListener(`click`,e=>{e.stopPropagation(),I({anchorEl:t,title:`Database Schemas`,searchable:ue.length>4,placeholder:`Search schemas...`,initialValue:le,items:ue.map(e=>({name:e,value:e,icon:`folder`,badge:e===le?`active`:``})),onSelect:async e=>{if(e!==le){a&&(a.textContent=e);try{let t=await w(e);t.success?(le=e,window.AppState.currentTable=null,window.AppState.currentTableBtnElement=null,await ie(!0),window.showToast&&window.showToast(`Switched to schema "${e}" (${t.data?.tableCount??0} tables)`,`success`)):(window.showToast&&window.showToast(`Failed to switch schema: ${t.error}`,`error`),await de())}catch(e){window.showToast&&window.showToast(`Schema switch error: ${e.message}`,`error`),await de()}}}})}))}catch{e.classList.add(`hidden`)}}function fe(e){if(document.getElementById(`sidebar-panel`)?.classList.contains(`collapsed`)){document.querySelectorAll(`.table-btn`).forEach(e=>e.style.display=`flex`),document.getElementById(`sidebar-no-match`)?.remove();return}let t=e.toLowerCase().trim(),n=document.querySelectorAll(`.table-btn`),r=0;n.forEach(e=>{let n=(e.dataset.table||``).toLowerCase();!t||n.includes(t)?(e.style.display=`flex`,r++):e.style.display=`none`});let i=document.getElementById(`table-nav`),a=document.getElementById(`sidebar-no-match`);r===0&&n.length>0?a||(a=document.createElement(`div`),a.id=`sidebar-no-match`,a.className=`sidebar-empty-state`,a.innerHTML=`
|
|
716
|
+
<span class="material-symbols-outlined icon-20 text-soft">search_off</span>
|
|
717
|
+
<span>No matching tables</span>
|
|
718
|
+
`,i?.appendChild(a)):a&&a.remove();let o=document.getElementById(`table-count-badge`);o&&(o.textContent=r.toString())}function pe(){let e=document.getElementById(`search-input`),t=document.getElementById(`search-clear-btn`),n=document.getElementById(`refresh-tables-btn`),r=document.getElementById(`sidebar-collapse-btn`),i=document.getElementById(`sidebar-panel`);e&&(e.addEventListener(`input`,e=>{let n=e.target.value;t&&(n?t.classList.remove(`hidden`):t.classList.add(`hidden`)),fe(n)}),e.addEventListener(`keydown`,t=>{if(t.key===`Enter`){t.preventDefault();let n=document.querySelector(`.table-btn:not([style*="display: none"])`);n&&(n.click(),e.blur())}}),t&&t.addEventListener(`click`,()=>{e.value=``,t.classList.add(`hidden`),fe(``),e.focus()}));let a=document.getElementById(`add-table-btn`);if(a&&a.addEventListener(`click`,()=>{ne()}),n&&n.addEventListener(`click`,async()=>{n.classList.add(`animate-spin`),await ie(!0),setTimeout(()=>{n.classList.remove(`animate-spin`),window.showToast&&window.showToast(`Tables refreshed`,`success`)},300)}),i){localStorage.getItem(`drixio_sidebar_collapsed`)===`true`&&(i.classList.add(`collapsed`),me(!0)),r&&r.addEventListener(`click`,n=>{n.stopPropagation(),i.classList.toggle(`collapsed`);let r=i.classList.contains(`collapsed`);localStorage.setItem(`drixio_sidebar_collapsed`,r.toString()),me(r),r&&e&&(e.value=``,t?.classList.add(`hidden`),fe(``))});let n=document.getElementById(`sidebar-header`);n&&n.addEventListener(`click`,()=>{i.classList.contains(`collapsed`)&&(i.classList.remove(`collapsed`),localStorage.setItem(`drixio_sidebar_collapsed`,`false`),me(!1))})}document.addEventListener(`keydown`,n=>{if(n.key===`/`&&document.activeElement?.tagName!==`INPUT`&&document.activeElement?.tagName!==`TEXTAREA`){if(n.preventDefault(),window.AppState?.currentTab===`erd-btn`){let e=document.getElementById(`erd-search-input`);if(e){e.focus(),e.select();return}}e?.focus(),e?.select()}else n.key===`Escape`&&document.activeElement===e&&(e.value&&(e.value=``,t?.classList.add(`hidden`),fe(``)),e.blur())})}function me(e){let t=document.getElementById(`sidebar-collapse-icon`),n=document.getElementById(`sidebar-collapse-btn`),r=document.getElementById(`sidebar-header`);t&&(t.textContent=e?`menu`:`menu_open`),n&&(n.title=e?`Expand sidebar`:`Collapse sidebar`),r&&(r.title=e?`Click to expand sidebar`:``)}function he(e,t){document.querySelectorAll(`#${e} .cell-in-range, #${e} .cell-selected, #${e} .range-top, #${e} .range-bottom, #${e} .range-left, #${e} .range-right, #${e} .row-header-selected`).forEach(e=>{e.classList.remove(`cell-in-range`,`cell-selected`,`range-top`,`range-bottom`,`range-left`,`range-right`,`row-header-selected`)});let n=t.selection;if(!n||n.startRow===-1)return;let r=Math.min(n.startRow,n.endRow),i=Math.max(n.startRow,n.endRow),a=Math.min(n.startCol,n.endCol),o=Math.max(n.startCol,n.endCol);document.querySelectorAll(`#${e} td.data-cell`).forEach(e=>{let s=parseInt(e.dataset.rowIdx),c=parseInt(e.dataset.colIdx);s>=r&&s<=i&&c>=a&&c<=o&&(e.classList.add(`cell-in-range`),s===r&&e.classList.add(`range-top`),s===i&&e.classList.add(`range-bottom`),c===a&&e.classList.add(`range-left`),c===o&&e.classList.add(`range-right`),s===n.startRow&&c===n.startCol&&(e.classList.add(`cell-selected`),t.selectedCell=e))}),(n.isRowSelection||n.isDraggingRow)&&document.querySelectorAll(`#${e} td.row-header`).forEach(e=>{let t=parseInt(e.dataset.rowIdx);t>=r&&t<=i&&e.classList.add(`row-header-selected`)})}function ge(e,t,n){let r=document.querySelectorAll(`#${e} tbody tr:not(.ghost-row-tr)`).length;r!==0&&(t.selection={isDragging:!1,isDraggingRow:!1,isRowSelection:!0,startRow:0,endRow:r-1,startCol:0,endCol:Math.max(0,n-1)},he(e,t))}function _e(e,t,n,r){e.addEventListener(`mousedown`,e=>{if(e.target.tagName===`INPUT`||e.target.tagName===`SELECT`)return;let i=e.target.closest(`td.row-header`);if(i&&e.button!==2){let a=parseInt(i.dataset.rowIdx);if(isNaN(a))return;e.shiftKey&&n.selection&&n.selection.startRow!==-1?(n.selection.isDragging=!1,n.selection.isDraggingRow=!1,n.selection.isRowSelection=!0,n.selection.endRow=a,n.selection.startCol=0,n.selection.endCol=r-1):n.selection={isDragging:!1,isDraggingRow:!0,isRowSelection:!0,startRow:a,endRow:a,startCol:0,endCol:r-1},he(t,n);return}let a=e.target.closest(`td.data-cell`);if(!a)return;let o=parseInt(a.dataset.rowIdx),s=parseInt(a.dataset.colIdx);isNaN(o)||isNaN(s)||(e.shiftKey&&n.selection&&n.selection.startRow!==-1?(n.selection.isDragging=!1,n.selection.isDraggingRow=!1,n.selection.isRowSelection=!1,n.selection.endRow=o,n.selection.endCol=s):n.selection={isDragging:!0,isRowSelection:!1,startRow:o,startCol:s,endRow:o,endCol:s},he(t,n))}),e.addEventListener(`mouseover`,e=>{if(n.selection.isDraggingRow){let r=e.target.closest(`td.row-header`);if(!r)return;n.selection.endRow=parseInt(r.dataset.rowIdx),he(t,n);return}if(!n.selection.isDragging)return;let r=e.target.closest(`td.data-cell`);r&&(n.selection.endRow=parseInt(r.dataset.rowIdx),n.selection.endCol=parseInt(r.dataset.colIdx),he(t,n))}),e.addEventListener(`mouseup`,()=>{n.selection&&(n.selection.isDragging=!1,n.selection.isDraggingRow=!1)})}function ve(e,t){let n=document.createElement(`div`);n.className=`resizer`,e.appendChild(n),n.addEventListener(`click`,e=>e.stopPropagation()),n.addEventListener(`dblclick`,t=>{t.preventDefault(),t.stopPropagation(),e.style.width=``,e.style.minWidth=``,e.style.maxWidth=``}),n.addEventListener(`mousedown`,n=>{n.preventDefault(),n.stopPropagation();let r=n.pageX,i=e.offsetWidth,a=!1;document.body.style.userSelect=`none`;let o=n=>{a=!0,t&&(t.isResizing=!0);let o=Math.max(60,i+(n.pageX-r));e.style.width=o+`px`,e.style.minWidth=o+`px`,e.style.maxWidth=o+`px`},s=()=>{document.removeEventListener(`mousemove`,o),document.removeEventListener(`mouseup`,s),document.body.style.cursor=``,document.body.style.userSelect=``,a&&setTimeout(()=>{t&&(t.isResizing=!1)},100)};document.body.style.cursor=`col-resize`,document.addEventListener(`mousemove`,o),document.addEventListener(`mouseup`,s)})}var ye=[{category:`COMPARISON`,operators:[{label:`Equals`,op:`=`,symbol:`=`},{label:`Not equal`,op:`<>`,symbol:`<>`},{label:`Greater than`,op:`>`,symbol:`>`},{label:`Less than`,op:`<`,symbol:`<`},{label:`Greater or equal`,op:`>=`,symbol:`>=`},{label:`Less or equal`,op:`<=`,symbol:`<=`}]},{category:`PATTERN MATCHING`,operators:[{label:`Like`,op:`LIKE`,symbol:`~~`},{label:`iLike`,op:`ILIKE`,symbol:`~~*`},{label:`Not Like`,op:`NOT LIKE`,symbol:`!~~`}]},{category:`NULL CHECKS`,operators:[{label:`Is null`,op:`IS NULL`,symbol:`is null`,isUnary:!0},{label:`Is not null`,op:`IS NOT NULL`,symbol:`is not null`,isUnary:!0},{label:`In list`,op:`IN`,symbol:`in`}]}],be=new Map;function xe(e){return be.has(e)||be.set(e,{activeFilters:[],freeTextSearch:``,draft:null}),be.get(e)}function Se(e){let t=xe(e);t.activeFilters=[],t.freeTextSearch=``,t.draft=null}function Ce(e){let t=xe(e);return t.activeFilters.length>0||!!t.freeTextSearch}function we(e,t=[]){let n=xe(e),r=[],i=e=>`"${(e||``).replace(/"/g,`""`)}"`,a=e=>(e||``).replace(/'/g,`''`);for(let e of n.activeFilters){if(e.type===`sql`||e.op===`RAW`){e.val&&e.val.trim()&&r.push(`(${e.val.trim()})`);continue}let{col:t,op:n,val:o}=e;if(!t||!n)continue;if(n===`IS NULL`||n===`IS NOT NULL`){r.push(`${i(t)} ${n}`);continue}if(n===`IN`){let e=(o||``).trim();e.startsWith(`(`)||(e=`(${e})`),r.push(`${i(t)} IN ${e}`);continue}let s=a(o);if(n===`LIKE`){let e=s.includes(`%`)||s.includes(`_`)?s:`%${s}%`;r.push(`${i(t)} LIKE '${e}'`);continue}if(n===`ILIKE`){let e=s.includes(`%`)||s.includes(`_`)?s:`%${s}%`;r.push(`LOWER(${i(t)}) LIKE LOWER('${e}')`);continue}if(n===`NOT LIKE`){let e=s.includes(`%`)||s.includes(`_`)?s:`%${s}%`;r.push(`${i(t)} NOT LIKE '${e}'`);continue}let c=!isNaN(Number(o))&&o.trim()!==``?o.trim():`'${s}'`,l=n===`!=`?`<>`:n;r.push(`${i(t)} ${l} ${c}`)}if(n.freeTextSearch&&n.freeTextSearch.trim()){let e=a(n.freeTextSearch.trim()),o=t.filter(e=>{let t=(e.type||``).toUpperCase();return t.includes(`CHAR`)||t.includes(`TEXT`)||t.includes(`VARCHAR`)||t.includes(`STRING`)||!t}).map(e=>e.name),s=o.length>0?o:t.map(e=>e.name);if(s.length>0){let t=s.map(t=>`LOWER(${i(t)}) LIKE LOWER('%${e}%')`);r.push(`(${t.join(` OR `)})`)}}return r.join(` AND `)}function Te({container:e,tableName:t,schema:n=[],onFilterChange:r}){let i=xe(t),a=n.map(e=>e.name),o=a[0]||`id`,s=`Filter table (e.g. ${o} = 1, status = 'active') or search...`;e.innerHTML=`
|
|
719
|
+
<div class="filter-bar-container" id="filter-bar-container-${t}">
|
|
720
|
+
<div class="filter-bar-input-box" id="filter-bar-box-${t}">
|
|
721
|
+
<span class="material-symbols-outlined filter-bar-search-icon">search</span>
|
|
722
|
+
<div class="filter-bar-pills" id="filter-bar-pills-${t}"></div>
|
|
723
|
+
<input
|
|
724
|
+
type="text"
|
|
725
|
+
id="filter-bar-input-${t}"
|
|
726
|
+
class="filter-bar-input"
|
|
727
|
+
placeholder="${s}"
|
|
728
|
+
autocomplete="off"
|
|
729
|
+
spellcheck="false"
|
|
730
|
+
value="${i.freeTextSearch||``}"
|
|
731
|
+
/>
|
|
732
|
+
<button
|
|
733
|
+
type="button"
|
|
734
|
+
id="filter-bar-clear-all-${t}"
|
|
735
|
+
class="filter-bar-clear-btn ${Ce(t)?``:`hidden`}"
|
|
736
|
+
title="Clear all filters"
|
|
737
|
+
>
|
|
738
|
+
<span class="material-symbols-outlined">close</span>
|
|
739
|
+
</button>
|
|
740
|
+
</div>
|
|
741
|
+
<div class="filter-popover hidden" id="filter-popover-${t}"></div>
|
|
742
|
+
</div>
|
|
743
|
+
`;let c=document.getElementById(`filter-bar-pills-${t}`),l=document.getElementById(`filter-bar-input-${t}`),u=document.getElementById(`filter-bar-clear-all-${t}`),d=document.getElementById(`filter-popover-${t}`),f=document.getElementById(`filter-bar-box-${t}`),p=()=>{i.activeFilters.length>0?l.placeholder=`Add more filters or SQL conditions...`:l.placeholder=s,u&&(Ce(t)?u.classList.remove(`hidden`):u.classList.add(`hidden`))},m=()=>{c.innerHTML=``,i.activeFilters.forEach(e=>{let t=document.createElement(`div`);if(t.className=`filter-pill`,t.dataset.filterId=e.id,i.draft&&i.draft.id===e.id&&t.classList.add(`is-editing`),e.type===`sql`||e.op===`RAW`)t.innerHTML=`
|
|
744
|
+
<span class="material-symbols-outlined" style="font-size: 14px; color: var(--color-primary); line-height: 1;">code</span>
|
|
745
|
+
<span class="filter-pill-val" title="${h(e.val)}">${h(e.val)}</span>
|
|
746
|
+
<button type="button" class="filter-pill-remove" title="Remove condition">×</button>
|
|
747
|
+
`;else{let n=e.op===`IS NULL`||e.op===`IS NOT NULL`?``:`<span class="filter-pill-val" title="${h(e.val)}">${h(e.val)}</span>`;t.innerHTML=`
|
|
748
|
+
<span class="filter-pill-col">${h(e.col)}</span>
|
|
749
|
+
<span class="filter-pill-op">${h(e.symbol||e.op)}</span>
|
|
750
|
+
${n}
|
|
751
|
+
<button type="button" class="filter-pill-remove" title="Remove filter">×</button>
|
|
752
|
+
`}let n=t.querySelector(`.filter-pill-remove`);n.onclick=t=>{t.stopPropagation(),i.activeFilters=i.activeFilters.filter(t=>t.id!==e.id),i.draft&&i.draft.id===e.id&&(i.draft=null),m(),p(),r()},t.onclick=t=>{if(t.stopPropagation(),e.type===`sql`||e.op===`RAW`){i.activeFilters=i.activeFilters.filter(t=>t.id!==e.id),m(),l.value=e.val,l.focus(),T(),w();return}let n=e.op===`IS NULL`||e.op===`IS NOT NULL`;i.draft={...e,step:n?`operator`:`value`},T()},c.appendChild(t)}),p()},h=e=>e?String(e).replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`):``,g=e=>{if(!e||/\b(AND|OR)\b/i.test(e))return null;let t=e.match(/^\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*(=|!=|<>|>=|<=|>|<|LIKE|ILIKE)\s*(.+)$/i);if(!t)return null;let[,n,r,i]=t,o=a.find(e=>e.toLowerCase()===n.toLowerCase());if(!o)return null;let s=i.trim().replace(/^['"]|['"]$/g,``);return{col:o,op:r.toUpperCase()===`<>`?`!=`:r.toUpperCase(),val:s}},_=e=>{if(!e||typeof e!=`string`)return null;let t=e.trim();if(!t)return null;if(/^SELECT\b/i.test(t)){let e=t.match(/\bWHERE\s+([\s\S]+)$/i);return e&&e[1].trim()?{type:`sql`,val:e[1].trim(),isExtracted:!0}:{type:`select_warning`}}return/^WHERE\s+/i.test(t)&&(t=t.replace(/^WHERE\s+/i,``).trim(),t)||/\b(AND|OR|IN\s*\(|NOT\s+IN\s*\(|BETWEEN|NOT\s+BETWEEN|IS\s+NULL|IS\s+NOT\s+NULL)\b/i.test(t)&&a.some(e=>RegExp(`(^|[^a-zA-Z0-9_])${e}([^a-zA-Z0-9_]|$)`,`i`).test(t))?{type:`sql`,val:t}:null},v=(e=``)=>{let t=e.toLowerCase().trim(),r=a.filter(e=>!t||e.toLowerCase().includes(t)),s=g(e),c=s?null:_(e),u=``;s?u+=`
|
|
753
|
+
<div class="filter-popover-item filter-popover-smart-item active" data-smart-action="true">
|
|
754
|
+
<div class="filter-popover-item-label">
|
|
755
|
+
<span class="material-symbols-outlined" style="font-size: 16px; color: var(--color-primary);">filter_alt</span>
|
|
756
|
+
<span>Add filter: <strong>${s.col}</strong> ${s.op} <strong>${h(s.val)}</strong></span>
|
|
757
|
+
</div>
|
|
758
|
+
<span class="filter-kbd-hint">↵ Enter</span>
|
|
759
|
+
</div>
|
|
760
|
+
`:c&&c.type===`sql`?u+=`
|
|
761
|
+
<div class="filter-popover-item filter-popover-smart-item active" data-sql-action="true">
|
|
762
|
+
<div class="filter-popover-item-label">
|
|
763
|
+
<span class="material-symbols-outlined" style="font-size: 16px; color: #10b981;">code</span>
|
|
764
|
+
<span>Apply SQL condition: <strong>${h(c.val)}</strong></span>
|
|
765
|
+
</div>
|
|
766
|
+
<span class="filter-kbd-hint">↵ Enter</span>
|
|
767
|
+
</div>
|
|
768
|
+
`:c&&c.type===`select_warning`&&(u+=`
|
|
769
|
+
<div class="filter-popover-empty">
|
|
770
|
+
<span class="material-symbols-outlined empty-icon" style="color: var(--color-warning);">lightbulb</span>
|
|
771
|
+
<div class="empty-text">
|
|
772
|
+
<span class="empty-title">This bar filters the current table directly</span>
|
|
773
|
+
<span class="empty-hint">Enter conditions like <code>${o} = 1</code> without SELECT, or switch to <strong>SQL Editor</strong> for full queries.</span>
|
|
774
|
+
</div>
|
|
775
|
+
</div>
|
|
776
|
+
`),r.length===0&&!s&&(!c||c.type!==`sql`)?(!c||c.type!==`select_warning`)&&(u=`
|
|
777
|
+
<div class="filter-popover-empty">
|
|
778
|
+
<span class="material-symbols-outlined empty-icon">search</span>
|
|
779
|
+
<div class="empty-text">
|
|
780
|
+
<span class="empty-title">No columns match "<strong>${h(e)}</strong>"</span>
|
|
781
|
+
<span class="empty-hint">Press <kbd class="filter-kbd-hint">↵ Enter</kbd> to search table records</span>
|
|
782
|
+
</div>
|
|
783
|
+
</div>
|
|
784
|
+
`):u+=r.map((e,t)=>{let r=n.find(t=>t.name===e)?.type||``;return`
|
|
785
|
+
<div class="filter-popover-item ${!s&&(!c||c.type!==`sql`)&&t===0?`active`:``}" data-col="${e}">
|
|
786
|
+
<div class="filter-popover-item-label">
|
|
787
|
+
<span>${e}</span>
|
|
788
|
+
</div>
|
|
789
|
+
${r?`<span class="filter-popover-col-type">${r}</span>`:``}
|
|
790
|
+
</div>`}).join(``),d.innerHTML=`
|
|
791
|
+
<div class="filter-popover-scroll">
|
|
792
|
+
${u}
|
|
793
|
+
</div>
|
|
794
|
+
`;let f=d.querySelector(`[data-smart-action="true"]`);f&&(f.addEventListener(`mouseenter`,()=>{d.querySelectorAll(`.filter-popover-item`).forEach(e=>e.classList.remove(`active`)),f.classList.add(`active`)}),f.onclick=e=>{e.stopPropagation(),x(s)});let p=d.querySelector(`[data-sql-action="true"]`);p&&c&&(p.addEventListener(`mouseenter`,()=>{d.querySelectorAll(`.filter-popover-item`).forEach(e=>e.classList.remove(`active`)),p.classList.add(`active`)}),p.onclick=e=>{e.stopPropagation(),x({id:`sql-${Date.now()}`,type:`sql`,col:`SQL`,op:`RAW`,symbol:`SQL`,val:c.val})}),d.querySelectorAll(`.filter-popover-item:not([data-smart-action="true"]):not([data-sql-action="true"])`).forEach(e=>{e.addEventListener(`mouseenter`,()=>{d.querySelectorAll(`.filter-popover-item`).forEach(e=>e.classList.remove(`active`)),e.classList.add(`active`)}),e.onclick=t=>{t.stopPropagation();let n=e.dataset.col;i.draft={id:i.draft?.id||null,col:n,op:i.draft?.op||`=`,val:i.draft?.val||``,step:`operator`},l.value=``,y(),l.focus(),w()}})},y=()=>{let e=``,t=!0;ye.forEach(n=>{e+=`<div class="filter-category-title">${n.category}</div>`,n.operators.forEach(n=>{let r=i.draft?.op?i.draft.op===n.op:t;r&&(t=!1),e+=`
|
|
795
|
+
<div class="filter-popover-item ${r?`active`:``}" data-op="${n.op}" data-symbol="${n.symbol}" data-unary="${!!n.isUnary}">
|
|
796
|
+
<span>${n.label}</span>
|
|
797
|
+
<span class="filter-op-badge">${n.symbol}</span>
|
|
798
|
+
</div>
|
|
799
|
+
`})}),d.innerHTML=`
|
|
800
|
+
<div class="filter-popover-header">
|
|
801
|
+
<button type="button" class="filter-popover-back-btn" id="filter-popover-back-col" title="Back to columns">
|
|
802
|
+
<span class="material-symbols-outlined">arrow_back</span>
|
|
803
|
+
</button>
|
|
804
|
+
<div class="filter-popover-title-text">
|
|
805
|
+
<span>Filter by</span>
|
|
806
|
+
<strong>${i.draft.col}</strong>
|
|
807
|
+
</div>
|
|
808
|
+
</div>
|
|
809
|
+
<div class="filter-popover-scroll">
|
|
810
|
+
${e}
|
|
811
|
+
</div>
|
|
812
|
+
`;let n=document.getElementById(`filter-popover-back-col`);n&&(n.onclick=e=>{e.stopPropagation(),i.draft.step=`column`,v(),l.focus(),w()}),d.querySelectorAll(`.filter-popover-item`).forEach(e=>{e.addEventListener(`mouseenter`,()=>{d.querySelectorAll(`.filter-popover-item`).forEach(e=>e.classList.remove(`active`)),e.classList.add(`active`)}),e.onclick=t=>{t.stopPropagation();let n=e.dataset.op,r=e.dataset.symbol,a=e.dataset.unary===`true`;i.draft.op=n,i.draft.symbol=r,a?x({...i.draft,val:``}):(i.draft.step=`value`,b())}})},b=()=>{let e=i.draft.col,t=i.draft.op,n=i.draft.symbol||t,r=`Enter value...`;t===`LIKE`||t===`ILIKE`||t===`NOT LIKE`?r=`Search string or %pattern%...`:t===`IN`&&(r=`val1, val2, val3...`),d.innerHTML=`
|
|
813
|
+
<div class="filter-popover-header">
|
|
814
|
+
<button type="button" class="filter-popover-back-btn" id="filter-popover-back-op" title="Back to operators">
|
|
815
|
+
<span class="material-symbols-outlined">arrow_back</span>
|
|
816
|
+
</button>
|
|
817
|
+
<div class="filter-popover-title-text">
|
|
818
|
+
<span>${e}</span>
|
|
819
|
+
<span class="filter-op-badge">${n}</span>
|
|
820
|
+
</div>
|
|
821
|
+
</div>
|
|
822
|
+
<div class="filter-popover-form">
|
|
823
|
+
<input
|
|
824
|
+
type="text"
|
|
825
|
+
id="filter-popover-val-input"
|
|
826
|
+
class="filter-popover-value-input"
|
|
827
|
+
placeholder="${r}"
|
|
828
|
+
value="${i.draft.val||``}"
|
|
829
|
+
autocomplete="off"
|
|
830
|
+
/>
|
|
831
|
+
<div class="filter-popover-form-actions">
|
|
832
|
+
<button type="button" class="filter-popover-btn" id="filter-popover-cancel-btn">Cancel</button>
|
|
833
|
+
<button type="button" class="filter-popover-btn primary" id="filter-popover-apply-btn">
|
|
834
|
+
Apply <span class="filter-kbd-hint">↵</span>
|
|
835
|
+
</button>
|
|
836
|
+
</div>
|
|
837
|
+
</div>
|
|
838
|
+
`;let a=document.getElementById(`filter-popover-val-input`),o=document.getElementById(`filter-popover-apply-btn`),s=document.getElementById(`filter-popover-cancel-btn`),c=document.getElementById(`filter-popover-back-op`);c&&(c.onclick=e=>{e.stopPropagation(),i.draft.step=`operator`,y(),l.focus(),w()}),s&&(s.onclick=e=>{e.stopPropagation(),E()});let u=()=>{let e=a.value.trim();x({...i.draft,val:e})};o&&(o.onclick=u),a&&(a.focus(),a.onkeydown=e=>{e.key===`Enter`?(e.preventDefault(),u()):e.key===`Escape`?(e.preventDefault(),E()):e.key===`Backspace`&&!a.value&&(e.preventDefault(),i.draft.step=`operator`,y(),l.focus(),w())})},x=e=>{if(e.id&&i.activeFilters.some(t=>t.id===e.id)){let t=i.activeFilters.findIndex(t=>t.id===e.id);i.activeFilters[t]={...i.activeFilters[t],...e}}else{let t={id:e.id||`filter-${Date.now()}-${Math.random().toString(36).slice(2,6)}`,type:e.type||`standard`,col:e.col,op:e.op,symbol:e.symbol||e.op,val:e.val};i.activeFilters.push(t)}i.draft=null,E(),m(),l.value=``,i.freeTextSearch=``,r()},S=null,C=(e,t)=>{if(!e)return 0;S||=document.createElement(`canvas`);let n=S.getContext(`2d`);return n?(n.font=t||`12px Inter, sans-serif`,n.measureText(e).width):e.length*7},w=()=>{if(!d||d.classList.contains(`hidden`))return;let e=document.getElementById(`filter-bar-container-${t}`);if(!e)return;let n=14;if(i.draft&&i.draft.id){let e=c.querySelector(`[data-filter-id="${i.draft.id}"]`);e&&(n=e.offsetLeft)}else if(l){let e=window.getComputedStyle(l),t=`${e.fontSize||`12px`} ${e.fontFamily||`Inter, sans-serif`}`,r=l.selectionStart??l.value.length,i=l.value.slice(0,r),a=C(i,t);n=l.offsetLeft+a}let r=e.clientWidth||600,a=d.offsetWidth||280,o=Math.max(14,r-a-16),s=Math.max(14,Math.min(n,o));d.style.left=`${s}px`},T=()=>{d.classList.remove(`hidden`),i.draft?i.draft.step===`column`?v(l.value):i.draft.step===`operator`?y():i.draft.step===`value`&&b():(i.draft={step:`column`},v(l.value)),requestAnimationFrame(w)},E=()=>{d.classList.add(`hidden`),i.draft=null,m()};l.addEventListener(`focus`,()=>{T()}),l.addEventListener(`input`,()=>{let e=l.value;i.freeTextSearch=e,p(),!d.classList.contains(`hidden`)&&(!i.draft||i.draft.step===`column`)&&v(e),w()}),l.addEventListener(`keyup`,()=>{w()}),l.addEventListener(`click`,()=>{w()}),l.addEventListener(`keydown`,e=>{let t=!d.classList.contains(`hidden`);if(t&&(e.key===`ArrowDown`||e.key===`ArrowUp`)){e.preventDefault();let t=Array.from(d.querySelectorAll(`.filter-popover-item`));if(t.length>0){let n=t.findIndex(e=>e.classList.contains(`active`));n=e.key===`ArrowDown`?n===-1?0:(n+1)%t.length:n===-1?t.length-1:(n-1+t.length)%t.length,t.forEach((e,t)=>{e.classList.toggle(`active`,t===n)}),t[n].scrollIntoView({block:`nearest`})}return}if(e.key===`Escape`){E(),l.blur();return}if(e.key===`Enter`){if(e.preventDefault(),t){let e=d.querySelector(`.filter-popover-item.active`)||d.querySelector(`.filter-popover-item`);if(e){e.click();return}}let n=l.value.trim(),i=g(n);if(i){x(i);return}let a=_(n);if(a&&a.type===`sql`){x({id:`sql-${Date.now()}`,type:`sql`,col:`SQL`,op:`RAW`,symbol:`SQL`,val:a.val});return}E(),r();return}if(e.key===`Backspace`&&!l.value){if(t&&i.draft&&i.draft.step===`operator`){e.preventDefault(),i.draft.step=`column`,v(``),l.focus(),w();return}i.activeFilters.length>0&&(i.activeFilters.pop(),m(),r(),w())}}),f.addEventListener(`click`,e=>{e.target.closest(`.filter-pill`)||e.target.closest(`.filter-bar-clear-btn`)||l.focus()}),u&&(u.onclick=e=>{e.stopPropagation(),Se(t),l.value=``,m(),E(),r()});let D=e=>{let n=document.getElementById(`filter-bar-container-${t}`);if(!n){document.removeEventListener(`click`,D);return}n.contains(e.target)||E()};document.addEventListener(`click`,D),m()}function Ee(e){let t=e||window.AppState?.currentTable;if(!t)return``;let n=we(t,window.TableStates?.[t]?.dataGrid?.schema||window.DataGrid?.schema||[]);if(n)return n;let r=e=>`"${(e||``).replace(/"/g,`""`)}"`,i=e=>(e||``).replace(/'/g,`''`),a=document.getElementById(`filter-val-${t}`)?.value.trim(),o=document.getElementById(`filter-op-${t}`)?.value,s=document.getElementById(`filter-col-${t}`)?.value;if(!s||!o)return``;if(o===`IS NULL`||o===`IS NOT NULL`)return`${r(s)} ${o}`;if(!a)return``;if(o===`IN`){let e=a;return e.startsWith(`(`)||(e=`(${e})`),`${r(s)} IN ${e}`}let c=!isNaN(Number(a))&&a.trim()!==``?a:`'${i(a)}'`;return`${r(s)} ${o} ${c}`}function De(e,t){if(e==null||e===`null`||e===``)return{html:`<em>NULL</em>`,title:``};let n=(t?.type||``).toUpperCase(),r=n===`DATE`,i=n.includes(`DATETIME`)||n.includes(`TIMESTAMP`),a=n.includes(`BOOL`)||n===`TINYINT(1)`,o=String(e).trim(),s=n.match(/(?:NUMERIC|DECIMAL|FLOAT|DOUBLE)\s*\(\s*\d+\s*,\s*(\d+)\s*\)/);if(s&&!isNaN(Number(o))&&!o.toLowerCase().includes(`e`)){let e=parseInt(s[1],10);if(e>0){let[t,n=``]=o.split(`.`);if(n.length<e){n=n.padEnd(e,`0`);let r=`${t}.${n}`;return{html:r,title:o===r?``:`Raw: ${o}`}}}}if(a){let e=o===`1`||o.toLowerCase()===`true`,t=o===`0`||o.toLowerCase()===`false`;if(e)return{html:`<span class="badge-bool bool-true">true</span>`,title:`Boolean: ${o}`};if(t)return{html:`<span class="badge-bool bool-false">false</span>`,title:`Boolean: ${o}`}}if((r||i)&&o){let e=null,t=!1;if(/^\d{13}$/.test(o)?(e=new Date(parseInt(o,10)),t=!0):/^\d{10}$/.test(o)?(e=new Date(parseInt(o,10)*1e3),t=!0):(o.includes(`T`)||o.includes(`Z`)||r&&o.length>10&&o.includes(` `))&&(e=new Date(o.includes(` `)&&!o.includes(`T`)?o.replace(` `,`T`):o)),e&&!isNaN(e.getTime())){let n=`${e.getFullYear()}-${String(e.getMonth()+1).padStart(2,`0`)}-${String(e.getDate()).padStart(2,`0`)}`;if(i){let t=String(e.getHours()).padStart(2,`0`),r=String(e.getMinutes()).padStart(2,`0`),i=String(e.getSeconds()).padStart(2,`0`);n+=` ${t}:${r}:${i}`}return{html:n.replace(/</g,`<`),title:t?`Raw Timestamp: ${o}`:o===n?``:`Raw: ${o}`}}}return{html:o.replace(/</g,`<`),title:``}}function Oe(e,t,n,r,i=[]){let a=`<td class="row-header" data-row-idx="${t}">${t+1}</td>`,o=n?e[n]:t;return r.forEach((n,r)=>{let s=i.find(e=>e.name===n),c=e[n];typeof c==`object`&&c&&(c=c.type===`Buffer`&&Array.isArray(c.data)?`[BLOB ${c.data.length} B]`:JSON.stringify(c));let l=c==null?`null`:String(c),u=l.replace(/"/g,`"`),{html:d,title:f}=De(l,s),p=f?` title="${f}"`:``;if(s&&s.fkTarget&&l!==`null`){let e=s.fkTarget.table,i=s.fkTarget.column;a+=`<td class="data-cell is-fk-cell" data-row-idx="${t}" data-col-idx="${r}" data-pk="${o}" data-col="${n}" data-original="${u}" data-fk-table="${e}" data-fk-col="${i}"${p}><span class="cell-text">${d}</span><button type="button" class="fk-jump-btn" title="View & jump to ${e} (${i} = ${u})" data-fk-table="${e}" data-fk-col="${i}" data-fk-val="${u}"><span class="material-symbols-outlined">open_in_new</span></button></td>`}else a+=`<td class="data-cell" data-row-idx="${t}" data-col-idx="${r}" data-pk="${o}" data-col="${n}" data-original="${u}"${p}><span class="cell-text">${d}</span></td>`}),a}async function ke(){if(!window.DataGrid)return;let{pendingEdits:e,pendingInserts:t,pendingDeletes:n,pkColumn:r,schema:i}=window.DataGrid,a=window.AppState.currentTable;if(!a||!r){alert(`Cannot save: No Primary Key detected for this table.`);return}let o=Object.keys(e||{}).length>0,s=t?.some(e=>Object.values(e).some(e=>e!==``&&e!=null)),c=n&&n.size>0;if(!o&&!s&&!c)return;let l=n?Array.from(n):[];try{let n=await d(a,{pkColumn:r,edits:e,inserts:t,deletes:l,schema:i||[]});if(n.success)window.DataGrid&&(window.DataGrid.pendingEdits={},window.DataGrid.pendingInserts=[{}],window.DataGrid.pendingDeletes=new Set,window.DataGrid.history=[],window.DataGrid.currentTransaction=null,window.DataGrid.refreshData?window.DataGrid.refreshData():window.renderCurrentView()),window.updateSidebarActiveTable?.(),window.loadTableStats?.(),window.showToast&&window.showToast(`Data saved successfully!`);else{let e=n.error||`Failed to save changes`;window.showToast?window.showToast(`Save failed: `+e,`error`):alert(`Save failed:
|
|
839
|
+
`+e),document.querySelectorAll(`.cell-edited`).forEach(e=>{e.classList.remove(`cell-edited`),e.classList.add(`cell-error`)})}}catch(e){let t=e.message||`Network error`;window.showToast?window.showToast(`Save failed: `+t,`error`):alert(`Save failed:
|
|
840
|
+
`+t),document.querySelectorAll(`.cell-edited`).forEach(e=>{e.classList.remove(`cell-edited`),e.classList.add(`cell-error`)})}window.updateSidebarDirtyState?.()}function Ae(e,t,n,r=!0){if(r&&window.DataGrid.currentTransaction){let n=e.textContent;(n===`null`||e.classList.contains(`ghost-row`))&&(n=``),n!==t&&window.DataGrid.currentTransaction.push({td:e,oldVal:n,newVal:t})}let i=e.dataset.col,a=e.dataset.colIdx===`0`?`<span class="ghost-cell-hint">+ Add Row</span>`:``,o=De(t,(window.DataGrid?.schema||[]).find(e=>e.name===i));if(e.innerHTML=t?`<span class="cell-text">${o.html}</span>`:e.dataset.insertIndex===void 0?`<em>NULL</em>`:a,o.title?e.title=o.title:e.removeAttribute(`title`),e.dataset.insertIndex!==void 0){let r=parseInt(e.dataset.insertIndex);if(window.DataGrid.pendingInserts[r]||(window.DataGrid.pendingInserts[r]={}),t){if(window.DataGrid.pendingInserts[r][i]=t,e.classList.add(`cell-edited`),e.classList.remove(`ghost-row`),r===window.DataGrid.pendingInserts.length-1){e.closest(`tr`).classList.remove(`ghost-row-tr`),window.DataGrid.pendingInserts.push({});let t=document.querySelector(`#data-grid-table-${window.AppState.currentTable} tbody`),i=document.createElement(`tr`);i.className=`ghost-row-tr`;let a=parseInt(e.dataset.rowIdx)+1;i.innerHTML+=`<td class="row-header" data-row-idx="${a}">*</td>`,n.forEach((e,t)=>{let n=t===0?`<span class="ghost-cell-hint">+ Add Row</span>`:``;i.innerHTML+=`<td class="data-cell ghost-row" data-row-idx="${a}" data-col-idx="${t}" data-insert-index="${r+1}" data-col="${e}">${n}</td>`}),t.appendChild(i)}}else window.DataGrid.pendingInserts[r]&&delete window.DataGrid.pendingInserts[r][i],e.classList.remove(`cell-edited`),e.classList.add(`ghost-row`)}else{let n=e.dataset.pk;t===e.dataset.original?(e.classList.remove(`cell-edited`),e.classList.remove(`cell-error`),window.DataGrid.pendingEdits[n]&&(delete window.DataGrid.pendingEdits[n][i],Object.keys(window.DataGrid.pendingEdits[n]).length===0&&delete window.DataGrid.pendingEdits[n])):(window.DataGrid.pendingEdits[n]||(window.DataGrid.pendingEdits[n]={}),window.DataGrid.pendingEdits[n][i]=t,e.classList.add(`cell-edited`),e.classList.remove(`cell-error`))}window.updateSidebarDirtyState?.()}function je(e,t=!0){let n=document.querySelector(`#data-grid-table-${window.AppState.currentTable} td.data-cell[data-row-idx="${e}"]`)?.closest(`tr`);if(!n||n.classList.contains(`ghost-row-tr`)||n.classList.contains(`row-deleted`))return;let r=n.querySelector(`td.data-cell`),i=r?.dataset.pk;if(i!==void 0)t&&window.DataGrid.currentTransaction&&window.DataGrid.currentTransaction.push({type:`delete`,rowIdx:e,pk:i}),window.DataGrid.pendingDeletes.add(i),n.classList.add(`row-deleted`);else if(r?.dataset.insertIndex!==void 0){let e=parseInt(r.dataset.insertIndex);window.DataGrid.pendingInserts[e]&&delete window.DataGrid.pendingInserts[e],n.remove()}window.updateSidebarDirtyState?.()}function Me(e,t){let n=document.querySelector(`#data-grid-table-${window.AppState.currentTable} td.data-cell[data-row-idx="${e}"]`)?.closest(`tr`);n&&n.classList.remove(`row-deleted`),window.DataGrid.pendingDeletes.delete(t),window.updateSidebarDirtyState?.()}function Ne(e,t){e.forEach(e=>{let n=window.AppState.currentTable,r=document.querySelector(`#data-grid-table-${n} td.data-cell[data-row-idx="${e}"]`)?.closest(`tr`);if(!r||r.classList.contains(`ghost-row-tr`))return;let i=document.querySelector(`#data-grid-table-${n} .ghost-row-tr`);i&&t.forEach((e,n)=>{let a=r.querySelector(`td.data-cell[data-col-idx="${n}"]`),o=i.querySelector(`td.data-cell[data-col-idx="${n}"]`);if(a&&o){let n=a.dataset.insertIndex===void 0?a.classList.contains(`cell-edited`)?window.DataGrid.pendingEdits[a.dataset.pk]?.[e]:a.dataset.original:window.DataGrid.pendingInserts[a.dataset.insertIndex][e];n!=null&&n!==`null`&&Ae(o,n,t,!0)}})})}var Pe=null;function Fe({td:e,colName:t,colType:n=``,initialValue:r=``,onSave:i,onCancel:a}){Pe&&Pe.close(!0);let o=document.createElement(`div`);o.className=`supabase-editor-backdrop hidden`;let s=document.createElement(`div`);s.className=`supabase-cell-editor-popover`;let c=document.createElement(`textarea`);c.className=`supabase-editor-textarea`,c.placeholder=`Enter value...`,c.value=r;let l=document.createElement(`div`);l.className=`supabase-editor-footer`;let u=document.createElement(`div`);u.className=`supabase-editor-footer-left`;let d=document.createElement(`button`);d.type=`button`,d.className=`supabase-editor-shortcut-btn btn-save`,d.title=`Save changes (Enter or Ctrl+Enter)`,d.innerHTML=`
|
|
841
|
+
<span class="supabase-kbd-badge">↵</span>
|
|
842
|
+
<span>Save changes</span>
|
|
843
|
+
`;let f=document.createElement(`button`);f.type=`button`,f.className=`supabase-editor-shortcut-btn btn-cancel`,f.title=`Cancel changes (Esc)`,f.innerHTML=`
|
|
844
|
+
<span class="supabase-kbd-badge">Esc</span>
|
|
845
|
+
<span>Cancel changes</span>
|
|
846
|
+
`,u.appendChild(d),u.appendChild(f);let p=document.createElement(`div`);p.className=`supabase-editor-footer-right`;let m=document.createElement(`button`);m.type=`button`,m.className=`supabase-editor-expand-btn`,m.title=`Toggle Fullscreen Editor`,m.innerHTML=`<span class="material-symbols-outlined supabase-expand-icon">fullscreen</span>`,p.appendChild(m),l.appendChild(u),l.appendChild(p),s.appendChild(c),s.appendChild(l),document.body.appendChild(o),document.body.appendChild(s);let h=()=>{if(g)return;let t=e.getBoundingClientRect(),n=Math.max(t.width,360),r=t.top,i=t.left;r+240>window.innerHeight-12&&(r=Math.max(12,window.innerHeight-240-12)),i+n>window.innerWidth-12&&(i=Math.max(12,window.innerWidth-n-12)),s.style.top=`${r}px`,s.style.left=`${i}px`,s.style.width=`${n}px`,s.style.height=`240px`},g=!1,_=!1;h();let v=()=>{g=!g,g?(s.classList.add(`is-expanded`),o.classList.remove(`hidden`),m.innerHTML=`<span class="material-symbols-outlined supabase-expand-icon">fullscreen_exit</span>`,m.title=`Collapse Editor`):(s.classList.remove(`is-expanded`),o.classList.add(`hidden`),m.innerHTML=`<span class="material-symbols-outlined supabase-expand-icon">fullscreen</span>`,m.title=`Toggle Fullscreen Editor`,h()),c.focus()};m.onclick=e=>{e.stopPropagation(),v()},o.onclick=e=>{e.stopPropagation(),y(!0)};let y=(e=!1)=>{_||(_=!0,document.removeEventListener(`pointerdown`,b),window.removeEventListener(`resize`,h),s.remove(),o.remove(),Pe=null,e?i&&i(c.value):a&&a())},b=e=>{s.contains(e.target)||y(!0)};setTimeout(()=>{_||document.addEventListener(`pointerdown`,b)},80),window.addEventListener(`resize`,h),d.onclick=e=>{e.stopPropagation(),y(!0)},f.onclick=e=>{e.stopPropagation(),y(!1)},c.addEventListener(`keydown`,e=>{if(e.key===`Enter`&&!e.shiftKey)e.preventDefault(),y(!0);else if(e.key===`Enter`&&(e.ctrlKey||e.metaKey))e.preventDefault(),y(!0);else if(e.key===`Escape`)e.preventDefault(),y(!1);else if(e.key===`Tab`){e.preventDefault();let t=c.selectionStart,n=c.selectionEnd;c.value=c.value.substring(0,t)+` `+c.value.substring(n),c.selectionStart=c.selectionEnd=t+2}}),Pe={close:y},requestAnimationFrame(()=>{c.focus(),c.setSelectionRange(c.value.length,c.value.length)})}function Ie(e,t,n){e.addEventListener(`dblclick`,e=>{let r=e.target.closest(`td.data-cell`);if(!r||r.querySelector(`input, select`))return;let i=r.dataset.col,a=t.find(e=>e.name===i),o=a?.type?.toUpperCase()||``;if(o.includes(`BLOB`)||o===`BYTEA`||o.includes(`BINARY`)){window.showToast&&window.showToast(`Binary (BLOB) data cannot be edited directly.`,`info`);return}let c=o.includes(`ENUM`)||a?.enumValues&&a.enumValues.length>0,l=o===`DATE`,u=o.includes(`DATETIME`)||o.includes(`TIMESTAMP`),d=o.includes(`BOOL`)||o===`TINYINT(1)`,f=o===`TIME`||o.startsWith(`TIME(`),p=r.classList.contains(`ghost-row`)||r.dataset.insertIndex!==void 0&&!r.classList.contains(`cell-edited`),m=r.dataset.pk,h=r.dataset.insertIndex,g=h===void 0?m===void 0?void 0:window.DataGrid?.pendingEdits?.[m]?.[i]:window.DataGrid?.pendingInserts?.[parseInt(h,10)]?.[i],_=``;p||(_=g===void 0?r.dataset.original===void 0?r.querySelector(`.cell-text`)?.textContent.trim()||r.textContent.trim():r.dataset.original:String(g)),(_===`null`||_===`+ New`||_===`+ Add Row`)&&(_=``);let v;if(a&&a.fkTarget){let{table:e,column:t}=a.fkTarget;s(e,{limit:200}).then(i=>{if(i.success&&i.data&&i.data.rows){let o=t;if(i.data.columns){let e=[`name`,`title`,`label`,`description`,`username`,`email`],t=i.data.columns.find(t=>e.includes(t.toLowerCase()));t&&(o=t)}let s=[];a.nullable&&s.push({name:`-- None --`,value:``,desc:`Set to NULL`,icon:`block`}),i.data.rows.forEach(n=>{let r=String(n[t]),i=o!==t&&n[o]?String(n[o]):`Row in ${e}`;s.push({name:r,value:r,desc:i,icon:`link`})}),_&&!i.data.rows.find(e=>String(e[t])===_)&&s.unshift({name:_,value:_,desc:`Current value`,icon:`link`}),I({anchorEl:r,title:`REFERENCES ${e.toUpperCase()}.${t.toUpperCase()}`,placeholder:`Search ${e} by ${o}...`,items:s,initialValue:_,width:360,onSelect:e=>{window.DataGrid.currentTransaction=[],Ae(r,e,n),window.DataGrid.currentTransaction.length>0&&window.DataGrid.history.push(window.DataGrid.currentTransaction),window.DataGrid.currentTransaction=null}})}}).catch(e=>{window.showToast&&window.showToast(`Failed to load FK targets: `+e.message,`error`)});return}else if(c){let e=a?.enumValues||[];if(e.length===0&&a?.type){let t=a.type.match(/enum\((.*?)\)/i);t&&(e=t[1].split(`,`).map(e=>e.trim().replace(/^'|'$/g,``)))}let t=[];a?.nullable&&t.push({name:`-- None --`,value:``,desc:`Set to NULL`,icon:`block`}),e.forEach(e=>{t.push({name:e,value:e,desc:`Enum option`,icon:`label`})}),I({anchorEl:r,title:`ENUM VALUES`,placeholder:`Search enum values...`,items:t,initialValue:_,width:300,onSelect:e=>{window.DataGrid.currentTransaction=[],Ae(r,e,n),window.DataGrid.currentTransaction.length>0&&window.DataGrid.history.push(window.DataGrid.currentTransaction),window.DataGrid.currentTransaction=null}});return}else if(l){v=document.createElement(`input`),v.type=`date`;let e=``;if(_)if(/^\d{13}$/.test(_)){let t=new Date(parseInt(_,10));isNaN(t.getTime())||(e=`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,`0`)}-${String(t.getDate()).padStart(2,`0`)}`)}else if(/^\d{10}$/.test(_)){let t=new Date(parseInt(_,10)*1e3);isNaN(t.getTime())||(e=`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,`0`)}-${String(t.getDate()).padStart(2,`0`)}`)}else if(/^\d{4}-\d{2}-\d{2}/.test(_))e=_.slice(0,10);else{let t=new Date(_);isNaN(t.getTime())||(e=`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,`0`)}-${String(t.getDate()).padStart(2,`0`)}`)}v.value=e}else if(u){v=document.createElement(`input`),v.type=`datetime-local`;let e=``;if(_){let t=null;t=/^\d{13}$/.test(_)?new Date(parseInt(_,10)):/^\d{10}$/.test(_)?new Date(parseInt(_,10)*1e3):_.includes(`T`)||_.includes(` `)?new Date(_.replace(` `,`T`)):new Date(_),t&&!isNaN(t.getTime())&&(e=`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,`0`)}-${String(t.getDate()).padStart(2,`0`)}T${String(t.getHours()).padStart(2,`0`)}:${String(t.getMinutes()).padStart(2,`0`)}`)}v.value=e}else if(f)v=document.createElement(`input`),v.type=`time`,v.step=`1`,v.value=_?_.slice(0,8):``;else if(d){let e=!a||a.nullable!==!1,t=_===`1`||_.toLowerCase()===`true`,i=_===`0`||_.toLowerCase()===`false`,o=t?`1`:i?`0`:``,s=[{name:`true`,value:`1`,desc:`Logical True`,icon:`check_circle`,badge:`true`,badgeClass:`bool-true`},{name:`false`,value:`0`,desc:`Logical False`,icon:`cancel`,badge:`false`,badgeClass:`bool-false`}];e&&s.push({name:`null`,value:``,desc:`Empty / NULL value`,icon:`remove`,badge:`null`,badgeClass:`bool-null`}),I({anchorEl:r,title:`BOOLEAN VALUE`,placeholder:`Filter true / false / null...`,items:s,initialValue:o,width:280,onSelect:e=>{window.DataGrid.currentTransaction=[],Ae(r,e,n),window.DataGrid.currentTransaction.length>0&&window.DataGrid.history.push(window.DataGrid.currentTransaction),window.DataGrid.currentTransaction=null}});return}else{if(!c&&!l&&!u&&!f&&!d&&!(a&&a.fkTarget)&&(o===``||o.includes(`TEXT`)||o.includes(`VARCHAR`)||o.includes(`CHAR`)||o.includes(`JSON`)||o.includes(`CLOB`)||o.includes(`STRING`))){Fe({td:r,colName:i,colType:o,initialValue:_,onSave:e=>{window.DataGrid.currentTransaction=[],Ae(r,e,n),window.DataGrid.currentTransaction.length>0&&window.DataGrid.history.push(window.DataGrid.currentTransaction),window.DataGrid.currentTransaction=null}});return}v=document.createElement(`input`),v.type=`text`,v.value=_}let y=!1,b=()=>{y=!0,Fe({td:r,colName:i,colType:o,initialValue:v.value,onSave:e=>{v.value=e,y=!1,x()},onCancel:()=>{y=!1,v.focus()}})};if(r.innerHTML=``,r.classList.add(`cell-editing`),r.appendChild(v),o===``||!c&&!l&&!u&&!f&&!d&&!(a&&a.fkTarget)){let e=document.createElement(`span`);e.className=`material-symbols-outlined cell-expand-btn`,e.textContent=`open_in_full`,e.title=`Open Full Editor (Shift + Enter)`,e.onmousedown=e=>{e.preventDefault(),e.stopPropagation()},e.onclick=e=>{e.preventDefault(),e.stopPropagation(),b()},r.appendChild(e),v.style.paddingRight=`32px`,v.addEventListener(`keydown`,e=>{e.key===`Enter`&&e.shiftKey?(e.preventDefault(),b()):e.key===`Enter`&&v.blur(),e.key===`Tab`&&(e.preventDefault(),v.blur(),document.body.dispatchEvent(new KeyboardEvent(`keydown`,{key:`Tab`,shiftKey:e.shiftKey,bubbles:!0})))})}else v.addEventListener(`keydown`,e=>{if(e.key===`Enter`&&v.blur(),e.key===`Tab`){if(l||u||f)return;e.preventDefault(),v.blur(),document.body.dispatchEvent(new KeyboardEvent(`keydown`,{key:`Tab`,shiftKey:e.shiftKey,bubbles:!0}))}});v.focus();let x=()=>{if(r.classList.remove(`cell-editing`),y)return;let e=v.value,t=o.match(/(?:NUMERIC|DECIMAL|FLOAT|DOUBLE)\s*\(\s*\d+\s*,\s*(\d+)\s*\)/);if(t&&e&&!isNaN(Number(e))&&!e.toLowerCase().includes(`e`)){let n=parseInt(t[1],10);if(n>0){let[t,r=``]=e.split(`.`);r.length<n&&(r=r.padEnd(n,`0`),e=`${t}.${r}`)}}if(f&&e&&/^\d{2}:\d{2}$/.test(e)&&(e+=`:00`),l&&e){if(/^\d{13}$/.test(_)){let t=new Date(e);isNaN(t.getTime())||(e=String(t.getTime()))}else if(/^\d{10}$/.test(_)){let t=new Date(e);isNaN(t.getTime())||(e=String(Math.floor(t.getTime()/1e3)))}}else if(u&&e)if(/^\d{13}$/.test(_)){let t=new Date(e);isNaN(t.getTime())||(e=String(t.getTime()))}else if(/^\d{10}$/.test(_)){let t=new Date(e);isNaN(t.getTime())||(e=String(Math.floor(t.getTime()/1e3)))}else e=e.replace(`T`,` `),/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/.test(e)&&(e+=`:00`);window.DataGrid.currentTransaction=[],Ae(r,e,n),window.DataGrid.currentTransaction.length>0&&window.DataGrid.history.push(window.DataGrid.currentTransaction),window.DataGrid.currentTransaction=null};v.addEventListener(`blur`,()=>{setTimeout(()=>{!y&&document.activeElement!==v&&x()},120)})})}var z=null,Le=null,Re=null,ze=null;function Be(){return z||(z=document.createElement(`div`),z.id=`fk-preview-popover`,z.className=`fk-preview-popover hidden`,document.body.appendChild(z),z.addEventListener(`mouseenter`,()=>{Re&&=(clearTimeout(Re),null)}),z.addEventListener(`mouseleave`,e=>{e.relatedTarget&&ze&&ze.contains(e.relatedTarget)||Ve()}),document.addEventListener(`keydown`,e=>{e.key===`Escape`&&z&&!z.classList.contains(`hidden`)&&Ve()}),window.addEventListener(`scroll`,e=>{z&&!z.classList.contains(`hidden`)&&(z.contains(e.target)||Ve())},!0),z)}function Ve(){Le&&=(clearTimeout(Le),null),z&&z.classList.add(`hidden`),ze=null}function He(e,t,n,r){Ve(),window.fkNavHistory={fromTable:r,toTable:e,timestamp:Date.now()};let i=`"${t}" = ${/^-?\d+(\.\d+)?$/.test(n)?n:`'${n.replace(/'/g,`''`)}'`}`,a=document.querySelector(`.table-btn[data-table="${e}"]`)||Array.from(document.querySelectorAll(`.table-btn`)).find(t=>(t.dataset.table||t.querySelector(`span`)?.textContent.trim())===e);window.AppState.currentTable=e,window.AppState.currentTableBtnElement=a||null,window.AppState.currentTab=`data-btn`,document.querySelectorAll(`.table-btn`).forEach(e=>e.classList.remove(`active`)),a&&a.classList.add(`active`);let o=`view-data-btn-${e}`,s=document.getElementById(o);s&&(s.innerHTML=``),window.renderCurrentView(i),window.showToast&&window.showToast(`Jumped to ${e} (${i})`,`info`)}function Ue(e,t){let n=Be();e.addEventListener(`click`,e=>{let n=e.target.closest(`.fk-jump-btn`);if(!n)return;e.stopPropagation();let r=n.dataset.fkTable,i=n.dataset.fkCol,a=n.dataset.fkVal;r&&i&&a!==void 0&&He(r,i,a,t)}),e.addEventListener(`mouseover`,e=>{let r=e.target.closest(`.is-fk-cell`);r&&(Re&&=(clearTimeout(Re),null),ze!==r&&(ze=r,Le&&clearTimeout(Le),Le=setTimeout(async()=>{let e=r.dataset.fkTable,i=r.dataset.fkCol,a=r.dataset.original;if(!e||!i||a===void 0||a===`null`)return;n.innerHTML=`
|
|
847
|
+
<div class="fk-popover-header">
|
|
848
|
+
<div class="fk-popover-title">
|
|
849
|
+
<span class="material-symbols-outlined icon-16" style="color: var(--color-primary);">table_chart</span>
|
|
850
|
+
<span class="fk-popover-table">${e}</span>
|
|
851
|
+
<span class="fk-popover-badge">FK → ${i}</span>
|
|
852
|
+
</div>
|
|
853
|
+
</div>
|
|
854
|
+
<div class="fk-popover-body">
|
|
855
|
+
<div class="fk-popover-loading">
|
|
856
|
+
<span class="material-symbols-outlined animate-spin icon-16">progress_activity</span>
|
|
857
|
+
<span>Loading relation...</span>
|
|
858
|
+
</div>
|
|
859
|
+
</div>
|
|
860
|
+
`,We(r,n),n.classList.remove(`hidden`);let o=/^-?\d+(\.\d+)?$/.test(a)?a:`'${a.replace(/'/g,`''`)}'`;try{let c=await s(e,{where:`"${i}" = ${o}`,limit:1});if(ze!==r)return;if(c.success&&c.data&&c.data.rows&&c.data.rows.length>0){let o=c.data.rows[0],s=Object.keys(o).slice(0,7),l=``;s.forEach(e=>{let t=o[e];t=t==null?`null`:typeof t==`object`?JSON.stringify(t):String(t),l+=`
|
|
861
|
+
<div class="fk-field-row">
|
|
862
|
+
<span class="fk-field-name">${e}</span>
|
|
863
|
+
<span class="fk-field-value ${t===`null`?`is-null`:``}" title="${t.replace(/"/g,`"`)}">${t}</span>
|
|
864
|
+
</div>
|
|
865
|
+
`}),n.innerHTML=`
|
|
866
|
+
<div class="fk-popover-header">
|
|
867
|
+
<div class="fk-popover-title">
|
|
868
|
+
<span class="material-symbols-outlined icon-16" style="color: var(--color-primary);">table_chart</span>
|
|
869
|
+
<span class="fk-popover-table">${e}</span>
|
|
870
|
+
<span class="fk-popover-badge">${i} = ${a}</span>
|
|
871
|
+
</div>
|
|
872
|
+
<button type="button" class="fk-popover-jump-btn" id="btn-fk-popover-jump">
|
|
873
|
+
<span>Jump</span>
|
|
874
|
+
<span class="material-symbols-outlined icon-14">arrow_forward</span>
|
|
875
|
+
</button>
|
|
876
|
+
</div>
|
|
877
|
+
<div class="fk-popover-body">
|
|
878
|
+
<div class="fk-fields-list">${l}</div>
|
|
879
|
+
</div>
|
|
880
|
+
<div class="fk-popover-footer">
|
|
881
|
+
<span class="material-symbols-outlined icon-14">touch_app</span>
|
|
882
|
+
<span>Click to inspect full record</span>
|
|
883
|
+
</div>
|
|
884
|
+
`,n.querySelector(`#btn-fk-popover-jump`)?.addEventListener(`click`,n=>{n.stopPropagation(),He(e,i,a,t)}),n.onclick=()=>{He(e,i,a,t)},We(r,n)}else n.innerHTML=`
|
|
885
|
+
<div class="fk-popover-header">
|
|
886
|
+
<div class="fk-popover-title">
|
|
887
|
+
<span class="material-symbols-outlined icon-16" style="color: var(--color-primary);">table_chart</span>
|
|
888
|
+
<span class="fk-popover-table">${e}</span>
|
|
889
|
+
</div>
|
|
890
|
+
</div>
|
|
891
|
+
<div class="fk-popover-body">
|
|
892
|
+
<div class="fk-popover-empty">
|
|
893
|
+
<span class="material-symbols-outlined icon-18">error_outline</span>
|
|
894
|
+
<span>No matching row found for ${i} = ${a}</span>
|
|
895
|
+
</div>
|
|
896
|
+
</div>
|
|
897
|
+
`,We(r,n)}catch(e){if(ze!==r)return;n.innerHTML=`
|
|
898
|
+
<div class="fk-popover-body">
|
|
899
|
+
<div class="fk-popover-empty">
|
|
900
|
+
<span>Failed to fetch preview: ${e.message}</span>
|
|
901
|
+
</div>
|
|
902
|
+
</div>
|
|
903
|
+
`,We(r,n)}},250)))}),e.addEventListener(`mouseout`,e=>{let t=e.target.closest(`.is-fk-cell`);t&&(e.relatedTarget&&(t.contains(e.relatedTarget)||n&&n.contains(e.relatedTarget))||(Le&&=(clearTimeout(Le),null),Re=setTimeout(()=>{Ve()},350)))})}function We(e,t){let n=e.getBoundingClientRect(),r=t.getBoundingClientRect(),i=n.bottom+2,a=n.left;i+r.height>window.innerHeight-10&&(i=Math.max(10,n.top-r.height-2)),a+r.width>window.innerWidth-10&&(a=Math.max(10,window.innerWidth-r.width-10)),t.style.top=`${i}px`,t.style.left=`${a}px`}async function Ge(e,t,n=``,r=!1,i=null){if(window.updateSidebarActiveTable)window.updateSidebarActiveTable(e);else{document.querySelectorAll(`.table-btn`).forEach(e=>e.classList.remove(`active`));let n=t||document.querySelector(`.table-btn[data-table="${e}"]`);n&&(n.classList.add(`active`),window.AppState&&(window.AppState.currentTableBtnElement=n))}let a=document.getElementById(`table-name`);a&&(a.textContent=e);let o=i||document.getElementById(`main-content`);r||(o.innerHTML=`<div class='p-6'>Loading...</div>`);try{let i=await c(e);if(!i.success)throw Error(i.error);let a=i.data,l=a.find(e=>e.isPk)?.name;(!r||!window.DataGrid)&&(window.DataGrid={schema:a,pkColumn:l,pendingEdits:{},pendingInserts:[{}],pendingDeletes:new Set,selectedCell:null,history:[],currentTransaction:null,sortState:{col:l||a[0].name,asc:!0},pagination:{limit:50,offset:0,isLoading:!1,hasMore:!0},selection:{isDragging:!1,isDraggingRow:!1,startRow:-1,startCol:-1,endRow:-1,endCol:-1}}),window.TableStates&&window.TableStates[e]&&(window.TableStates[e].dataGrid=window.DataGrid);let u=await s(e,{where:n,limit:window.DataGrid.pagination.limit,offset:window.DataGrid.pagination.offset,orderCol:window.DataGrid.sortState.col,orderAsc:window.DataGrid.sortState.asc});if(u.success&&u.data){window.loadTableStats?.();let i=u.data.rows,c=u.data.columns;if(window.DataGrid.rows=i,window.DataGrid.columns=c,window.DataGrid.pagination.hasMore=i.length===window.DataGrid.pagination.limit,!r){o.innerHTML=`
|
|
904
|
+
<div class="toolbar">
|
|
905
|
+
${window.fkNavHistory&&window.fkNavHistory.toTable===e?`<button type="button" id="btn-fk-back-${e}" class="fk-back-btn" title="Back to table '${window.fkNavHistory.fromTable}'">
|
|
906
|
+
<span class="material-symbols-outlined icon-14">arrow_back</span>
|
|
907
|
+
<span>Back to ${window.fkNavHistory.fromTable}</span>
|
|
908
|
+
</button>`:``}
|
|
909
|
+
<div id="filter-bar-mount-${e}" class="flex-1" style="height: 100%; display: flex; align-items: center; min-width: 0;"></div>
|
|
910
|
+
<button id="btn-refresh-data-${e}" class="refresh-btn" title="Refresh Data (F5)">
|
|
911
|
+
<span class="material-symbols-outlined">refresh</span>
|
|
912
|
+
</button>
|
|
913
|
+
</div>
|
|
914
|
+
<div class="table-container" id="data-grid-container-${e}"></div>
|
|
915
|
+
`;let n=document.getElementById(`filter-bar-mount-${e}`);n&&Te({container:n,tableName:e,schema:a,onFilterChange:()=>{window.DataGrid&&(window.DataGrid.pagination.offset=0),Ge(e,t,Ee(e),!0)}})}let d=document.getElementById(`data-grid-container-${e}`);if(!d)return;if((!i||i.length===0)&&!window.forceEmptyGrid)if(n){d.innerHTML=`
|
|
916
|
+
<div class="table-empty-container">
|
|
917
|
+
<div class="table-empty-card">
|
|
918
|
+
<div class="table-empty-icon-wrap">
|
|
919
|
+
<span class="material-symbols-outlined table-empty-icon">search_off</span>
|
|
920
|
+
</div>
|
|
921
|
+
<h3 class="table-empty-title">No matching records found</h3>
|
|
922
|
+
<p class="table-empty-desc">No records in "${e}" matched the current filter conditions.</p>
|
|
923
|
+
<div class="table-empty-actions">
|
|
924
|
+
<button type="button" class="table-empty-btn-primary" id="btn-empty-clear-filter-${e}">
|
|
925
|
+
<span class="material-symbols-outlined icon-16">filter_alt_off</span>
|
|
926
|
+
<span>Clear Filter</span>
|
|
927
|
+
</button>
|
|
928
|
+
</div>
|
|
929
|
+
</div>
|
|
930
|
+
</div>
|
|
931
|
+
`;let n=document.getElementById(`btn-empty-clear-filter-${e}`);n&&(n.onclick=()=>{Se(e),Ge(e,t,``,!1)})}else{d.innerHTML=`
|
|
932
|
+
<div class="table-empty-container">
|
|
933
|
+
<div class="table-empty-card">
|
|
934
|
+
<div class="table-empty-icon-wrap">
|
|
935
|
+
<span class="material-symbols-outlined table-empty-icon">dataset</span>
|
|
936
|
+
</div>
|
|
937
|
+
<h3 class="table-empty-title">Table "${e}" is empty</h3>
|
|
938
|
+
<p class="table-empty-desc">This table currently contains 0 records. You can insert a new row manually or generate realistic mock data with one click.</p>
|
|
939
|
+
<div class="table-empty-actions">
|
|
940
|
+
<button type="button" class="table-empty-btn-secondary" id="btn-empty-add-row-${e}">
|
|
941
|
+
<span class="material-symbols-outlined icon-16">add</span>
|
|
942
|
+
<span>Insert Row</span>
|
|
943
|
+
</button>
|
|
944
|
+
<button type="button" class="table-empty-btn-primary" id="btn-empty-mock-${e}">
|
|
945
|
+
<span class="material-symbols-outlined icon-16">auto_fix_high</span>
|
|
946
|
+
<span>Generate Mock Data</span>
|
|
947
|
+
</button>
|
|
948
|
+
</div>
|
|
949
|
+
</div>
|
|
950
|
+
</div>
|
|
951
|
+
`;let n=document.getElementById(`btn-empty-mock-${e}`);n&&(n.onclick=()=>{M(e,a,()=>{window.forceEmptyGrid=!1,Ge(e,t,``,!0)})});let r=document.getElementById(`btn-empty-add-row-${e}`);r&&(r.onclick=()=>{window.forceEmptyGrid=!0,Ge(e,t,``,!0)})}else{let n=`<table class="data-table" id="data-grid-table-${e}"><thead><tr>`;n+=`<th class="row-header table-corner-header" id="th-row-header-${e}" title="Click to select all, right-click for table actions">#</th>`,c.forEach(e=>{let t=a.find(t=>t.name===e),r=``,i=t?.type||``;if(t){let e=!!t.isPk,n=!!t.fkTarget;e?r=`<span class="material-symbols-outlined col-pk-key-icon" title="Primary Key">key</span>`:(n||t.name.toLowerCase().includes(`id`))&&(r=`<span class="material-symbols-outlined col-fk-icon" title="Foreign Key">link</span>`)}let o=window.DataGrid.sortState.col===e;n+=`<th class="sortable ${o?`sorted`:``}" data-col="${e}" title="Click for column options">
|
|
952
|
+
<div class="th-content-wrapper">
|
|
953
|
+
<div class="th-main-group">
|
|
954
|
+
${r}
|
|
955
|
+
<span class="th-col-name">${e}</span>
|
|
956
|
+
${i?`<span class="th-col-type">${i.toLowerCase()}</span>`:``}
|
|
957
|
+
</div>
|
|
958
|
+
<div class="th-actions-group">
|
|
959
|
+
<button type="button" class="th-menu-trigger ${o?`is-sorted`:``}" title="Column actions">
|
|
960
|
+
<span class="material-symbols-outlined th-chevron-icon">keyboard_arrow_down</span>
|
|
961
|
+
</button>
|
|
962
|
+
</div>
|
|
963
|
+
</div>
|
|
964
|
+
</th>`}),n+=`</tr></thead><tbody>`,i&&i.length>0&&i.forEach((e,t)=>{n+=`<tr>${Oe(e,t,l,c,a)}</tr>`}),n+=`<tr class="ghost-row-tr">`;let r=i?i.length:0;n+=`<td class="row-header" data-row-idx="${r}">*</td>`,c.forEach((e,t)=>{n+=`<td class="data-cell ghost-row" data-row-idx="${r}" data-col-idx="${t}" data-insert-index="0" data-col="${e}">${t===0?`<span class="ghost-cell-hint">+ Add Row</span>`:``}</td>`}),n+=`</tr></tbody></table>`,d.innerHTML=n,d.querySelectorAll(`th.sortable`).forEach(n=>{let r=n.dataset.col,i=e,o=e=>{if(window.DataGrid&&window.DataGrid.isResizing||e.target.closest(`.resizer`))return;let o=window.TableStates?.[i]?.dataGrid||window.DataGrid,s=o?.sortState?.col===r,c=s&&o?.sortState?.asc,l=()=>Object.keys(window.DataGrid.pendingEdits||{}).length>0||(window.DataGrid.pendingInserts||[]).length>1||window.DataGrid.pendingDeletes&&window.DataGrid.pendingDeletes.size>0?(window.showToast?window.showToast(`You have unsaved changes. Please save changes before sorting.`,`warning`):alert(`You have unsaved changes. Please save changes before sorting.`),!1):!0,u=e=>{l()&&(o&&(o.sortState.col=r,o.sortState.asc=e,o.pagination.offset=0),Ge(i,t,Ee(i),!0),window.showToast&&window.showToast(`Sorted by ${r} (${e?`Ascending`:`Descending`})`,`info`))},d=()=>{l()&&(o&&(o.sortState.col=null,o.sortState.asc=!0,o.pagination.offset=0),Ge(i,t,Ee(i),!0),window.showToast&&window.showToast(`Cleared sort on "${r}"`,`info`))},p=[{icon:`arrow_upward`,label:`Sort Ascending`,checked:s&&c,action:()=>u(!0)},{icon:`arrow_downward`,label:`Sort Descending`,checked:s&&!c,action:()=>u(!1)}];s&&p.push({icon:`restart_alt`,label:`Clear Sort`,action:()=>d()}),p.push(`divider`,{icon:`content_copy`,label:`Copy name`,action:async()=>{try{await navigator.clipboard.writeText(r)}catch{let e=document.createElement(`textarea`);e.value=r,document.body.appendChild(e),e.select(),document.execCommand(`copy`),e.remove()}window.showToast&&window.showToast(`Copied "${r}" to clipboard!`,`success`)}},{icon:`edit`,label:`Edit column`,action:()=>{typeof window.handleSwitchTab==`function`&&(window.handleSwitchTab(`schema-btn`),window.showToast&&window.showToast(`Switched to Schema Editor for "${r}"`,`info`))}},{icon:`filter_alt`,label:`Filter by this column`,action:()=>{let e=document.getElementById(`filter-bar-input-${i}`);e&&(e.focus(),e.value=`${r} = `,e.dispatchEvent(new Event(`input`,{bubbles:!0})))}},`divider`,{icon:`delete`,label:`Delete column`,danger:!0,action:async()=>{if(confirm(`Are you sure you want to delete column "${r}" from table "${i}"?\nThis action cannot be undone!`))try{let e=await f(i,{pendingDeletes:[r],columns:a});e.success?(window.showToast&&window.showToast(`Deleted column "${r}"`,`success`),Ge(i,t,``,!0)):alert(`Failed to delete column: ${e.error}`)}catch(e){alert(`Error deleting column: ${e.message}`)}}});let m=n.querySelector(`.th-menu-trigger`),h=m?m.getBoundingClientRect():n.getBoundingClientRect();A({clientX:h.left,clientY:h.bottom+4,preventDefault:()=>{},stopPropagation:()=>{}},p)};n.onclick=o,n.oncontextmenu=e=>{e.preventDefault(),o(e)},ve(n,window.DataGrid)}),_e(d,`data-grid-table-${e}`,window.DataGrid,c.length),Ie(d,a,c),Ue(d,e);let o=document.getElementById(`th-row-header-${e}`);o&&(o.addEventListener(`click`,t=>{t.preventDefault(),t.stopPropagation(),ge(`data-grid-table-${e}`,window.DataGrid,c.length)}),o.addEventListener(`contextmenu`,n=>{n.preventDefault&&n.preventDefault(),n.stopPropagation&&n.stopPropagation();let r=Ee(e),i=window.DataGrid?.sortState,o=!!r,s=t=>{let n=`/api/tables/${encodeURIComponent(e)}/export?format=${t}`;return r&&(n+=`&where=${encodeURIComponent(r)}`),i&&i.col&&(n+=`&orderCol=${encodeURIComponent(i.col)}&orderAsc=${i.asc}`),n};A(n,[{icon:`select_all`,label:`Select All Cells`,shortcut:`Ctrl+A`,action:()=>{ge(`data-grid-table-${e}`,window.DataGrid,c.length)}},`divider`,{icon:`auto_fix_high`,label:`Generate Mock Data...`,action:()=>{M(e,a,()=>{Ge(e,t,Ee(e),!0)})}},{icon:`content_copy`,label:`Copy Table Name`,action:async()=>{try{await navigator.clipboard.writeText(e)}catch{let t=document.createElement(`textarea`);t.value=e,document.body.appendChild(t),t.select(),document.execCommand(`copy`),t.remove()}window.showToast&&window.showToast(`Copied "${e}"`,`success`)}},{type:`divider`},{icon:`download`,label:o?`Export as CSV (Filtered)`:`Export as CSV`,action:()=>{window.open(s(`csv`),`_blank`)}},{icon:`download`,label:o?`Export as JSON (Filtered)`:`Export as JSON`,action:()=>{window.open(s(`json`),`_blank`)}},{type:`divider`},{icon:`mop`,label:`Truncate Table (Clear Rows)`,danger:!0,action:async()=>{if(confirm(`Are you sure you want to delete ALL rows from table "${e}"? This cannot be undone!`)){let n=await p(e);n.success?(window.showToast&&window.showToast(`Cleared all rows from "${e}"`,`info`),Ge(e,t,``,!0)):alert(`Failed to clear table: ${n.error}`)}}}])}))}let m=document.getElementById(`btn-fk-back-${e}`);if(m&&(m.onclick=()=>{let e=window.fkNavHistory?.fromTable;if(window.fkNavHistory=null,e){let t=document.querySelector(`.table-btn[data-table="${e}"]`)||Array.from(document.querySelectorAll(`.table-btn`)).find(t=>t.textContent.includes(e));window.AppState.currentTable=e,window.AppState.currentTableBtnElement=t||null,document.querySelectorAll(`.table-btn`).forEach(e=>e.classList.remove(`active`)),t&&t.classList.add(`active`);let n=`view-data-btn-${e}`,r=document.getElementById(n);r&&(r.innerHTML=``),window.renderCurrentView()}}),!r){let n=()=>{(Object.keys(window.DataGrid.pendingEdits).length>0||window.DataGrid.pendingInserts.length>1||window.DataGrid.pendingDeletes.size>0)&&!confirm(`You have unsaved changes. Are you sure you want to refresh and discard them?`)||(window.DataGrid.pendingEdits={},window.DataGrid.pendingInserts=[{}],window.DataGrid.pendingDeletes=new Set,window.DataGrid.history=[],window.DataGrid.currentTransaction=null,window.updateSidebarDirtyState?.(),window.DataGrid&&(window.DataGrid.pagination.offset=0),Ge(e,t,Ee(e),!0))},r=document.getElementById(`btn-refresh-data-${e}`);r&&(r.onclick=n),window.DataGrid.refreshData=n;let i=async()=>{if(window.DataGrid.pagination.isLoading||!window.DataGrid.pagination.hasMore)return;window.DataGrid.pagination.isLoading=!0,window.DataGrid.pagination.offset+=window.DataGrid.pagination.limit;let t={where:Ee(e),limit:window.DataGrid.pagination.limit,offset:window.DataGrid.pagination.offset,orderCol:window.DataGrid.sortState.col,orderAsc:window.DataGrid.sortState.asc};try{let n=await s(e,t);if(window.DataGrid.pagination.isLoading=!1,n.success&&n.data){let t=n.data.rows;window.DataGrid.pagination.hasMore=t.length===window.DataGrid.pagination.limit;let r=document.querySelector(`#data-grid-table-${e} tbody`);if(!r)return;let i=r.querySelector(`td[data-insert-index]`)?.closest(`tr`),o=window.DataGrid.pagination.offset;t.forEach((e,t)=>{let n=document.createElement(`tr`);n.innerHTML=Oe(e,o+t,l,c,a),i?r.insertBefore(n,i):r.appendChild(n)});let s=o+t.length,u=r.querySelectorAll(`td[data-insert-index]`),d=new Set;u.forEach(e=>{let t=e.closest(`tr`);if(d.has(t))return;d.add(t);let n=t.querySelector(`.row-header`);n&&(n.dataset.rowIdx=s),t.querySelectorAll(`td.data-cell`).forEach(e=>e.dataset.rowIdx=s),s++})}}catch{window.DataGrid.pagination.isLoading=!1}};setTimeout(()=>{let t=document.getElementById(`data-grid-container-${e}`);t&&t.addEventListener(`scroll`,e=>{let{scrollTop:t,scrollHeight:n,clientHeight:r}=e.target;t+r>=n-50&&i()})},50)}}else o.innerHTML=`<div class="p-6 text-error">Error: ${u.error}</div>`}catch(e){o.innerHTML=`<div class="p-6 text-error">Failed to load data: ${e.message}</div>`}}async function Ke(){if(!window.SchemaGrid)return;let{pendingEdits:e,pendingInserts:t,pendingDeletes:n,pendingIndexEdits:r,schema:i}=window.SchemaGrid,a=window.AppState.currentTable;if(!a)return;let o=n&&n.size>0||Object.keys(e||{}).length>0||t&&t.some(e=>e.name&&e.name.trim()!==``),s=r&&(r.added.length>0||r.dropped.length>0);if(!o&&!s)return;let c=n?Array.from(n):[];try{let n=await f(a,{pendingEdits:e,pendingInserts:t,pendingDeletes:c,pendingIndexEdits:r,columns:i});if(n.success){window.SchemaGrid&&(window.SchemaGrid.pendingEdits={},window.SchemaGrid.pendingInserts=[{}],window.SchemaGrid.pendingDeletes=new Set,window.SchemaGrid.pendingIndexEdits={added:[],dropped:[]},window.SchemaGrid.history=[],window.SchemaGrid.currentTransaction=null),window.TableStates?.[a]&&(window.TableStates[a].dataGrid=null);let e=document.getElementById(`btn-refresh-schema-${a}`);e?e.click():window.renderCurrentView(),window.updateSidebarDirtyState?.(),window.loadTableStats?.(),window.showToast&&window.showToast(`Schema saved successfully!`,`success`)}else{let e=n.error||`Failed to apply schema changes`;window.showToast?window.showToast(`Migration failed: ${e}`,`error`):alert(`Migration failed: ${e}`)}}catch(e){let t=e.message||`Network error`;window.showToast?window.showToast(`Migration failed: ${t}`,`error`):alert(`Migration failed: ${t}`)}}function qe(e,t,n,r=!0){if(r&&window.SchemaGrid.currentTransaction){let n=e.textContent;(n===`null`||e.classList.contains(`ghost-row`))&&(n=``),n!==t&&window.SchemaGrid.currentTransaction.push({td:e,oldVal:n,newVal:t})}let i=e.dataset.colKey,a=e.dataset.colIdx===`0`?`<span class="ghost-cell-hint">+ Add Column</span>`:``,o=t;if(i===`isPk`&&t){let e=t.includes(`PK`)||t.includes(`PFK`),n=t.match(/(?:FK|PFK)(?:\s*\(|:\s*|\s*→\s*|\s*->\s*)([a-zA-Z0-9_]+)\.([a-zA-Z0-9_]+)(?:\s*\((CASCADE|SET NULL|RESTRICT|NO ACTION)\))?/i),r=n&&n[3]&&n[3].toUpperCase()!==`NO ACTION`?` (${n[3].toUpperCase()})`:``;e&&n?o=`<span class="col-badge badge-pfk" title="Primary Foreign Key (Composite Key referencing ${n[1]}.${n[2]}${r?` [ON DELETE ${n[3]}]`:``})">PFK → ${n[1]}.${n[2]}${r}</span>`:e?o=`<span class="col-badge badge-pk">PK</span>`:n&&(o=`<span class="col-badge badge-fk" title="References ${n[1]}.${n[2]}${r?` [ON DELETE ${n[3]}]`:``}">FK → ${n[1]}.${n[2]}${r}</span>`)}else i===`nullable`&&t?o=t===`Yes`||t===`1`?`<span class="badge-bool bool-true">Yes</span>`:`<span class="badge-bool bool-false">No</span>`:i===`isUnique`&&t&&(o=t===`Yes`||t===`1`?`<span class="badge-unique">Yes</span>`:`<span class="text-soft">-</span>`);if(e.innerHTML=o||(e.dataset.insertIndex===void 0?`<span class="text-soft">-</span>`:a),e.dataset.insertIndex!==void 0){let r=parseInt(e.dataset.insertIndex);if(window.SchemaGrid.pendingInserts[r]||(window.SchemaGrid.pendingInserts[r]={}),t){if(window.SchemaGrid.pendingInserts[r][i]=t,e.classList.add(`cell-edited`),e.classList.remove(`ghost-row`),r===window.SchemaGrid.pendingInserts.length-1){e.closest(`tr`).classList.remove(`ghost-row-tr`),window.SchemaGrid.pendingInserts.push({});let t=document.querySelector(`#schema-grid-table-${window.AppState.currentTable} tbody`),i=document.createElement(`tr`);i.className=`ghost-row-tr`;let a=parseInt(e.dataset.rowIdx)+1;i.innerHTML+=`<td class="row-header" data-row-idx="${a}">*</td>`,n.forEach((e,t)=>{if(e===`indexing`)return;let n=t===0?`<span class="ghost-cell-hint">+ Add Column</span>`:``;i.innerHTML+=`<td class="data-cell ghost-row" data-row-idx="${a}" data-col-idx="${t}" data-insert-index="${r+1}" data-col-key="${e}">${n}</td>`}),t.appendChild(i);let o=document.querySelector(`#schema-grid-table-${window.AppState.currentTable} .manage-indexes-cell`);if(o){let e=parseInt(o.getAttribute(`rowspan`)||`1`,10);o.setAttribute(`rowspan`,String(e+1))}}}else window.SchemaGrid.pendingInserts[r]&&delete window.SchemaGrid.pendingInserts[r][i],e.classList.remove(`cell-edited`),e.classList.add(`ghost-row`)}else{let n=e.dataset.pk,r=e.dataset.original;r===`-`&&(r=``),t===r?(e.classList.remove(`cell-edited`),e.classList.remove(`cell-error`),window.SchemaGrid.pendingEdits[n]&&(delete window.SchemaGrid.pendingEdits[n][i],Object.keys(window.SchemaGrid.pendingEdits[n]).length===0&&delete window.SchemaGrid.pendingEdits[n])):(window.SchemaGrid.pendingEdits[n]||(window.SchemaGrid.pendingEdits[n]={}),window.SchemaGrid.pendingEdits[n][i]=t,e.classList.add(`cell-edited`),e.classList.remove(`cell-error`))}window.updateSidebarDirtyState?.()}function Je(e,t=!0){let n=document.querySelector(`#schema-grid-table-${window.AppState.currentTable} td.data-cell[data-row-idx="${e}"]`)?.closest(`tr`);if(!n||n.classList.contains(`ghost-row-tr`)||n.classList.contains(`row-deleted`))return;let r=n.querySelector(`td.data-cell`)?.dataset.pk;r!==void 0&&(t&&window.SchemaGrid.currentTransaction&&window.SchemaGrid.currentTransaction.push({type:`delete`,rowIdx:e,pk:r}),window.SchemaGrid.pendingDeletes.add(r),n.classList.add(`row-deleted`)),window.updateSidebarDirtyState?.()}function Ye(e,t){let n=document.querySelector(`#schema-grid-table-${window.AppState.currentTable} td.data-cell[data-row-idx="${e}"]`)?.closest(`tr`);n&&n.classList.remove(`row-deleted`),window.SchemaGrid.pendingDeletes.delete(t),window.updateSidebarDirtyState?.()}function Xe(e,t){e.forEach(e=>{let n=window.AppState.currentTable,r=document.querySelector(`#schema-grid-table-${n} td.data-cell[data-row-idx="${e}"]`)?.closest(`tr`);if(!r||r.classList.contains(`ghost-row-tr`))return;let i=document.querySelector(`#schema-grid-table-${n} .ghost-row-tr`);i&&t.forEach((e,n)=>{let a=r.querySelector(`td.data-cell[data-col-idx="${n}"]`),o=i.querySelector(`td.data-cell[data-col-idx="${n}"]`);if(a&&o){let n=a.dataset.insertIndex===void 0?a.classList.contains(`cell-edited`)?window.SchemaGrid.pendingEdits[a.dataset.pk]?.[e]:a.dataset.original:window.SchemaGrid.pendingInserts[a.dataset.insertIndex][e];n!=null&&n!==`null`&&(e===`name`&&(n+=`_copy`),qe(o,n,t,!0))}})})}function Ze(){let e=window.SchemaGrid;if(!e)return;let t=document.getElementById(`index-modal`);t&&t.remove();let n=[...e.indexes],r=[...e.pendingIndexEdits.added],i=new Set(e.pendingIndexEdits.dropped),a=n.filter(e=>!i.has(e.name)).concat(r),o=e.schema.map(e=>e.name);e.pendingInserts.forEach(e=>{e.name&&e.name.trim()!==``&&o.push(e.name.trim())});let s=[];t=document.createElement(`div`),t.id=`index-modal`,t.className=`modal-overlay`,t.innerHTML=`
|
|
965
|
+
<div class="modal-container idx-modal-container">
|
|
966
|
+
<div class="modal-header">
|
|
967
|
+
<div class="flex items-center gap-2">
|
|
968
|
+
<span class="material-symbols-outlined text-primary" style="font-size:20px;">key</span>
|
|
969
|
+
<h3 class="m-0 text-15 font-semibold">Manage Indexes</h3>
|
|
970
|
+
</div>
|
|
971
|
+
<button id="close-idx-modal" class="modal-close-btn" title="Close"><span class="material-symbols-outlined">close</span></button>
|
|
972
|
+
</div>
|
|
973
|
+
|
|
974
|
+
<div class="modal-body-scroll" style="max-height: 220px; padding: 16px;">
|
|
975
|
+
<div class="idx-section-title">Current Indexes</div>
|
|
976
|
+
<div id="idx-list-container"></div>
|
|
977
|
+
</div>
|
|
978
|
+
|
|
979
|
+
<div class="modal-section-bg" style="padding: 16px 20px;">
|
|
980
|
+
<div class="idx-section-title" style="margin-bottom: 24px;">+ Create New Index</div>
|
|
981
|
+
<div class="flex flex-col gap-3">
|
|
982
|
+
<div>
|
|
983
|
+
<label class="text-11 text-secondary block mb-1.5">Index Name</label>
|
|
984
|
+
<input type="text" id="new-idx-name" placeholder="Optional (auto-generated if empty)" class="modal-input w-full" style="padding: 8px 12px; font-size: 13px;" />
|
|
985
|
+
</div>
|
|
986
|
+
|
|
987
|
+
<div>
|
|
988
|
+
<div class="flex items-center justify-between mb-1">
|
|
989
|
+
<label class="text-11 text-secondary">Columns (click to select in order)</label>
|
|
990
|
+
<button type="button" id="btn-clear-selected-cols" class="idx-clear-btn hidden" title="Clear column selection">
|
|
991
|
+
<span class="material-symbols-outlined" style="font-size:12px; line-height:1;">close</span>
|
|
992
|
+
<span>Clear selection</span>
|
|
993
|
+
</button>
|
|
994
|
+
</div>
|
|
995
|
+
<div id="idx-chips-container" class="idx-chips-container"></div>
|
|
996
|
+
</div>
|
|
997
|
+
|
|
998
|
+
<div class="flex items-center justify-between pt-1">
|
|
999
|
+
<label class="flex items-center gap-2 cursor-pointer select-none text-13">
|
|
1000
|
+
<input type="checkbox" id="new-idx-unique" style="accent-color: var(--color-primary);" />
|
|
1001
|
+
<span>Unique Index</span>
|
|
1002
|
+
</label>
|
|
1003
|
+
<button type="button" id="add-idx-btn" class="btn-add-idx">
|
|
1004
|
+
<span class="material-symbols-outlined">add</span>
|
|
1005
|
+
<span>Add to List</span>
|
|
1006
|
+
</button>
|
|
1007
|
+
</div>
|
|
1008
|
+
</div>
|
|
1009
|
+
</div>
|
|
1010
|
+
|
|
1011
|
+
<div class="modal-footer">
|
|
1012
|
+
<button id="cancel-idx-btn" class="btn-secondary">Cancel</button>
|
|
1013
|
+
<button id="save-idx-btn" class="btn-primary">Save Changes</button>
|
|
1014
|
+
</div>
|
|
1015
|
+
</div>
|
|
1016
|
+
`,document.body.appendChild(t);let c=()=>t.remove();document.getElementById(`close-idx-modal`).onclick=c,document.getElementById(`cancel-idx-btn`).onclick=c;let l=()=>{let e=document.getElementById(`idx-chips-container`),t=document.getElementById(`btn-clear-selected-cols`);e&&(t&&(s.length>0?(t.classList.remove(`hidden`),t.onclick=()=>{s=[],l()}):t.classList.add(`hidden`)),e.innerHTML=o.map(e=>{let t=s.indexOf(e),n=t!==-1,r=n?`<span class="idx-chip-badge">${t+1}</span>`:``;return`
|
|
1017
|
+
<div class="idx-chip ${n?`selected`:``}" data-col="${e}">
|
|
1018
|
+
${r}
|
|
1019
|
+
<span>${e}</span>
|
|
1020
|
+
</div>
|
|
1021
|
+
`}).join(``),e.querySelectorAll(`.idx-chip`).forEach(e=>{e.onclick=()=>{let t=e.dataset.col,n=s.indexOf(t);n===-1?s.push(t):s.splice(n,1),l()}}))};l();let u=()=>{let e=document.getElementById(`idx-list-container`);if(e){if(a.length===0){e.innerHTML=`
|
|
1022
|
+
<div style="display:flex; flex-direction:column; align-items:center; justify-content:center; padding: 24px 0; color: var(--color-text-soft);">
|
|
1023
|
+
<span class="material-symbols-outlined" style="font-size:32px; opacity:0.4; margin-bottom:6px;">dataset</span>
|
|
1024
|
+
<div style="font-size:12px;">No indexes defined for this table yet.</div>
|
|
1025
|
+
</div>
|
|
1026
|
+
`;return}e.innerHTML=a.map((e,t)=>`
|
|
1027
|
+
<div class="idx-card">
|
|
1028
|
+
<div class="idx-card-info">
|
|
1029
|
+
<div class="idx-card-header">
|
|
1030
|
+
<span class="idx-name">${e.name||`unnamed_idx`}</span>
|
|
1031
|
+
${e.isUnique?`<span class="badge-unique" style="font-size:10px; padding:1px 6px; line-height:14px;">UNIQUE</span>`:``}
|
|
1032
|
+
</div>
|
|
1033
|
+
<div class="idx-cols-tags">
|
|
1034
|
+
${e.columns.map(e=>`<span class="idx-col-pill">${e}</span>`).join(``)}
|
|
1035
|
+
</div>
|
|
1036
|
+
</div>
|
|
1037
|
+
<button type="button" class="idx-del-btn delete-idx-btn" data-idx="${t}" title="Remove index">
|
|
1038
|
+
<span class="material-symbols-outlined">delete</span>
|
|
1039
|
+
</button>
|
|
1040
|
+
</div>
|
|
1041
|
+
`).join(``),e.querySelectorAll(`.delete-idx-btn`).forEach(e=>{e.onclick=e=>{let t=parseInt(e.currentTarget.dataset.idx,10);a.splice(t,1),u()}})}};u(),document.getElementById(`add-idx-btn`).onclick=()=>{if(s.length===0){window.showToast?window.showToast(`Please select at least one column for the index.`,`warning`):alert(`Please select at least one column for the index.`);return}let e=window.AppState?.currentTable||`table`,t=document.getElementById(`new-idx-name`).value.trim()||`idx_${e}_${s.join(`_`)}`,n=document.getElementById(`new-idx-unique`).checked;a.push({name:t,columns:[...s],isUnique:n}),document.getElementById(`new-idx-name`).value=``,document.getElementById(`new-idx-unique`).checked=!1,s=[],l(),u()},document.getElementById(`save-idx-btn`).onclick=()=>{let t=[],r=new Set;n.forEach(e=>{a.find(t=>t.name===e.name&&JSON.stringify(t.columns)===JSON.stringify(e.columns))||r.add(e.name)}),a.forEach(e=>{n.find(t=>t.name===e.name&&JSON.stringify(t.columns)===JSON.stringify(e.columns))||t.push(e)}),e.pendingIndexEdits.added=t,e.pendingIndexEdits.dropped=Array.from(r),window.updateSidebarDirtyState?.(),window.renderSchemaGrid(),c()}}async function Qe(e,t){if(!window.SchemaGrid)return;let n=document.getElementById(`pkfk-modal`);n&&n.remove();let r=e.dataset.insertIndex!==void 0,i=r?parseInt(e.dataset.insertIndex,10):null,a=e.dataset.pk||e.parentElement?.querySelector(`[data-col-key="name"]`)?.textContent?.trim()||``,o=``;o=r&&i!==null&&window.SchemaGrid?.pendingInserts?.[i]?window.SchemaGrid.pendingInserts[i].isPk||``:a&&window.SchemaGrid?.pendingEdits?.[a]?.isPk!==void 0?window.SchemaGrid.pendingEdits[a].isPk||``:t||e.textContent?.trim()||e.dataset.original||``;let s=o.includes(`PK`)||o.includes(`PFK`),c=!1,l=``,u=``,d=`NO ACTION`,f=`NO ACTION`,p=(window.SchemaGrid?.schema||[]).find(e=>e.name===a);p?.fkTarget&&(l=p.fkTarget.table||``,u=p.fkTarget.column||``,p.fkTarget.onDelete&&(d=p.fkTarget.onDelete),p.fkTarget.onUpdate&&(f=p.fkTarget.onUpdate),c=!0);let m=r&&i!==null?window.SchemaGrid?.pendingInserts?.[i]?.fkTarget:a?window.SchemaGrid?.pendingEdits?.[a]?.fkTarget:void 0;m&&(m.table&&(l=m.table),m.column&&(u=m.column),m.onDelete&&(d=m.onDelete),m.onUpdate&&(f=m.onUpdate),c=!0);let h=o.match(/(?:FK|PFK)(?:\s*\(|:\s*|\s*→\s*|\s*->\s*)([a-zA-Z0-9_]+)\.([a-zA-Z0-9_]+)(?:\s*\((CASCADE|SET NULL|RESTRICT|NO ACTION)\))?/i);h?(l=h[1],u=h[2],h[3]&&(d=h[3].toUpperCase()),c=!0):(o.includes(`FK`)||o.includes(`PFK`))&&(c=!0),n=document.createElement(`div`),n.id=`pkfk-modal`,n.className=`modal-overlay`,n.innerHTML=`
|
|
1042
|
+
<div class="keys-modal-container">
|
|
1043
|
+
<div class="modal-header">
|
|
1044
|
+
<div class="flex items-center gap-2">
|
|
1045
|
+
<span class="material-symbols-outlined text-primary" style="font-size:20px;">vpn_key</span>
|
|
1046
|
+
<h3 class="m-0 text-15 font-semibold">Manage Keys</h3>
|
|
1047
|
+
${a?`<span class="keys-col-badge">${a}</span>`:``}
|
|
1048
|
+
</div>
|
|
1049
|
+
<button id="close-pkfk-modal" class="modal-close-btn" title="Close"><span class="material-symbols-outlined">close</span></button>
|
|
1050
|
+
</div>
|
|
1051
|
+
|
|
1052
|
+
<div class="key-cards-list">
|
|
1053
|
+
<!-- Primary Key Card -->
|
|
1054
|
+
<div class="key-card ${s?`active`:``}" id="pk-card">
|
|
1055
|
+
<div class="key-card-header" id="pk-card-toggle">
|
|
1056
|
+
<div class="key-card-icon pk">
|
|
1057
|
+
<span class="material-symbols-outlined">key</span>
|
|
1058
|
+
</div>
|
|
1059
|
+
<div class="key-card-content">
|
|
1060
|
+
<div class="key-card-title">Primary Key (PK)</div>
|
|
1061
|
+
<div class="key-card-desc">Uniquely identifies each record in this table</div>
|
|
1062
|
+
</div>
|
|
1063
|
+
<label class="key-switch" id="pk-switch-label">
|
|
1064
|
+
<input type="checkbox" id="modal-is-pk" ${s?`checked`:``} />
|
|
1065
|
+
<span class="key-slider"></span>
|
|
1066
|
+
</label>
|
|
1067
|
+
</div>
|
|
1068
|
+
</div>
|
|
1069
|
+
|
|
1070
|
+
<!-- Foreign Key Card -->
|
|
1071
|
+
<div class="key-card ${c?`active`:``}" id="fk-card">
|
|
1072
|
+
<div class="key-card-header" id="fk-card-toggle">
|
|
1073
|
+
<div class="key-card-icon fk">
|
|
1074
|
+
<span class="material-symbols-outlined">link</span>
|
|
1075
|
+
</div>
|
|
1076
|
+
<div class="key-card-content">
|
|
1077
|
+
<div class="key-card-title">Foreign Key (FK)</div>
|
|
1078
|
+
<div class="key-card-desc">Reference a column in another table</div>
|
|
1079
|
+
</div>
|
|
1080
|
+
<label class="key-switch" id="fk-switch-label">
|
|
1081
|
+
<input type="checkbox" id="modal-is-fk" ${c?`checked`:``} />
|
|
1082
|
+
<span class="key-slider"></span>
|
|
1083
|
+
</label>
|
|
1084
|
+
</div>
|
|
1085
|
+
|
|
1086
|
+
<!-- FK Configuration Drawer -->
|
|
1087
|
+
<div id="fk-settings-container" class="key-fk-drawer ${c?`open`:``}">
|
|
1088
|
+
<div class="key-field-group">
|
|
1089
|
+
<label class="key-field-label">Target Table</label>
|
|
1090
|
+
<button type="button" id="modal-fk-table" class="key-select" style="display: flex; justify-content: space-between; align-items: center; width: 100%; height: 32px; cursor: pointer; text-align: left; padding: 0 10px;">
|
|
1091
|
+
<span id="modal-fk-table-display" style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${l||`Select a table...`}</span>
|
|
1092
|
+
<span class="material-symbols-outlined key-select-arrow" style="font-size: 16px;">expand_more</span>
|
|
1093
|
+
</button>
|
|
1094
|
+
</div>
|
|
1095
|
+
|
|
1096
|
+
<div class="key-field-group">
|
|
1097
|
+
<label class="key-field-label">Target Column</label>
|
|
1098
|
+
<button type="button" id="modal-fk-col" class="key-select" ${l?``:`disabled`} style="display: flex; justify-content: space-between; align-items: center; width: 100%; height: 32px; cursor: pointer; text-align: left; padding: 0 10px;">
|
|
1099
|
+
<span id="modal-fk-col-display" style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${u||(l?`Select column...`:`Select a table first`)}</span>
|
|
1100
|
+
<span class="material-symbols-outlined key-select-arrow" style="font-size: 16px;">expand_more</span>
|
|
1101
|
+
</button>
|
|
1102
|
+
</div>
|
|
1103
|
+
|
|
1104
|
+
<div class="key-field-group" style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;">
|
|
1105
|
+
<div>
|
|
1106
|
+
<label class="key-field-label">On Delete</label>
|
|
1107
|
+
<button type="button" id="modal-fk-ondelete" class="key-select" style="display: flex; justify-content: space-between; align-items: center; width: 100%; height: 32px; cursor: pointer; text-align: left; padding: 0 10px;">
|
|
1108
|
+
<span id="modal-fk-ondelete-display">${d||`NO ACTION`}</span>
|
|
1109
|
+
<span class="material-symbols-outlined key-select-arrow" style="font-size: 16px;">expand_more</span>
|
|
1110
|
+
</button>
|
|
1111
|
+
</div>
|
|
1112
|
+
<div>
|
|
1113
|
+
<label class="key-field-label">On Update</label>
|
|
1114
|
+
<button type="button" id="modal-fk-onupdate" class="key-select" style="display: flex; justify-content: space-between; align-items: center; width: 100%; height: 32px; cursor: pointer; text-align: left; padding: 0 10px;">
|
|
1115
|
+
<span id="modal-fk-onupdate-display">${f||`NO ACTION`}</span>
|
|
1116
|
+
<span class="material-symbols-outlined key-select-arrow" style="font-size: 16px;">expand_more</span>
|
|
1117
|
+
</button>
|
|
1118
|
+
</div>
|
|
1119
|
+
</div>
|
|
1120
|
+
</div>
|
|
1121
|
+
</div>
|
|
1122
|
+
</div>
|
|
1123
|
+
|
|
1124
|
+
<div class="modal-footer">
|
|
1125
|
+
<button id="cancel-pkfk-btn" class="btn-secondary">Cancel</button>
|
|
1126
|
+
<button id="save-pkfk-btn" class="btn-primary flex items-center gap-1.5">
|
|
1127
|
+
<span class="material-symbols-outlined" style="font-size:16px;">check</span>
|
|
1128
|
+
<span>Save Changes</span>
|
|
1129
|
+
</button>
|
|
1130
|
+
</div>
|
|
1131
|
+
</div>
|
|
1132
|
+
`,document.body.appendChild(n);let g=e=>{e.key===`Escape`&&_()};document.addEventListener(`keydown`,g);let _=()=>{document.removeEventListener(`keydown`,g),n.remove()};document.getElementById(`close-pkfk-modal`).onclick=_,document.getElementById(`cancel-pkfk-btn`).onclick=_,n.addEventListener(`click`,e=>{e.target===n&&_()});let v=document.getElementById(`pk-card`),y=document.getElementById(`modal-is-pk`),b=document.getElementById(`fk-card`),x=document.getElementById(`modal-is-fk`),S=document.getElementById(`fk-settings-container`),C=document.getElementById(`modal-fk-table`),w=document.getElementById(`modal-fk-col`);document.getElementById(`pk-card-toggle`).addEventListener(`click`,e=>{e.target.closest(`.key-switch`)||(y.checked=!y.checked,y.dispatchEvent(new Event(`change`)))}),y.addEventListener(`change`,()=>{v.classList.toggle(`active`,y.checked)}),document.getElementById(`fk-card-toggle`).addEventListener(`click`,e=>{e.target.closest(`.key-switch`)||(x.checked=!x.checked,x.dispatchEvent(new Event(`change`)))});let T=[],E=l||``,D=u||``,O=d||`NO ACTION`,k=f||`NO ACTION`,A=[];x.addEventListener(`change`,()=>{let e=x.checked;if(e&&T.length===0){x.checked=!1,b.classList.remove(`active`),S.classList.remove(`open`),window.showToast?window.showToast(`No other tables available in the database to link as a foreign key.`,`warning`):alert(`No other tables available in the database to link as a foreign key.`);return}if(b.classList.toggle(`active`,e),e){if(S.classList.add(`open`),!E&&T.length>0){let e=T[0];E=e;let t=document.getElementById(`modal-fk-table-display`);t&&(t.textContent=e),j(e)}}else S.classList.remove(`open`)});try{let e=await(await fetch(`/api/tables`)).json();if(e.success&&e.data){let t=window.AppState?.currentTable;T=e.data.filter(e=>e!==t),E&&T.includes(E)&&j(E,D)}}catch{}async function j(e,t=``){w.disabled=!0;let n=document.getElementById(`modal-fk-col-display`);n&&(n.textContent=`Loading columns...`);try{let r=await(await fetch(`/api/tables/${e}/schema`)).json();if(r.success&&r.data){let i=e===window.AppState?.currentTable?r.data.filter(e=>e.name!==a):r.data;A=i;let o=t;if(!o||!i.some(e=>e.name===o)){let e=i.find(e=>e.isPk||e.name.toLowerCase()===`id`);o=e?e.name:i[0]?.name||``}D=o,n&&(n.textContent=o||`Select column...`),w.disabled=!1}}catch{n&&(n.textContent=`Error loading columns`)}}C.onclick=e=>{e.stopPropagation(),I({anchorEl:C,title:`Select Target Table`,searchable:!0,placeholder:`Search tables...`,initialValue:E,items:T.map(e=>({name:e,value:e,icon:`table_chart`})),onSelect:e=>{E=e;let t=document.getElementById(`modal-fk-table-display`);t&&(t.textContent=e),j(e)}})},w.onclick=e=>{e.stopPropagation(),!(!E||A.length===0)&&I({anchorEl:w,title:`Columns in ${E}`,searchable:!0,placeholder:`Search columns...`,initialValue:D,items:A.map(e=>({name:e.name,value:e.name,icon:`tag`,desc:e.type||``})),onSelect:e=>{D=e;let t=document.getElementById(`modal-fk-col-display`);t&&(t.textContent=e)}})};let M=document.getElementById(`modal-fk-ondelete`),N=document.getElementById(`modal-fk-onupdate`);M&&(M.onclick=e=>{e.stopPropagation(),I({anchorEl:M,title:`ON DELETE Action`,searchable:!1,initialValue:O,items:[`NO ACTION`,`CASCADE`,`RESTRICT`,`SET NULL`],onSelect:e=>{O=e;let t=document.getElementById(`modal-fk-ondelete-display`);t&&(t.textContent=e)}})}),N&&(N.onclick=e=>{e.stopPropagation(),I({anchorEl:N,title:`ON UPDATE Action`,searchable:!1,initialValue:k,items:[`NO ACTION`,`CASCADE`,`RESTRICT`,`SET NULL`],onSelect:e=>{k=e;let t=document.getElementById(`modal-fk-onupdate-display`);t&&(t.textContent=e)}})}),document.getElementById(`save-pkfk-btn`).onclick=()=>{let t=y.checked,n=x.checked,o=E,p=D,m=O||`NO ACTION`,h=k||`NO ACTION`;if(t===s&&n===c&&(!n||o===l&&p===u&&m===d&&h===f)){_();return}let g=m&&m!==`NO ACTION`?` (${m})`:``,v=``;if(t&&n){if(!o||!p){window.showToast?window.showToast(`Please select both Target Table and Target Column for the Foreign Key.`,`warning`):alert(`Please select both Target Table and Target Column for the Foreign Key.`);return}v=`PFK: ${o}.${p}${g}`}else if(t)v=`PK`;else if(n){if(!o||!p){window.showToast?window.showToast(`Please select both Target Table and Target Column for the Foreign Key.`,`warning`):alert(`Please select both Target Table and Target Column for the Foreign Key.`);return}v=`FK: ${o}.${p}${g}`}let b=n&&o&&p?{table:o,column:p,onDelete:m===`NO ACTION`?void 0:m,onUpdate:h===`NO ACTION`?void 0:h}:null;r&&i!==null?(window.SchemaGrid.pendingInserts[i]||(window.SchemaGrid.pendingInserts[i]={}),window.SchemaGrid.pendingInserts[i].fkTarget=b):a&&(window.SchemaGrid.pendingEdits[a]||(window.SchemaGrid.pendingEdits[a]={}),window.SchemaGrid.pendingEdits[a].fkTarget=b),window.SchemaGrid.currentTransaction=[],qe(e,v,[`name`,`type`,`isPk`,`nullable`,`defaultValue`,`indexing`]),window.SchemaGrid.currentTransaction.length>0&&window.SchemaGrid.history.push(window.SchemaGrid.currentTransaction),window.SchemaGrid.currentTransaction=null,_()}}async function $e(e,t){let n=window.SchemaGrid;if(!n)return;let r=document.getElementById(`enum-modal`);r&&r.remove();let i=e.dataset.insertIndex!==void 0,a=i?parseInt(e.dataset.insertIndex,10):null,o=e.dataset.pk||e.parentElement?.querySelector(`[data-col-key="name"]`)?.textContent?.trim()||`column`,s=(window.AppState?.dbType||`sqlite`).toLowerCase(),c=[],l=``,u=i?n.pendingInserts?.[a]:n.pendingEdits?.[o];u?.enumValues&&u.enumValues.length>0?(c=[...u.enumValues],l=u.type||``,u.isNewEnum):t?.enumValues&&t.enumValues.length>0&&(c=[...t.enumValues],l=t.type||``,t.isExistingEnum),l||=o&&o!==`column`?o.charAt(0).toUpperCase()+o.slice(1)+`Enum`:`CustomEnum`;let d=[];try{let e=await T();e?.success&&Array.isArray(e.data)&&(d=e.data)}catch(e){console.warn(`Failed to load database enums:`,e)}r=document.createElement(`div`),r.id=`enum-modal`,r.className=`modal-overlay`;let f=s===`sqlite`?`SQLite enforces enums using a <code>CHECK(col IN (...))</code> table constraint.`:s===`postgres`?`PostgreSQL creates custom ENUM types using <code>CREATE TYPE ... AS ENUM (...)</code>.`:`MySQL stores enums as native <code>ENUM(...)</code> column types.`,p=d.length>0,m=d.find(e=>e.name.toLowerCase()===l.toLowerCase());r.innerHTML=`
|
|
1133
|
+
<div class="keys-modal-container" style="width: 520px;">
|
|
1134
|
+
<div class="modal-header">
|
|
1135
|
+
<div class="flex items-center gap-2">
|
|
1136
|
+
<span class="material-symbols-outlined text-primary" style="font-size:20px;">list_alt</span>
|
|
1137
|
+
<h3 class="m-0 text-15 font-semibold">Configure ENUM Column</h3>
|
|
1138
|
+
<span class="keys-col-badge">${o}</span>
|
|
1139
|
+
<span class="snippet-param-badge">${s}</span>
|
|
1140
|
+
</div>
|
|
1141
|
+
<button id="close-enum-modal" class="modal-close-btn" title="Close"><span class="material-symbols-outlined">close</span></button>
|
|
1142
|
+
</div>
|
|
1143
|
+
|
|
1144
|
+
<div class="key-cards-list" style="gap: 14px;">
|
|
1145
|
+
<div style="font-size: 11.5px; color: var(--color-text-secondary); line-height: 1.5; background: var(--color-bg-secondary-dark); padding: 8px 12px; border-radius: 6px; border: 1px solid var(--color-border);">
|
|
1146
|
+
${f}
|
|
1147
|
+
</div>
|
|
1148
|
+
|
|
1149
|
+
${p?`
|
|
1150
|
+
<div class="table-fk-popover-field">
|
|
1151
|
+
<label style="font-size: 11px; font-weight: 500; color: var(--color-text-secondary);">Enum Source</label>
|
|
1152
|
+
<button type="button" id="modal-enum-source" class="table-col-select" style="display: flex; justify-content: space-between; align-items: center; width: 100%; height: 32px; cursor: pointer; text-align: left; padding: 0 10px;">
|
|
1153
|
+
<span id="modal-enum-source-display" style="overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${m?m.name:`+ Define New Enum...`}</span>
|
|
1154
|
+
<span class="material-symbols-outlined table-col-select-arrow" style="font-size: 16px;">expand_more</span>
|
|
1155
|
+
</button>
|
|
1156
|
+
</div>
|
|
1157
|
+
`:``}
|
|
1158
|
+
|
|
1159
|
+
<div id="modal-enum-custom-fields" class="flex flex-col gap-3">
|
|
1160
|
+
<div>
|
|
1161
|
+
<label style="font-size: 11px; font-weight: 500; color: var(--color-text-secondary); display: block; margin-bottom: 5px;">
|
|
1162
|
+
Enum Name ${s===`postgres`?`<span style="color: var(--color-error);">*</span>`:`(Optional)`}
|
|
1163
|
+
</label>
|
|
1164
|
+
<input type="text" id="modal-enum-name" class="modal-input w-full" style="padding: 7px 10px; font-size: 12px; font-family: var(--font-mono);" placeholder="e.g. OrderStatus" value="${l}" />
|
|
1165
|
+
</div>
|
|
1166
|
+
|
|
1167
|
+
<div>
|
|
1168
|
+
<label style="font-size: 11px; font-weight: 500; color: var(--color-text-secondary); display: block; margin-bottom: 5px;">
|
|
1169
|
+
Allowed Values (comma separated) <span style="color: var(--color-error);">*</span>
|
|
1170
|
+
</label>
|
|
1171
|
+
<input type="text" id="modal-enum-values" class="modal-input w-full" style="padding: 7px 10px; font-size: 12px; font-family: var(--font-mono);" placeholder="e.g. PENDING, PAID, CANCELLED, REFUNDED" value="${c.join(`, `)}" />
|
|
1172
|
+
<span style="font-size: 10.5px; color: var(--color-text-soft); margin-top: 4px; display: block;">Separate values with commas. Quotes are optional.</span>
|
|
1173
|
+
</div>
|
|
1174
|
+
</div>
|
|
1175
|
+
|
|
1176
|
+
<!-- SQL DDL Preview -->
|
|
1177
|
+
<div>
|
|
1178
|
+
<label style="font-size: 11px; font-weight: 500; color: var(--color-text-secondary); display: block; margin-bottom: 5px;">
|
|
1179
|
+
Preview
|
|
1180
|
+
</label>
|
|
1181
|
+
<div style="background: var(--color-bg-secondary-dark); border: 1px solid var(--color-border); border-radius: 6px; padding: 10px 12px; font-family: var(--font-mono); font-size: 11.5px; color: var(--color-text); word-break: break-all; min-height: 38px; display: flex; align-items: center;" id="modal-enum-preview">
|
|
1182
|
+
-
|
|
1183
|
+
</div>
|
|
1184
|
+
</div>
|
|
1185
|
+
</div>
|
|
1186
|
+
|
|
1187
|
+
<div class="modal-footer">
|
|
1188
|
+
<button id="cancel-enum-btn" class="btn-secondary">Cancel</button>
|
|
1189
|
+
<button id="save-enum-btn" class="btn-primary flex items-center gap-1.5">
|
|
1190
|
+
<span class="material-symbols-outlined" style="font-size:16px;">check</span>
|
|
1191
|
+
<span>Apply Enum</span>
|
|
1192
|
+
</button>
|
|
1193
|
+
</div>
|
|
1194
|
+
</div>
|
|
1195
|
+
`,document.body.appendChild(r);let h=()=>{document.removeEventListener(`keydown`,g),r.remove()},g=e=>{e.key===`Escape`&&h()};document.addEventListener(`keydown`,g),document.getElementById(`close-enum-modal`).onclick=h,document.getElementById(`cancel-enum-btn`).onclick=h,r.addEventListener(`click`,e=>{e.target===r&&h()});let _=document.getElementById(`modal-enum-source`),v=document.getElementById(`modal-enum-custom-fields`),y=document.getElementById(`modal-enum-name`),b=document.getElementById(`modal-enum-values`),x=document.getElementById(`modal-enum-preview`),S=m?m.name:`__NEW__`;function C(){if(_&&S!==`__NEW__`){let e=d.find(e=>e.name===S);return e?[...e.values]:[]}return b.value.split(`,`).map(e=>e.trim().replace(/^['"]|['"]$/g,``)).filter(Boolean)}function w(){let e=C(),t=(y.value.trim()||l||`CustomEnum`).trim(),n=e.map(e=>`'${e.replace(/'/g,`''`)}'`).join(`, `);if(e.length===0){x.textContent=`Enter enum values above to see SQL preview`;return}s===`sqlite`?x.textContent=`TEXT CHECK("${o}" IN (${n}))`:s===`postgres`?_&&S!==`__NEW__`?x.textContent=`Type: "${S}" (Values: ${n})`:x.textContent=`CREATE TYPE "${t}" AS ENUM (${n});`:x.textContent=`ENUM(${n})`}_&&(_.onclick=e=>{e.stopPropagation();let t=[{name:`+ Define New Enum...`,value:`__NEW__`,icon:`add_circle`},...d.map(e=>({name:e.name,value:e.name,desc:e.values.slice(0,3).join(`, `)+(e.values.length>3?`...`:``),icon:`list`,group:`Existing Database Enums`}))];I({anchorEl:_,title:`Select Enum Source`,searchable:!0,placeholder:`Search enum types...`,initialValue:S,items:t,onSelect:e=>{S=e;let t=document.getElementById(`modal-enum-source-display`);t&&(t.textContent=e===`__NEW__`?`+ Define New Enum...`:e),e===`__NEW__`?v.style.display=`flex`:v.style.display=`none`,w()}})},S!==`__NEW__`&&(v.style.display=`none`)),y.oninput=w,b.oninput=w,w(),document.getElementById(`save-enum-btn`).onclick=()=>{let t=C();if(t.length===0){window.showToast?window.showToast(`Please specify at least one enum value.`,`error`):alert(`Please specify at least one enum value.`),b.focus();return}let r=``,c=!0;_&&S!==`__NEW__`?(r=S,c=!1):(r=(y.value.trim()||l||`CustomEnum`).trim(),c=!0);let u=s===`postgres`?r:r||`ENUM`;i&&a!==null?(n.pendingInserts[a]||(n.pendingInserts[a]={}),n.pendingInserts[a].type=u,n.pendingInserts[a].enumValues=t,n.pendingInserts[a].isNewEnum=c):o&&(n.pendingEdits[o]||(n.pendingEdits[o]={}),n.pendingEdits[o].type=u,n.pendingEdits[o].enumValues=t,n.pendingEdits[o].isNewEnum=c),window.SchemaGrid.currentTransaction=[],qe(e,u,[`name`,`type`,`isPk`,`nullable`,`isUnique`,`defaultValue`,`indexing`]),window.SchemaGrid.currentTransaction.length>0&&window.SchemaGrid.history.push(window.SchemaGrid.currentTransaction),window.SchemaGrid.currentTransaction=null,h(),window.updateSidebarDirtyState?.(),window.showToast?.(`Enum column configured!`,`success`)}}function et(e,t){e.addEventListener(`click`,e=>{if(e.target.closest(`.btn-manage-indexes`)||e.target.closest(`.manage-indexes-cell`)){Ze();return}let t=e.target.closest(`.badge-enum`);if(t){let e=t.closest(`td.data-cell`);if(e){let t=e.dataset.pk,n=window.SchemaGrid?.schema?.find(e=>e.name===t);$e(e,n);return}}}),e.addEventListener(`dblclick`,e=>{let n=e.target.closest(`td.data-cell`);if(!n||n.querySelector(`input, select, textarea`))return;let r=n.dataset.colKey;if(r===`indexing`){Ze();return}let i=n.dataset.insertIndex!==void 0,a=i||n.textContent===`-`||n.textContent===`+ New`||n.textContent.includes(`Add column`)||n.textContent.includes(`Add Column`)||n.textContent===`+ Add Row`||n.textContent===`null`?``:n.dataset.original!==void 0&&n.dataset.original!==`-`?n.dataset.original:n.textContent.trim().startsWith(`Yes`)?`1`:n.textContent.trim()===`No`?``:n.textContent.trim();if(r===`name`||r===`defaultValue`){Fe({td:n,colName:r===`name`?`Column Name`:`Default Value`,colType:r===`name`?`VARCHAR`:`DEFAULT`,initialValue:a,onSave:e=>{window.SchemaGrid.currentTransaction=[],qe(n,e,t),window.SchemaGrid.currentTransaction.length>0&&window.SchemaGrid.history.push(window.SchemaGrid.currentTransaction),window.SchemaGrid.currentTransaction=null}});return}let o;if(r===`isPk`){Qe(n,n.textContent);return}else if(r===`nullable`){let e=n.dataset.pk,r=window.SchemaGrid?.schema?.find(t=>t.name===e),o=window.SchemaGrid?.pendingEdits?.[e]?.isPk,s=o===void 0?r?r.isPk:!1:typeof o==`string`?o.includes(`PK`)||o.includes(`PFK`):!!o,c=i&&window.SchemaGrid?.pendingInserts?.[parseInt(n.dataset.insertIndex,10)]?.isPk,l=typeof c==`string`?c.includes(`PK`)||c.includes(`PFK`):!!c;if(s||l){window.showToast?window.showToast(`Primary Key cannot be nullable.`,`error`):alert(`Primary Key cannot be nullable.`);return}I({anchorEl:n,title:`Nullable Constraint`,placeholder:`Search constraint...`,items:[{name:`No`,value:`No`,desc:`NOT NULL constraint (Cannot be empty)`,icon:`lock`},{name:`Yes`,value:`Yes`,desc:`Allows NULL / empty values`,icon:`check`}],initialValue:a===`1`||a===`Yes`?`Yes`:`No`,onSelect:e=>{window.SchemaGrid.currentTransaction=[],qe(n,e,t),window.SchemaGrid.currentTransaction.length>0&&window.SchemaGrid.history.push(window.SchemaGrid.currentTransaction),window.SchemaGrid.currentTransaction=null}});return}else if(r===`isUnique`){let e=n.dataset.pk,r=window.SchemaGrid?.schema?.find(t=>t.name===e),o=window.SchemaGrid?.pendingEdits?.[e]?.isPk,s=o===void 0?r?r.isPk:!1:typeof o==`string`?o.includes(`PK`)||o.includes(`PFK`):!!o,c=i&&window.SchemaGrid?.pendingInserts?.[parseInt(n.dataset.insertIndex,10)]?.isPk,l=typeof c==`string`?c.includes(`PK`)||c.includes(`PFK`):!!c;if(s||l){window.showToast?window.showToast(`Primary Key is always unique and locked.`,`error`):alert(`Primary Key is always unique and locked.`);return}I({anchorEl:n,title:`Unique Constraint`,placeholder:`Search constraint...`,items:[{name:`No`,value:`No`,desc:`Duplicate values allowed`,icon:`close`},{name:`Yes`,value:`Yes`,desc:`Enforces UNIQUE constraint`,icon:`verified`}],initialValue:a===`1`||a===`Yes`?`Yes`:`No`,onSelect:e=>{window.SchemaGrid.currentTransaction=[],qe(n,e,t),window.SchemaGrid.currentTransaction.length>0&&window.SchemaGrid.history.push(window.SchemaGrid.currentTransaction),window.SchemaGrid.currentTransaction=null}});return}else if(r===`type`){let e=n.dataset.pk,r=window.SchemaGrid?.schema?.find(t=>t.name===e);te({anchorEl:n,initialValue:a,dialect:window.AppState?.dbType,onSelect:e=>{if(e===`ENUM`){setTimeout(()=>$e(n,r),10);return}window.SchemaGrid.currentTransaction=[],qe(n,e,t),window.SchemaGrid.currentTransaction.length>0&&window.SchemaGrid.history.push(window.SchemaGrid.currentTransaction),window.SchemaGrid.currentTransaction=null}});return}else o=document.createElement(`input`),o.type=`text`,o.value=a;n.innerHTML=``,n.classList.add(`cell-editing`),n.appendChild(o),o.addEventListener(`keydown`,e=>{e.key===`Enter`&&o.blur(),e.key===`Tab`&&(e.preventDefault(),o.blur(),document.body.dispatchEvent(new KeyboardEvent(`keydown`,{key:`Tab`,shiftKey:e.shiftKey,bubbles:!0})))}),o.focus();let s=()=>{n.classList.remove(`cell-editing`);let e=o.value;r===`type`&&e===`ENUM`||(window.SchemaGrid.currentTransaction=[],qe(n,e,t),window.SchemaGrid.currentTransaction.length>0&&window.SchemaGrid.history.push(window.SchemaGrid.currentTransaction),window.SchemaGrid.currentTransaction=null)};o.addEventListener(`blur`,()=>{s()})})}async function tt(e){let t=document.getElementById(`orm-export-modal`);t&&t.remove();let n=`prisma`,r=e||`*`,i=``;t=document.createElement(`div`),t.id=`orm-export-modal`,t.className=`modal-overlay`;let a=(window.AppState?.dbType||`database`).toUpperCase();t.innerHTML=`
|
|
1196
|
+
<div class="orm-modal-container">
|
|
1197
|
+
<div class="modal-header">
|
|
1198
|
+
<div class="flex items-center gap-2">
|
|
1199
|
+
<span class="material-symbols-outlined text-primary" style="font-size: 20px;">bolt</span>
|
|
1200
|
+
<h3 class="m-0 text-15 font-semibold">Generate ORM Schema</h3>
|
|
1201
|
+
<span class="badge-type" style="font-size: 10px; padding: 2px 7px; border-radius: 4px; background: rgba(59, 130, 246, 0.12); color: var(--color-primary); font-weight: 700;">${a}</span>
|
|
1202
|
+
</div>
|
|
1203
|
+
<button id="close-orm-modal-btn" class="modal-close-btn" title="Close"><span class="material-symbols-outlined">close</span></button>
|
|
1204
|
+
</div>
|
|
1205
|
+
|
|
1206
|
+
<div class="orm-controls-bar">
|
|
1207
|
+
<div class="orm-tab-group">
|
|
1208
|
+
<button type="button" id="tab-orm-prisma" class="orm-tab-btn active">
|
|
1209
|
+
<span class="material-symbols-outlined" style="font-size: 15px;">diamond</span>
|
|
1210
|
+
<span>Prisma ORM</span>
|
|
1211
|
+
</button>
|
|
1212
|
+
<button type="button" id="tab-orm-drizzle" class="orm-tab-btn">
|
|
1213
|
+
<span class="material-symbols-outlined" style="font-size: 15px;">water_drop</span>
|
|
1214
|
+
<span>Drizzle ORM</span>
|
|
1215
|
+
</button>
|
|
1216
|
+
</div>
|
|
1217
|
+
|
|
1218
|
+
<div class="orm-scope-group">
|
|
1219
|
+
<label class="orm-scope-label" for="orm-scope-select">Scope:</label>
|
|
1220
|
+
<button type="button" id="orm-scope-select" class="orm-scope-select" style="display: flex; justify-content: space-between; align-items: center; gap: 6px; cursor: pointer; height: 28px; padding: 0 8px; font-size: 12px; background: var(--color-bg-secondary); border: 1px solid var(--color-border); border-radius: 6px; color: var(--color-text);">
|
|
1221
|
+
<span id="orm-scope-display">${e?`Table: ${e}`:`All Tables (Database)`}</span>
|
|
1222
|
+
<span class="material-symbols-outlined" style="font-size: 16px; color: var(--color-text-soft);">expand_more</span>
|
|
1223
|
+
</button>
|
|
1224
|
+
</div>
|
|
1225
|
+
</div>
|
|
1226
|
+
|
|
1227
|
+
<div class="orm-code-wrapper">
|
|
1228
|
+
<div class="orm-code-header">
|
|
1229
|
+
<div class="orm-code-file-badge">
|
|
1230
|
+
<span class="material-symbols-outlined" style="font-size: 14px;">description</span>
|
|
1231
|
+
<span id="orm-file-name-label">schema.prisma</span>
|
|
1232
|
+
</div>
|
|
1233
|
+
<button type="button" id="orm-copy-code-btn" class="orm-code-copy-btn" title="Copy code to clipboard">
|
|
1234
|
+
<span class="material-symbols-outlined" style="font-size: 14px;">content_copy</span>
|
|
1235
|
+
<span id="orm-copy-btn-text">Copy Code</span>
|
|
1236
|
+
</button>
|
|
1237
|
+
</div>
|
|
1238
|
+
<div class="orm-code-scroll">
|
|
1239
|
+
<pre><code id="orm-code-display" class="orm-code-content">// Generating schema...</code></pre>
|
|
1240
|
+
</div>
|
|
1241
|
+
</div>
|
|
1242
|
+
|
|
1243
|
+
<div class="modal-footer" style="justify-content: space-between; align-items: center;">
|
|
1244
|
+
<div class="orm-modal-footer-hint">
|
|
1245
|
+
<span class="material-symbols-outlined" style="font-size: 15px; color: #f59e0b;">lightbulb</span>
|
|
1246
|
+
<span>Automatic project setup (<code>drixio add prisma/drizzle</code>) coming soon!</span>
|
|
1247
|
+
</div>
|
|
1248
|
+
<button type="button" id="close-orm-btn" class="btn-secondary">Close</button>
|
|
1249
|
+
</div>
|
|
1250
|
+
</div>
|
|
1251
|
+
`,document.body.appendChild(t);let o=()=>{document.removeEventListener(`keydown`,s),t.remove()},s=e=>{e.key===`Escape`&&o()};document.addEventListener(`keydown`,s),t.addEventListener(`click`,e=>{e.target===t&&o()}),document.getElementById(`close-orm-modal-btn`).onclick=o,document.getElementById(`close-orm-btn`).onclick=o;let c=document.getElementById(`orm-code-display`),l=document.getElementById(`orm-file-name-label`),u=document.getElementById(`orm-copy-code-btn`),d=document.getElementById(`orm-copy-btn-text`),f=document.getElementById(`tab-orm-prisma`),p=document.getElementById(`tab-orm-drizzle`),m=document.getElementById(`orm-scope-select`),h=[];try{let e=await(await fetch(`/api/tables`)).json();e.success&&e.data&&(h=e.data)}catch{}let g=async()=>{c.textContent=`// Loading ${n===`prisma`?`Prisma`:`Drizzle`} schema...`,l.textContent=n===`prisma`?`schema.prisma`:`schema.ts`;try{let e=await(await fetch(`/api/generate-orm?target=${encodeURIComponent(n)}&table=${encodeURIComponent(r)}`)).json();e.success&&e.data?(i=e.data.code||``,c.textContent=i):c.textContent=`// Error generating schema:\n// ${e.error||`Unknown error`}`}catch(e){c.textContent=`// Failed to fetch ORM schema: ${e.message}`}};f.onclick=()=>{n!==`prisma`&&(n=`prisma`,f.classList.add(`active`),p.classList.remove(`active`),g())},p.onclick=()=>{n!==`drizzle`&&(n=`drizzle`,p.classList.add(`active`),f.classList.remove(`active`),g())},m.onclick=e=>{e.stopPropagation();let t=[{name:`All Tables (Database)`,value:`*`,icon:`database`},...h.map(e=>({name:`Table: ${e}`,value:e,icon:`table_chart`}))];I({anchorEl:m,title:`ORM Generation Scope`,searchable:h.length>4,placeholder:`Search tables...`,initialValue:r,items:t,onSelect:e=>{r=e;let t=document.getElementById(`orm-scope-display`);t&&(t.textContent=e===`*`?`All Tables (Database)`:`Table: ${e}`),g()}})},u.onclick=()=>{i&&(navigator.clipboard.writeText(i),d.textContent=`Copied!`,window.showToast&&window.showToast(`${n===`prisma`?`Prisma`:`Drizzle`} schema copied to clipboard!`,`success`),setTimeout(()=>{d.textContent=`Copy Code`},1800))},g()}function nt(){let e=document.getElementById(`schema-diff-modal`);e&&e.remove();let t=(window.AppState?.dbType||`database`).toUpperCase(),n=window.AppState?.dbName||`Current DB`,i=document.createElement(`div`);i.id=`schema-diff-modal`,i.className=`modal-overlay`,i.innerHTML=`
|
|
1252
|
+
<div class="diff-modal-container" id="diff-container-box">
|
|
1253
|
+
<div class="modal-header">
|
|
1254
|
+
<div class="flex items-center gap-2">
|
|
1255
|
+
<span class="material-symbols-outlined text-primary" style="font-size: 20px;">compare_arrows</span>
|
|
1256
|
+
<h3 class="m-0 text-15 font-semibold">Schema Diff & Migration</h3>
|
|
1257
|
+
<span class="badge-type" style="font-size: 10px; padding: 2px 7px; border-radius: 4px; background: rgba(59, 130, 246, 0.12); color: var(--color-primary); font-weight: 700;">${t}: ${n}</span>
|
|
1258
|
+
</div>
|
|
1259
|
+
<button id="close-diff-modal-btn" class="modal-close-btn" title="Close"><span class="material-symbols-outlined">close</span></button>
|
|
1260
|
+
</div>
|
|
1261
|
+
|
|
1262
|
+
<div class="diff-controls-bar">
|
|
1263
|
+
<div class="diff-target-tabs">
|
|
1264
|
+
<button type="button" id="tab-diff-file" class="diff-tab-btn active">
|
|
1265
|
+
<span class="material-symbols-outlined" style="font-size: 15px;">data_object</span>
|
|
1266
|
+
<span>Snapshot File</span>
|
|
1267
|
+
</button>
|
|
1268
|
+
<button type="button" id="tab-diff-url" class="diff-tab-btn">
|
|
1269
|
+
<span class="material-symbols-outlined" style="font-size: 15px;">cloud_sync</span>
|
|
1270
|
+
<span>Database Connection</span>
|
|
1271
|
+
</button>
|
|
1272
|
+
</div>
|
|
1273
|
+
|
|
1274
|
+
<button type="button" id="btn-save-current-snapshot" class="diff-action-btn" title="Export current DB schema as portable JSON snapshot">
|
|
1275
|
+
<span class="material-symbols-outlined" style="font-size: 15px;">download</span>
|
|
1276
|
+
<span>Save Current Snapshot</span>
|
|
1277
|
+
</button>
|
|
1278
|
+
</div>
|
|
1279
|
+
|
|
1280
|
+
<!-- Target Input Area -->
|
|
1281
|
+
<div class="diff-input-panel">
|
|
1282
|
+
<!-- File Input Mode -->
|
|
1283
|
+
<div id="diff-panel-file" class="diff-panel-section">
|
|
1284
|
+
<div id="diff-dropzone" class="diff-dropzone">
|
|
1285
|
+
<input type="file" id="diff-file-input" accept=".json" style="display: none;" />
|
|
1286
|
+
<span class="material-symbols-outlined" style="font-size: 28px; color: var(--color-primary); margin-bottom: 6px;">upload_file</span>
|
|
1287
|
+
<div id="diff-file-prompt" class="text-13" style="color: var(--color-text-secondary);">
|
|
1288
|
+
<span class="font-semibold" style="color: var(--color-primary); cursor: pointer;">Click to choose</span> or drag & drop a <code>schema.json</code> snapshot
|
|
1289
|
+
</div>
|
|
1290
|
+
<div id="diff-file-info" class="text-12 hidden" style="color: #10b981; font-weight: 600; margin-top: 6px;"></div>
|
|
1291
|
+
</div>
|
|
1292
|
+
</div>
|
|
1293
|
+
|
|
1294
|
+
<!-- URL Input Mode -->
|
|
1295
|
+
<div id="diff-panel-url" class="diff-panel-section hidden">
|
|
1296
|
+
<div class="flex items-center gap-2">
|
|
1297
|
+
<input type="text" id="diff-target-url-input" class="diff-url-input" placeholder="e.g. postgres://user:pass@remote-host:5432/mydb or mysql://... or file:backup.db" />
|
|
1298
|
+
<button type="button" id="btn-run-diff" class="diff-run-btn" disabled>
|
|
1299
|
+
<span class="material-symbols-outlined" style="font-size: 16px;">play_arrow</span>
|
|
1300
|
+
<span>Compare</span>
|
|
1301
|
+
</button>
|
|
1302
|
+
</div>
|
|
1303
|
+
</div>
|
|
1304
|
+
</div>
|
|
1305
|
+
|
|
1306
|
+
<!-- Diff Results Container (Only displayed after comparison) -->
|
|
1307
|
+
<div id="diff-results-wrap" class="diff-results-wrap hidden">
|
|
1308
|
+
<!-- Stats Bar -->
|
|
1309
|
+
<div id="diff-stats-bar" class="diff-stats-bar"></div>
|
|
1310
|
+
|
|
1311
|
+
<!-- Split Grid: Left Visual Diff, Right Migration SQL -->
|
|
1312
|
+
<div class="diff-body-split">
|
|
1313
|
+
<!-- Left: Changes Tree -->
|
|
1314
|
+
<div class="diff-tree-column">
|
|
1315
|
+
<div class="diff-column-header">
|
|
1316
|
+
<span class="material-symbols-outlined" style="font-size: 15px;">account_tree</span>
|
|
1317
|
+
<span>Schema Changes</span>
|
|
1318
|
+
</div>
|
|
1319
|
+
<div id="diff-tree-content" class="diff-tree-content"></div>
|
|
1320
|
+
</div>
|
|
1321
|
+
|
|
1322
|
+
<!-- Right: Generated SQL -->
|
|
1323
|
+
<div class="diff-sql-column">
|
|
1324
|
+
<div class="diff-column-header flex items-center justify-between">
|
|
1325
|
+
<div class="flex items-center gap-2">
|
|
1326
|
+
<span class="material-symbols-outlined" style="font-size: 15px;">code</span>
|
|
1327
|
+
<span id="diff-sql-title">Migration SQL (Up)</span>
|
|
1328
|
+
</div>
|
|
1329
|
+
<div class="flex items-center gap-2">
|
|
1330
|
+
<button type="button" id="btn-toggle-sql-direction" class="diff-mini-btn" title="Toggle between Forward (Up) and Rollback (Down)">
|
|
1331
|
+
<span class="material-symbols-outlined" style="font-size: 13px;">swap_vert</span>
|
|
1332
|
+
<span id="sql-direction-label">Rollback (Down)</span>
|
|
1333
|
+
</button>
|
|
1334
|
+
<button type="button" id="btn-copy-diff-sql" class="diff-mini-btn" title="Copy SQL">
|
|
1335
|
+
<span class="material-symbols-outlined" style="font-size: 13px;">content_copy</span>
|
|
1336
|
+
<span>Copy</span>
|
|
1337
|
+
</button>
|
|
1338
|
+
<button type="button" id="btn-download-diff-sql" class="diff-mini-btn" title="Download .sql">
|
|
1339
|
+
<span class="material-symbols-outlined" style="font-size: 13px;">download</span>
|
|
1340
|
+
<span>.sql</span>
|
|
1341
|
+
</button>
|
|
1342
|
+
</div>
|
|
1343
|
+
</div>
|
|
1344
|
+
<pre id="diff-sql-preview" class="diff-sql-pre"><code>-- SQL will appear here...</code></pre>
|
|
1345
|
+
</div>
|
|
1346
|
+
</div>
|
|
1347
|
+
</div>
|
|
1348
|
+
|
|
1349
|
+
<!-- Standard Modal Footer -->
|
|
1350
|
+
<div class="modal-footer" style="justify-content: space-between; align-items: center;">
|
|
1351
|
+
<div class="orm-modal-footer-hint" id="diff-footer-hint">
|
|
1352
|
+
<span class="material-symbols-outlined" style="font-size: 15px; color: var(--color-primary);">info</span>
|
|
1353
|
+
<span>Select a target snapshot or connection string to compare.</span>
|
|
1354
|
+
</div>
|
|
1355
|
+
<div class="flex items-center gap-2">
|
|
1356
|
+
<button type="button" id="close-diff-footer-btn" class="btn-secondary">Close</button>
|
|
1357
|
+
<button type="button" id="btn-apply-diff-migration" class="btn-primary hidden" style="background: #10b981; border-color: #10b981; gap: 6px;">
|
|
1358
|
+
<span class="material-symbols-outlined" style="font-size: 16px;">bolt</span>
|
|
1359
|
+
<span>Apply Migration</span>
|
|
1360
|
+
</button>
|
|
1361
|
+
</div>
|
|
1362
|
+
</div>
|
|
1363
|
+
</div>
|
|
1364
|
+
`,document.body.appendChild(i);let a=null,o=null,s=!1,c=document.getElementById(`diff-container-box`),l=document.getElementById(`close-diff-modal-btn`),u=document.getElementById(`close-diff-footer-btn`),d=document.getElementById(`tab-diff-file`),f=document.getElementById(`tab-diff-url`),p=document.getElementById(`diff-panel-file`),m=document.getElementById(`diff-panel-url`),h=document.getElementById(`diff-dropzone`),y=document.getElementById(`diff-file-input`),b=document.getElementById(`diff-file-info`),x=document.getElementById(`diff-target-url-input`),S=document.getElementById(`btn-run-diff`),C=document.getElementById(`btn-save-current-snapshot`),w=document.getElementById(`diff-results-wrap`),T=document.getElementById(`diff-stats-bar`),E=document.getElementById(`diff-tree-content`),D=document.getElementById(`diff-sql-preview`),O=document.getElementById(`btn-toggle-sql-direction`),k=document.getElementById(`btn-copy-diff-sql`),A=document.getElementById(`btn-download-diff-sql`),j=document.getElementById(`btn-apply-diff-migration`),M=document.getElementById(`diff-footer-hint`),N=()=>{document.removeEventListener(`keydown`,P),i.remove()},P=e=>{e.key===`Escape`&&N()};document.addEventListener(`keydown`,P),i.addEventListener(`click`,e=>{e.target===i&&N()}),l.onclick=N,u.onclick=N,C.onclick=()=>{v()},d.onclick=()=>{d.classList.add(`active`),f.classList.remove(`active`),p.classList.remove(`hidden`),m.classList.add(`hidden`)},f.onclick=()=>{f.classList.add(`active`),d.classList.remove(`active`),m.classList.remove(`hidden`),p.classList.add(`hidden`)},h.onclick=()=>y.click(),h.ondragover=e=>{e.preventDefault(),h.classList.add(`dragover`)},h.ondragleave=()=>h.classList.remove(`dragover`),h.ondrop=e=>{e.preventDefault(),h.classList.remove(`dragover`),e.dataTransfer.files&&e.dataTransfer.files.length>0&&F(e.dataTransfer.files[0])},y.onchange=e=>{e.target.files&&e.target.files.length>0&&F(e.target.files[0])};function F(e){if(!e.name.endsWith(`.json`)){alert(`Please select a valid .json schema snapshot file.`);return}let t=new FileReader;t.onload=async t=>{try{let n=JSON.parse(t.target.result);if(!n.tables||!Array.isArray(n.tables)){alert(`Invalid schema snapshot format. Missing tables array.`);return}a=n,b.textContent=`✔ Loaded "${e.name}" (${(e.size/1024).toFixed(1)} KB, ${n.tables.length} tables)`,b.classList.remove(`hidden`),await I({snapshot:a})}catch(e){alert(`Failed to parse snapshot JSON: ${e.message}`)}},t.readAsText(e)}x.oninput=()=>{S.disabled=!x.value.trim()},x.onkeydown=e=>{e.key===`Enter`&&x.value.trim()&&S.click()},S.onclick=async()=>{let e=x.value.trim();if(e){S.disabled=!0,S.innerHTML=`<span class="material-symbols-outlined spin" style="font-size: 16px;">sync</span><span>Comparing...</span>`;try{await I({targetUrl:e})}finally{S.disabled=!1,S.innerHTML=`<span class="material-symbols-outlined" style="font-size: 16px;">play_arrow</span><span>Compare</span>`}}};async function I(e){w.classList.remove(`hidden`),c.classList.add(`has-results`),T.innerHTML=`<div class="p-2 text-13" style="color: var(--color-text-secondary);">Comparing schemas...</div>`,E.innerHTML=`<div class="p-4 text-center text-13" style="color: var(--color-text-secondary);">Computing differences...</div>`,D.textContent=`-- Computing diff...`;try{let t=await g(e);if(!t.success){T.innerHTML=`<div class="diff-stat-error">✘ Diff Error: ${t.error}</div>`,E.innerHTML=``,D.textContent=`-- Diff failed:\n-- ${t.error}`;return}o=t.data,s=!1,L(o)}catch(e){T.innerHTML=`<div class="diff-stat-error">✘ Request failed: ${e.message}</div>`}}function L(e){let{diff:t,migrationSql:n,rollbackSql:r}=e,{stats:i}=t;if(!t.hasChanges){T.innerHTML=`
|
|
1365
|
+
<div class="diff-stat-item clean">
|
|
1366
|
+
<span class="material-symbols-outlined" style="color: #10b981;">check_circle</span>
|
|
1367
|
+
<span>Schemas are identical! No migration required.</span>
|
|
1368
|
+
</div>
|
|
1369
|
+
`,E.innerHTML=`<div class="p-4 text-center text-13" style="color: #10b981;">Both schemas match completely.</div>`,D.textContent=`-- Schemas are identical.`,j.classList.add(`hidden`),M.innerHTML=`<span class="material-symbols-outlined" style="font-size: 15px; color: #10b981;">check_circle</span><span>No differences detected. Database is up to date.</span>`;return}j.classList.remove(`hidden`),M.innerHTML=`<span class="material-symbols-outlined" style="font-size: 15px; color: #f59e0b;">warning</span><span>Review changes carefully before applying to database.</span>`,T.innerHTML=`
|
|
1370
|
+
<div class="diff-stat-group">
|
|
1371
|
+
${i.addedTablesCount>0?`<span class="diff-stat-badge added">+${i.addedTablesCount} Tables Added</span>`:``}
|
|
1372
|
+
${i.droppedTablesCount>0?`<span class="diff-stat-badge dropped">-${i.droppedTablesCount} Tables Dropped</span>`:``}
|
|
1373
|
+
${i.alteredTablesCount>0?`<span class="diff-stat-badge altered">~${i.alteredTablesCount} Tables Altered</span>`:``}
|
|
1374
|
+
<span class="diff-stat-badge neutral">${i.addedColumnsCount+i.droppedColumnsCount+i.modifiedColumnsCount} Column Changes</span>
|
|
1375
|
+
${i.addedIndexesCount+i.droppedIndexesCount>0?`<span class="diff-stat-badge neutral">${i.addedIndexesCount+i.droppedIndexesCount} Index Changes</span>`:``}
|
|
1376
|
+
</div>
|
|
1377
|
+
`;let a=``;for(let e of t.tables)e.type===`added`?a+=`
|
|
1378
|
+
<div class="diff-tree-table-item">
|
|
1379
|
+
<div class="diff-table-header added">
|
|
1380
|
+
<span class="material-symbols-outlined" style="font-size: 16px;">add_box</span>
|
|
1381
|
+
<span class="font-semibold">${e.tableName}</span>
|
|
1382
|
+
<span class="diff-pill added">NEW TABLE</span>
|
|
1383
|
+
</div>
|
|
1384
|
+
<div class="diff-table-details">
|
|
1385
|
+
${e.addedColumns.map(e=>`<div class="diff-item added">+ ${e.name} <span class="diff-type">${e.type}</span></div>`).join(``)}
|
|
1386
|
+
</div>
|
|
1387
|
+
</div>
|
|
1388
|
+
`:e.type===`dropped`?a+=`
|
|
1389
|
+
<div class="diff-tree-table-item">
|
|
1390
|
+
<div class="diff-table-header dropped">
|
|
1391
|
+
<span class="material-symbols-outlined" style="font-size: 16px;">indeterminate_check_box</span>
|
|
1392
|
+
<span class="font-semibold">${e.tableName}</span>
|
|
1393
|
+
<span class="diff-pill dropped">DROP TABLE</span>
|
|
1394
|
+
</div>
|
|
1395
|
+
</div>
|
|
1396
|
+
`:e.type===`altered`&&(a+=`
|
|
1397
|
+
<div class="diff-tree-table-item">
|
|
1398
|
+
<div class="diff-table-header altered">
|
|
1399
|
+
<span class="material-symbols-outlined" style="font-size: 16px;">change_circle</span>
|
|
1400
|
+
<span class="font-semibold">${e.tableName}</span>
|
|
1401
|
+
<span class="diff-pill altered">ALTER TABLE</span>
|
|
1402
|
+
</div>
|
|
1403
|
+
<div class="diff-table-details">
|
|
1404
|
+
${e.addedColumns.map(e=>`<div class="diff-item added">+ ADD COLUMN ${e.name} <span class="diff-type">${e.type}</span></div>`).join(``)}
|
|
1405
|
+
${e.droppedColumns.map(e=>`<div class="diff-item dropped">- DROP COLUMN ${e.name}</div>`).join(``)}
|
|
1406
|
+
${e.modifiedColumns.map(e=>`<div class="diff-item modified">~ MODIFY ${e.name}: <span class="diff-type">${e.oldType}</span> ➔ <span class="diff-type">${e.newType}</span></div>`).join(``)}
|
|
1407
|
+
${e.addedIndexes.map(e=>`<div class="diff-item added">+ INDEX ${e.name||`unnamed`} (${e.columns.join(`, `)})</div>`).join(``)}
|
|
1408
|
+
${e.droppedIndexes.map(e=>`<div class="diff-item dropped">- DROP INDEX ${e.name||`unnamed`}</div>`).join(``)}
|
|
1409
|
+
</div>
|
|
1410
|
+
</div>
|
|
1411
|
+
`);E.innerHTML=a,R()}function R(){if(!o)return;let e=s?o.rollbackSql:o.migrationSql;D.textContent=e,document.getElementById(`diff-sql-title`).textContent=s?`Rollback SQL (Down)`:`Migration SQL (Up)`,document.getElementById(`sql-direction-label`).textContent=s?`Migration (Up)`:`Rollback (Down)`}O.onclick=()=>{s=!s,R()},k.onclick=async()=>{let e=D.textContent;try{await navigator.clipboard.writeText(e);let t=k.querySelector(`span:last-child`),n=t.textContent;t.textContent=`Copied!`,setTimeout(()=>{t.textContent=n},1800)}catch{alert(`Failed to copy to clipboard.`)}},A.onclick=()=>{let e=D.textContent,t=new Blob([e],{type:`application/sql`}),n=URL.createObjectURL(t),r=document.createElement(`a`);r.href=n,r.download=`migration_${new Date().toISOString().replace(/[:.]/g,`-`)}.sql`,document.body.appendChild(r),r.click(),document.body.removeChild(r),URL.revokeObjectURL(n)},j.onclick=async()=>{let e=D.textContent;if(!(!e||!o?.diff?.hasChanges)&&confirm(`Are you sure you want to execute this ${s?`ROLLBACK`:`MIGRATION`} script against the current database?\n\nThis will execute DDL statements immediately!`)){j.disabled=!0,j.innerHTML=`<span class="material-symbols-outlined spin" style="font-size: 16px;">sync</span><span>Applying...</span>`;try{let t=await _(e);t.success?(alert(`✔ Migration applied successfully to the database!`),await r(),i.remove(),window.renderSchemaGrid&&window.location.reload()):alert(`✘ Migration execution failed: ${t.error}`)}catch(e){alert(`✘ Migration failed: ${e.message}`)}finally{j.disabled=!1,j.innerHTML=`<span class="material-symbols-outlined" style="font-size: 16px;">bolt</span><span>Apply Migration</span>`}}}}async function rt(e,t,n=null){document.querySelectorAll(`.table-btn`).forEach(e=>e.classList.remove(`active`)),t&&t.classList.add(`active`);let r=document.getElementById(`table-name`);r&&(r.textContent=e+` (Schema)`);let i=n||document.getElementById(`main-content`);i.innerHTML=`<div class='p-6'>Loading Schema...</div>`;try{let[r,a]=await Promise.all([c(e),l(e)]);if(r.success&&r.data){let o=r.data,s=a.success&&a.data?a.data:[];window.SchemaGrid={schema:o,indexes:s,pendingEdits:{},pendingIndexEdits:{added:[],dropped:[]},pendingInserts:[{}],pendingDeletes:new Set,selectedCell:null,history:[],currentTransaction:null,sortState:{colKey:null,asc:!0},filterText:``,isResizing:!1,selection:{startRow:-1,startCol:-1,endRow:-1,endCol:-1,isDragging:!1}},window.TableStates&&window.TableStates[e]&&(window.TableStates[e].schemaGrid=window.SchemaGrid),i.innerHTML=`
|
|
1412
|
+
<div class="toolbar">
|
|
1413
|
+
<div class="filter-bar-container" style="max-width: 320px; position: relative;">
|
|
1414
|
+
<div class="filter-bar-input-box" id="schema-search-box-${e}">
|
|
1415
|
+
<span class="material-symbols-outlined filter-bar-search-icon">search</span>
|
|
1416
|
+
<input
|
|
1417
|
+
type="text"
|
|
1418
|
+
id="schema-search-val-${e}"
|
|
1419
|
+
class="filter-bar-input"
|
|
1420
|
+
placeholder="Search name or type..."
|
|
1421
|
+
value="${window.SchemaGrid.filterText}"
|
|
1422
|
+
autocomplete="off"
|
|
1423
|
+
spellcheck="false"
|
|
1424
|
+
/>
|
|
1425
|
+
<button
|
|
1426
|
+
type="button"
|
|
1427
|
+
id="btn-clear-schema-search-${e}"
|
|
1428
|
+
class="filter-bar-clear-btn ${window.SchemaGrid.filterText?``:`hidden`}"
|
|
1429
|
+
title="Clear search"
|
|
1430
|
+
>
|
|
1431
|
+
<span class="material-symbols-outlined">close</span>
|
|
1432
|
+
</button>
|
|
1433
|
+
</div>
|
|
1434
|
+
<div class="schema-autocomplete-popover hidden" id="schema-search-popover-${e}"></div>
|
|
1435
|
+
</div>
|
|
1436
|
+
<div class="flex-1"></div>
|
|
1437
|
+
<button type="button" id="btn-export-orm-${e}" class="header-btn secondary" style="height: 30px; font-size: 12px; gap: 6px; padding: 0 12px;" title="Export Prisma / Drizzle ORM Schema">
|
|
1438
|
+
<span class="material-symbols-outlined" style="font-size: 16px; color: var(--color-primary);">bolt</span>
|
|
1439
|
+
<span>Export ORM</span>
|
|
1440
|
+
</button>
|
|
1441
|
+
<button type="button" id="btn-schema-diff-${e}" class="header-btn secondary" style="height: 30px; font-size: 12px; gap: 6px; padding: 0 12px;" title="Compare Schemas & Generate Migration SQL">
|
|
1442
|
+
<span class="material-symbols-outlined" style="font-size: 16px; color: #10b981;">compare_arrows</span>
|
|
1443
|
+
<span>Schema Diff</span>
|
|
1444
|
+
</button>
|
|
1445
|
+
<button id="btn-refresh-schema-${e}" class="refresh-btn" title="Refresh Schema (F5)"><span class="material-symbols-outlined">refresh</span></button>
|
|
1446
|
+
</div>
|
|
1447
|
+
<div id="schema-grid-container-${e}" class="table-container"></div>
|
|
1448
|
+
`,window.renderSchemaGrid=()=>{let t=document.getElementById(`schema-grid-container-${e}`);if(!t)return;let n=[...window.SchemaGrid.schema];if(window.SchemaGrid.filterText){let e=window.SchemaGrid.filterText.toLowerCase();n=n.filter(t=>t.name&&t.name.toLowerCase().includes(e)||t.type&&t.type.toLowerCase().includes(e))}if(window.SchemaGrid.sortState.colKey&&n.sort((e,t)=>{let n=window.SchemaGrid.sortState.colKey,r=e[n],i=t[n];return r??=``,i??=``,r<i?window.SchemaGrid.sortState.asc?-1:1:r>i?window.SchemaGrid.sortState.asc?1:-1:0}),n.length===0&&window.SchemaGrid.filterText){t.innerHTML=`
|
|
1449
|
+
<div class="table-empty-container">
|
|
1450
|
+
<div class="table-empty-card">
|
|
1451
|
+
<div class="table-empty-icon-wrap">
|
|
1452
|
+
<span class="material-symbols-outlined table-empty-icon">search_off</span>
|
|
1453
|
+
</div>
|
|
1454
|
+
<h3 class="table-empty-title">No matching columns found</h3>
|
|
1455
|
+
<p class="table-empty-desc">No columns matched the filter condition "${window.SchemaGrid.filterText}".</p>
|
|
1456
|
+
<div class="table-empty-actions">
|
|
1457
|
+
<button type="button" class="table-empty-btn-primary" id="btn-empty-clear-schema-filter-${e}">
|
|
1458
|
+
<span class="material-symbols-outlined icon-16">filter_alt_off</span>
|
|
1459
|
+
<span>Clear Filter</span>
|
|
1460
|
+
</button>
|
|
1461
|
+
</div>
|
|
1462
|
+
</div>
|
|
1463
|
+
</div>
|
|
1464
|
+
`;let n=document.getElementById(`btn-empty-clear-schema-filter-${e}`);n&&(n.onclick=()=>{window.SchemaGrid.filterText=``;let t=document.getElementById(`schema-search-val-${e}`);t&&(t.value=``);let n=document.getElementById(`btn-clear-schema-search-${e}`);n&&n.classList.add(`hidden`),window.renderSchemaGrid()});return}let r=[{key:`name`,label:`Column Name`},{key:`type`,label:`Data Type`},{key:`isPk`,label:`Key`},{key:`nullable`,label:`Nullable`},{key:`isUnique`,label:`Unique`},{key:`defaultValue`,label:`Default Value`},{key:`indexing`,label:`Indexing`}],i=r.map(e=>e.key),a=`<table class="data-table" id="schema-grid-table-${e}"><thead><tr><th class="row-header table-corner-header" id="th-schema-row-header-${e}" title="Click to select all">#</th>`;r.forEach(e=>{if(e.key===`indexing`){a+=`<th class="th-indexing" data-col-key="indexing">
|
|
1465
|
+
<div class="th-content-wrapper">
|
|
1466
|
+
<span class="th-col-name">${e.label}</span>
|
|
1467
|
+
</div>
|
|
1468
|
+
</th>`;return}let t=window.SchemaGrid.sortState.colKey===e.key,n=`keyboard_arrow_down`;t&&(n=window.SchemaGrid.sortState.asc?`arrow_upward`:`arrow_downward`),a+=`
|
|
1469
|
+
<th class="sortable ${t?`sorted`:``}" data-col-key="${e.key}" title="Column actions">
|
|
1470
|
+
<div class="th-content-wrapper">
|
|
1471
|
+
<div class="th-main-group">
|
|
1472
|
+
<span class="th-col-name">${e.label}</span>
|
|
1473
|
+
</div>
|
|
1474
|
+
<div class="th-actions-group">
|
|
1475
|
+
<button type="button" class="th-menu-trigger ${t?`is-sorted`:``}" title="Column actions">
|
|
1476
|
+
<span class="material-symbols-outlined th-chevron-icon" style="font-size: 14px;">${n}</span>
|
|
1477
|
+
</button>
|
|
1478
|
+
</div>
|
|
1479
|
+
</div>
|
|
1480
|
+
</th>
|
|
1481
|
+
`}),a+=`</tr></thead><tbody>`,n&&n.length>0&&n.forEach((e,t)=>{a+=`<tr><td class="row-header" data-row-idx="${t}">${t+1}</td>`,i.forEach((r,i)=>{if(r===`indexing`){if(t===0){let e=(n?n.length:0)+1,t=window.SchemaGrid.indexes?.length||0,r=window.SchemaGrid.pendingIndexEdits?.added?.length||0,i=window.SchemaGrid.pendingIndexEdits?.dropped?.length||0,o=t+r-i;a+=`<td class="manage-indexes-cell" rowspan="${e}">
|
|
1482
|
+
<div class="manage-indexes-wrapper">
|
|
1483
|
+
<button type="button" class="btn-manage-indexes" title="Open Index Manager">
|
|
1484
|
+
<span class="material-symbols-outlined">key</span>
|
|
1485
|
+
<span>Manage Indexes</span>
|
|
1486
|
+
<span class="index-count-pill">${Math.max(0,o)}</span>
|
|
1487
|
+
</button>
|
|
1488
|
+
</div>
|
|
1489
|
+
</td>`}return}let o=``,s=``;if(r===`name`)s=String(e.name||``).replace(/"/g,`"`),o=`<span class="cell-text">${String(e.name||``).replace(/</g,`<`)}</span>`;else if(r===`type`){s=String(e.type||``).replace(/"/g,`"`);let t=e.enumValues&&e.enumValues.length>0?` <span class="badge-enum" style="font-size: 10px; padding: 1px 5px; border-radius: 4px; background: rgba(59, 130, 246, 0.15); color: var(--color-primary); border: 1px solid rgba(59, 130, 246, 0.3); margin-left: 4px; cursor: pointer;" title="Configured Enum values: ${e.enumValues.join(`, `)}">ENUM</span>`:``;o=`<span class="schema-type-mono">${String(e.type||``).replace(/</g,`<`)}</span>${t}`}else if(r===`isPk`){let t=!!e.isPk,n=!!e.fkTarget;if(t&&n){let t=e.fkTarget.onDelete&&e.fkTarget.onDelete!==`NO ACTION`?` (${e.fkTarget.onDelete})`:``,n=[e.fkTarget.onDelete?`ON DELETE ${e.fkTarget.onDelete}`:``,e.fkTarget.onUpdate?`ON UPDATE ${e.fkTarget.onUpdate}`:``].filter(Boolean).join(`, `),r=`Primary Foreign Key (Composite Key referencing ${e.fkTarget.table}.${e.fkTarget.column}${n?` [${n}]`:``})`;s=`PFK: ${e.fkTarget.table}.${e.fkTarget.column}${t}`,o=`<span class="col-badge badge-pfk" title="${r}">PFK → ${e.fkTarget.table}.${e.fkTarget.column}${t}</span>`}else if(t)s=`PK`,o=`<span class="col-badge badge-pk">PK</span>`;else if(n){let t=e.fkTarget.onDelete&&e.fkTarget.onDelete!==`NO ACTION`?` (${e.fkTarget.onDelete})`:``,n=[e.fkTarget.onDelete?`ON DELETE ${e.fkTarget.onDelete}`:``,e.fkTarget.onUpdate?`ON UPDATE ${e.fkTarget.onUpdate}`:``].filter(Boolean).join(`, `),r=`References ${e.fkTarget.table}.${e.fkTarget.column}${n?` [${n}]`:``}`;s=`FK: ${e.fkTarget.table}.${e.fkTarget.column}${t}`,o=`<span class="col-badge badge-fk" title="${r}">FK → ${e.fkTarget.table}.${e.fkTarget.column}${t}</span>`}else s=`-`,o=`<span class="text-soft">-</span>`}else if(r===`nullable`){let t=!!e.isPk,n=!t&&!!e.nullable;s=n?`Yes`:`No`,o=t?`<span class="badge-bool bool-false badge-locked" title="Primary Key cannot be nullable"><span class="material-symbols-outlined" style="font-size: 11px;">lock</span> No</span>`:n?`<span class="badge-bool bool-true">Yes</span>`:`<span class="badge-bool bool-false">No</span>`}else if(r===`isUnique`){let t=!!e.isPk,n=t||!!e.isUnique;s=n?`Yes`:`No`,o=t?`<span class="badge-unique-pk" title="Primary Key is always unique"><span class="material-symbols-outlined" style="font-size: 11px;">lock</span> Yes</span>`:n?`<span class="badge-unique">Yes</span>`:`<span class="text-soft">-</span>`}else if(r===`defaultValue`){let t=e.defaultValue;typeof t==`string`&&t.startsWith(`'''`)&&t.endsWith(`'''`)&&(t=t.slice(2,-2)),s=String(t??``).replace(/"/g,`"`),o=t!=null&&t!==``?`<span class="cell-text text-soft">${String(t).replace(/</g,`<`)}</span>`:`<span class="text-soft">-</span>`}a+=`<td class="data-cell" data-row-idx="${t}" data-col-idx="${i}" data-pk="${e.name}" data-col-key="${r}" data-original="${s}">${o}</td>`}),a+=`</tr>`}),a+=`<tr class="ghost-row-tr">`;let o=n?n.length:0;a+=`<td class="row-header" data-row-idx="${o}">*</td>`,i.forEach((e,t)=>{e!==`indexing`&&(a+=`<td class="data-cell ghost-row" data-row-idx="${o}" data-col-idx="${t}" data-insert-index="0" data-col-key="${e}">${t===0?`<span class="ghost-cell-hint">+ Add Column</span>`:``}</td>`)}),a+=`</tr></tbody></table>`,t.innerHTML=a,document.querySelectorAll(`#schema-grid-table-${e} th.sortable`).forEach(e=>{let t=e.dataset.colKey,n=r.find(e=>e.key===t),i=()=>Object.keys(window.SchemaGrid.pendingEdits||{}).length>0||(window.SchemaGrid.pendingInserts||[]).length>1||window.SchemaGrid.pendingDeletes&&window.SchemaGrid.pendingDeletes.size>0||(window.SchemaGrid.pendingIndexEdits?.added||[]).length>0||(window.SchemaGrid.pendingIndexEdits?.dropped||[]).length>0?(window.showToast?window.showToast(`You have unsaved changes! Please press Ctrl+S to save them before sorting.`,`warning`):alert(`You have unsaved changes! Please press Ctrl+S to save them before sorting.`),!1):!0,a=e=>{i()&&(window.SchemaGrid.sortState.colKey=t,window.SchemaGrid.sortState.asc=e,window.SchemaGrid.selection={startRow:-1,startCol:-1,endRow:-1,endCol:-1,isDragging:!1},window.renderSchemaGrid(),window.showToast&&window.showToast(`Sorted by ${n?n.label:t} (${e?`Ascending`:`Descending`})`,`info`))},o=()=>{i()&&(window.SchemaGrid.sortState.colKey=null,window.SchemaGrid.sortState.asc=!0,window.SchemaGrid.selection={startRow:-1,startCol:-1,endRow:-1,endCol:-1,isDragging:!1},window.renderSchemaGrid(),window.showToast&&window.showToast(`Cleared sort on "${n?n.label:t}"`,`info`))},s=r=>{if(window.SchemaGrid.isResizing||r.target.closest(`.resizer`))return;let i=window.SchemaGrid.sortState.colKey===t,s=i&&window.SchemaGrid.sortState.asc,c=[{icon:`arrow_upward`,label:`Sort Ascending`,checked:i&&s,action:()=>a(!0)},{icon:`arrow_downward`,label:`Sort Descending`,checked:i&&!s,action:()=>a(!1)}];i&&c.push({icon:`restart_alt`,label:`Clear Sort`,action:()=>o()}),c.push(`divider`,{icon:`content_copy`,label:`Copy name`,action:async()=>{let e=n?n.label:t;try{await navigator.clipboard.writeText(e)}catch{let t=document.createElement(`textarea`);t.value=e,document.body.appendChild(t),t.select(),document.execCommand(`copy`),t.remove()}window.showToast&&window.showToast(`Copied "${e}" to clipboard!`,`success`)}});let l=e.querySelector(`.th-menu-trigger`),u=e.getBoundingClientRect(),d=r&&r.type===`contextmenu`,f=u.left,p=u.bottom+4;if(l){let e=l.getBoundingClientRect();f=e.left,p=e.bottom+4}d&&r&&typeof r.clientX==`number`&&(f=r.clientX,p=r.clientY),A({clientX:f,clientY:p,preventDefault:()=>{},stopPropagation:()=>{}},c)};e.onclick=s,e.oncontextmenu=e=>{e.preventDefault(),s(e)},ve(e,window.SchemaGrid)}),_e(t,`schema-grid-table-${e}`,window.SchemaGrid,i.length-1),et(t,i);let s=document.getElementById(`th-schema-row-header-${e}`);s&&s.addEventListener(`click`,t=>{t.preventDefault(),t.stopPropagation(),ge(`schema-grid-table-${e}`,window.SchemaGrid,i.length-1)})},window.renderSchemaGrid();let c=document.getElementById(`schema-search-val-${e}`),l=document.getElementById(`btn-clear-schema-search-${e}`),u=document.getElementById(`schema-search-popover-${e}`),d=-1,f=[],p=()=>{!l||!c||(c.value.trim()===``?l.classList.add(`hidden`):l.classList.remove(`hidden`))},m=()=>{u&&(u.classList.add(`hidden`),u.innerHTML=``),d=-1,f=[]},h=e=>String(e).replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`),g=(e,t)=>{if(!t)return h(e);let n=e.toLowerCase(),r=t.toLowerCase(),i=n.indexOf(r);return i===-1?h(e):`${h(e.slice(0,i))}<span class="schema-autocomplete-match-highlight">${h(e.slice(i,i+t.length))}</span>${h(e.slice(i+t.length))}`},_=e=>{c&&(c.value=e,window.SchemaGrid.filterText=e,p(),window.SchemaGrid.selection={startRow:-1,startCol:-1,endRow:-1,endCol:-1,isDragging:!1},window.renderSchemaGrid(),m(),c.focus())},v=()=>{if(!u||!c)return;let e=c.value.trim().toLowerCase(),t=window.SchemaGrid?.schema||[],n=Array.from(new Set(t.map(e=>e.name).filter(Boolean))),r=Array.from(new Set(t.map(e=>e.type).filter(Boolean))),i=e?n.filter(t=>t.toLowerCase().includes(e)):n.slice(0,6),a=e?r.filter(t=>t.toLowerCase().includes(e)):r.slice(0,4);if(f=[...i.map(e=>({text:e,type:`column`,icon:`view_column`,badge:`Column`})),...a.map(e=>({text:e,type:`type`,icon:`data_object`,badge:`Type`}))],f.length===0){u.innerHTML=`
|
|
1490
|
+
<div class="schema-autocomplete-empty">
|
|
1491
|
+
<span>No matching columns or data types</span>
|
|
1492
|
+
</div>
|
|
1493
|
+
`,u.classList.remove(`hidden`);return}d=-1;let o=`<div class="schema-autocomplete-scroll">`;i.length>0&&(o+=`<div class="schema-autocomplete-section-title">Columns (${i.length})</div>`,i.forEach(t=>{let n=f.findIndex(e=>e.text===t&&e.type===`column`);o+=`
|
|
1494
|
+
<div class="schema-autocomplete-item" data-suggestion-idx="${n}">
|
|
1495
|
+
<div class="schema-autocomplete-item-left">
|
|
1496
|
+
<span class="material-symbols-outlined schema-autocomplete-item-icon">view_column</span>
|
|
1497
|
+
<span>${g(t,e)}</span>
|
|
1498
|
+
</div>
|
|
1499
|
+
<span class="schema-autocomplete-type-badge">Column</span>
|
|
1500
|
+
</div>
|
|
1501
|
+
`})),a.length>0&&(o+=`<div class="schema-autocomplete-section-title">Data Types (${a.length})</div>`,a.forEach(t=>{let n=f.findIndex(e=>e.text===t&&e.type===`type`);o+=`
|
|
1502
|
+
<div class="schema-autocomplete-item" data-suggestion-idx="${n}">
|
|
1503
|
+
<div class="schema-autocomplete-item-left">
|
|
1504
|
+
<span class="material-symbols-outlined schema-autocomplete-item-icon">data_object</span>
|
|
1505
|
+
<span>${g(t,e)}</span>
|
|
1506
|
+
</div>
|
|
1507
|
+
<span class="schema-autocomplete-type-badge">Type</span>
|
|
1508
|
+
</div>
|
|
1509
|
+
`})),o+=`</div>`,u.innerHTML=o,u.classList.remove(`hidden`),u.querySelectorAll(`.schema-autocomplete-item`).forEach(e=>{e.addEventListener(`mousedown`,t=>{t.preventDefault();let n=parseInt(e.dataset.suggestionIdx,10);f[n]&&_(f[n].text)})})},y=()=>{u&&u.querySelectorAll(`.schema-autocomplete-item`).forEach((e,t)=>{t===d?(e.classList.add(`active`),e.scrollIntoView({block:`nearest`})):e.classList.remove(`active`)})};c&&(c.addEventListener(`input`,e=>{window.SchemaGrid.filterText=e.target.value,p(),window.SchemaGrid.selection={startRow:-1,startCol:-1,endRow:-1,endCol:-1,isDragging:!1},window.renderSchemaGrid(),v()}),c.addEventListener(`focus`,()=>{v()}),c.addEventListener(`keydown`,e=>{u&&!u.classList.contains(`hidden`)&&f.length>0&&(e.key===`ArrowDown`?(e.preventDefault(),d=(d+1)%f.length,y()):e.key===`ArrowUp`?(e.preventDefault(),d=(d-1+f.length)%f.length,y()):e.key===`Enter`?d>=0&&f[d]&&(e.preventDefault(),_(f[d].text)):e.key===`Escape`&&(e.preventDefault(),m()))}));let b=c?.closest(`.filter-bar-input-box`);b&&c&&b.addEventListener(`click`,e=>{e.target.closest(`.filter-bar-clear-btn`)||c.focus()}),l&&(l.onclick=()=>{c&&(c.value=``,window.SchemaGrid.filterText=``,p(),m(),window.renderSchemaGrid(),c.focus())}),document.addEventListener(`click`,e=>{u&&!u.contains(e.target)&&c&&!c.contains(e.target)&&m()});let x=document.getElementById(`btn-export-orm-${e}`);x&&(x.onclick=()=>tt(e));let S=document.getElementById(`btn-schema-diff-${e}`);S&&(S.onclick=()=>nt());let C=document.getElementById(`btn-refresh-schema-${e}`);C&&(C.onclick=()=>{(Object.keys(window.SchemaGrid.pendingEdits).length>0||window.SchemaGrid.pendingInserts.length>1||window.SchemaGrid.pendingDeletes.size>0||window.SchemaGrid.pendingIndexEdits.added.length>0||window.SchemaGrid.pendingIndexEdits.dropped.length>0)&&!confirm(`You have unsaved changes. Are you sure you want to refresh and discard them?`)||(window.SchemaGrid.pendingEdits={},window.SchemaGrid.pendingInserts=[{}],window.SchemaGrid.pendingDeletes.clear(),window.SchemaGrid.pendingIndexEdits={added:[],dropped:[]},window.SchemaGrid.history=[],window.SchemaGrid.currentTransaction=null,window.updateSidebarDirtyState?.(),rt(e,t,n))})}else i.innerHTML=`<div class="p-6 text-error">Error: ${r.error}</div>`}catch(e){i.innerHTML=`<div class="p-6 text-error">Failed to load schema: ${e.message}</div>`}}var it=()=>localStorage.getItem(`drixio_safe_mode_disabled`)!==`true`,at=e=>{e?localStorage.removeItem(`drixio_safe_mode_disabled`):localStorage.setItem(`drixio_safe_mode_disabled`,`true`),window.dispatchEvent(new CustomEvent(`drixio-safemode-changed`,{detail:{enabled:e}}))},ot=async e=>{if(!e)return{isDangerous:!1};try{let t=await(await fetch(`/api/query/analyze`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({sql:e})})).json();return t.success?{isDangerous:t.isDangerous,type:t.type,title:t.title,description:t.description,sql:t.sql}:{isDangerous:!1}}catch(e){return console.error(e),{isDangerous:!1}}},st=({dangerInfo:e,fullSql:t,onConfirm:n,onCancel:r})=>{let i=document.getElementById(`safe-mode-modal`);i&&i.remove();let a=document.createElement(`div`);a.id=`safe-mode-modal`,a.className=`modal-overlay`;let o=(e?.sql||t||``).trim();a.innerHTML=`
|
|
1510
|
+
<div class="modal-container safe-modal-container">
|
|
1511
|
+
<div class="modal-header">
|
|
1512
|
+
<div class="modal-title-wrap">
|
|
1513
|
+
<div class="modal-icon-badge warning">
|
|
1514
|
+
<span class="material-symbols-outlined">warning</span>
|
|
1515
|
+
</div>
|
|
1516
|
+
<div class="modal-title-col">
|
|
1517
|
+
<div class="modal-title-row">
|
|
1518
|
+
<h3 class="modal-title-text">${e.title||`Destructive Operation Warning`}</h3>
|
|
1519
|
+
<span class="modal-title-badge" style="color: var(--color-error); border-color: rgba(239, 68, 68, 0.3);">${e.type||`HIGH RISK`}</span>
|
|
1520
|
+
</div>
|
|
1521
|
+
<p class="modal-subtitle-text">Destructive queries require confirmation under Safe Mode</p>
|
|
1522
|
+
</div>
|
|
1523
|
+
</div>
|
|
1524
|
+
<button id="safe-modal-close-btn" class="modal-close-btn" title="Cancel (Esc)">
|
|
1525
|
+
<span class="material-symbols-outlined">close</span>
|
|
1526
|
+
</button>
|
|
1527
|
+
</div>
|
|
1528
|
+
|
|
1529
|
+
<div class="safe-modal-body">
|
|
1530
|
+
<p class="safe-modal-desc">${e.description}</p>
|
|
1531
|
+
|
|
1532
|
+
<div class="safe-sql-preview-label">Statement to be executed:</div>
|
|
1533
|
+
<div class="safe-sql-preview">
|
|
1534
|
+
<code>${o.replace(/</g,`<`).replace(/>/g,`>`)}</code>
|
|
1535
|
+
</div>
|
|
1536
|
+
|
|
1537
|
+
<div class="safe-modal-preference">
|
|
1538
|
+
<label class="safe-checkbox-label">
|
|
1539
|
+
<input type="checkbox" id="safe-dont-warn-checkbox" />
|
|
1540
|
+
<span class="safe-checkbox-text">不再提示 (Don't warn me again)</span>
|
|
1541
|
+
</label>
|
|
1542
|
+
<span class="safe-preference-hint">You can re-enable Safe Mode anytime from the Console toolbar.</span>
|
|
1543
|
+
</div>
|
|
1544
|
+
</div>
|
|
1545
|
+
|
|
1546
|
+
<div class="safe-modal-footer">
|
|
1547
|
+
<button type="button" id="safe-modal-cancel-btn" class="btn-secondary">
|
|
1548
|
+
Cancel
|
|
1549
|
+
</button>
|
|
1550
|
+
<button type="button" id="safe-modal-confirm-btn" class="safe-btn-confirm">
|
|
1551
|
+
<span class="material-symbols-outlined" style="font-size: 16px;">gavel</span>
|
|
1552
|
+
<span>Confirm & Run</span>
|
|
1553
|
+
</button>
|
|
1554
|
+
</div>
|
|
1555
|
+
</div>
|
|
1556
|
+
`,document.body.appendChild(a);let s=()=>{document.removeEventListener(`keydown`,c),a.remove()},c=e=>{e.key===`Escape`&&(s(),r&&r())};document.addEventListener(`keydown`,c),a.addEventListener(`click`,e=>{e.target===a&&(s(),r&&r())}),document.getElementById(`safe-modal-close-btn`).onclick=()=>{s(),r&&r()},document.getElementById(`safe-modal-cancel-btn`).onclick=()=>{s(),r&&r()},document.getElementById(`safe-modal-confirm-btn`).onclick=()=>{document.getElementById(`safe-dont-warn-checkbox`)?.checked&&(at(!1),window.showToast&&window.showToast(`Safe Mode disabled. Future queries will run without confirmation.`,`info`)),s(),n&&n()}},ct=0;function lt(){window.AppState.consoleMessages=[],dt()}function ut(e){window.AppState.consoleMessages||(window.AppState.consoleMessages=[]);let t=new Date,n=t.toTimeString().split(` `)[0]+`.`+String(t.getMilliseconds()).padStart(3,`0`),r={id:`msg-`+Date.now()+`-`+Math.random().toString(36).substring(2,6),time:n,...e};window.AppState.consoleMessages.push(r),window.AppState.consoleMessages.length>200&&window.AppState.consoleMessages.shift(),dt()}function dt(){let e=document.getElementById(`console-messages-content`);if(!e)return;let t=window.AppState.consoleMessages||[];if(t.length===0){e.innerHTML=`
|
|
1557
|
+
<div class="messages-empty-state">
|
|
1558
|
+
<span class="material-symbols-outlined messages-empty-icon">info</span>
|
|
1559
|
+
<p class="messages-empty-text">No messages yet. Query execution logs, rows affected, and error details will appear here.</p>
|
|
1560
|
+
</div>
|
|
1561
|
+
`;return}e.innerHTML=t.map(e=>{let t=e.type||`info`;return`
|
|
1562
|
+
<div class="messages-log-entry ${t}">
|
|
1563
|
+
<div class="messages-log-header">
|
|
1564
|
+
<span class="messages-badge badge-${t}">${(e.type||`INFO`).toUpperCase()}</span>
|
|
1565
|
+
<span class="messages-time">${e.time}</span>
|
|
1566
|
+
<span class="messages-title">${e.title||``}</span>
|
|
1567
|
+
${e.ms?`<span class="messages-duration">${e.ms}ms</span>`:``}
|
|
1568
|
+
</div>
|
|
1569
|
+
${e.sql?`<pre class="messages-sql">${e.sql.trim()}</pre>`:``}
|
|
1570
|
+
${e.details?`<div class="messages-detail">${e.details}</div>`:``}
|
|
1571
|
+
</div>
|
|
1572
|
+
`}).join(``);let n=document.getElementById(`console-messages-body`);n&&(n.scrollTop=n.scrollHeight)}function ft(e,t){if(!t)return;let n=document.getElementById(`results-meta-count`),r=document.getElementById(`results-export-wrapper`);if(!e){n&&(n.textContent=`Ready to run query`),r&&(r.style.display=`none`),t.innerHTML=`
|
|
1573
|
+
<div class="console-welcome-msg" id="console-welcome-msg">
|
|
1574
|
+
<div class="console-welcome-icon-box">
|
|
1575
|
+
<span class="material-symbols-outlined">terminal</span>
|
|
1576
|
+
</div>
|
|
1577
|
+
<h2 class="console-welcome-heading">Drixio SQL Workstation</h2>
|
|
1578
|
+
<p class="console-welcome-sub">Write and execute queries with multi-tab support, syntax highlighting, and instant tabular results.</p>
|
|
1579
|
+
|
|
1580
|
+
<div class="console-shortcuts-hint">
|
|
1581
|
+
<span class="shortcut-pill"><kbd>Ctrl</kbd> + <kbd>Enter</kbd> Run</span>
|
|
1582
|
+
<span class="shortcut-pill"><kbd>Ctrl</kbd> + <kbd>S</kbd> Save</span>
|
|
1583
|
+
<span class="shortcut-pill"><kbd>Tab</kbd> Indent</span>
|
|
1584
|
+
</div>
|
|
1585
|
+
</div>
|
|
1586
|
+
`;return}if(e.type===`executing`){n&&(n.innerHTML=`<span class="material-symbols-outlined animate-spin icon-12">sync</span> Executing query...`),r&&(r.style.display=`none`),t.innerHTML=`
|
|
1587
|
+
<div class="console-msg-executing">
|
|
1588
|
+
<span class="material-symbols-outlined animate-spin icon-20">sync</span>
|
|
1589
|
+
<span>Executing query...</span>
|
|
1590
|
+
</div>
|
|
1591
|
+
`;return}if(e.type===`error`){n&&(n.innerHTML=`<span style="color: var(--color-error);">Error · ${e.ms||0}ms</span>`),r&&(r.style.display=`none`),t.innerHTML=`
|
|
1592
|
+
<div class="console-result-error">
|
|
1593
|
+
<div class="console-result-error-header">
|
|
1594
|
+
<span class="material-symbols-outlined">error</span>
|
|
1595
|
+
<span>Query Execution Failed</span>
|
|
1596
|
+
</div>
|
|
1597
|
+
<pre class="console-result-error-msg">${e.error}</pre>
|
|
1598
|
+
</div>
|
|
1599
|
+
`;return}if(e.type===`connection`){n&&(n.innerHTML=`<span style="color: #10b981;">Connected · ${e.ms}ms</span>`),r&&(r.style.display=`none`),t.innerHTML=`
|
|
1600
|
+
<div class="console-mutation-banner" style="background: rgba(16, 185, 129, 0.08); border-color: rgba(16, 185, 129, 0.25);">
|
|
1601
|
+
<div class="console-mutation-info" style="color: #10b981;">
|
|
1602
|
+
<span class="material-symbols-outlined icon">check_circle</span>
|
|
1603
|
+
<span><b>${e.title}:</b> ${e.detail}</span>
|
|
1604
|
+
<span class="text-soft">·</span>
|
|
1605
|
+
<span class="text-soft">${e.ms}ms</span>
|
|
1606
|
+
</div>
|
|
1607
|
+
</div>
|
|
1608
|
+
`;return}if(e.type===`mutation`){let i=e.affectedRows??0;n&&(n.innerHTML=`Query OK, <b>${i}</b> row${i===1?``:`s`} affected · ${e.ms}ms`),r&&(r.style.display=`none`),t.innerHTML=`
|
|
1609
|
+
<div class="console-mutation-banner">
|
|
1610
|
+
<div class="console-mutation-info">
|
|
1611
|
+
<span class="material-symbols-outlined icon">task_alt</span>
|
|
1612
|
+
<span>Query OK, <b>${i}</b> row${i===1?``:`s`} affected</span>
|
|
1613
|
+
<span class="text-soft">·</span>
|
|
1614
|
+
<span class="text-soft">${e.ms}ms</span>
|
|
1615
|
+
</div>
|
|
1616
|
+
</div>
|
|
1617
|
+
`;return}if(e.type===`table`){let i=e.rows||[],a=e.rowCount||i.length,o=e.ms||`0.0`,s=e.cols||(i.length>0?Object.keys(i[0]):[]);if(n&&(n.innerHTML=`<b>${a}</b> row${a===1?``:`s`}${a>1e3?` (Showing first 1000)`:``} · ${o}ms`),r){r.style.display=`flex`;let e=document.getElementById(`console-export-btn`);e&&(e.onclick=e=>{A(e,[{icon:`description`,label:`Export as CSV`,action:()=>m({rows:i},`csv`)},{icon:`data_object`,label:`Export as JSON`,action:()=>m({rows:i},`json`)}])})}if(i.length===0){t.innerHTML=`
|
|
1618
|
+
<div class="console-mutation-banner">
|
|
1619
|
+
<div class="console-mutation-info">
|
|
1620
|
+
<span class="material-symbols-outlined icon">task_alt</span>
|
|
1621
|
+
<span>Query returned 0 rows · ${o}ms</span>
|
|
1622
|
+
</div>
|
|
1623
|
+
</div>
|
|
1624
|
+
`;return}t.innerHTML=``;let c=document.createElement(`div`);c.className=`table-container console-results-table-container`;let l=document.createElement(`table`);l.className=`data-table`;let u=document.createElement(`thead`),d=document.createElement(`tr`),f=document.createElement(`th`);f.className=`row-header`,f.textContent=`#`,d.appendChild(f),s.forEach(e=>{let t=document.createElement(`th`);t.innerHTML=`
|
|
1625
|
+
<div class="th-content-wrapper">
|
|
1626
|
+
<span class="th-col-name">${e}</span>
|
|
1627
|
+
</div>
|
|
1628
|
+
`,d.appendChild(t)}),u.appendChild(d),l.appendChild(u);let p=document.createElement(`tbody`);i.slice(0,1e3).forEach((e,t)=>{let n=document.createElement(`tr`),r=document.createElement(`td`);r.className=`row-header`,r.textContent=String(t+1),n.appendChild(r),s.forEach(t=>{let r=document.createElement(`td`);r.className=`data-cell data-cell-truncate`;let a=e[t];if(a==null)r.innerHTML=`<em>NULL</em>`;else if(typeof a==`boolean`)r.innerHTML=`<span class="badge-bool ${a?`bool-true`:`bool-false`}">${a?`true`:`false`}</span>`;else{let e=String(a);r.textContent=e,e.length>50&&(r.title=e)}r.ondblclick=async e=>{e.stopPropagation();let t=a==null?`NULL`:String(a);try{await navigator.clipboard.writeText(t),window.showToast&&window.showToast(`Copied cell value`,`info`)}catch{}},r.oncontextmenu=t=>{t.preventDefault(),t.stopPropagation();let n=a==null?`NULL`:String(a);A(t,[{icon:`content_copy`,label:`Copy Cell Value`,action:async()=>{await navigator.clipboard.writeText(n),window.showToast&&window.showToast(`Copied cell value`,`info`)}},{icon:`data_object`,label:`Copy Row as JSON`,action:async()=>{await navigator.clipboard.writeText(JSON.stringify(e,null,2)),window.showToast&&window.showToast(`Copied row as JSON`,`info`)}},`divider`,{icon:`download`,label:`Export All as CSV`,action:()=>m({rows:i},`csv`)}])},n.appendChild(r)}),p.appendChild(n)}),l.appendChild(p),c.appendChild(l),t.appendChild(c);return}if(e.type===`explain`){let i=e.rows||[],a=e.cols||[],o=e.ms||`0.0`;n&&(n.innerHTML=`Execution Plan · <b>${i.length}</b> line${i.length===1?``:`s`} · ${o}ms`),r&&(r.style.display=`none`),t.innerHTML=``;let s=document.createElement(`div`);s.className=`table-container console-results-table-container`;let c=document.createElement(`table`);c.className=`data-table`;let l=document.createElement(`thead`),u=document.createElement(`tr`),d=document.createElement(`th`);d.className=`row-header`,d.textContent=`#`,u.appendChild(d),a.forEach(e=>{let t=document.createElement(`th`);t.textContent=e,u.appendChild(t)}),l.appendChild(u),c.appendChild(l);let f=document.createElement(`tbody`);i.forEach((e,t)=>{let n=document.createElement(`tr`),r=document.createElement(`td`);r.className=`row-header`,r.textContent=String(t+1),n.appendChild(r),a.forEach(t=>{let r=document.createElement(`td`);r.className=`data-cell font-mono text-12`;let i=e[t];r.textContent=i==null?`NULL`:String(i),n.appendChild(r)}),f.appendChild(n)}),c.appendChild(f),s.appendChild(c),t.appendChild(s)}}async function pt(e,t,n,r=!1,i=null){let a=e||(t?t.value.substring(t.selectionStart,t.selectionEnd).trim():``);if(!a&&t&&(a=t.value.trim()),!a)return null;if(a.toLowerCase()===`clear`||a.toLowerCase()===`clear;`)return ft(null,n),null;if(!r&&it()){let e=ot(a);if(e.isDangerous)return st({dangerInfo:e,fullSql:a,onConfirm:()=>{pt(a,t,n,!0,i)}}),null}window.AppState.lastQuery=a,window.AppState.queryHistory||(window.AppState.queryHistory=[]);let o=window.AppState.queryHistory;o.includes(a)||(o.unshift(a),o.length>20&&o.pop(),window.dispatchEvent(new Event(`query-history-updated`))),ct++,ft({type:`executing`},n);try{let e=performance.now(),t=await u(a),r=(performance.now()-e).toFixed(1);if(!t.success){let e={type:`error`,error:t.error,ms:r,sql:a};return ut({type:`error`,title:`Query Execution Failed`,sql:a,ms:r,details:t.error}),ft(e,n),i&&i(e),e}let o=t.data.rows||[],s=o.length,c=o.length===1&&o[0].Result===`Success`&&o[0].AffectedRows!==void 0||s===0&&t.data.affectedRows!==void 0;if(t.data?.connectionChanged){window.showToast&&window.showToast(`Database updated: ${t.data.dbConfig?.dbName||`Connected`}`,`success`),window.initSidebar&&window.initSidebar(!0);let e=t.data.dbConfig?.type&&t.data.dbConfig.type!==`none`;if(window.setStudioConnectionMode&&window.setStudioConnectionMode(!!e),t.data.dbConfig?.type){window.AppState.dbType=t.data.dbConfig.type;let e=document.getElementById(`brand-db-type`);e&&(e.textContent=t.data.dbConfig.type===`none`?`NO DATABASE`:t.data.dbConfig.type.toUpperCase());let n=document.getElementById(`sidebar-db-status-dot`);n&&(t.data.dbConfig.type===`none`?(n.classList.remove(`status-connected`,`status-error`),n.classList.add(`status-warning`)):(n.classList.remove(`status-error`,`status-warning`),n.classList.add(`status-connected`)))}let s=o[0]?.Result||`Connected`,c=o[0]?.Database||o[0]?.Target||o[0]?.Path||`Database ready`,l={type:`connection`,title:s,detail:c,ms:r,sql:a};return ut({type:`info`,title:`Database: ${s}`,sql:a,ms:r,details:c}),ft(l,n),i&&i(l),l}if(/^\s*(CREATE|DROP|ALTER)\s+(TABLE|VIEW)/i.test(a)&&window.initSidebar?.(!0),c){let e=t.data.affectedRows??o[0]?.AffectedRows??0,s={type:`mutation`,affectedRows:e,ms:r,sql:a};return ut({type:`mutation`,title:`Query OK, ${e} row${e===1?``:`s`} affected`,sql:a,ms:r,details:`${e} row(s) updated/deleted/inserted`}),ft(s,n),i&&i(s),s}let l={type:`table`,rows:o,rowCount:s,cols:o.length>0?Object.keys(o[0]):[],ms:r,sql:a};return ut({type:`success`,title:`Query OK, returned ${s} row${s===1?``:`s`}`,sql:a,ms:r,details:`Successfully fetched ${s} row(s) with ${l.cols.length} column(s)`}),ft(l,n),i&&i(l),l}catch(e){let t={type:`error`,error:e.message,ms:`0.0`,sql:a};return ut({type:`error`,title:`Query Exception`,sql:a,ms:`0.0`,details:e.message}),ft(t,n),i&&i(t),t}}async function mt(e,t,n,r=null){let i=e||(t?t.value.substring(t.selectionStart,t.selectionEnd).trim():``);if(!i&&t&&(i=t.value.trim()),!i)return null;ft({type:`executing`},n);try{let e=await h(i);if(!e.success)throw Error(e.error||`Failed to explain query`);let t={type:`explain`,rows:e.data.rows||[],cols:e.data.columns||[],ms:e.data.durationMs,dialect:e.data.dialect,sql:i};return ut({type:`info`,title:`Execution Plan (EXPLAIN)`,sql:i,ms:e.data.durationMs,details:`${t.rows.length} plan node(s) (Dialect: ${t.dialect||`SQL`})`}),ft(t,n),r&&r(t),t}catch(e){let t={type:`error`,error:e.message,ms:`0.0`,sql:i};return ut({type:`error`,title:`EXPLAIN Failed`,sql:i,ms:`0.0`,details:e.message}),ft(t,n),r&&r(t),t}}function ht(e){if(!e)return[];let t=new Set,n=/\{\{\s*([a-zA-Z0-9_]+)\s*\}\}/g,r;for(;(r=n.exec(e))!==null;)r[1]&&t.add(r[1]);let i=/(?:^|[^:]):([a-zA-Z0-9_]+)/g;for(;(r=i.exec(e))!==null;)r[1]&&t.add(r[1]);return Array.from(t)}function gt({snippetId:e=null,defaultTitle:t=``,defaultSql:n=``,defaultDescription:r=``,defaultTags:i=[],onSaved:a=null}){let o=document.getElementById(`snippet-save-modal`);o&&o.remove();let s=document.createElement(`div`);s.id=`snippet-save-modal`,s.className=`modal-overlay`;let c=!!e,l=ht(n);s.innerHTML=`
|
|
1629
|
+
<div class="modal-container snippet-modal-container">
|
|
1630
|
+
<div class="modal-header">
|
|
1631
|
+
<div class="modal-title-wrap">
|
|
1632
|
+
<div class="modal-icon-badge">
|
|
1633
|
+
<span class="material-symbols-outlined">${c?`edit_note`:`bookmark_add`}</span>
|
|
1634
|
+
</div>
|
|
1635
|
+
<div class="modal-title-col">
|
|
1636
|
+
<h3 class="modal-title-text">${c?`Edit Query Snippet`:`Save Query as Snippet`}</h3>
|
|
1637
|
+
<p class="modal-subtitle-text">${c?`Modify snippet details and SQL`:`Save this SQL query to .drixio/snippets.json`}</p>
|
|
1638
|
+
</div>
|
|
1639
|
+
</div>
|
|
1640
|
+
<button type="button" id="close-save-snippet-btn" class="modal-close-btn" title="Close (Esc)" aria-label="Close modal">
|
|
1641
|
+
<span class="material-symbols-outlined">close</span>
|
|
1642
|
+
</button>
|
|
1643
|
+
</div>
|
|
1644
|
+
|
|
1645
|
+
<div class="snippet-modal-body">
|
|
1646
|
+
<div class="snippet-field-group">
|
|
1647
|
+
<div class="snippet-field-label">
|
|
1648
|
+
<span>Title <span style="color: var(--color-error); font-size: 11px;">*</span></span>
|
|
1649
|
+
</div>
|
|
1650
|
+
<input type="text" id="save-snippet-title" class="snippet-input" placeholder="e.g. Active Users by Status" value="${t}" autofocus />
|
|
1651
|
+
</div>
|
|
1652
|
+
|
|
1653
|
+
<div class="snippet-field-group">
|
|
1654
|
+
<div class="snippet-field-label">
|
|
1655
|
+
<span>Description</span>
|
|
1656
|
+
<span class="snippet-field-hint">Optional</span>
|
|
1657
|
+
</div>
|
|
1658
|
+
<input type="text" id="save-snippet-desc" class="snippet-input" placeholder="e.g. Summarizes user counts with role filter" value="${r}" />
|
|
1659
|
+
</div>
|
|
1660
|
+
|
|
1661
|
+
<div class="snippet-field-group">
|
|
1662
|
+
<div class="snippet-field-label">
|
|
1663
|
+
<span>Tags</span>
|
|
1664
|
+
<span class="snippet-field-hint">Comma separated</span>
|
|
1665
|
+
</div>
|
|
1666
|
+
<input type="text" id="save-snippet-tags" class="snippet-input" placeholder="e.g. users, metrics, cleanup" value="${i.join(`, `)}" />
|
|
1667
|
+
</div>
|
|
1668
|
+
|
|
1669
|
+
<div class="snippet-field-group">
|
|
1670
|
+
<div class="snippet-field-label">
|
|
1671
|
+
<span>SQL Query <span style="color: var(--color-error); font-size: 11px;">*</span></span>
|
|
1672
|
+
<div id="save-snippet-param-badges" class="snippet-badges-wrap">
|
|
1673
|
+
${l.map(e=>`<span class="snippet-param-badge">:${e}</span>`).join(``)}
|
|
1674
|
+
</div>
|
|
1675
|
+
</div>
|
|
1676
|
+
<textarea id="save-snippet-sql" class="snippet-textarea" placeholder="SELECT * FROM table...">${n}</textarea>
|
|
1677
|
+
</div>
|
|
1678
|
+
</div>
|
|
1679
|
+
|
|
1680
|
+
<div class="modal-footer">
|
|
1681
|
+
<div class="text-11 text-soft flex items-center gap-1" style="user-select: none;">
|
|
1682
|
+
<span class="material-symbols-outlined" style="font-size: 14px;">keyboard</span>
|
|
1683
|
+
<span>Ctrl + Enter to save</span>
|
|
1684
|
+
</div>
|
|
1685
|
+
<div class="snippet-footer-actions">
|
|
1686
|
+
<button type="button" id="cancel-save-snippet-btn" class="btn-secondary">Cancel</button>
|
|
1687
|
+
<button type="button" id="confirm-save-snippet-btn" class="btn-primary" style="display: inline-flex; align-items: center; gap: 6px;">
|
|
1688
|
+
<span class="material-symbols-outlined" style="font-size: 16px;">check</span>
|
|
1689
|
+
<span id="confirm-save-snippet-btn-text">${c?`Update Snippet`:`Save Snippet`}</span>
|
|
1690
|
+
</button>
|
|
1691
|
+
</div>
|
|
1692
|
+
</div>
|
|
1693
|
+
</div>
|
|
1694
|
+
`,document.body.appendChild(s);let u=()=>{document.removeEventListener(`keydown`,d),s.remove()},d=e=>{e.key===`Escape`&&u(),(e.ctrlKey||e.metaKey)&&e.key===`Enter`&&(e.preventDefault(),_.click())};document.addEventListener(`keydown`,d),s.addEventListener(`click`,e=>{e.target===s&&u()}),document.getElementById(`close-save-snippet-btn`).onclick=u,document.getElementById(`cancel-save-snippet-btn`).onclick=u;let f=document.getElementById(`save-snippet-title`),p=document.getElementById(`save-snippet-desc`),m=document.getElementById(`save-snippet-tags`),h=document.getElementById(`save-snippet-sql`),g=document.getElementById(`save-snippet-param-badges`),_=document.getElementById(`confirm-save-snippet-btn`),v=document.getElementById(`confirm-save-snippet-btn-text`);h.oninput=()=>{let e=ht(h.value);g.innerHTML=e.map(e=>`<span class="snippet-param-badge">:${e}</span>`).join(``)},_.onclick=async()=>{let t=f.value.trim(),n=h.value.trim();if(!n){window.showToast?window.showToast(`SQL query cannot be empty.`,`error`):alert(`SQL query cannot be empty.`),h.focus();return}let r=p.value.trim(),i=m.value.split(`,`).map(e=>e.trim()).filter(Boolean);_.disabled=!0,v&&(v.textContent=`Saving...`);try{let o;o=c?await x(e,{title:t||`Untitled Snippet`,sql:n,description:r,tags:i}):await b({title:t||`Untitled Snippet`,sql:n,description:r,tags:i}),o.success?(window.showToast&&window.showToast(c?`Snippet updated`:`Snippet saved to .drixio/snippets.json`,`success`),a&&a(o.data),u()):window.showToast?window.showToast(`Failed to save snippet: ${o.error}`,`error`):alert(`Failed to save snippet: ${o.error}`)}catch(e){window.showToast?window.showToast(`Error saving snippet: ${e.message}`,`error`):alert(`Error saving snippet: ${e.message}`)}finally{_.disabled=!1,v&&(v.textContent=c?`Update Snippet`:`Save Snippet`)}},f.focus()}function _t(e={}){let{id:t=`modal-${Date.now()}`,className:n=``,icon:r=``,iconColor:i=`primary`,title:a=``,subtitle:o=``,badge:s=``,width:c=`520px`,maxWidth:l=`92vw`,body:u=``,footer:d=null,showCloseBtn:f=!0,closeOnEsc:p=!0,closeOnBackdrop:m=!0,onClose:h=null,onOpen:g=null}=e,_=document.getElementById(t);_&&_.remove();let v=document.createElement(`div`);v.id=t,v.className=`modal-overlay`;let y=[`primary`,`warning`,`error`,`success`].includes(i),b=y?`modal-icon-badge ${i}`:`modal-icon-badge`,x=!y&&i?`color: ${i};`:``;v.innerHTML=`
|
|
1695
|
+
<div class="modal-container ${n}" style="width: ${c}; max-width: ${l};">
|
|
1696
|
+
<div class="modal-header">
|
|
1697
|
+
<div class="modal-title-wrap">
|
|
1698
|
+
${r?`<div class="${b}" style="${x}">
|
|
1699
|
+
<span class="material-symbols-outlined">${r}</span>
|
|
1700
|
+
</div>`:``}
|
|
1701
|
+
<div class="modal-title-col">
|
|
1702
|
+
<div class="modal-title-row">
|
|
1703
|
+
<h3 class="modal-title-text">${a}</h3>
|
|
1704
|
+
${s?`<span class="modal-title-badge">${s}</span>`:``}
|
|
1705
|
+
</div>
|
|
1706
|
+
${o?`<p class="modal-subtitle-text">${o}</p>`:``}
|
|
1707
|
+
</div>
|
|
1708
|
+
</div>
|
|
1709
|
+
${f?`<button type="button" class="modal-close-btn" title="Close (Esc)" aria-label="Close modal">
|
|
1710
|
+
<span class="material-symbols-outlined">close</span>
|
|
1711
|
+
</button>`:``}
|
|
1712
|
+
</div>
|
|
1713
|
+
<div class="modal-body"></div>
|
|
1714
|
+
${d?`<div class="modal-footer"></div>`:``}
|
|
1715
|
+
</div>
|
|
1716
|
+
`;let S=v.querySelector(`.modal-container`),C=v.querySelector(`.modal-header`),w=v.querySelector(`.modal-body`),T=v.querySelector(`.modal-footer`),E=v.querySelector(`.modal-close-btn`);w&&(typeof u==`string`?w.innerHTML=u:u instanceof HTMLElement&&w.appendChild(u)),T&&d&&(typeof d==`string`?T.innerHTML=d:d instanceof HTMLElement&&T.appendChild(d));let D=!1,O=()=>{D||(D=!0,window.removeEventListener(`keydown`,k),v.remove(),typeof h==`function`&&h())},k=e=>{p&&e.key===`Escape`&&(e.preventDefault(),O())};return E&&(E.onclick=e=>{e.stopPropagation(),O()}),m&&(v.onclick=e=>{e.target===v&&O()}),window.addEventListener(`keydown`,k),document.body.appendChild(v),typeof g==`function`&&g(v),{overlay:v,container:S,header:C,body:w,footer:T,close:O,destroy:O,setTitle:e=>{let t=C.querySelector(`.modal-title-text`);t&&(t.textContent=e)},setSubtitle:e=>{let t=C.querySelector(`.modal-subtitle-text`);if(!t&&e){let e=C.querySelector(`.modal-title-col`);e&&(t=document.createElement(`p`),t.className=`modal-subtitle-text`,e.appendChild(t))}t&&(t.textContent=e)}}}function vt(e){return e==null?``:String(e).replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`).replace(/'/g,`'`)}function yt(e){if(e===0||!e)return`0 B`;let t=1024,n=[`B`,`KB`,`MB`,`GB`,`TB`],r=Math.floor(Math.log(e)/Math.log(t));return parseFloat((e/t**r).toFixed(2))+` `+n[r]}function bt(e){if(!e||e<=0)return`N/A`;let t=Math.floor(e/(3600*24)),n=Math.floor(e%(3600*24)/3600),r=Math.floor(e%3600/60),i=``;return t>0&&(i+=`${t}d `),n>0&&(i+=`${n}h `),i+=`${r}m`,i||`< 1m`}var xt=`drixio_query_history`;function St(){try{let e=localStorage.getItem(xt);if(e){let t=JSON.parse(e);if(Array.isArray(t))return t}}catch{}return[]}function Ct(e){try{localStorage.setItem(xt,JSON.stringify(e||[]))}catch(e){console.warn(`Failed to save query history to localStorage:`,e)}}var wt=null;function Tt(e){wt=e,window.AppState.queryHistory=St();let t=document.getElementById(`console-clear-history-btn`);t&&(t.onclick=()=>{confirm(`Clear all recent query history?`)&&(window.AppState.queryHistory=[],Ct([]),Dt())}),window.removeEventListener(`query-history-updated`,Et),window.addEventListener(`query-history-updated`,Et),Dt()}function Et(){Ct(window.AppState.queryHistory),Dt()}function Dt(){let e=document.getElementById(`console-recent-history-list`),t=document.getElementById(`console-clear-history-btn`);if(!e)return;let n=window.AppState.queryHistory||[];if(t&&(n.length>0?t.classList.remove(`hidden`):t.classList.add(`hidden`)),n.length===0){e.innerHTML=`<div class="sidebar-tree-empty">No recent queries</div>`;return}e.innerHTML=n.slice(0,20).map((e,t)=>`
|
|
1717
|
+
<button type="button" class="table-btn query-item-btn history-item" data-idx="${t}" title="${vt(e)}">
|
|
1718
|
+
<div class="table-btn-label">
|
|
1719
|
+
<i class="material-symbols-outlined table-item-icon">history</i>
|
|
1720
|
+
<span class="table-name-text">${vt(e.replace(/\s+/g,` `))}</span>
|
|
1721
|
+
</div>
|
|
1722
|
+
<span class="query-item-delete-btn" title="Remove from history">
|
|
1723
|
+
<span class="material-symbols-outlined">close</span>
|
|
1724
|
+
</span>
|
|
1725
|
+
</button>
|
|
1726
|
+
`).join(``),e.querySelectorAll(`.history-item`).forEach(e=>{let t=parseInt(e.getAttribute(`data-idx`)||`0`,10),r=n[t];if(!r)return;e.onclick=e=>{e.target.closest(`.query-item-delete-btn`)||wt&&wt(r)};let i=e.querySelector(`.query-item-delete-btn`);i&&(i.onclick=e=>{e.stopPropagation(),window.AppState.queryHistory.splice(t,1),Ct(window.AppState.queryHistory),Dt()})})}var Ot=(e,t)=>{let n=window.AppState.lastQuery||`SELECT 1 + 1 AS ping;`,r=[];try{let e=localStorage.getItem(`drixio_sql_tabs`);e&&(r=JSON.parse(e))}catch{r=[]}(!Array.isArray(r)||r.length===0)&&(r=[{id:`tab-1`,title:`Untitled query 1`,sql:n,isDirty:!1,lastResult:null}]);let i=localStorage.getItem(`drixio_active_tab_id`)||r[0].id;r.find(e=>e.id===i)||(i=r[0].id);let a=()=>{try{let e=r.map(e=>{let t=e.lastResult;return t&&t.data&&t.data.rows&&t.data.rows.length>100&&(t={...t,data:{...t.data,rows:t.data.rows.slice(0,100)}}),{id:e.id,title:e.title,sql:e.sql,isDirty:e.isDirty,isPreview:!!e.isPreview,lastResult:t}});localStorage.setItem(`drixio_sql_tabs`,JSON.stringify(e)),localStorage.setItem(`drixio_active_tab_id`,i)}catch(e){console.warn(`Failed to save tabs to localStorage`,e)}},o=()=>r.find(e=>e.id===i)||r[0],s=document.getElementById(`console-tabs-container`);s&&s.addEventListener(`wheel`,e=>{e.deltaY!==0&&(e.preventDefault(),s.scrollLeft+=e.deltaY)},{passive:!1});let c=()=>{s&&(s.innerHTML=``,r.forEach(e=>{let t=e.id===i,n=document.createElement(`div`);n.className=`console-tab ${t?`active`:``} ${e.isPreview?`preview`:``}`,n.dataset.tabId=e.id;let o=document.createElement(`span`);o.className=`material-symbols-outlined console-tab-icon`,o.textContent=`terminal`;let d=document.createElement(`span`);if(d.className=`console-tab-title`,d.textContent=e.title,d.title=e.isPreview?`Preview tab (double click or edit to keep)`:`Double click to rename, right click for options`,n.appendChild(o),n.appendChild(d),e.isDirty){let e=document.createElement(`span`);e.className=`tab-dirty-dot`,e.title=`Unsaved changes`,n.appendChild(e)}if(r.length>1){let t=document.createElement(`button`);t.type=`button`,t.className=`tab-close-btn`,t.title=`Close tab (Ctrl+W)`,t.innerHTML=`<span class="material-symbols-outlined" style="font-size: 14px;">close</span>`,t.onclick=t=>{t.stopPropagation(),f(e.id)},n.appendChild(t)}n.onclick=()=>{i!==e.id&&l(e.id)},n.addEventListener(`mousedown`,t=>{t.button===1&&(t.preventDefault(),t.stopPropagation(),f(e.id))}),n.oncontextmenu=t=>{t.preventDefault(),t.stopPropagation(),A(t,[{icon:`edit`,label:`Rename Tab`,shortcut:`Double Click`,action:()=>{d.dispatchEvent(new MouseEvent(`dblclick`,{bubbles:!0}))}},{icon:`content_copy`,label:`Duplicate Tab`,action:()=>{u(`${e.title} (Copy)`,e.sql)}},`divider`,{icon:`close`,label:`Close Tab`,shortcut:`Ctrl+W`,action:()=>{f(e.id)}},{icon:`close_fullscreen`,label:`Close Other Tabs`,disabled:r.length<=1,action:()=>{r=[e],i=e.id,a(),c()}},{icon:`playlist_remove`,label:`Close Tabs to Right`,disabled:r.indexOf(e)===r.length-1,action:()=>{let t=r.indexOf(e);r=r.slice(0,t+1),r.find(e=>e.id===i)||(i=e.id),a(),c()}}])},d.ondblclick=t=>{t.stopPropagation(),e.isPreview&&(e.isPreview=!1,a());let n=document.createElement(`input`);n.type=`text`,n.className=`console-tab-rename-input`,n.value=e.title,n.onblur=()=>{let t=n.value.trim();t&&(e.title=t),a(),c()},n.onkeydown=e=>{e.key===`Enter`?(e.preventDefault(),n.blur()):e.key===`Escape`&&(e.preventDefault(),c())},d.replaceWith(n),n.focus(),n.select()},s.appendChild(n),t&&setTimeout(()=>{n.scrollIntoView({behavior:`smooth`,block:`nearest`,inline:`nearest`})},0)}))},l=n=>{let r=o();r&&(r.sql=e.value),i=n;let s=o();s&&(e.value=s.sql,P(),v(),ft(s.lastResult,t)),a(),c(),e.focus({preventScroll:!0})},u=(n=null,s=``,l=!1)=>{let u=o();u&&(u.sql=e.value);let d=`tab-${Date.now()}`,f={id:d,title:n||`Untitled query ${r.length+1}`,sql:s||`SELECT * FROM users LIMIT 50;`,isDirty:!1,isPreview:!!l,lastResult:null};r.push(f),i=d,e.value=f.sql,P(),v(),ft(null,t),a(),c(),e.focus({preventScroll:!0})},d=(n,i,s=!1)=>{let d=o(),f=d&&!d.isDirty&&d.title.startsWith(`Untitled query`)&&(d.sql.trim()===``||d.sql.trim()===`SELECT 1 + 1 AS ping;`),p=r.find(e=>e.isPreview);if(s&&p){p.title=n,p.sql=i,p.lastResult=null,p.isDirty=!1,l(p.id);return}f?(d.title=n||d.title,d.sql=i,d.lastResult=null,d.isPreview=!!s,e.value=i,P(),v(),ft(null,t),a(),c(),e.focus({preventScroll:!0})):u(n,i,s)},f=n=>{let s=r.findIndex(e=>e.id===n);if(s===-1)return;if(r.splice(s,1),r.length===0)r.push({id:`tab-${Date.now()}`,title:`Untitled query 1`,sql:`SELECT 1 + 1 AS ping;`,isDirty:!1,lastResult:null}),i=r[0].id;else if(i===n){let e=Math.max(0,s-1);i=r[e].id}let l=o();e.value=l.sql,P(),v(),ft(l.lastResult,t),a(),c(),e.focus({preventScroll:!0})},p=document.getElementById(`console-add-tab-btn`);p&&(p.onclick=()=>u());let m=document.getElementById(`console-new-query-btn`);m&&(m.onclick=()=>u());let h=document.getElementById(`sql-line-numbers`),g=e=>{let t=e.split(`
|
|
1727
|
+
`),n=[],r=[],i=0;for(let e=0;e<t.length;e++){n.push(i);let a=t[e].length;i+=a,e<t.length-1&&(i+=1),r.push(i)}return{lines:t,starts:n,ends:r}},_=()=>{if(!h)return;let t=e.selectionStart,n=e.selectionEnd,{starts:r,ends:i}=g(e.value);h.querySelectorAll(`.sql-line-num`).forEach((e,a)=>{let o=r[a],s=i[a];t!==n&&Math.max(t,o)<Math.min(n,s)?e.classList.add(`selected`):e.classList.remove(`selected`)})},v=()=>{if(!h)return;let t=e.value.split(`
|
|
1728
|
+
`).length,n=``;for(let e=1;e<=t;e++)n+=`<div class="sql-line-num" data-line="${e}">${e}</div>`;h.innerHTML=n,_()};if(e.addEventListener(`scroll`,()=>{h&&(h.scrollTop=e.scrollTop)}),h){let t=!1,n=1,r=t=>{let n=h.getBoundingClientRect(),r=t.clientY-n.top+h.scrollTop-12,i=e.value.split(`
|
|
1729
|
+
`).length,a=Math.floor(r/19.5)+1;return Math.max(1,Math.min(i,a))},i=i=>{if(i.button!==0)return;i.preventDefault();let s=r(i);n=s,t=!0;let{starts:c,ends:l}=g(e.value),u=c[s-1]??0,d=l[s-1]??e.value.length;e.focus({preventScroll:!0}),e.setSelectionRange(u,d),C(),window.addEventListener(`mousemove`,a),window.addEventListener(`mouseup`,o)},a=i=>{if(!t)return;let a=r(i),{starts:o,ends:s}=g(e.value);if(a>=n){let t=o[n-1]??0,r=s[a-1]??e.value.length;e.setSelectionRange(t,r,`forward`)}else{let t=o[a-1]??0,r=s[n-1]??e.value.length;e.setSelectionRange(t,r,`backward`)}C()},o=()=>{t&&(t=!1,window.removeEventListener(`mousemove`,a),window.removeEventListener(`mouseup`,o))};h.addEventListener(`mousedown`,i)}let b=``,x=(e,t)=>{if(!e||!e.includes(`;`))return e.trim();let n=[],r=0,i=!1,a=!1;for(let t=0;t<e.length;t++){let o=e[t],s=t>0?e[t-1]:``;o===`'`&&!a&&s!==`\\`?i=!i:o===`"`&&!i&&s!==`\\`?a=!a:o===`;`&&!i&&!a&&(n.push({start:r,end:t+1,sql:e.slice(r,t+1).trim()}),r=t+1)}if(r<e.length){let t=e.slice(r).trim();t&&n.push({start:r,end:e.length,sql:t})}return n.find(e=>t>=e.start&&t<=e.end)?.sql||n[n.length-1]?.sql||e.trim()},C=()=>{let t=e.selectionStart,n=e.selectionEnd,r=document.getElementById(`run-sql-btn-text`);if(_(),typeof t==`number`&&typeof n==`number`&&t!==n){let i=e.value.substring(t,n).trim();if(i){b=i,r&&(r.textContent=`Run Selection`);return}}b=``,r&&(r.textContent=`Run`)},w=async()=>{let n=b;if(!n){let t=e.selectionStart,r=e.selectionEnd;typeof t==`number`&&typeof r==`number`&&t!==r&&(n=e.value.substring(t,r).trim())}if(!n){let t=e.value.trim();t&&(n=x(e.value,e.selectionStart)||t)}if(n){let r=o();r&&r.isPreview&&(r.isPreview=!1,a(),c());let i=await pt(n,e,t,!1,e=>{r&&(r.lastResult=e,r.isDirty=!1,a(),c())});i&&r&&(r.lastResult=i,r.isDirty=!1,a(),c()),loadAndRenderHistory()}P()},T=document.getElementById(`run-sql-btn`);T&&(T.onclick=()=>w());let E=document.getElementById(`explain-sql-btn`);E&&(E.onclick=async()=>{let n=b;if(!n){let t=e.selectionStart,r=e.selectionEnd;typeof t==`number`&&typeof r==`number`&&t!==r&&(n=e.value.substring(t,r).trim())}if(!n){let t=e.value.trim();t&&(n=x(e.value,e.selectionStart)||t)}if(n){let r=o(),i=await mt(n,e,t,e=>{r&&(r.lastResult=e,a())});i&&r&&(r.lastResult=i,a())}});let D=document.getElementById(`console-safe-toggle-btn`),O=document.getElementById(`console-safe-text`),k=()=>{D&&(it()?(D.className=`console-safe-shield-btn active`,D.title=`Safe Mode: Protection ON (Destructive queries will prompt for confirmation)`,O&&(O.textContent=`Safe Mode ON`)):(D.className=`console-safe-shield-btn disabled`,D.title=`Safe Mode: Protection OFF (Click to re-enable guard)`,O&&(O.textContent=`Safe Mode OFF`)))};D&&(k(),D.onclick=()=>{let e=it();at(!e),k(),window.showToast&&window.showToast(e?`Safe Mode disabled (Guard OFF)`:`Safe Mode enabled (Destructive query guard ON)`,e?`info`:`success`)},window.addEventListener(`drixio-safemode-changed`,k));let j=document.getElementById(`console-save-snippet-btn`),M=()=>{let t=o();gt({defaultTitle:t?.title||`Untitled query`,defaultSql:e.value,onSaved:()=>{t&&(t.isDirty=!1,t.isPreview=!1,a(),c()),Ee()}})};j&&(j.onclick=M);let N=document.getElementById(`sql-highlight-layer`),P=()=>{let t=e.value;if(t===``){N&&(N.innerHTML=``);return}t=t.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`);let n=RegExp(`('.*?'|".*?")|\\b(\\d+)\\b|\\b(${`SELECT.FROM.WHERE.AND.OR.IN.NOT.NULL.IS.ORDER.BY.GROUP.ASC.DESC.LIMIT.OFFSET.JOIN.INNER.LEFT.RIGHT.ON.AS.CREATE.TABLE.DROP.ALTER.INSERT.INTO.VALUES.UPDATE.SET.DELETE.PRAGMA.EXPLAIN.QUERY.PLAN.WITH.UNION.ALL.HAVING.LIKE.BETWEEN.EXISTS.CASE.WHEN.THEN.ELSE.END.CAST.DEFAULT.PRIMARY.KEY.FOREIGN.UNIQUE.CHECK.REFERENCES.AUTOINCREMENT.COUNT.SUM.AVG.MIN.MAX.COALESCE.BEGIN.COMMIT.ROLLBACK.INDEX.VIEW.TRIGGER.REPLACE.CROSS.FULL.OUTER`.split(`.`).join(`|`)})\\b`,`gi`);t=t.replace(n,(e,t,n,r)=>t?`<span class="hl-string">${t}</span>`:n?`<span class="hl-number">${n}</span>`:r?`<span class="hl-keyword">${r.toUpperCase()}</span>`:e),t.endsWith(`
|
|
1730
|
+
`)&&(t+=` `),N&&(N.innerHTML=t)},F=document.getElementById(`console-autocomplete-popup`),I=0,L=[],R=null,ee=`SELECT.FROM.WHERE.INSERT INTO.UPDATE.SET.DELETE FROM.JOIN.LEFT JOIN.RIGHT JOIN.INNER JOIN.ON.ORDER BY.GROUP BY.HAVING.LIMIT.OFFSET.AND.OR.NOT.IN.IS NULL.IS NOT NULL.LIKE.BETWEEN.EXISTS.CREATE TABLE.ALTER TABLE.DROP TABLE.PRIMARY KEY.FOREIGN KEY.COUNT(*).SUM().AVG().DISTINCT.VALUES.EXPLAIN`.split(`.`),te=()=>{let e=document.querySelectorAll(`.table-btn[data-table]`);return Array.from(e).map(e=>e.getAttribute(`data-table`)).filter(Boolean)},ne=()=>{F&&(F.classList.add(`hidden`),F.innerHTML=``),L=[],I=0,R=null},re=()=>{if(!F||L.length===0){ne();return}F.innerHTML=L.map((e,t)=>`
|
|
1731
|
+
<div class="autocomplete-item ${t===I?`active`:``}" data-idx="${t}">
|
|
1732
|
+
<div class="autocomplete-item-left">
|
|
1733
|
+
<span class="material-symbols-outlined autocomplete-item-icon">${e.icon}</span>
|
|
1734
|
+
<span class="autocomplete-item-text">${e.text}</span>
|
|
1735
|
+
</div>
|
|
1736
|
+
<span class="autocomplete-item-type">${e.type}</span>
|
|
1737
|
+
</div>
|
|
1738
|
+
`).join(``),F.classList.remove(`hidden`),F.querySelectorAll(`.autocomplete-item`).forEach(e=>{e.onmousedown=t=>{t.preventDefault();let n=parseInt(e.dataset.idx,10);ae(n)}})},ie=()=>{let t=e.selectionStart,n=e.value.slice(0,t).match(/([a-zA-Z0-9_]+)$/);if(!n){ne();return}let r=n[1];R={prefix:r,start:t-r.length,end:t};let i=r.toLowerCase(),a=[];te().forEach(e=>{e.toLowerCase().startsWith(i)&&e.toLowerCase()!==i&&a.push({text:e,type:`table`,icon:`table_chart`})}),ee.forEach(e=>{e.toLowerCase().startsWith(i)&&e.toLowerCase()!==i&&a.push({text:e,type:`keyword`,icon:`code`})}),L=a.slice(0,8),I=0,L.length>0?re():ne()},ae=(t=I)=>{let n=L[t];if(!n||!R)return;let r=e.value.slice(0,R.start),i=e.value.slice(R.end),a=n.text+` `;e.value=r+a+i;let o=R.start+a.length;e.selectionStart=o,e.selectionEnd=o,ne(),P(),v(),e.focus({preventScroll:!0})},oe=document.getElementById(`console-cursor-pos`),se=document.getElementById(`console-sel-separator`),ce=document.getElementById(`console-selection-info`),le=document.getElementById(`console-status-dialect`),ue=document.getElementById(`console-format-sql-btn`),de=document.getElementById(`console-shortcuts-btn`),fe=()=>{if(!e)return;let t=e.selectionStart,n=e.selectionEnd,r=e.value,i=r.substring(0,n).split(`
|
|
1739
|
+
`),a=i.length,o=i[i.length-1].length+1;if(oe&&(oe.textContent=`Ln ${a}, Col ${o}`),ce)if(typeof t==`number`&&typeof n==`number`&&t!==n){let e=Math.abs(n-t),i=r.substring(t,n).split(`
|
|
1740
|
+
`).length;ce.textContent=i>1?`${e} selected (${i} lines)`:`${e} selected`,ce.classList.remove(`hidden`),se&&se.classList.remove(`hidden`)}else ce.classList.add(`hidden`),se&&se.classList.add(`hidden`);if(le){let e=window.AppState?.dbType||`SQLITE`;le.textContent=(e===`none`?`SQL`:e).toUpperCase()}},pe=e=>{if(!e||!e.trim())return e;let t=e.trim().replace(/\r\n/g,`
|
|
1741
|
+
`),n=`SELECT.FROM.WHERE.AND.OR.ORDER BY.GROUP BY.LEFT JOIN.RIGHT JOIN.INNER JOIN.CROSS JOIN.FULL JOIN.JOIN.ON.AS.INSERT INTO.VALUES.UPDATE.SET.DELETE FROM.CREATE TABLE.DROP TABLE.ALTER TABLE.HAVING.LIMIT.OFFSET.UNION ALL.UNION.CASE.WHEN.THEN.ELSE.END.PRIMARY KEY.FOREIGN KEY.NOT NULL.DEFAULT.DESC.ASC.IN.IS NULL.IS NOT NULL.IS.LIKE.BETWEEN.EXISTS.COUNT.SUM.AVG.MIN.MAX.PRAGMA.EXPLAIN`.split(`.`),r=t.split(/('(?:''|[^'])*'|"(?:""|[^"])*")/g);for(let e=0;e<r.length;e+=2)r[e]&&n.forEach(t=>{let n=RegExp(`\\b${t}\\b`,`gi`);r[e]=r[e].replace(n,t)});return t=r.join(``),[`FROM`,`WHERE`,`GROUP BY`,`HAVING`,`ORDER BY`,`LIMIT`,`OFFSET`,`LEFT JOIN`,`RIGHT JOIN`,`INNER JOIN`,`CROSS JOIN`,`JOIN`,`SET`,`VALUES`].forEach(e=>{let n=RegExp(`[ \\t]+(${e}\\b)`,`g`);t=t.replace(n,`
|
|
1742
|
+
$1`)}),t=t.replace(/[ \t]+(AND\b|OR\b)/g,`
|
|
1743
|
+
$1`),t},me=()=>{let t=e.value,n=e.selectionStart,r=e.selectionEnd,i=!1;if(typeof n==`number`&&typeof r==`number`&&n!==r){let t=e.value.substring(n,r),a=pe(t);a!==t&&(e.setRangeText(a,n,r,`select`),i=!0)}else{let n=pe(e.value);n!==t&&(e.value=n,i=!0)}let s=o();s&&(s.sql=e.value,i&&(s.isDirty=!0,s.isPreview&&=!1,a(),c())),P(),v(),fe(),window.showToast&&window.showToast(i?`SQL Formatted`:`Already formatted`,`info`)};ue&&(ue.onclick=me),de&&(de.onclick=()=>{_t({id:`console-shortcuts-modal`,icon:`keyboard`,iconColor:`primary`,title:`SQL Console Shortcuts`,subtitle:`Keyboard shortcuts for query editing and execution`,badge:`Editor`,width:`480px`,body:`
|
|
1744
|
+
<div class="shortcuts-modal-body" style="padding: 0;">
|
|
1745
|
+
<div class="shortcut-row">
|
|
1746
|
+
<span class="shortcut-row-label">Run Query / Run Selection</span>
|
|
1747
|
+
<div class="shortcut-row-keys"><kbd>Ctrl</kbd> + <kbd>↵ Enter</kbd></div>
|
|
1748
|
+
</div>
|
|
1749
|
+
<div class="shortcut-row">
|
|
1750
|
+
<span class="shortcut-row-label">Format SQL</span>
|
|
1751
|
+
<div class="shortcut-row-keys"><kbd>Shift</kbd> + <kbd>Alt</kbd> + <kbd>F</kbd></div>
|
|
1752
|
+
</div>
|
|
1753
|
+
<div class="shortcut-row">
|
|
1754
|
+
<span class="shortcut-row-label">Save Query</span>
|
|
1755
|
+
<div class="shortcut-row-keys"><kbd>Ctrl</kbd> + <kbd>S</kbd></div>
|
|
1756
|
+
</div>
|
|
1757
|
+
<div class="shortcut-row">
|
|
1758
|
+
<span class="shortcut-row-label">New Query Tab</span>
|
|
1759
|
+
<div class="shortcut-row-keys"><kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>N</kbd></div>
|
|
1760
|
+
</div>
|
|
1761
|
+
<div class="shortcut-row">
|
|
1762
|
+
<span class="shortcut-row-label">Close Tab</span>
|
|
1763
|
+
<div class="shortcut-row-keys"><kbd>Ctrl</kbd> + <kbd>W</kbd> or <kbd>Middle Click</kbd></div>
|
|
1764
|
+
</div>
|
|
1765
|
+
<div class="shortcut-row">
|
|
1766
|
+
<span class="shortcut-row-label">Indent / Outdent Selection</span>
|
|
1767
|
+
<div class="shortcut-row-keys"><kbd>Tab</kbd> / <kbd>Shift</kbd> + <kbd>Tab</kbd></div>
|
|
1768
|
+
</div>
|
|
1769
|
+
<div class="shortcut-row">
|
|
1770
|
+
<span class="shortcut-row-label">Toggle Fullscreen Editor</span>
|
|
1771
|
+
<div class="shortcut-row-keys"><kbd>Esc</kbd> to exit</div>
|
|
1772
|
+
</div>
|
|
1773
|
+
</div>
|
|
1774
|
+
`})}),e.addEventListener(`input`,()=>{let t=o();t&&(t.sql=e.value,t.isDirty=!0,t.isPreview&&=!1,c()),P(),v(),ie(),C(),fe()}),e.addEventListener(`select`,()=>{C(),fe()}),e.addEventListener(`mouseup`,()=>{C(),fe()}),e.addEventListener(`click`,fe),e.addEventListener(`keyup`,e=>{fe(),(e.key.startsWith(`Arrow`)||e.key===`Home`||e.key===`End`||e.shiftKey)&&C()}),e.addEventListener(`blur`,()=>{setTimeout(ne,150)}),e.addEventListener(`scroll`,()=>{N&&(N.scrollTop=e.scrollTop,N.scrollLeft=e.scrollLeft)}),e.addEventListener(`keydown`,t=>{if(F&&!F.classList.contains(`hidden`)&&L.length>0){if(t.key===`ArrowDown`){t.preventDefault(),I=(I+1)%L.length,re();return}else if(t.key===`ArrowUp`){t.preventDefault(),I=(I-1+L.length)%L.length,re();return}else if(t.key===`Tab`||t.key===`Enter`&&!t.ctrlKey&&!t.metaKey){t.preventDefault(),ae(I);return}else if(t.key===`Escape`){t.preventDefault(),ne();return}}if((t.ctrlKey||t.metaKey)&&t.key===`Enter`){t.preventDefault(),ne(),w();return}if(t.shiftKey&&t.altKey&&t.key.toLowerCase()===`f`){t.preventDefault(),me();return}if((t.ctrlKey||t.metaKey)&&t.key.toLowerCase()===`s`){t.preventDefault(),M();return}if((t.ctrlKey||t.metaKey)&&t.key.toLowerCase()===`w`){t.preventDefault(),f(i);return}if((t.ctrlKey||t.metaKey)&&t.altKey&&t.key.toLowerCase()===`n`){t.preventDefault(),u();return}if(t.key===`Tab`){t.preventDefault();let n=e.selectionStart,r=e.selectionEnd,i=e.value;if(n===r)document.queryCommandSupported?.(`insertText`)?document.execCommand(`insertText`,!1,` `):(e.value=i.substring(0,n)+` `+i.substring(r),e.selectionStart=e.selectionEnd=n+2);else{let a=i.substring(0,n),o=i.substring(r),s=a.lastIndexOf(`
|
|
1775
|
+
`)+1,c=i.substring(s,r).split(`
|
|
1776
|
+
`);if(t.shiftKey){let t=c.map(e=>e.replace(/^(?: | )/,``)).join(`
|
|
1777
|
+
`);e.value=i.substring(0,s)+t+o,e.selectionStart=s,e.selectionEnd=s+t.length}else{let t=c.map(e=>` `+e).join(`
|
|
1778
|
+
`);e.value=i.substring(0,s)+t+o,e.selectionStart=s,e.selectionEnd=s+t.length}}P(),v()}});let he=document.getElementById(`console-sidebar`),ge=document.getElementById(`console-toggle-sidebar-btn`),_e=document.getElementById(`console-new-query-btn`),ve=document.getElementById(`console-sidebar-search-input`),ye=document.getElementById(`console-search-clear-btn`),be=document.getElementById(`console-db-type`),xe=document.getElementById(`brand-db-type`);be&&xe&&(be.textContent=xe.textContent||`CONNECTED`);let Se=e=>{he&&(e?he.classList.add(`collapsed`):he.classList.remove(`collapsed`),localStorage.setItem(`drixio_console_sidebar_collapsed`,e?`true`:`false`))};localStorage.getItem(`drixio_console_sidebar_collapsed`)===`true`&&Se(!0),ge&&(ge.onclick=()=>{let e=he?.classList.contains(`collapsed`)||!1;Se(!e)}),_e&&(_e.onclick=()=>{u()}),document.querySelectorAll(`.console-tree-section-header`).forEach(e=>{e.onclick=()=>{let t=e.closest(`.console-tree-section`);t&&t.classList.toggle(`collapsed`)}});let Ce=document.getElementById(`console-saved-queries-list`),we=document.getElementById(`saved-queries-count`),Te=[],Ee=async()=>{if(Ce)try{let e=await y();e.success&&Array.isArray(e.data)?(Te=e.data,we&&(we.textContent=String(Te.length)),De()):Ce.innerHTML=`<div class="sidebar-tree-empty">No saved queries</div>`}catch{Ce.innerHTML=`<div class="sidebar-tree-empty">Error loading</div>`}},De=()=>{if(!Ce)return;let e=(ve?.value||``).trim().toLowerCase(),t=Te;if(e&&(t=Te.filter(t=>(t.title||``).toLowerCase().includes(e)||(t.sql||``).toLowerCase().includes(e))),t.length===0){Ce.innerHTML=`<div class="sidebar-tree-empty">No queries found</div>`;return}Ce.innerHTML=t.map(e=>`
|
|
1779
|
+
<button type="button" class="table-btn query-item-btn" data-id="${e.id}" title="${e.title}">
|
|
1780
|
+
<div class="table-btn-label">
|
|
1781
|
+
<i class="material-symbols-outlined table-item-icon">description</i>
|
|
1782
|
+
<span class="table-name-text">${e.title}</span>
|
|
1783
|
+
</div>
|
|
1784
|
+
${e.isBuiltin?``:`<span class="query-item-delete-btn" title="Delete query">
|
|
1785
|
+
<span class="material-symbols-outlined">delete</span>
|
|
1786
|
+
</span>`}
|
|
1787
|
+
</button>
|
|
1788
|
+
`).join(``),Ce.querySelectorAll(`.query-item-btn`).forEach(e=>{let t=e.getAttribute(`data-id`),n=Te.find(e=>e.id===t);if(!n)return;e.onclick=e=>{e.target.closest(`.query-item-delete-btn`)||d(n.title,n.sql)};let r=e.querySelector(`.query-item-delete-btn`);r&&(r.onclick=async e=>{e.stopPropagation(),confirm(`Delete query "${n.title}"?`)&&(await S(n.id)).success&&Ee()})})};ve&&(ve.oninput=()=>{let e=ve.value.trim();ye&&(e?ye.classList.remove(`hidden`):ye.classList.add(`hidden`)),De(),Ae()},ye&&(ye.onclick=()=>{ve.value=``,ye.classList.add(`hidden`),De(),Ae(),ve.focus()}));let Oe=document.getElementById(`console-templates-list`),ke=[{title:`Select all users`,sql:`SELECT * FROM user LIMIT 50;`},{title:`Count rows in table`,sql:`SELECT count(*) AS total_rows FROM user;`},{title:`Show database tables`,sql:`SELECT * FROM sqlite_master WHERE type='table';`},{title:`Create table blueprint`,sql:`CREATE TABLE example (
|
|
1789
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1790
|
+
name VARCHAR(255) NOT NULL,
|
|
1791
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
1792
|
+
);`},{title:`Inspect table schema`,sql:`PRAGMA table_info('user');`},{title:`Check index coverage`,sql:`PRAGMA index_list('user');`},{title:`Ping / Health check`,sql:`SELECT 1 + 1 AS ping;`}],Ae=()=>{if(!Oe)return;let e=document.getElementById(`templates-count`),t=(ve?.value||``).trim().toLowerCase(),n=ke;if(t&&(n=ke.filter(e=>e.title.toLowerCase().includes(t)||e.sql.toLowerCase().includes(t))),e&&(e.textContent=n.length),n.length===0){Oe.innerHTML=`<div class="sidebar-tree-empty">No templates match</div>`;return}Oe.innerHTML=n.map(e=>`
|
|
1793
|
+
<button type="button" class="table-btn query-item-btn template-item" data-title="${e.title}" title="${e.title}">
|
|
1794
|
+
<div class="table-btn-label">
|
|
1795
|
+
<i class="material-symbols-outlined table-item-icon" style="color: #f59e0b;">lightbulb</i>
|
|
1796
|
+
<span class="table-name-text">${e.title}</span>
|
|
1797
|
+
</div>
|
|
1798
|
+
</button>
|
|
1799
|
+
`).join(``),Oe.querySelectorAll(`.template-item`).forEach(e=>{let t=e.getAttribute(`data-title`),n=ke.find(e=>e.title===t);n&&(e.onclick=()=>{d(n.title,n.sql,!0)})})};Tt(e=>{d(`Recent query`,e,!0)});let je=document.getElementById(`console-clear-results-btn`);je&&(je.onclick=()=>{let e=o();e&&(e.lastResult=null,a()),ft(null,t)});let Me=document.querySelector(`.console-h-resizer .console-resizer-handle`),Ne=document.getElementById(`console-editor-pane`);if(Me&&Ne){let e=!1,t=0,n=0,r=r=>{r.button===0&&(e=!0,t=r.clientY,n=Ne.getBoundingClientRect().height,Me.classList.add(`is-dragging`),document.body.style.userSelect=`none`,document.body.style.cursor=`row-resize`,window.addEventListener(`mousemove`,i),window.addEventListener(`mouseup`,a))},i=r=>{if(!e)return;let i=r.clientY-t,a=n+i,o=Math.max(120,window.innerHeight*.75);a=Math.max(120,Math.min(a,o)),Ne.style.height=`${a}px`},a=()=>{if(!e)return;e=!1,Me.classList.remove(`is-dragging`),document.body.style.userSelect=``,document.body.style.cursor=``,window.removeEventListener(`mousemove`,i),window.removeEventListener(`mouseup`,a);let t=Ne.getBoundingClientRect().height;localStorage.setItem(`drixio_console_editor_height`,Math.round(t).toString())};Me.addEventListener(`mousedown`,r)}let Pe=document.getElementById(`console-expand-editor-btn`),Fe=document.getElementById(`console-expand-btn-icon`),Ie=()=>{if(!Ne)return;let t=Ne.classList.toggle(`is-fullscreen`);Fe&&(Fe.textContent=t?`close_fullscreen`:`open_in_full`),Pe&&(Pe.title=t?`Exit Fullscreen (Esc)`:`Toggle Fullscreen Editor (Esc to exit)`),setTimeout(()=>{e.focus({preventScroll:!0}),P(),v()},50)};Pe&&(Pe.onclick=Ie),window.addEventListener(`keydown`,e=>{e.key===`Escape`&&Ne?.classList.contains(`is-fullscreen`)&&(e.preventDefault(),Ie())});let z=document.querySelectorAll(`.results-tab-btn`),Le=document.getElementById(`console-results-body`),Re=document.getElementById(`console-messages-body`),ze=document.getElementById(`console-clear-messages-btn`);z.forEach(e=>{e.onclick=()=>{z.forEach(e=>e.classList.remove(`active`)),e.classList.add(`active`);let t=e.getAttribute(`data-tab`);t===`results`?(Le?.classList.remove(`hidden`),Re?.classList.add(`hidden`)):t===`messages`&&(Le?.classList.add(`hidden`),Re?.classList.remove(`hidden`),dt())}}),ze&&(ze.onclick=()=>{lt()});let Be=o();e.value=Be.sql,c(),P(),v(),ft(Be.lastResult,t),Ee(),Ae(),fe(),e.focus({preventScroll:!0})};function kt(e,t,n){document.querySelectorAll(`.table-btn`).forEach(e=>e.classList.remove(`active`)),t&&t.classList.add(`active`);let r=document.getElementById(`table-name`);r&&(r.textContent=`SQL Editor`),n.innerHTML=`
|
|
1800
|
+
<div class="sql-console-wrapper">
|
|
1801
|
+
<!-- 1. Left Query SideNav (Matching Table Editor SideNav Layout) -->
|
|
1802
|
+
<aside id="console-sidebar" class="console-sidebar">
|
|
1803
|
+
<!-- Sidebar Header -->
|
|
1804
|
+
<div class="console-sidebar-header" id="console-sidebar-header">
|
|
1805
|
+
<div class="sidebar-header-title">
|
|
1806
|
+
<span class="sidebar-header-text">SQL Editor</span>
|
|
1807
|
+
</div>
|
|
1808
|
+
</div>
|
|
1809
|
+
|
|
1810
|
+
<!-- Sidebar Content (Search & + New Query Row) -->
|
|
1811
|
+
<div class="console-sidebar-content">
|
|
1812
|
+
<!-- Search Row with + New Query Button -->
|
|
1813
|
+
<div class="console-sidebar-search-row" id="console-sidebar-search-row">
|
|
1814
|
+
<div class="console-search-bar" id="console-search-bar" role="search">
|
|
1815
|
+
<span class="material-symbols-outlined search-icon">search</span>
|
|
1816
|
+
<input type="text" placeholder="Search queries..." id="console-sidebar-search-input" aria-label="Search queries" autocomplete="off" spellcheck="false" />
|
|
1817
|
+
<button type="button" id="console-search-clear-btn" class="search-clear-btn hidden" title="Clear filter" aria-label="Clear filter">✕</button>
|
|
1818
|
+
</div>
|
|
1819
|
+
<button type="button" id="console-new-query-btn" class="console-sidebar-add-btn" title="New Query (Ctrl+Alt+N)" aria-label="New Query">
|
|
1820
|
+
<span class="material-symbols-outlined">add</span>
|
|
1821
|
+
</button>
|
|
1822
|
+
</div>
|
|
1823
|
+
|
|
1824
|
+
<!-- Tree Navigation / Query List (Matching #table-nav) -->
|
|
1825
|
+
<div class="console-query-nav" id="console-sidebar-tree">
|
|
1826
|
+
<!-- Section: SAVED QUERIES -->
|
|
1827
|
+
<div class="console-tree-section">
|
|
1828
|
+
<div class="console-tree-section-header" data-section="saved">
|
|
1829
|
+
<div class="tree-header-left">
|
|
1830
|
+
<span class="material-symbols-outlined tree-arrow">expand_more</span>
|
|
1831
|
+
<span class="tree-section-label">SAVED QUERIES</span>
|
|
1832
|
+
</div>
|
|
1833
|
+
<span class="table-btn-badge" id="saved-queries-count">0</span>
|
|
1834
|
+
</div>
|
|
1835
|
+
<div class="console-tree-section-items" id="console-saved-queries-list">
|
|
1836
|
+
<!-- Dynamically populated -->
|
|
1837
|
+
</div>
|
|
1838
|
+
</div>
|
|
1839
|
+
|
|
1840
|
+
<!-- Section: TEMPLATES -->
|
|
1841
|
+
<div class="console-tree-section">
|
|
1842
|
+
<div class="console-tree-section-header" data-section="templates">
|
|
1843
|
+
<div class="tree-header-left">
|
|
1844
|
+
<span class="material-symbols-outlined tree-arrow">expand_more</span>
|
|
1845
|
+
<span class="tree-section-label">TEMPLATES</span>
|
|
1846
|
+
</div>
|
|
1847
|
+
<span class="table-btn-badge" id="templates-count">7</span>
|
|
1848
|
+
</div>
|
|
1849
|
+
<div class="console-tree-section-items" id="console-templates-list">
|
|
1850
|
+
<!-- Predefined templates -->
|
|
1851
|
+
</div>
|
|
1852
|
+
</div>
|
|
1853
|
+
|
|
1854
|
+
<!-- Section: RECENT -->
|
|
1855
|
+
<div class="console-tree-section">
|
|
1856
|
+
<div class="console-tree-section-header" data-section="history">
|
|
1857
|
+
<div class="tree-header-left">
|
|
1858
|
+
<span class="material-symbols-outlined tree-arrow">expand_more</span>
|
|
1859
|
+
<span class="tree-section-label">RECENT</span>
|
|
1860
|
+
</div>
|
|
1861
|
+
<button type="button" id="console-clear-history-btn" class="tree-header-clear-btn hidden" title="Clear all recent history">
|
|
1862
|
+
<span class="material-symbols-outlined" style="font-size: 14px;">delete_sweep</span>
|
|
1863
|
+
</button>
|
|
1864
|
+
</div>
|
|
1865
|
+
<div class="console-tree-section-items" id="console-recent-history-list">
|
|
1866
|
+
<!-- Recent execution history -->
|
|
1867
|
+
</div>
|
|
1868
|
+
</div>
|
|
1869
|
+
</div>
|
|
1870
|
+
</div>
|
|
1871
|
+
|
|
1872
|
+
<!-- Sidebar Footer (Matching #sidebar-footer) -->
|
|
1873
|
+
<div class="console-sidebar-footer" id="console-sidebar-footer">
|
|
1874
|
+
<div class="sidebar-db-status" title="Database Connection Status">
|
|
1875
|
+
<span class="status-indicator-dot status-connected" id="console-db-status-dot"></span>
|
|
1876
|
+
<span id="console-db-type">CONNECTED</span>
|
|
1877
|
+
</div>
|
|
1878
|
+
<button type="button" id="console-clear-results-btn" class="console-sidebar-clear-btn" title="Clear current results">
|
|
1879
|
+
<span class="material-symbols-outlined" style="font-size: 14px;">cleaning_services</span>
|
|
1880
|
+
<span>Clear</span>
|
|
1881
|
+
</button>
|
|
1882
|
+
</div>
|
|
1883
|
+
</aside>
|
|
1884
|
+
|
|
1885
|
+
<!-- 2. Main Workstation Area -->
|
|
1886
|
+
<div class="console-workstation">
|
|
1887
|
+
<!-- Top Tabs & Action Bar -->
|
|
1888
|
+
<div class="console-top-bar">
|
|
1889
|
+
<div class="console-top-left">
|
|
1890
|
+
<button type="button" id="console-toggle-sidebar-btn" class="console-icon-btn" title="Toggle Sidebar">
|
|
1891
|
+
<span class="material-symbols-outlined">dock_to_left</span>
|
|
1892
|
+
</button>
|
|
1893
|
+
<!-- Tabs Container -->
|
|
1894
|
+
<div class="console-tabs-container" id="console-tabs-container">
|
|
1895
|
+
<!-- Dynamically rendered tabs -->
|
|
1896
|
+
</div>
|
|
1897
|
+
<button type="button" id="console-add-tab-btn" class="console-add-tab-btn" title="New Query Tab">
|
|
1898
|
+
<span class="material-symbols-outlined">add</span>
|
|
1899
|
+
</button>
|
|
1900
|
+
</div>
|
|
1901
|
+
|
|
1902
|
+
<div class="console-top-right">
|
|
1903
|
+
<!-- Safe Mode Compact Shield Toggle (Plan A) -->
|
|
1904
|
+
<button type="button" id="console-safe-toggle-btn" class="console-safe-shield-btn active" title="Safe Mode: Protection ON (Destructive queries will prompt for confirmation)">
|
|
1905
|
+
<span class="material-symbols-outlined safe-shield-icon">security</span>
|
|
1906
|
+
<span class="safe-indicator-dot"></span>
|
|
1907
|
+
</button>
|
|
1908
|
+
|
|
1909
|
+
<!-- Save Button -->
|
|
1910
|
+
<button type="button" id="console-save-snippet-btn" class="console-action-btn secondary" title="Save Query (Ctrl+S)">
|
|
1911
|
+
<span class="material-symbols-outlined icon">bookmark_add</span>
|
|
1912
|
+
<span>Save</span>
|
|
1913
|
+
<kbd class="console-kbd-hint">Ctrl S</kbd>
|
|
1914
|
+
</button>
|
|
1915
|
+
|
|
1916
|
+
<!-- Drixio Brand Primary Blue Run Button -->
|
|
1917
|
+
<button type="button" id="run-sql-btn" class="console-run-btn" title="Run SQL Query (Ctrl+Enter)">
|
|
1918
|
+
<span class="material-symbols-outlined" style="font-size: 16px;">play_arrow</span>
|
|
1919
|
+
<span id="run-sql-btn-text">Run</span>
|
|
1920
|
+
<kbd class="console-run-kbd">Ctrl ↵</kbd>
|
|
1921
|
+
</button>
|
|
1922
|
+
</div>
|
|
1923
|
+
</div>
|
|
1924
|
+
|
|
1925
|
+
<!-- Upper Editor Pane -->
|
|
1926
|
+
<div id="console-editor-pane" class="console-editor-pane">
|
|
1927
|
+
<div class="sql-editor-wrapper">
|
|
1928
|
+
<div class="sql-editor-layer-container">
|
|
1929
|
+
<!-- Autocomplete Floating Popup (VS Code style) -->
|
|
1930
|
+
<div id="console-autocomplete-popup" class="console-autocomplete-popup hidden"></div>
|
|
1931
|
+
<!-- Line Numbers Gutter -->
|
|
1932
|
+
<div id="sql-line-numbers" class="sql-line-numbers">1</div>
|
|
1933
|
+
<!-- Syntax Highlight Layer (Background) -->
|
|
1934
|
+
<div id="sql-highlight-layer" class="sql-editor"></div>
|
|
1935
|
+
<!-- Real Textarea (Foreground) -->
|
|
1936
|
+
<textarea id="sql-editor" class="sql-editor" spellcheck="false" autocomplete="off" autocorrect="off" autocapitalize="off" data-gramm="false" data-gramm_editor="false" data-enable-grammarly="false" placeholder="Write your SQL query here (e.g. SELECT * FROM users)..."></textarea>
|
|
1937
|
+
</div>
|
|
1938
|
+
</div>
|
|
1939
|
+
|
|
1940
|
+
<!-- Editor Status Bar (Plan A: 下沉式状态栏) -->
|
|
1941
|
+
<div class="console-editor-status-bar" id="console-editor-status-bar">
|
|
1942
|
+
<div class="status-bar-left">
|
|
1943
|
+
<span class="status-bar-item" id="console-cursor-pos" title="Cursor Line and Column">Ln 1, Col 1</span>
|
|
1944
|
+
<span class="status-bar-separator hidden" id="console-sel-separator">·</span>
|
|
1945
|
+
<span class="status-bar-item hidden" id="console-selection-info" title="Current Selection">0 selected</span>
|
|
1946
|
+
<span class="status-bar-separator">·</span>
|
|
1947
|
+
<span class="status-bar-item font-mono" id="console-status-dialect">SQLITE</span>
|
|
1948
|
+
</div>
|
|
1949
|
+
<div class="status-bar-right">
|
|
1950
|
+
<button type="button" id="console-format-sql-btn" class="status-bar-btn" title="Format SQL Query (Shift+Alt+F)">
|
|
1951
|
+
<span class="material-symbols-outlined" style="font-size: 13px;">auto_fix_high</span>
|
|
1952
|
+
<span>Format</span>
|
|
1953
|
+
<kbd class="status-bar-kbd">Shift Alt F</kbd>
|
|
1954
|
+
</button>
|
|
1955
|
+
<button type="button" id="explain-sql-btn" class="status-bar-btn" title="Analyze Query Execution Plan (EXPLAIN)">
|
|
1956
|
+
<span class="material-symbols-outlined" style="font-size: 13px;">psychology</span>
|
|
1957
|
+
<span>Explain</span>
|
|
1958
|
+
</button>
|
|
1959
|
+
<button type="button" id="console-expand-editor-btn" class="status-bar-btn" title="Toggle Fullscreen Editor (Esc to exit)">
|
|
1960
|
+
<span class="material-symbols-outlined" id="console-expand-btn-icon" style="font-size: 13px;">open_in_full</span>
|
|
1961
|
+
</button>
|
|
1962
|
+
<button type="button" id="console-shortcuts-btn" class="status-bar-btn" title="Keyboard Shortcuts (?)">
|
|
1963
|
+
<span class="material-symbols-outlined" style="font-size: 14px;">help_outline</span>
|
|
1964
|
+
</button>
|
|
1965
|
+
</div>
|
|
1966
|
+
</div>
|
|
1967
|
+
</div>
|
|
1968
|
+
|
|
1969
|
+
<!-- Draggable Horizontal Resizer -->
|
|
1970
|
+
<div id="console-resizer" class="console-h-resizer">
|
|
1971
|
+
<div class="console-resizer-handle" title="Drag to adjust editor/results ratio">
|
|
1972
|
+
<div class="console-resizer-pill"></div>
|
|
1973
|
+
</div>
|
|
1974
|
+
</div>
|
|
1975
|
+
|
|
1976
|
+
<!-- Lower Results Pane -->
|
|
1977
|
+
<div id="console-results-pane" class="console-results-pane">
|
|
1978
|
+
<!-- Results Sub-header -->
|
|
1979
|
+
<div class="console-results-header">
|
|
1980
|
+
<div class="console-results-tabs">
|
|
1981
|
+
<button type="button" class="results-tab-btn active" data-tab="results">Results</button>
|
|
1982
|
+
<button type="button" class="results-tab-btn" data-tab="messages">Messages</button>
|
|
1983
|
+
</div>
|
|
1984
|
+
<div class="console-results-meta" id="console-results-meta">
|
|
1985
|
+
<span class="results-meta-text" id="results-meta-count">Ready to run query</span>
|
|
1986
|
+
<div class="results-export-wrapper" id="results-export-wrapper" style="display: none;">
|
|
1987
|
+
<button type="button" id="console-export-btn" class="console-export-btn" title="Export Results">
|
|
1988
|
+
<span class="material-symbols-outlined" style="font-size: 14px;">download</span>
|
|
1989
|
+
<span>Export</span>
|
|
1990
|
+
<span class="material-symbols-outlined" style="font-size: 14px;">expand_more</span>
|
|
1991
|
+
</button>
|
|
1992
|
+
</div>
|
|
1993
|
+
</div>
|
|
1994
|
+
</div>
|
|
1995
|
+
|
|
1996
|
+
<!-- Results Body (Table) -->
|
|
1997
|
+
<div class="console-results-body" id="console-results-body">
|
|
1998
|
+
<div class="console-welcome-msg" id="console-welcome-msg">
|
|
1999
|
+
<div class="console-welcome-icon-box">
|
|
2000
|
+
<span class="material-symbols-outlined">terminal</span>
|
|
2001
|
+
</div>
|
|
2002
|
+
<h2 class="console-welcome-heading">Drixio SQL Workstation</h2>
|
|
2003
|
+
<p class="console-welcome-sub">Write and execute queries with multi-tab support, syntax highlighting, and instant tabular results.</p>
|
|
2004
|
+
|
|
2005
|
+
<div class="console-shortcuts-hint">
|
|
2006
|
+
<span class="shortcut-pill"><kbd>Ctrl</kbd> + <kbd>Enter</kbd> Run</span>
|
|
2007
|
+
<span class="shortcut-pill"><kbd>Ctrl</kbd> + <kbd>S</kbd> Save</span>
|
|
2008
|
+
<span class="shortcut-pill"><kbd>Tab</kbd> Indent</span>
|
|
2009
|
+
</div>
|
|
2010
|
+
</div>
|
|
2011
|
+
</div>
|
|
2012
|
+
|
|
2013
|
+
<!-- Messages Body (Execution Logs) -->
|
|
2014
|
+
<div class="console-messages-body hidden" id="console-messages-body">
|
|
2015
|
+
<div class="console-messages-toolbar">
|
|
2016
|
+
<span class="messages-toolbar-title">Console Output & Execution Logs</span>
|
|
2017
|
+
<button type="button" id="console-clear-messages-btn" class="console-clear-messages-btn" title="Clear log messages">
|
|
2018
|
+
<span class="material-symbols-outlined" style="font-size: 14px;">delete_sweep</span>
|
|
2019
|
+
<span>Clear Logs</span>
|
|
2020
|
+
</button>
|
|
2021
|
+
</div>
|
|
2022
|
+
<div class="console-messages-content" id="console-messages-content">
|
|
2023
|
+
<div class="messages-log-entry info">
|
|
2024
|
+
<div class="messages-log-header">
|
|
2025
|
+
<span class="messages-badge badge-info">INFO</span>
|
|
2026
|
+
<span class="messages-time">[System]</span>
|
|
2027
|
+
<span class="messages-title">SQL Workstation ready</span>
|
|
2028
|
+
</div>
|
|
2029
|
+
<div class="messages-detail">Query execution statistics, server notices, and execution plans will appear here.</div>
|
|
2030
|
+
</div>
|
|
2031
|
+
</div>
|
|
2032
|
+
</div>
|
|
2033
|
+
</div>
|
|
2034
|
+
</div>
|
|
2035
|
+
</div>
|
|
2036
|
+
`;let i=document.getElementById(`sql-editor`),a=document.getElementById(`console-results-body`),o=localStorage.getItem(`drixio_console_editor_height`)||localStorage.getItem(`drixio_console_pane_height`),s=n.querySelector(`#console-editor-pane`);if(o&&s){let e=parseInt(o,10);!isNaN(e)&&e>=120&&e<=800&&(s.style.height=`${e}px`)}let c=document.querySelector(`main`);c&&(c.scrollTop=0),window.scrollTo(0,0),Ot(i,a)}var B={erdData:[],undoStack:[],selectedTables:new Set,clipboard:[],draggingNode:null,draggingColInfo:null,draggedNodesStart:[],dragStartMouse:{x:0,y:0},targetColForDelete:null,zoom:1,isPanning:!1,panStart:{x:0,y:0},scrollStart:{left:0,top:0},drawingFkStartCol:null,drawingFkStartCoords:null,contextMenuPos:{x:0,y:0},snapToGrid:!0,pushUndoState(){this.undoStack.push(JSON.parse(JSON.stringify(this.erdData))),this.undoStack.length>50&&this.undoStack.shift()},undo(){return this.undoStack.length>0?(this.erdData=this.undoStack.pop(),!0):!1},clearSelection(){this.selectedTables.clear()},toggleSelection(e){this.selectedTables.has(e)?this.selectedTables.delete(e):this.selectedTables.add(e)},setSelection(e){this.selectedTables.clear(),this.selectedTables.add(e)}};async function At(){let e=await r();if(!e||!e.success||!e.data)return[];let t=Array.isArray(e.data)?[...e.data].sort():[];return(await Promise.all(t.map(async e=>{let t=await c(e);return t&&t.success?{table:e,columns:t.data}:null}))).filter(Boolean)}function jt(){try{let e=localStorage.getItem(`drixio-erd-positions`);return e?JSON.parse(e):{}}catch{return{}}}function Mt(e){localStorage.setItem(`drixio-erd-positions`,JSON.stringify(e))}async function Nt(e){if(!e)return!0;try{for(let t of e)if(t.isDraft){let e=await f(t.table,{columns:t.columns});if(!e.success)throw Error(e.error||`Failed to create table`)}else{let e=t.columns.filter(e=>e.isDraft);if(e.length>0){let n=await f(t.table,{pendingInserts:e});if(!n.success)throw Error(n.error||`Failed to add column`)}}return!0}catch(e){return window.showToast?.(`Failed to save: `+e.message,`error`),!1}}function Pt(e){if(!e)return 0;let t=0;return e.forEach(e=>{e.isDraft?t++:e.columns.forEach(e=>{e.isDraft&&t++})}),t}function Ft(e){if(!e||e.length===0)return;let t={};e.forEach(e=>t[e.table]=new Set),e.forEach(e=>{e.columns.forEach(n=>{n.fkTarget&&n.fkTarget.table&&t[e.table]&&n.fkTarget.table!==e.table&&t[e.table].add(n.fkTarget.table)})});let n={};e.forEach(e=>n[e.table]=0);let r=!0,i=0;for(;r&&i<e.length+5;){r=!1,i++;for(let i of e){let e=n[i.table];for(let a of t[i.table])n[a]!==void 0&&n[a]>=e&&(n[i.table]=n[a]+1,r=!0)}}let a={};for(let t of e){let e=n[t.table]||0;a[e]||(a[e]=[]),a[e].push(t.table)}let o=jt()||{};return Object.keys(a).forEach(e=>{let t=parseInt(e);a[e].forEach((e,n)=>{o[e]={x:600+t*360,y:400+n*260}})}),Mt(o),o}function It(e){if(!e||e.length===0)return``;let t=(window.AppState?.dbType||`sqlite`).toLowerCase()===`mysql`?"`":`"`,n=[];return e.forEach(e=>{let r=e.columns.map(e=>{let n=` ${t}${e.name}${t} ${e.type}`;return e.isPk&&(n+=` PRIMARY KEY`),!e.nullable&&!e.isPk&&(n+=` NOT NULL`),e.isUnique&&!e.isPk&&(n+=` UNIQUE`),n});e.columns.forEach(e=>{e.fkTarget&&e.fkTarget.table&&e.fkTarget.column&&r.push(` FOREIGN KEY (${t}${e.name}${t}) REFERENCES ${t}${e.fkTarget.table}${t}(${t}${e.fkTarget.column}${t})`)}),n.push(`CREATE TABLE ${t}${e.table}${t} (\n${r.join(`,
|
|
2037
|
+
`)}\n);`)}),n.join(`
|
|
2038
|
+
|
|
2039
|
+
`)}function Lt(){let e=document.querySelectorAll(`.erd-node`),t=document.getElementById(`erd-svg-layer`);if(!e||e.length===0||!t)return null;let n=1/0,r=1/0,i=-1/0,a=-1/0;e.forEach(e=>{let t=parseFloat(e.style.left)||0,o=parseFloat(e.style.top)||0,s=e.offsetWidth||290,c=e.offsetHeight||150;t<n&&(n=t),o<r&&(r=o),t+s>i&&(i=t+s),o+c>a&&(a=o+c)});let o=Math.max(400,Math.round(i-n+120)),s=Math.max(300,Math.round(a-r+120)),c=n-60,l=r-60,u=document.documentElement.getAttribute(`data-theme`)!==`light`,d=u?`#111113`:`#f8fafc`,f=u?`#18181b`:`#ffffff`,p=u?`#27272a`:`#e2e8f0`,m=u?`#f4f4f5`:`#0f172a`,h=u?`#a1a1aa`:`#64748b`,g=``;e.forEach(e=>{let t=(parseFloat(e.style.left)||0)-c,n=(parseFloat(e.style.top)||0)-l,r=e.offsetWidth||290,i=e.offsetHeight||150,a=e.querySelector(`.erd-node-name`)?.textContent||``,o=Array.from(e.querySelectorAll(`.erd-column`)).map(e=>{let t=e.querySelector(`.erd-col-name`)?.textContent||``,n=e.querySelector(`.erd-col-type`)?.textContent||``,r=!!e.querySelector(`.erd-badge-pk`),i=!!e.querySelector(`.erd-badge-pfk`);return{name:t,type:n,isPk:r,isPfk:i,isFk:!i&&!!e.querySelector(`.erd-badge-fk`),isUq:!!e.querySelector(`.erd-badge-unique`)}}),s=``;o.forEach((e,t)=>{let n=40+t*32,i=``;i=e.isPfk?`<rect x="12" y="${n+6}" width="26" height="15" rx="3" fill="rgba(99,102,241,0.2)" stroke="#818cf8" stroke-width="0.8"/><text x="25" y="${n+17}" font-size="9" font-family="monospace" font-weight="700" fill="#a5b4fc" text-anchor="middle">PFK</text>`:e.isPk?`<rect x="12" y="${n+6}" width="20" height="15" rx="3" fill="rgba(245,158,11,0.2)" stroke="#f59e0b" stroke-width="0.8"/><text x="22" y="${n+17}" font-size="9" font-family="monospace" font-weight="700" fill="#fbbf24" text-anchor="middle">PK</text>`:e.isFk?`<rect x="12" y="${n+6}" width="20" height="15" rx="3" fill="rgba(59,130,246,0.2)" stroke="#3b82f6" stroke-width="0.8"/><text x="22" y="${n+17}" font-size="9" font-family="monospace" font-weight="700" fill="#60a5fa" text-anchor="middle">FK</text>`:e.isUq?`<rect x="12" y="${n+6}" width="20" height="15" rx="3" fill="rgba(14,165,233,0.2)" stroke="#0ea5e9" stroke-width="0.8"/><text x="22" y="${n+17}" font-size="9" font-family="monospace" font-weight="700" fill="#38bdf8" text-anchor="middle">UQ</text>`:`<circle cx="18" cy="${n+14}" r="2" fill="${h}"/>`;let a=e.isPfk?44:e.isPk||e.isFk||e.isUq?38:28;s+=`
|
|
2040
|
+
<line x1="0" y1="${n}" x2="${r}" y2="${n}" stroke="${p}" stroke-width="0.5"/>
|
|
2041
|
+
${i}
|
|
2042
|
+
<text x="${a}" y="${n+18}" font-size="12" font-family="monospace" font-weight="${e.isPk?`700`:`500`}" fill="${m}">${e.name}</text>
|
|
2043
|
+
<text x="${r-12}" y="${n+18}" font-size="10.5" font-family="monospace" fill="#3b82f6" text-anchor="end">${e.type}</text>
|
|
2044
|
+
`}),g+=`
|
|
2045
|
+
<g transform="translate(${t}, ${n})">
|
|
2046
|
+
<rect width="${r}" height="${i}" rx="8" fill="${f}" stroke="${p}" stroke-width="1"/>
|
|
2047
|
+
<rect width="${r}" height="36" rx="8" fill="${u?`#202024`:`#f1f5f9`}"/>
|
|
2048
|
+
<rect y="28" width="${r}" height="8" fill="${u?`#202024`:`#f1f5f9`}"/>
|
|
2049
|
+
<line x1="0" y1="36" x2="${r}" y2="36" stroke="${p}" stroke-width="1"/>
|
|
2050
|
+
<text x="14" y="23" font-size="13" font-family="sans-serif" font-weight="700" fill="${m}">${a}</text>
|
|
2051
|
+
<text x="${r-14}" y="23" font-size="11" font-family="sans-serif" fill="${h}" text-anchor="end">${o.length} cols</text>
|
|
2052
|
+
${s}
|
|
2053
|
+
</g>
|
|
2054
|
+
`});let _=Array.from(t.querySelectorAll(`.erd-relationship-path`)),v=``;return _.forEach(e=>{let t=(e.getAttribute(`d`)||``).replace(/([0-9.]+)\s+([0-9.]+)/g,(e,t,n)=>`${(parseFloat(t)-c).toFixed(1)} ${(parseFloat(n)-l).toFixed(1)}`);v+=`<path d="${t}" fill="none" stroke="${u?`#818cf8`:`#6366f1`}" stroke-width="2" opacity="0.8" marker-start="url(#crows-foot-zero-many)" marker-end="url(#one-and-only-one)"/>`}),{svgStr:`<?xml version="1.0" standalone="no"?>
|
|
2055
|
+
<svg width="${o}" height="${s}" viewBox="0 0 ${o} ${s}" xmlns="http://www.w3.org/2000/svg">
|
|
2056
|
+
<defs>
|
|
2057
|
+
<pattern id="grid" width="20" height="20" patternUnits="userSpaceOnUse">
|
|
2058
|
+
<circle cx="2" cy="2" r="1" fill="${u?`#27272a`:`#cbd5e1`}" />
|
|
2059
|
+
</pattern>
|
|
2060
|
+
<marker id="crows-foot-zero-many" viewBox="0 0 16 10" refX="16" refY="5" markerWidth="10" markerHeight="10" orient="auto-start-reverse">
|
|
2061
|
+
<path d="M 16 0 L 8 5 M 16 10 L 8 5 M 16 5 L 8 5 M 4 5 L 0 5" fill="none" stroke="${u?`#818cf8`:`#6366f1`}" stroke-width="1.5" />
|
|
2062
|
+
<circle cx="5" cy="5" r="3" fill="${d}" stroke="${u?`#818cf8`:`#6366f1`}" stroke-width="1.5" />
|
|
2063
|
+
</marker>
|
|
2064
|
+
<marker id="one-and-only-one" viewBox="0 0 16 10" refX="13" refY="5" markerWidth="10" markerHeight="10" orient="auto">
|
|
2065
|
+
<path d="M 10 0 L 10 10 M 6 0 L 6 10" fill="none" stroke="${u?`#818cf8`:`#6366f1`}" stroke-width="1.5" />
|
|
2066
|
+
</marker>
|
|
2067
|
+
</defs>
|
|
2068
|
+
<rect width="100%" height="100%" fill="${d}" />
|
|
2069
|
+
<rect width="100%" height="100%" fill="url(#grid)" />
|
|
2070
|
+
<g>${v}</g>
|
|
2071
|
+
<g>${g}</g>
|
|
2072
|
+
</svg>`,width:o,height:s}}function Rt(){let e=Lt();if(!e){window.showToast?.(`No tables to export`,`warning`);return}let t=new Blob([e.svgStr],{type:`image/svg+xml;charset=utf-8`}),n=URL.createObjectURL(t),r=document.createElement(`a`);r.href=n,r.download=`erd-diagram-${new Date().toISOString().slice(0,10)}.svg`,r.click(),URL.revokeObjectURL(n),window.showToast?.(`Exported ERD as SVG!`,`success`)}function zt(){let e=Lt();if(!e){window.showToast?.(`No tables to export`,`warning`);return}let t=new Blob([e.svgStr],{type:`image/svg+xml;charset=utf-8`}),n=URL.createObjectURL(t),r=new Image;r.onload=()=>{let t=document.createElement(`canvas`);t.width=e.width*2,t.height=e.height*2;let i=t.getContext(`2d`);if(!i){URL.revokeObjectURL(n),window.showToast?.(`Failed to create canvas context`,`error`);return}i.scale(2,2),i.drawImage(r,0,0),URL.revokeObjectURL(n),t.toBlob(e=>{if(!e){window.showToast?.(`Failed to generate PNG blob`,`error`);return}let t=URL.createObjectURL(e),n=document.createElement(`a`);n.href=t,n.download=`erd-diagram-${new Date().toISOString().slice(0,10)}.png`,n.click(),URL.revokeObjectURL(t),window.showToast?.(`Exported ERD as PNG (2x HiDPI)!`,`success`)},`image/png`)},r.onerror=()=>{URL.revokeObjectURL(n),window.showToast?.(`Failed to render ERD PNG`,`error`)},r.src=n}function Bt(){if(B.selectedTables.size===0)return;let e=[];B.selectedTables.forEach(t=>{let n=B.erdData.find(e=>e.table===t);if(n){let t=JSON.parse(JSON.stringify(n));t.columns.forEach(e=>{e.fkTarget&&delete e.fkTarget}),e.push(t)}}),B.clipboard=e,window.showToast(`Copied ${e.length} table(s)`,`success`)}function Vt(){B.selectedTables.size!==0&&(B.pushUndoState(),B.erdData.forEach(e=>{e.columns.forEach(e=>{e.fkTarget&&B.selectedTables.has(e.fkTarget.table)&&delete e.fkTarget})}),B.erdData=B.erdData.filter(e=>!B.selectedTables.has(e.table)),B.clearSelection(),V(),window.showToast(`Deleted selected table(s)`,`success`))}function Ht(){if(!B.clipboard||B.clipboard.length===0)return;B.pushUndoState(),B.clearSelection();let e=jt();B.clipboard.forEach((t,n)=>{let r=JSON.parse(JSON.stringify(t)),i=`_copy`,a=r.table+i,o=1;for(;B.erdData.some(e=>e.table===a);)o++,a=r.table+i+o;r.table=a,r.isDraft=!0,r.columns.forEach(e=>e.isDraft=!0),e[t.table]?e[a]={x:e[t.table].x+40+n*20,y:e[t.table].y+40+n*20}:e[a]={x:100+n*20,y:100+n*20},B.erdData.push(r),B.selectedTables.add(a)}),Mt(e),V(),window.showToast(`Pasted ${B.clipboard.length} table(s)`,`success`)}function Ut(){document.addEventListener(`keydown`,async e=>{window.AppState&&window.AppState.currentTab!==`erd-btn`||e.target.tagName===`INPUT`||e.target.tagName===`SELECT`||e.target.tagName===`TEXTAREA`||((e.ctrlKey||e.metaKey)&&e.key===`s`&&(e.preventDefault(),await Nt(B.erdData)&&(window.showToast(`ERD Schema saved successfully!`,`success`),pn(document.getElementById(`main-content`)))),(e.ctrlKey||e.metaKey)&&e.key===`z`&&(e.preventDefault(),B.undo()?(V(),window.showToast(`Undid last action`,`success`)):window.showToast(`Nothing to undo`,`info`)),e.ctrlKey&&e.key===`c`&&(e.preventDefault(),Bt()),e.ctrlKey&&e.key===`v`&&(e.preventDefault(),Ht()),e.key===`Delete`&&(e.preventDefault(),Vt()))})}function Wt(){let e=document.getElementById(`erd-workspace`);document.querySelectorAll(`.erd-node`).forEach(t=>{let n=B.erdData.find(e=>e.table===t.dataset.table);if(!n)return;let r=t.querySelector(`.erd-node-header`);if(r.addEventListener(`mousedown`,r=>{if(r.button===1||r.target.tagName===`INPUT`)return;if(!B.selectedTables.has(n.table))!r.ctrlKey&&!r.metaKey&&B.clearSelection(),B.selectedTables.add(n.table),document.querySelectorAll(`.erd-node`).forEach(e=>{B.selectedTables.has(e.dataset.table)?e.classList.add(`is-selected`):e.classList.remove(`is-selected`)});else if(r.ctrlKey||r.metaKey){B.selectedTables.delete(n.table),t.classList.remove(`is-selected`);return}B.draggingNode=t;let i=e.getBoundingClientRect();B.dragStartMouse={x:r.clientX,y:r.clientY},B.draggedNodesStart=[],B.selectedTables.forEach(e=>{let t=document.querySelector(`.erd-node[data-table="${e}"]`);if(t){let e=t.getBoundingClientRect();B.draggedNodesStart.push({node:t,startX:(e.left-i.left)/B.zoom,startY:(e.top-i.top)/B.zoom}),t.style.zIndex=`10`}}),document.body.style.cursor=`grabbing`}),t.addEventListener(`contextmenu`,e=>{e.preventDefault(),e.stopPropagation(),B.selectedTables.has(n.table)||(B.setSelection(n.table),document.querySelectorAll(`.erd-node`).forEach(e=>{B.selectedTables.has(e.dataset.table)?e.classList.add(`is-selected`):e.classList.remove(`is-selected`)})),A(e,[{label:`Copy`,icon:`content_copy`,action:Bt},{label:`Cut`,icon:`content_cut`,action:()=>{Bt(),Vt()}},`divider`,{label:`Delete`,icon:`delete`,danger:!0,action:Vt}])}),t.querySelector(`.erd-add-col-btn`).addEventListener(`click`,()=>{B.pushUndoState(),n.columns.push({name:`new_col_${n.columns.length}`,type:`TEXT`,isDraft:!0,nullable:!0}),V()}),n.isDraft){let e=r.querySelector(`.erd-node-name`);r.addEventListener(`dblclick`,t=>{if(t.target.tagName===`INPUT`)return;t.stopPropagation();let r=document.createElement(`input`);r.value=n.table,r.className=`erd-inline-input`,e.replaceWith(r),r.focus(),r.addEventListener(`blur`,()=>{let e=n.table,t=r.value.trim()||e;if(e!==t){B.pushUndoState(),n.table=t;let r=jt();r[e]&&(r[t]=r[e],delete r[e],Mt(r)),B.erdData.forEach(n=>{n.columns.forEach(n=>{n.fkTarget&&n.fkTarget.table===e&&(n.fkTarget.table=t)})}),B.selectedTables.has(e)&&(B.selectedTables.delete(e),B.selectedTables.add(t))}V()}),r.addEventListener(`keydown`,e=>{e.key===`Enter`&&r.blur()})})}else r.addEventListener(`dblclick`,e=>{e.stopPropagation(),window.AppState.currentTable=t.dataset.table;let n=document.querySelector(`.table-btn[data-table="${t.dataset.table}"]`);n&&(window.AppState.currentTableBtnElement=n,document.querySelectorAll(`.table-btn`).forEach(e=>e.classList.remove(`active`)),n.classList.add(`active`)),window.handleSwitchTab?window.handleSwitchTab(`schema-btn`):window.renderCurrentView&&(window.AppState.currentTab=`schema-btn`,window.renderCurrentView())});let i=t.querySelectorAll(`.erd-column`);i.forEach(t=>{let r=parseInt(t.dataset.index),i=n.columns[r];if(t.addEventListener(`contextmenu`,e=>{i.isPk||!i.isDraft&&!n.isDraft||(e.preventDefault(),e.stopPropagation(),A(e,[{label:`Delete Column`,icon:`delete`,danger:!0,action:()=>{B.pushUndoState(),B.erdData.forEach(e=>{e.columns.forEach(e=>{e.fkTarget&&e.fkTarget.table===n.table&&e.fkTarget.column===i.name&&delete e.fkTarget})}),n.columns.splice(r,1),V()}}]))}),!i.isDraft&&!n.isDraft)return;t.querySelectorAll(`.editable-field`).forEach(e=>{e.addEventListener(`dblclick`,t=>{if(t.target.tagName===`INPUT`||t.target.tagName===`SELECT`)return;t.stopPropagation();let r=e.dataset.field;if(r===`type`){te({anchorEl:e,initialValue:i.type,dialect:window.AppState?.dbType,onSelect:e=>{e&&e!==i.type&&(B.pushUndoState(),i.type=e,V())}});return}let a=document.createElement(`input`);a.value=r===`name`&&i.nullable?i.name+` ?`:i[r],a.className=`erd-inline-input`,e.replaceWith(a),a.focus(),a.addEventListener(`blur`,()=>{let e=i[r],t=r===`type`?a.value.toUpperCase():a.value;if(t=t.trim()||e,e===t&&!t.endsWith(`?`)&&!t.endsWith(`!`)){V();return}B.pushUndoState(),r===`name`&&(t.endsWith(`?`)?(i.nullable=!0,t=t.slice(0,-1).trim()):(i.nullable=!1,t.endsWith(`!`)&&(t=t.slice(0,-1).trim()))),r===`name`&&i.isPk&&e!==t&&B.erdData.forEach(r=>{r.columns.forEach(r=>{r.fkTarget&&r.fkTarget.table===n.table&&r.fkTarget.column===e&&(r.fkTarget.column=t)})}),i[r]=t,V()}),a.addEventListener(`keydown`,e=>{e.key===`Enter`&&a.blur()})})});let a=t.querySelector(`.erd-drag-handle`);a&&a.addEventListener(`mousedown`,t=>{t.stopPropagation(),B.drawingFkStartCol={table:n.table,col:i.name,colIndex:r};let o=a.getBoundingClientRect(),s=e.getBoundingClientRect();B.drawingFkStartCoords={x:(o.right-s.left)/B.zoom,y:(o.top+o.height/2-s.top)/B.zoom}})}),t.querySelectorAll(`.erd-reorder-handle`).forEach(e=>{e.addEventListener(`dragstart`,t=>{let r=e.closest(`.erd-column`);B.draggingColInfo={table:n.table,colIndex:parseInt(r.dataset.index)},t.dataTransfer.effectAllowed=`move`,t.dataTransfer.setData(`text/plain`,n.table)})}),i.forEach(e=>{e.addEventListener(`dragover`,e=>{!B.draggingColInfo||B.draggingColInfo.table!==n.table||(e.preventDefault(),e.dataTransfer.dropEffect=`move`)}),e.addEventListener(`drop`,t=>{if(t.preventDefault(),!B.draggingColInfo||B.draggingColInfo.table!==n.table)return;let r=B.draggingColInfo.colIndex,i=parseInt(e.dataset.index);if(r!==i){B.pushUndoState();let[e]=n.columns.splice(r,1);n.columns.splice(i,0,e),V()}B.draggingColInfo=null})})})}var Gt=!1;function Kt(){Gt||(Gt=!0,document.addEventListener(`mousemove`,e=>{if(B.drawingFkStartCol){let t=document.getElementById(`erd-workspace`);if(!t)return;let n=t.getBoundingClientRect(),r=(e.clientX-n.left)/B.zoom,i=(e.clientY-n.top)/B.zoom,a=document.getElementById(`erd-temp-path`),o=document.getElementById(`erd-svg-layer`);a||(a=document.createElementNS(`http://www.w3.org/2000/svg`,`path`),a.id=`erd-temp-path`,a.setAttribute(`fill`,`none`),a.setAttribute(`stroke`,`var(--color-primary)`),a.setAttribute(`stroke-width`,`2`),a.setAttribute(`stroke-dasharray`,`5,5`),o&&o.appendChild(a)),a&&a.setAttribute(`d`,`M ${B.drawingFkStartCoords.x} ${B.drawingFkStartCoords.y} L ${r} ${i}`);return}if(!B.draggingNode)return;let t=(e.clientX-B.dragStartMouse.x)/B.zoom,n=(e.clientY-B.dragStartMouse.y)/B.zoom;B.draggedNodesStart.forEach(e=>{let r=e.startX+t,i=e.startY+n;r<0&&(r=0),i<0&&(i=0),B.snapToGrid&&(r=Math.round(r/20)*20,i=Math.round(i/20)*20),e.node.style.left=`${r}px`,e.node.style.top=`${i}px`}),nn()}),document.addEventListener(`mouseup`,e=>{if(B.drawingFkStartCol){let t=document.elementFromPoint(e.clientX,e.clientY),n=t?t.closest(`.erd-node`):null;if(n){let e=n.dataset.table;if(e!==B.drawingFkStartCol.table){let t=B.erdData.find(t=>t.table===e).columns.find(e=>e.isPk);if(t){let n=confirm(`Create Foreign Key to ${e}?\n\nClick OK for Nullable (0..N)\nClick Cancel for Not Nullable (1..N)`);B.pushUndoState();let r=B.erdData.find(e=>e.table===B.drawingFkStartCol.table),i=r.columns[B.drawingFkStartCol.colIndex];i.fkTarget={table:e,column:t.name},i.nullable=n,r.columns.splice(B.drawingFkStartCol.colIndex,1),r.columns.push(i),V()}else window.showToast(`Target table has no Primary Key!`,`error`)}}else{let e=B.erdData.find(e=>e.table===B.drawingFkStartCol.table).columns[B.drawingFkStartCol.colIndex];e.fkTarget&&(B.pushUndoState(),delete e.fkTarget,V())}let r=document.getElementById(`erd-temp-path`);r&&r.remove(),B.drawingFkStartCol=null}if(B.draggingNode){document.body.style.cursor=``;let e=jt();B.draggedNodesStart.forEach(t=>{t.node.style.zIndex=``,e[t.node.dataset.table]={x:parseFloat(t.node.style.left),y:parseFloat(t.node.style.top)}}),Mt(e),dn(),B.draggingNode=null,B.draggedNodesStart=[]}}))}var qt=!1,Jt={x:0,y:0},Yt=null;function Xt(e){let t=()=>{if(!e)return;let t=20*B.zoom,n=Math.max(1,1.25*B.zoom);e.style.backgroundSize=`${t}px ${t}px`,e.style.backgroundImage=`radial-gradient(var(--color-border) ${n}px, transparent ${n}px)`,e.style.backgroundPosition=`${-(e.scrollLeft%t)}px ${-(e.scrollTop%t)}px`};e.addEventListener(`scroll`,()=>{dn(),t()}),e.addEventListener(`mousedown`,t=>{t.button===1||t.button===0&&t.altKey?(t.preventDefault(),B.isPanning=!0,B.panStart.x=t.clientX,B.panStart.y=t.clientY,B.scrollStart.left=e.scrollLeft,B.scrollStart.top=e.scrollTop,document.body.style.cursor=`grabbing`):t.button===0&&(t.target.id===`erd-workspace`||t.target.id===`erd-svg-layer`||t.target.id===`erd-nodes-layer`||t.target.id===`erd-wrapper`)&&(qt=!0,Jt={x:t.clientX,y:t.clientY},!t.ctrlKey&&!t.metaKey&&(B.clearSelection(),V()),Yt=document.createElement(`div`),Yt.style.position=`fixed`,Yt.style.border=`1px solid var(--color-primary)`,Yt.style.backgroundColor=`rgba(59, 130, 246, 0.12)`,Yt.style.borderRadius=`4px`,Yt.style.zIndex=`1000`,Yt.style.pointerEvents=`none`,document.body.appendChild(Yt))});let n=()=>{let e=document.getElementById(`btn-erd-zoom-reset`);e&&(e.textContent=Math.round(B.zoom*100)+`%`)},r=e=>{B.zoom=Math.max(.2,Math.min(e,2.5));let r=document.getElementById(`erd-workspace`);r&&(r.style.transform=`scale(${B.zoom})`,nn(),dn(),n(),t())},i=document.getElementById(`btn-erd-zoom-in`);i&&(i.onclick=()=>r(B.zoom+.15));let a=document.getElementById(`btn-erd-zoom-out`);a&&(a.onclick=()=>r(B.zoom-.15));let o=document.getElementById(`btn-erd-zoom-reset`);o&&(o.onclick=()=>r(1));let s=()=>{let n=document.querySelectorAll(`.erd-node`);if(!n||n.length===0)return;let i=1/0,a=1/0,o=-1/0,s=-1/0;if(n.forEach(e=>{let t=parseFloat(e.style.left)||0,n=parseFloat(e.style.top)||0,r=e.offsetWidth||290,c=e.offsetHeight||150;t<i&&(i=t),n<a&&(a=n),t+r>o&&(o=t+r),n+c>s&&(s=n+c)}),i===1/0)return;let c=o-i+240,l=s-a+240,u=e.clientWidth,d=e.clientHeight,f=u/c,p=d/l,m=Math.min(1.1,Math.max(.35,Math.min(f,p)));r(m);let h=(i+o)/2,g=(a+s)/2;e.scrollLeft=h*m-u/2,e.scrollTop=g*m-d/2,t()},c=document.getElementById(`btn-erd-fit-view`);c&&(c.onclick=s);let l=document.getElementById(`btn-erd-fullscreen`);l&&(l.onclick=()=>{let e=document.getElementById(`erd-main-viewport`);if(e)if(document.fullscreenElement){document.exitFullscreen?.().catch(()=>{}),l.classList.remove(`active`);let e=l.querySelector(`.material-symbols-outlined`);e&&(e.textContent=`fullscreen`)}else{e.requestFullscreen?.().catch(()=>{}),l.classList.add(`active`);let t=l.querySelector(`.material-symbols-outlined`);t&&(t.textContent=`fullscreen_exit`)}},document.addEventListener(`fullscreenchange`,()=>{if(!document.fullscreenElement&&l){l.classList.remove(`active`);let e=l.querySelector(`.material-symbols-outlined`);e&&(e.textContent=`fullscreen`)}}));let u=document.getElementById(`btn-erd-toggle-minimap`),d=document.getElementById(`btn-erd-close-minimap`),f=document.getElementById(`erd-minimap-container`),p=e=>{if(!f)return;let t=f.classList.contains(`hidden`)||f.style.display===`none`;(e===void 0?t:e)?(f.classList.remove(`hidden`),f.style.display=`block`,u&&u.classList.add(`active`),dn()):(f.classList.add(`hidden`),f.style.display=`none`,u&&u.classList.remove(`active`))};u&&(u.onclick=()=>p()),d&&(d.onclick=e=>{e.stopPropagation(),p(!1)});let m=document.getElementById(`btn-erd-add-table`);m&&(m.onclick=()=>{let t=e.scrollLeft/B.zoom,n=e.scrollTop/B.zoom,r=e.clientWidth/B.zoom,i=e.clientHeight/B.zoom;fn(Math.max(60,t+r/2-150),Math.max(60,n+i/2-100)),window.showToast(`Created new table draft`,`success`)});let h=document.getElementById(`btn-erd-auto-layout`);h&&(h.onclick=()=>{B.pushUndoState(),Ft(B.erdData),V(),setTimeout(s,50),window.showToast(`Auto Layout Applied`,`success`)});let g=document.getElementById(`btn-erd-snap-grid`);g&&(g.onclick=()=>{B.snapToGrid=!B.snapToGrid,B.snapToGrid?(g.classList.add(`active`),window.showToast(`Grid Snapping: ON (20px)`,`info`)):(g.classList.remove(`active`),window.showToast(`Grid Snapping: OFF (Free)`,`info`))});let _=document.getElementById(`btn-erd-export`);_&&(_.onclick=e=>{e.stopPropagation(),A(e,[{label:`Export as SVG Diagram`,icon:`image`,action:()=>Rt()},{label:`Export as PNG (2x HiDPI)`,icon:`photo_camera`,action:()=>zt()},{label:`Copy SQL Schema (DDL)`,icon:`code`,action:()=>{let e=It(B.erdData);e&&(navigator.clipboard.writeText(e),window.showToast?.(`SQL schema copied to clipboard!`,`success`))}}])});let v=document.getElementById(`btn-erd-save-drafts`);v&&(v.onclick=async()=>{await Nt(B.erdData)&&(window.showToast(`ERD Schema saved successfully!`,`success`),pn(document.getElementById(`main-content`)))}),e.addEventListener(`wheel`,e=>{(e.ctrlKey||e.metaKey)&&(e.preventDefault(),r(B.zoom-e.deltaY*.001))},{passive:!1}),document.addEventListener(`mousemove`,n=>{if(qt&&Yt){let e=Math.min(n.clientX,Jt.x),t=Math.min(n.clientY,Jt.y),r=Math.abs(n.clientX-Jt.x),i=Math.abs(n.clientY-Jt.y);Yt.style.left=e+`px`,Yt.style.top=t+`px`,Yt.style.width=r+`px`,Yt.style.height=i+`px`;return}if(B.isPanning){if(e){let r=n.clientX-B.panStart.x,i=n.clientY-B.panStart.y;e.scrollLeft=B.scrollStart.left-r,e.scrollTop=B.scrollStart.top-i,t()}return}}),document.addEventListener(`mouseup`,e=>{if(qt&&(qt=!1,Yt)){let e=Yt.getBoundingClientRect();Yt.remove(),Yt=null;let t=document.querySelectorAll(`.erd-node`),n=!1;t.forEach(t=>{let r=t.getBoundingClientRect();e.right<r.left||e.left>r.right||e.bottom<r.top||e.top>r.bottom||(B.selectedTables.add(t.dataset.table),n=!0)}),n&&V()}B.isPanning&&(B.isPanning=!1,document.body.style.cursor=``)}),e.addEventListener(`contextmenu`,e=>{(e.target.id===`erd-workspace`||e.target.id===`erd-svg-layer`||e.target.id===`erd-nodes-layer`||e.target.id===`erd-wrapper`)&&(e.preventDefault(),e.stopPropagation(),A(e,[{label:`Add Table`,icon:`add_box`,action:()=>{m&&m.click()}},`divider`,{label:`Auto Layout`,icon:`account_tree`,action:()=>{h&&h.click()}},{label:`Export as SVG`,icon:`image`,action:()=>Rt()},{label:`Export as PNG`,icon:`photo_camera`,action:()=>zt()}]))});let y=document.getElementById(`erd-search-input`),b=document.getElementById(`erd-search-results`);if(y&&b){y.addEventListener(`input`,n=>{let r=n.target.value.toLowerCase().trim();if(b.innerHTML=``,!r){b.classList.add(`hidden`);return}let i=B.erdData.filter(e=>e.table.toLowerCase().includes(r));if(i.length===0){b.innerHTML=`<div class="erd-search-item" style="color: var(--color-text-soft);">No tables found</div>`,b.classList.remove(`hidden`);return}i.forEach(n=>{let r=document.createElement(`div`);r.className=`erd-search-item`,r.innerHTML=`
|
|
2073
|
+
<span class="material-symbols-outlined icon-16" style="color: var(--color-primary);">table_chart</span>
|
|
2074
|
+
<span>${n.table}</span>
|
|
2075
|
+
<span class="erd-search-col-badge">${n.columns.length} cols</span>
|
|
2076
|
+
`,r.onclick=()=>{B.setSelection(n.table);let r=document.querySelector(`.erd-node[data-table="${n.table}"]`);if(r){let n=r.getBoundingClientRect(),i=e.getBoundingClientRect(),a=parseFloat(r.style.left),o=parseFloat(r.style.top);e.scrollLeft=a*B.zoom-i.width/2+n.width/2,e.scrollTop=o*B.zoom-i.height/2+n.height/2,t()}V(),b.classList.add(`hidden`),y.value=``},b.appendChild(r)}),b.classList.remove(`hidden`)}),document.addEventListener(`click`,e=>{!y.contains(e.target)&&!b.contains(e.target)&&b.classList.add(`hidden`)});let n=document.querySelector(`.erd-search-shortcut`);n&&(n.onclick=()=>y.focus(),n.style.cursor=`pointer`)}e.addEventListener(`mouseover`,e=>{let t=e.target.closest(`.erd-relationship-path`);if(t){sn(t.dataset.fromTable,t.dataset.fromCol,t.dataset.toTable,t.dataset.toCol);return}let n=e.target.closest(`.erd-column.is-fk`);if(n){let e=n.closest(`.erd-node`),t=n.dataset.col;if(e){let n=document.querySelector(`.erd-relationship-path[data-from-table="${e.dataset.table}"][data-from-col="${t}"]`);if(n){sn(n.dataset.fromTable,n.dataset.fromCol,n.dataset.toTable,n.dataset.toCol);return}}}let r=e.target.closest(`.erd-node-header`);if(r){let e=r.closest(`.erd-node`);e&&cn(e.dataset.table)}}),e.addEventListener(`mouseout`,e=>{let t=e.target.closest(`.erd-relationship-path`),n=e.target.closest(`.erd-column.is-fk`),r=e.target.closest(`.erd-node-header`);(t||n||r)&&ln()}),e.addEventListener(`click`,e=>{let t=e.target.closest(`.erd-relationship-path`);t&&(e.stopPropagation(),on({fromTable:t.dataset.fromTable,fromCol:t.dataset.fromCol,toTable:t.dataset.toTable,toCol:t.dataset.toCol,onDelete:t.dataset.onDelete,onUpdate:t.dataset.onUpdate,x:e.clientX,y:e.clientY}))}),t()}function Zt(e){let t=document.getElementById(`erd-minimap`);if(!t)return;let n=!1,r=n=>{let r=t.getBoundingClientRect(),i=n.clientX-r.left,a=n.clientY-r.top,{scale:o,offsetX:s,offsetY:c,minX:l,minY:u,vW:d,vH:f}=un(e,Array.from(document.querySelectorAll(`.erd-node`))),p=l+(i-s)/o,m=u+(a-c)/o;e.scrollLeft=(p-d/2)*B.zoom,e.scrollTop=(m-f/2)*B.zoom};t.addEventListener(`mousedown`,e=>{if(e.stopPropagation(),e.button!==0)return;n=!0,r(e);let t=e=>{n&&r(e)},i=()=>{n=!1,document.removeEventListener(`mousemove`,t),document.removeEventListener(`mouseup`,i)};document.addEventListener(`mousemove`,t),document.addEventListener(`mouseup`,i)})}var Qt=!1;function $t(){if(Qt)return;let e=document.getElementById(`erd-wrapper`);e&&(Xt(e),Zt(e),Ut(),Kt(),Qt=!0)}function en(){let e=document.getElementById(`btn-erd-save-drafts`);if(!e)return;let t=Pt(B.erdData),n=e.querySelector(`.erd-draft-count-badge`);t>0?(e.classList.remove(`disabled`),e.classList.add(`has-drafts`),n&&(n.textContent=t,n.style.display=`inline-flex`)):(e.classList.remove(`has-drafts`),n&&(n.style.display=`none`))}function V(){let e=document.getElementById(`erd-nodes-layer`);if(!e)return;if(e.innerHTML=``,!B.erdData||B.erdData.length===0){e.innerHTML=`
|
|
2077
|
+
<div class="erd-empty-workspace">
|
|
2078
|
+
<div class="erd-empty-icon-box">
|
|
2079
|
+
<span class="material-symbols-outlined">account_tree</span>
|
|
2080
|
+
</div>
|
|
2081
|
+
<h3>No tables in this database</h3>
|
|
2082
|
+
<p>Right-click the canvas or click "+ Add Table" above to create an entity.</p>
|
|
2083
|
+
</div>
|
|
2084
|
+
`,en();return}let t=jt(),n=600,r=400,i=0;B.erdData.forEach(a=>{let o,s;t[a.table]?(o=t[a.table].x,s=t[a.table].y):(o=n,s=r,n+=340,n>1800&&(n=600,r+=i+60,i=0));let c=tn(a,o,s);B.selectedTables.has(a.table)&&c.classList.add(`is-selected`),e.appendChild(c);let l=44+a.columns.length*34+36;l>i&&(i=l)}),Wt(),en(),setTimeout(()=>{nn(),dn()},10)}function tn(e,t,n){let r=document.createElement(`div`);r.className=`erd-node ${e.isDraft?`is-draft`:``}`,r.dataset.table=e.table,r.style.left=`${t}px`,r.style.top=`${n}px`;let i=e.columns.filter(e=>e.isPk).length,a=e.columns.filter(e=>e.fkTarget).length,o=e.columns.map((t,n)=>{let r=t.fkTarget?`is-fk`:``,i=t.isDraft?`is-draft`:``,a=(t.type||`TEXT`).toUpperCase(),o=`<span class="erd-col-bullet"></span>`;t.isPk&&t.fkTarget?o=`<span class="erd-badge-pfk" title="Primary Foreign Key referencing ${t.fkTarget.table}.${t.fkTarget.column}">PFK</span>`:t.isPk?o=`<span class="erd-badge-pk" title="Primary Key">PK</span>`:t.fkTarget?o=`<span class="erd-badge-fk" title="Foreign Key referencing ${t.fkTarget.table}.${t.fkTarget.column}">FK</span>`:t.isUnique&&(o=`<span class="erd-badge-unique" title="Unique Constraint">UQ</span>`);let s=t.nullable?`<span class="erd-col-nullable" title="Nullable column">null</span>`:``;return`
|
|
2085
|
+
<div class="erd-column ${r} ${i}" data-col="${t.name}" data-index="${n}">
|
|
2086
|
+
<div class="erd-col-left">
|
|
2087
|
+
<span class="erd-reorder-handle" draggable="true" title="Drag to reorder column">
|
|
2088
|
+
${o}
|
|
2089
|
+
</span>
|
|
2090
|
+
<span class="erd-col-name editable-field ${t.isPk?`is-pk`:``}" data-field="name">${t.name}</span>
|
|
2091
|
+
${s}
|
|
2092
|
+
</div>
|
|
2093
|
+
<div class="erd-col-right">
|
|
2094
|
+
<span class="erd-col-type editable-field" data-field="type">${a}</span>
|
|
2095
|
+
${(t.isDraft||e.isDraft)&&!t.isPk?`<div class="erd-drag-handle" title="Drag to create Foreign Key"></div>`:``}
|
|
2096
|
+
</div>
|
|
2097
|
+
</div>
|
|
2098
|
+
`}).join(``);return r.innerHTML=`
|
|
2099
|
+
<div class="erd-node-header" title="${e.isDraft?`Double-click to rename table`:`Double-click to open Schema view`}">
|
|
2100
|
+
<div class="erd-node-header-left">
|
|
2101
|
+
<div class="erd-node-icon-box">
|
|
2102
|
+
<span class="material-symbols-outlined erd-node-icon">table_chart</span>
|
|
2103
|
+
</div>
|
|
2104
|
+
<span class="erd-node-name">${e.table}</span>
|
|
2105
|
+
${e.isDraft?`<span class="erd-badge-draft">DRAFT</span>`:``}
|
|
2106
|
+
</div>
|
|
2107
|
+
<div class="erd-node-header-right">
|
|
2108
|
+
${i>0?`<span class="erd-header-badge pk-badge" title="${i} Primary Key(s)">${i} PK</span>`:``}
|
|
2109
|
+
${a>0?`<span class="erd-header-badge fk-badge" title="${a} Foreign Key(s)">${a} FK</span>`:``}
|
|
2110
|
+
<span class="erd-col-count" title="${e.columns.length} columns">${e.columns.length} cols</span>
|
|
2111
|
+
<span class="material-symbols-outlined erd-node-drag-handle">drag_indicator</span>
|
|
2112
|
+
</div>
|
|
2113
|
+
</div>
|
|
2114
|
+
<div class="erd-node-body">
|
|
2115
|
+
${o}
|
|
2116
|
+
</div>
|
|
2117
|
+
<div class="erd-add-col-btn">
|
|
2118
|
+
<span class="material-symbols-outlined" style="font-size: 15px;">add</span>
|
|
2119
|
+
<span>Add Column</span>
|
|
2120
|
+
</div>
|
|
2121
|
+
`,r}function nn(){let e=document.getElementById(`erd-svg-layer`);if(!e)return;let t=``;B.erdData.forEach(e=>{let n=e.table,r=document.querySelector(`.erd-node[data-table="${n}"]`);r&&e.columns.forEach(e=>{if(e.fkTarget){let i=e.fkTarget.table,a=e.fkTarget.column,o=document.querySelector(`.erd-node[data-table="${i}"]`);if(!o)return;let s=r.querySelector(`.erd-column[data-col="${e.name}"]`),c=o.querySelector(`.erd-column[data-col="${a}"]`);if(!s||!c)return;let l=s.getBoundingClientRect(),u=c.getBoundingClientRect(),d=document.getElementById(`erd-workspace`).getBoundingClientRect(),f,p,m,h;l.right<u.left+20?(f=(l.right-d.left)/B.zoom,m=(u.left-d.left)/B.zoom):(f=(l.left-d.left)/B.zoom,m=(u.right-d.left)/B.zoom),p=(l.top+l.height/2-d.top)/B.zoom,h=(u.top+u.height/2-d.top)/B.zoom;let g=Math.abs(m-f),_=Math.max(g*.45,50),v=f<m?f+_:f-_,y=p,b=f<m?m-_:m+_,x=h,S=e.nullable?`url(#zero-or-one)`:`url(#one-and-only-one)`;t+=`
|
|
2122
|
+
<g class="erd-relationship-group">
|
|
2123
|
+
<path
|
|
2124
|
+
class="erd-relationship-path"
|
|
2125
|
+
d="M ${f} ${p} C ${v} ${y}, ${b} ${x}, ${m} ${h}"
|
|
2126
|
+
fill="none"
|
|
2127
|
+
marker-start="url(#crows-foot-zero-many)"
|
|
2128
|
+
marker-end="${S}"
|
|
2129
|
+
data-from-table="${n}"
|
|
2130
|
+
data-from-col="${e.name}"
|
|
2131
|
+
data-to-table="${i}"
|
|
2132
|
+
data-to-col="${a}"
|
|
2133
|
+
data-on-delete="${e.fkTarget.onDelete||`NO ACTION`}"
|
|
2134
|
+
data-on-update="${e.fkTarget.onUpdate||`NO ACTION`}"
|
|
2135
|
+
/>
|
|
2136
|
+
<text class="erd-cardinality-badge cardinality-fk" x="${f+(f<m?14:-14)}" y="${p-6}" text-anchor="middle">N</text>
|
|
2137
|
+
<text class="erd-cardinality-badge cardinality-pk" x="${m+(f<m?-14:14)}" y="${h-6}" text-anchor="middle">1</text>
|
|
2138
|
+
</g>
|
|
2139
|
+
`}})}),e.innerHTML=`
|
|
2140
|
+
<defs>
|
|
2141
|
+
<!-- FK side: Zero or Many (0<) -->
|
|
2142
|
+
<marker id="crows-foot-zero-many" viewBox="0 0 16 10" refX="16" refY="5" markerWidth="10" markerHeight="10" orient="auto-start-reverse">
|
|
2143
|
+
<path d="M 16 0 L 8 5 M 16 10 L 8 5 M 16 5 L 8 5 M 4 5 L 0 5" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
|
2144
|
+
<circle cx="5" cy="5" r="3" fill="var(--color-bg-primary)" stroke="currentColor" stroke-width="1.5" />
|
|
2145
|
+
</marker>
|
|
2146
|
+
|
|
2147
|
+
<!-- PK side: One and Only One (||) -->
|
|
2148
|
+
<marker id="one-and-only-one" viewBox="0 0 16 10" refX="13" refY="5" markerWidth="10" markerHeight="10" orient="auto">
|
|
2149
|
+
<path d="M 10 0 L 10 10 M 6 0 L 6 10" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
|
2150
|
+
</marker>
|
|
2151
|
+
|
|
2152
|
+
<!-- PK side: Zero or One (0|) -->
|
|
2153
|
+
<marker id="zero-or-one" viewBox="0 0 16 10" refX="13" refY="5" markerWidth="10" markerHeight="10" orient="auto">
|
|
2154
|
+
<path d="M 10 0 L 10 10 M 6 0 L 6 10" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
|
|
2155
|
+
<circle cx="5" cy="5" r="3" fill="var(--color-bg-primary)" stroke="currentColor" stroke-width="1.5" />
|
|
2156
|
+
</marker>
|
|
2157
|
+
</defs>
|
|
2158
|
+
${t}
|
|
2159
|
+
`}var rn=null;function an(){rn&&=(rn.remove(),null)}function on({fromTable:e,fromCol:t,toTable:n,toCol:r,onDelete:i,onUpdate:a,x:o,y:s}){an();let c=document.createElement(`div`);c.className=`erd-rel-popover`,c.style.left=`${Math.min(window.innerWidth-300,Math.max(16,o-130))}px`,c.style.top=`${Math.min(window.innerHeight-180,Math.max(16,s-40))}px`,c.innerHTML=`
|
|
2160
|
+
<div class="erd-rel-pop-header">
|
|
2161
|
+
<div class="erd-rel-pop-title">
|
|
2162
|
+
<span class="material-symbols-outlined" style="font-size:16px; color:var(--color-primary);">link</span>
|
|
2163
|
+
<span>Foreign Key Relationship</span>
|
|
2164
|
+
</div>
|
|
2165
|
+
<button type="button" class="erd-rel-pop-close icon-btn" title="Close">
|
|
2166
|
+
<span class="material-symbols-outlined" style="font-size:14px;">close</span>
|
|
2167
|
+
</button>
|
|
2168
|
+
</div>
|
|
2169
|
+
<div class="erd-rel-pop-body">
|
|
2170
|
+
<div class="erd-rel-pop-row">
|
|
2171
|
+
<span class="erd-rel-pop-label">Source (FK):</span>
|
|
2172
|
+
<span class="erd-rel-pop-code">${e}.${t}</span>
|
|
2173
|
+
</div>
|
|
2174
|
+
<div class="erd-rel-pop-row">
|
|
2175
|
+
<span class="erd-rel-pop-label">Target (PK):</span>
|
|
2176
|
+
<span class="erd-rel-pop-code">${n}.${r}</span>
|
|
2177
|
+
</div>
|
|
2178
|
+
<div class="erd-rel-pop-rules">
|
|
2179
|
+
<span>ON DELETE: <strong>${i||`NO ACTION`}</strong></span>
|
|
2180
|
+
<span>ON UPDATE: <strong>${a||`NO ACTION`}</strong></span>
|
|
2181
|
+
</div>
|
|
2182
|
+
</div>
|
|
2183
|
+
<div class="erd-rel-pop-footer">
|
|
2184
|
+
<button type="button" class="btn-secondary erd-rel-pop-focus-btn">Focus Tables</button>
|
|
2185
|
+
</div>
|
|
2186
|
+
`,document.body.appendChild(c),rn=c,c.querySelector(`.erd-rel-pop-close`).onclick=e=>{e.stopPropagation(),an()},c.querySelector(`.erd-rel-pop-focus-btn`).onclick=i=>{i.stopPropagation(),sn(e,t,n,r),an()};let l=e=>{c.contains(e.target)||(an(),document.removeEventListener(`pointerdown`,l))};setTimeout(()=>{document.addEventListener(`pointerdown`,l)},50)}function sn(e,t,n,r){let i=document.querySelectorAll(`.erd-node`),a=document.querySelectorAll(`.erd-relationship-path`),o=document.querySelectorAll(`.erd-column`);i.forEach(t=>{let r=t.dataset.table;r===e||r===n?(t.classList.remove(`is-dimmed`),t.classList.add(`is-focused`)):(t.classList.add(`is-dimmed`),t.classList.remove(`is-focused`))}),a.forEach(i=>{i.dataset.fromTable===e&&i.dataset.fromCol===t&&i.dataset.toTable===n&&i.dataset.toCol===r?(i.classList.remove(`is-dimmed`),i.classList.add(`is-focused`)):(i.classList.add(`is-dimmed`),i.classList.remove(`is-focused`))}),o.forEach(i=>{let a=i.closest(`.erd-node`)?.dataset.table,o=i.dataset.col;a===e&&o===t||a===n&&o===r?i.classList.add(`is-focused-col`):i.classList.remove(`is-focused-col`)})}function cn(e){let t=document.querySelectorAll(`.erd-node`),n=document.querySelectorAll(`.erd-relationship-path`),r=new Set([e]);n.forEach(t=>{t.dataset.fromTable===e&&r.add(t.dataset.toTable),t.dataset.toTable===e&&r.add(t.dataset.fromTable)}),t.forEach(t=>{let n=t.dataset.table;r.has(n)?(t.classList.remove(`is-dimmed`),n===e&&t.classList.add(`is-focused`)):(t.classList.add(`is-dimmed`),t.classList.remove(`is-focused`))}),n.forEach(t=>{t.dataset.fromTable===e||t.dataset.toTable===e?(t.classList.remove(`is-dimmed`),t.classList.add(`is-focused`)):(t.classList.add(`is-dimmed`),t.classList.remove(`is-focused`))})}function ln(){document.querySelectorAll(`.erd-node`).forEach(e=>{e.classList.remove(`is-dimmed`,`is-focused`,`is-relationship-highlight`)}),document.querySelectorAll(`.erd-relationship-path`).forEach(e=>{e.classList.remove(`is-dimmed`,`is-focused`,`is-hovered`)}),document.querySelectorAll(`.erd-column`).forEach(e=>{e.classList.remove(`is-focused-col`)})}function un(e,t){if(!e||!t||t.length===0)return{mapW:180,mapH:120,scale:.1,offsetX:0,offsetY:0,minX:0,minY:0,vW:800,vH:600,vLeft:0,vTop:0};let n=e.clientWidth/B.zoom,r=e.clientHeight/B.zoom,i=e.scrollLeft/B.zoom,a=e.scrollTop/B.zoom,o=1/0,s=1/0,c=-1/0,l=-1/0;t.forEach(e=>{let t=parseFloat(e.style.left)||0,n=parseFloat(e.style.top)||0,r=e.offsetWidth||290,i=e.offsetHeight||150;t<o&&(o=t),n<s&&(s=n),t+r>c&&(c=t+r),n+i>l&&(l=n+i)}),o===1/0&&(o=0,s=0,c=800,l=600),o-=200,s-=200,c+=200,l+=200;let u=c-o,d=l-s,f=Math.max(u,n),p=Math.max(d,r),m=o+u/2,h=s+d/2,g=m-f/2,_=h-p/2,v=Math.min(180/f,120/p);return{mapW:180,mapH:120,scale:v,offsetX:(180-f*v)/2,offsetY:(120-p*v)/2,minX:g,minY:_,vW:n,vH:r,vLeft:i,vTop:a}}function dn(){let e=document.getElementById(`erd-minimap-content`);if(!e)return;e.innerHTML=``;let t=document.getElementById(`erd-nodes-layer`),n=document.getElementById(`erd-wrapper`);if(!t||!n)return;let r=Array.from(t.querySelectorAll(`.erd-node`));if(r.length===0)return;let{scale:i,offsetX:a,offsetY:o,minX:s,minY:c,vW:l,vH:u,vLeft:d,vTop:f}=un(n,r);r.forEach(t=>{let n=parseFloat(t.style.left)||0,r=parseFloat(t.style.top)||0,l=t.offsetWidth||290,u=t.offsetHeight||150,d=t.dataset.table,f=document.createElement(`div`);f.style.position=`absolute`,f.style.left=`${a+(n-s)*i}px`,f.style.top=`${o+(r-c)*i}px`,f.style.width=`${Math.max(l*i,8)}px`,f.style.height=`${Math.max(u*i,6)}px`,f.style.backgroundColor=`var(--color-primary)`,f.style.opacity=B.selectedTables.has(d)?`1`:`0.6`,f.style.borderRadius=`2px`,e.appendChild(f)});let p=document.getElementById(`erd-minimap-viewport`);p&&(p.style.left=`${a+(d-s)*i}px`,p.style.top=`${o+(f-c)*i}px`,p.style.width=`${l*i}px`,p.style.height=`${u*i}px`)}function fn(e=120,t=120){let n=`new_table_${Math.floor(Math.random()*1e3)}`,r={table:n,isDraft:!0,columns:[{name:`id`,type:`INTEGER`,isPk:!0,isDraft:!0,nullable:!1}]},i=jt();i[n]={x:e,y:t},Mt(i),B.pushUndoState(),B.erdData.push(r),B.setSelection(n),V()}async function pn(e){let t=document.getElementById(`table-name`);t&&(t.textContent=`Entity Relationship Diagram`);let n=document.getElementById(`table-icon`);n&&(n.textContent=`account_tree`),document.getElementById(`erd-main-viewport`)||(e.innerHTML=`
|
|
2187
|
+
<div id="erd-main-viewport" class="erd-main-viewport">
|
|
2188
|
+
<!-- Canvas Scrollable Workspace -->
|
|
2189
|
+
<div id="erd-wrapper" class="erd-grid-bg erd-workspace-container">
|
|
2190
|
+
<div id="erd-workspace" class="erd-workspace-canvas">
|
|
2191
|
+
<svg id="erd-svg-layer" class="erd-svg-layer"></svg>
|
|
2192
|
+
<div id="erd-nodes-layer" class="erd-nodes-layer"></div>
|
|
2193
|
+
</div>
|
|
2194
|
+
</div>
|
|
2195
|
+
|
|
2196
|
+
<!-- Top Floating Action HUD -->
|
|
2197
|
+
<div class="erd-top-hud">
|
|
2198
|
+
<div class="erd-hud-left">
|
|
2199
|
+
<div class="erd-search-box">
|
|
2200
|
+
<span class="material-symbols-outlined search-icon">search</span>
|
|
2201
|
+
<input type="text" id="erd-search-input" placeholder="Search tables..." autocomplete="off">
|
|
2202
|
+
<kbd class="erd-search-shortcut">/</kbd>
|
|
2203
|
+
<div id="erd-search-results" class="erd-search-results hidden"></div>
|
|
2204
|
+
</div>
|
|
2205
|
+
</div>
|
|
2206
|
+
|
|
2207
|
+
<div class="erd-hud-right">
|
|
2208
|
+
<button type="button" id="btn-erd-add-table" class="erd-hud-btn" title="Add a new draft table">
|
|
2209
|
+
<span class="material-symbols-outlined icon-18">add</span>
|
|
2210
|
+
<span>Add Table</span>
|
|
2211
|
+
</button>
|
|
2212
|
+
|
|
2213
|
+
<button type="button" id="btn-erd-auto-layout" class="erd-hud-btn" title="Auto arrange tables neatly">
|
|
2214
|
+
<span class="material-symbols-outlined icon-18">account_tree</span>
|
|
2215
|
+
<span>Auto Layout</span>
|
|
2216
|
+
</button>
|
|
2217
|
+
|
|
2218
|
+
<button type="button" id="btn-erd-snap-grid" class="erd-hud-btn active" title="Toggle magnetic grid snapping">
|
|
2219
|
+
<span class="material-symbols-outlined icon-18">grid_4x4</span>
|
|
2220
|
+
<span>Snap</span>
|
|
2221
|
+
</button>
|
|
2222
|
+
|
|
2223
|
+
<div class="erd-hud-divider"></div>
|
|
2224
|
+
|
|
2225
|
+
<button type="button" id="btn-erd-export" class="erd-hud-btn" title="Export diagram (SVG or SQL)">
|
|
2226
|
+
<span class="material-symbols-outlined icon-18">download</span>
|
|
2227
|
+
<span>Export</span>
|
|
2228
|
+
</button>
|
|
2229
|
+
|
|
2230
|
+
<button type="button" id="btn-erd-save-drafts" class="erd-hud-btn primary disabled" title="Save draft tables and columns (Ctrl+S)">
|
|
2231
|
+
<span class="material-symbols-outlined icon-18">save</span>
|
|
2232
|
+
<span>Save</span>
|
|
2233
|
+
<span class="erd-draft-count-badge" style="display: none;">0</span>
|
|
2234
|
+
</button>
|
|
2235
|
+
</div>
|
|
2236
|
+
</div>
|
|
2237
|
+
|
|
2238
|
+
<!-- Bottom-Left Shortcuts Helper -->
|
|
2239
|
+
<div class="erd-shortcuts-helper">
|
|
2240
|
+
<span class="erd-shortcut-item"><kbd>Alt</kbd>+Drag Pan</span>
|
|
2241
|
+
<span class="erd-shortcut-item"><kbd>Ctrl</kbd>+Scroll Zoom</span>
|
|
2242
|
+
<span class="erd-shortcut-item"><kbd>Ctrl</kbd>+<kbd>S</kbd> Save</span>
|
|
2243
|
+
</div>
|
|
2244
|
+
|
|
2245
|
+
<!-- Bottom-Right Floating Navigation Dock -->
|
|
2246
|
+
<div class="erd-bottom-dock">
|
|
2247
|
+
<!-- Collapsible Minimap -->
|
|
2248
|
+
<div id="erd-minimap-container" class="erd-minimap-container">
|
|
2249
|
+
<div class="erd-minimap-header">
|
|
2250
|
+
<span class="erd-minimap-title">
|
|
2251
|
+
<span class="material-symbols-outlined icon-14">map</span>
|
|
2252
|
+
Minimap
|
|
2253
|
+
</span>
|
|
2254
|
+
<button type="button" id="btn-erd-close-minimap" class="icon-btn" title="Hide minimap">
|
|
2255
|
+
<span class="material-symbols-outlined icon-14">close</span>
|
|
2256
|
+
</button>
|
|
2257
|
+
</div>
|
|
2258
|
+
<div id="erd-minimap" class="erd-minimap">
|
|
2259
|
+
<div id="erd-minimap-content" class="erd-minimap-content"></div>
|
|
2260
|
+
<div id="erd-minimap-viewport" class="erd-minimap-viewport"></div>
|
|
2261
|
+
</div>
|
|
2262
|
+
</div>
|
|
2263
|
+
|
|
2264
|
+
<!-- Zoom HUD Controls -->
|
|
2265
|
+
<div class="erd-zoom-controls">
|
|
2266
|
+
<button type="button" id="btn-erd-zoom-out" class="erd-dock-btn" title="Zoom Out">
|
|
2267
|
+
<span class="material-symbols-outlined icon-18">remove</span>
|
|
2268
|
+
</button>
|
|
2269
|
+
<button type="button" id="btn-erd-zoom-reset" class="erd-dock-btn erd-zoom-value" title="Reset Zoom to 100%">
|
|
2270
|
+
100%
|
|
2271
|
+
</button>
|
|
2272
|
+
<button type="button" id="btn-erd-zoom-in" class="erd-dock-btn" title="Zoom In">
|
|
2273
|
+
<span class="material-symbols-outlined icon-18">add</span>
|
|
2274
|
+
</button>
|
|
2275
|
+
<button type="button" id="btn-erd-fit-view" class="erd-dock-btn" title="Fit tables to view">
|
|
2276
|
+
<span class="material-symbols-outlined icon-18">crop_free</span>
|
|
2277
|
+
</button>
|
|
2278
|
+
<button type="button" id="btn-erd-fullscreen" class="erd-dock-btn" title="Toggle Fullscreen">
|
|
2279
|
+
<span class="material-symbols-outlined icon-18">fullscreen</span>
|
|
2280
|
+
</button>
|
|
2281
|
+
<div class="erd-dock-divider"></div>
|
|
2282
|
+
<button type="button" id="btn-erd-toggle-minimap" class="erd-dock-btn active" title="Toggle Minimap">
|
|
2283
|
+
<span class="material-symbols-outlined icon-18">map</span>
|
|
2284
|
+
</button>
|
|
2285
|
+
</div>
|
|
2286
|
+
</div>
|
|
2287
|
+
</div>
|
|
2288
|
+
`,document.getElementById(`erd-wrapper`).addEventListener(`contextmenu`,e=>{if(e.preventDefault(),e.target.closest(`.erd-node`))return;let t=document.getElementById(`erd-workspace`).getBoundingClientRect(),n=(e.clientX-t.left)/B.zoom,r=(e.clientY-t.top)/B.zoom;A(e,[{label:`Add Table`,icon:`table_chart`,action:()=>fn(n,r)},{label:`Paste`,icon:`content_paste`,action:Ht}])}),$t());let r=document.getElementById(`erd-nodes-layer`);r&&(r.innerHTML=`<div class="erd-loading-state"><span class="material-symbols-outlined animate-spin icon-24">progress_activity</span><span>Loading Schema Diagrams...</span></div>`),B.erdData=await At()||[],B.erdData.forEach(e=>{e.columns.sort((e,t)=>e.isPk&&!t.isPk?-1:!e.isPk&&t.isPk||e.fkTarget&&!t.fkTarget?1:!e.fkTarget&&t.fkTarget?-1:0),e.columns.forEach(e=>{e.isPk&&(e.nullable=!1)})}),B.undoStack=[],B.clearSelection(),V(),setTimeout(()=>{let e=document.getElementById(`erd-wrapper`);if(e&&e.scrollLeft===0&&e.scrollTop===0){let t=document.querySelectorAll(`.erd-node`);if(t.length>0){let n=1/0,r=1/0,i=-1/0,a=-1/0;t.forEach(e=>{let t=parseFloat(e.style.left)||0,o=parseFloat(e.style.top)||0,s=e.offsetWidth||290,c=e.offsetHeight||150;t<n&&(n=t),o<r&&(r=o),t+s>i&&(i=t+s),o+c>a&&(a=o+c)});let o=(n+i)/2,s=(r+a)/2;e.scrollLeft=o*B.zoom-e.clientWidth/2,e.scrollTop=s*B.zoom-e.clientHeight/2}}},40)}var mn=null,hn=null;function gn({icon:e,label:t,value:n,badge:r=``,badgeClass:i=`badge-neutral`}){return`
|
|
2289
|
+
<div class="status-dashboard-status-tile">
|
|
2290
|
+
<div class="status-dashboard-tile-icon">
|
|
2291
|
+
<span class="material-symbols-outlined">${e}</span>
|
|
2292
|
+
</div>
|
|
2293
|
+
<div class="status-dashboard-tile-main">
|
|
2294
|
+
<div class="status-dashboard-tile-label">${t}</div>
|
|
2295
|
+
<div class="status-dashboard-tile-value-row">
|
|
2296
|
+
<span class="status-dashboard-tile-value">${n}</span>
|
|
2297
|
+
${r?`<span class="status-dashboard-tile-badge ${i}">${r}</span>`:``}
|
|
2298
|
+
</div>
|
|
2299
|
+
</div>
|
|
2300
|
+
</div>
|
|
2301
|
+
`}function _n(e,t=0){let n=document.getElementById(`status-cards-container`);if(!n)return;let r=``,i=Date.now(),a=e.status===`connected`;r+=gn({icon:`apps`,label:`STATUS`,value:a?`Healthy`:`Disconnected`,badge:a?`NORMAL`:`OFFLINE`,badgeClass:a?`badge-status-ok`:`badge-status-err`});let o=(e.dbType||`UNKNOWN`).toUpperCase(),s=e.version?`v${e.version}`:`NANO`;r+=gn({icon:`memory`,label:`COMPUTE`,value:o,badge:s,badgeClass:`badge-primary`}),r+=gn({icon:`database`,label:`STORAGE`,value:yt(e.sizeBytes)});let c=e.activeConnections||(e.dbType,1);r+=gn({icon:`hub`,label:`CONNECTIONS`,value:`${c} Active`,badge:e.dbType===`sqlite`?`LOCAL`:`POOLED`,badgeClass:`badge-neutral`}),r+=gn({icon:`table_rows`,label:`TABLES`,value:`${e.totalTables??0} Tables`});let l=`${(t||0).toLocaleString()} Rows`;if(e.dbType===`mysql`&&e.queries){if(mn&&hn){let t=(i-hn)/1e3;l=`${(Math.max(0,e.queries-(mn.queries||0))/t).toFixed(1)} QPS`}}else if(e.dbType===`postgres`&&e.transactions&&mn&&hn){let t=(i-hn)/1e3;l=`${(Math.max(0,e.transactions-(mn.transactions||0))/t).toFixed(1)} TPS`}r+=gn({icon:`inventory_2`,label:`DATABASE RECORDS`,value:l}),n.innerHTML=r,mn=e,hn=i}window.navigateToTableData=function(e){let t=document.querySelector(`.table-btn[data-table="${e}"]`);t&&(window.AppState.currentTable=e,window.AppState.currentTableBtnElement=t,window.handleSwitchTab(`data-btn`))};function vn(e={}){let t=document.getElementById(`status-distribution-container`),n=document.getElementById(`status-toptables-container`);if(!t||!n)return 0;let r=Object.entries(e).sort((e,t)=>t[1]-e[1]),i=r.reduce((e,[t,n])=>e+n,0);if(r.length===0)return n.classList.remove(`hidden`),t.classList.add(`hidden`),n.style.gridColumn=`1 / -1`,n.innerHTML=`
|
|
2302
|
+
<div class="drixio-empty-analytics">
|
|
2303
|
+
<div class="drixio-empty-icon">
|
|
2304
|
+
<span class="material-symbols-outlined">table_chart_view</span>
|
|
2305
|
+
</div>
|
|
2306
|
+
<div class="drixio-empty-title">No tables found in this database</div>
|
|
2307
|
+
<div class="drixio-empty-desc">Create your first database table or import a SQL script to populate data analytics and track records.</div>
|
|
2308
|
+
<div class="drixio-empty-actions">
|
|
2309
|
+
<button type="button" class="btn-primary" onclick="window.openCreateTableModal ? window.openCreateTableModal() : document.getElementById('add-table-btn')?.click()">
|
|
2310
|
+
<span class="material-symbols-outlined" style="font-size: 15px;">add</span>
|
|
2311
|
+
<span>Create Table</span>
|
|
2312
|
+
</button>
|
|
2313
|
+
<button type="button" class="btn-secondary" onclick="document.getElementById('btn-quick-import-backup')?.click()">
|
|
2314
|
+
<span class="material-symbols-outlined" style="font-size: 15px;">upload</span>
|
|
2315
|
+
<span>Import SQL</span>
|
|
2316
|
+
</button>
|
|
2317
|
+
</div>
|
|
2318
|
+
</div>
|
|
2319
|
+
`,0;n.style.gridColumn=``;let a=r.slice(0,5),o=[`#3b82f6`,`#6366f1`,`#06b6d4`,`#f59e0b`,`#ec4899`],s=`
|
|
2320
|
+
<div class="analytics-panel-header">
|
|
2321
|
+
<div class="flex items-center gap-2">
|
|
2322
|
+
<span class="material-symbols-outlined text-primary" style="font-size: 18px;">leaderboard</span>
|
|
2323
|
+
<h3 class="analytics-panel-title">Largest Tables by Rows</h3>
|
|
2324
|
+
</div>
|
|
2325
|
+
<span class="analytics-panel-badge">${r.length} tables total</span>
|
|
2326
|
+
</div>
|
|
2327
|
+
<div class="top-tables-list">
|
|
2328
|
+
`,c=a[0]?Math.max(a[0][1],1):1;a.forEach(([e,t],n)=>{let r=(t/c*100).toFixed(1),a=i>0?(t/i*100).toFixed(1):0;s+=`
|
|
2329
|
+
<div class="top-table-item hover-clickable" data-table="${e}" onclick="window.navigateToTableData('${e}')" title="Click to view ${e} data">
|
|
2330
|
+
<div class="top-table-bar-fill" style="width: ${r}%; background-color: ${o[n]}"></div>
|
|
2331
|
+
<div class="top-table-content">
|
|
2332
|
+
<div class="top-table-left">
|
|
2333
|
+
<span class="top-table-rank">#${n+1}</span>
|
|
2334
|
+
<span class="top-table-name">${e}</span>
|
|
2335
|
+
</div>
|
|
2336
|
+
<div class="top-table-right">
|
|
2337
|
+
<span class="top-table-pct">${a}%</span>
|
|
2338
|
+
<span class="top-table-rows">${t.toLocaleString()} rows</span>
|
|
2339
|
+
<span class="material-symbols-outlined top-table-arrow">arrow_forward</span>
|
|
2340
|
+
</div>
|
|
2341
|
+
</div>
|
|
2342
|
+
</div>
|
|
2343
|
+
`}),s+=`</div>`,n.innerHTML=s,n.classList.remove(`hidden`),n.classList.add(`block`);let l=`
|
|
2344
|
+
<div class="analytics-panel-header">
|
|
2345
|
+
<div class="flex items-center gap-2">
|
|
2346
|
+
<span class="material-symbols-outlined text-primary" style="font-size: 18px;">pie_chart</span>
|
|
2347
|
+
<h3 class="analytics-panel-title">Row Count Distribution</h3>
|
|
2348
|
+
</div>
|
|
2349
|
+
<span class="analytics-panel-badge">${i.toLocaleString()} total rows</span>
|
|
2350
|
+
</div>
|
|
2351
|
+
<div class="distribution-content">
|
|
2352
|
+
`,u=`<div class="distribution-legend">`;if(i>0){let e=0,t=0,n=`<svg width="140" height="140" viewBox="0 0 140 140" class="distribution-pie" style="transform: rotate(-90deg);">`,s=(e,r,i,a)=>{2*Math.PI*(70/2);let o=2*Math.PI*35,s=r/100*o,c=-(t/100*o);n+=`<circle class="donut-slice" data-table="${i}" r="35" cx="${140/2}" cy="${140/2}"
|
|
2353
|
+
fill="transparent" stroke="${e}" stroke-width="70"
|
|
2354
|
+
stroke-dasharray="${s} ${o}" stroke-dashoffset="${c}"
|
|
2355
|
+
title="${i}: ${a.toLocaleString()} rows (${r.toFixed(1)}%)"
|
|
2356
|
+
style="transition: opacity 0.2s; cursor: pointer;"></circle>`,t+=r};a.forEach(([t,n],r)=>{let a=n/i*100;e+=n,s(o[r],a,t,n),u+=`
|
|
2357
|
+
<div class="legend-item" data-table="${t}" onclick="window.navigateToTableData('${t}')" style="cursor: pointer;" title="View ${t}">
|
|
2358
|
+
<div class="legend-color" style="background: ${o[r]}"></div>
|
|
2359
|
+
<span class="legend-name">${t}</span>
|
|
2360
|
+
<span class="legend-pct">${a.toFixed(1)}%</span>
|
|
2361
|
+
</div>
|
|
2362
|
+
`});let c=i-e;if(c>0&&r.length>5){let e=c/i*100;s(`#64748b`,e,`Others`,c),u+=`
|
|
2363
|
+
<div class="legend-item">
|
|
2364
|
+
<div class="legend-color" style="background: #64748b"></div>
|
|
2365
|
+
<span class="legend-name">Others (${r.length-5})</span>
|
|
2366
|
+
<span class="legend-pct">${e.toFixed(1)}%</span>
|
|
2367
|
+
</div>
|
|
2368
|
+
`}n+=`<circle r="${70*.74}" cx="${140/2}" cy="${140/2}" fill="var(--color-bg-secondary)"></circle>`,n+=`</svg>`;let d=i>999999?(i/1e6).toFixed(1)+`M`:i.toLocaleString();l+=`
|
|
2369
|
+
<div class="distribution-pie-wrapper stagger-in">
|
|
2370
|
+
${n}
|
|
2371
|
+
<div class="donut-center-info">
|
|
2372
|
+
<span class="donut-total-num">${d}</span>
|
|
2373
|
+
<span class="donut-total-label">Total Rows</span>
|
|
2374
|
+
</div>
|
|
2375
|
+
</div>
|
|
2376
|
+
`}else l+=`
|
|
2377
|
+
<div class="distribution-pie-wrapper stagger-in">
|
|
2378
|
+
<svg width="140" height="140" viewBox="0 0 140 140" class="distribution-pie">
|
|
2379
|
+
<circle r="35" cx="70" cy="70" fill="transparent" stroke="var(--color-border)" stroke-width="70"></circle>
|
|
2380
|
+
<circle r="51.8" cx="70" cy="70" fill="var(--color-bg-secondary)"></circle>
|
|
2381
|
+
</svg>
|
|
2382
|
+
<div class="donut-center-info">
|
|
2383
|
+
<span class="donut-total-num">0</span>
|
|
2384
|
+
<span class="donut-total-label">No Rows</span>
|
|
2385
|
+
</div>
|
|
2386
|
+
</div>
|
|
2387
|
+
`,u+=`<div class="text-soft" style="font-size: 13px;">No data rows found</div>`;u+=`</div>`,l+=u+`</div>`,t.innerHTML=l,t.classList.remove(`hidden`),t.classList.add(`block`);let d=n.querySelectorAll(`.top-table-item`),f=t.querySelectorAll(`.legend-item[data-table]`),p=t.querySelectorAll(`.donut-slice[data-table]`),m=(e,r)=>{n.querySelector(`.top-table-item[data-table="${e}"]`)?.classList.toggle(`is-hovered`,r),t.querySelector(`.legend-item[data-table="${e}"]`)?.classList.toggle(`is-hovered`,r),p.length>0&&(r?p.forEach(t=>{t.dataset.table===e?t.style.opacity=`1`:t.style.opacity=`0.3`}):p.forEach(e=>e.style.opacity=`1`))};return d.forEach(e=>{let t=e.dataset.table;e.addEventListener(`mouseenter`,()=>m(t,!0)),e.addEventListener(`mouseleave`,()=>m(t,!1))}),f.forEach(e=>{let t=e.dataset.table;e.addEventListener(`mouseenter`,()=>m(t,!0)),e.addEventListener(`mouseleave`,()=>m(t,!1))}),p.forEach(e=>{let t=e.dataset.table;e.addEventListener(`mouseenter`,()=>m(t,!0)),e.addEventListener(`mouseleave`,()=>m(t,!1))}),i}var yn=null,bn=`records`,xn=null;function Sn(e=`records`){bn=e,xn=null,yn||On(),Tn(),yn.classList.remove(`hidden`)}function Cn(){yn&&(yn.classList.add(`hidden`),xn=null,wn())}function wn(){let e=document.getElementById(`import-file-input`);e&&(e.value=``);let t=document.getElementById(`import-file-preview`);t&&t.classList.add(`hidden`);let n=document.getElementById(`import-dropzone`);n&&n.classList.remove(`has-file`,`drag-over`);let r=document.getElementById(`import-modal-submit-btn`);r&&(r.disabled=!0)}function Tn(){let e=window.AppState?.currentTable||``,t=document.getElementById(`import-target-table-wrap`),n=document.getElementById(`import-target-table-name`),r=document.getElementById(`import-format-hint`),i=document.getElementById(`import-file-input`),a=document.getElementById(`import-modal-submit-btn`);document.querySelectorAll(`.import-type-tab`).forEach(e=>{e.getAttribute(`data-type`)===bn?e.classList.add(`active`):e.classList.remove(`active`)}),bn===`records`?(t&&(t.style.display=`flex`),n&&(n.textContent=e||`No table selected`,e?n.classList.remove(`empty`):n.classList.add(`empty`)),r&&(r.textContent=`Accepts .csv or .json files`),i&&(i.accept=`.csv,.json`)):bn===`erd`?(t&&(t.style.display=`none`),r&&(r.textContent=`Accepts canvas layout JSON files (.json)`),i&&(i.accept=`.json`)):bn===`database`&&(t&&(t.style.display=`none`),r&&(r.textContent=`Accepts SQL dumps (.sql) or schema JSON`),i&&(i.accept=`.sql,.json`)),a&&(a.disabled=!xn)}function En(e){if(!e)return;let t=e.name.split(`.`).pop()?.toLowerCase(),n={records:[`csv`,`json`],erd:[`json`],database:[`sql`,`json`]}[bn]||[];if(!n.includes(t)){typeof window.showToast==`function`&&window.showToast(`Invalid file format for ${bn}. Please select a .${n.join(` or .`)} file.`,`error`);return}xn=e;let r=document.getElementById(`import-file-name`),i=document.getElementById(`import-file-size`),a=document.getElementById(`import-file-preview`),o=document.getElementById(`import-dropzone`),s=document.getElementById(`import-modal-submit-btn`);if(r&&(r.textContent=e.name),i){let t=e.size/1024;i.textContent=t>1024?`${(t/1024).toFixed(2)} MB`:`${t.toFixed(1)} KB`}a&&a.classList.remove(`hidden`),o&&o.classList.add(`has-file`),s&&(s.disabled=!1)}async function Dn(){if(!xn)return;let e=document.getElementById(`import-modal-submit-btn`),t=e?.innerHTML;e&&(e.disabled=!0,e.innerHTML=`
|
|
2388
|
+
<span class="material-symbols-outlined icon-spin" style="font-size: 16px;">progress_activity</span>
|
|
2389
|
+
<span>Importing...</span>
|
|
2390
|
+
`);try{if(bn===`records`){let e=window.AppState?.currentTable;if(!e){window.showToast?.(`Please select a table from the sidebar first.`,`error`);return}let t=xn.name.split(`.`).pop()?.toLowerCase()===`json`?`json`:`csv`,n=new FormData;n.append(`file`,xn),n.append(`format`,t),window.showToast?.(`Importing ${xn.name} into '${e}'...`,`success`);let r=await(await fetch(`/api/tables/${encodeURIComponent(e)}/import`,{method:`POST`,body:n})).json();if(r.success){if(window.showToast?.(r.message||`Import completed successfully`,`success`),window.AppState?.currentTab===`data-btn`){let t=`view-data-btn-${e}`,n=document.getElementById(t);n&&(n.innerHTML=``),window.renderCurrentView?.(``,!0)}Cn()}else window.showToast?.(r.error||`Import failed`,`error`)}else if(bn===`erd`){let e=await xn.text(),t=JSON.parse(e);if(typeof t!=`object`||!t)throw Error(`Invalid ERD layout JSON structure.`);let n=t.positions||t;localStorage.setItem(`drixio-erd-positions`,JSON.stringify(n)),window.showToast?.(`ERD layout imported successfully!`,`success`);let r=document.getElementById(`view-erd-btn`);r&&(r.innerHTML=``,window.AppState?.currentTab===`erd-btn`&&window.renderCurrentView?.()),Cn()}else if(bn===`database`){if(!confirm(`🚨 HIGH RISK ACTION: DATABASE RESTORE\n\nFile: ${xn.name} (${(xn.size/1024).toFixed(1)} KB)\n\nExecuting this SQL script directly against the database may modify, truncate, or drop existing tables.\n\nAre you sure you want to proceed?`))return;let e=new FormData;e.append(`file`,xn),window.showToast?.(`Executing ${xn.name} on database...`,`success`);let t=await(await fetch(`/api/database/import`,{method:`POST`,body:e})).json();t.success?(window.showToast?.(t.message||`Database restored successfully`,`success`),Cn(),setTimeout(()=>location.reload(),1500)):window.showToast?.(t.error||`Import failed`,`error`)}}catch(e){window.showToast?.(`Import error: ${e.message}`,`error`)}finally{e&&t&&(e.disabled=!xn,e.innerHTML=t)}}function On(){yn=document.createElement(`div`),yn.id=`import-modal-overlay`,yn.className=`modal-overlay hidden`,yn.innerHTML=`
|
|
2391
|
+
<div class="modal-container import-modal-card">
|
|
2392
|
+
<div class="modal-header">
|
|
2393
|
+
<div class="modal-title-wrap">
|
|
2394
|
+
<div class="modal-icon-badge">
|
|
2395
|
+
<span class="material-symbols-outlined">upload_file</span>
|
|
2396
|
+
</div>
|
|
2397
|
+
<div class="modal-title-col">
|
|
2398
|
+
<h3 class="modal-title-text">Import Data</h3>
|
|
2399
|
+
<p class="modal-subtitle-text">Upload records, ERD layouts, or restore your database</p>
|
|
2400
|
+
</div>
|
|
2401
|
+
</div>
|
|
2402
|
+
<button type="button" id="import-modal-close-btn" class="modal-close-btn" title="Close (Esc)" aria-label="Close modal">
|
|
2403
|
+
<span class="material-symbols-outlined">close</span>
|
|
2404
|
+
</button>
|
|
2405
|
+
</div>
|
|
2406
|
+
|
|
2407
|
+
<div class="import-modal-body">
|
|
2408
|
+
<!-- Tabs -->
|
|
2409
|
+
<div class="import-type-tabs" role="tablist">
|
|
2410
|
+
<button type="button" class="import-type-tab active" data-type="records">
|
|
2411
|
+
<span class="material-symbols-outlined">table_chart</span>
|
|
2412
|
+
<span>Table Records</span>
|
|
2413
|
+
</button>
|
|
2414
|
+
<button type="button" class="import-type-tab" data-type="erd">
|
|
2415
|
+
<span class="material-symbols-outlined">account_tree</span>
|
|
2416
|
+
<span>ERD Layout</span>
|
|
2417
|
+
</button>
|
|
2418
|
+
<button type="button" class="import-type-tab" data-type="database">
|
|
2419
|
+
<span class="material-symbols-outlined">database</span>
|
|
2420
|
+
<span>Database Restore</span>
|
|
2421
|
+
</button>
|
|
2422
|
+
</div>
|
|
2423
|
+
|
|
2424
|
+
<!-- Target Table Banner (Only for records) -->
|
|
2425
|
+
<div id="import-target-table-wrap" class="import-target-banner">
|
|
2426
|
+
<span class="material-symbols-outlined banner-icon">pin_drop</span>
|
|
2427
|
+
<span class="banner-label">Target Table:</span>
|
|
2428
|
+
<span id="import-target-table-name" class="target-table-badge">users</span>
|
|
2429
|
+
</div>
|
|
2430
|
+
|
|
2431
|
+
<!-- Drag & Drop Zone -->
|
|
2432
|
+
<div id="import-dropzone" class="import-dropzone">
|
|
2433
|
+
<span class="material-symbols-outlined dropzone-icon">cloud_upload</span>
|
|
2434
|
+
<div class="dropzone-info">
|
|
2435
|
+
<p class="dropzone-text">Drag & drop your file here, or <span class="dropzone-browse">browse files</span></p>
|
|
2436
|
+
<span id="import-format-hint" class="dropzone-hint">Accepts .csv or .json files</span>
|
|
2437
|
+
</div>
|
|
2438
|
+
<input type="file" id="import-file-input" class="hidden-file-input" />
|
|
2439
|
+
</div>
|
|
2440
|
+
|
|
2441
|
+
<!-- File Preview Card -->
|
|
2442
|
+
<div id="import-file-preview" class="import-file-preview hidden">
|
|
2443
|
+
<div class="preview-left">
|
|
2444
|
+
<span class="material-symbols-outlined preview-icon">description</span>
|
|
2445
|
+
<div class="preview-text">
|
|
2446
|
+
<span id="import-file-name" class="preview-filename">file.csv</span>
|
|
2447
|
+
<span id="import-file-size" class="preview-filesize">0 KB</span>
|
|
2448
|
+
</div>
|
|
2449
|
+
</div>
|
|
2450
|
+
<button type="button" id="import-file-remove-btn" class="preview-remove-btn" title="Remove selected file">✕</button>
|
|
2451
|
+
</div>
|
|
2452
|
+
</div>
|
|
2453
|
+
|
|
2454
|
+
<div class="modal-footer import-modal-footer">
|
|
2455
|
+
<button type="button" id="import-modal-cancel-btn" class="import-cancel-btn">Cancel</button>
|
|
2456
|
+
<button type="button" id="import-modal-submit-btn" class="import-submit-btn" disabled>
|
|
2457
|
+
<span class="material-symbols-outlined btn-icon">upload</span>
|
|
2458
|
+
<span>Import File</span>
|
|
2459
|
+
</button>
|
|
2460
|
+
</div>
|
|
2461
|
+
</div>
|
|
2462
|
+
`,document.body.appendChild(yn),kn()}function kn(){if(!yn)return;let e=document.getElementById(`import-modal-close-btn`),t=document.getElementById(`import-modal-cancel-btn`),n=document.getElementById(`import-modal-submit-btn`),r=document.getElementById(`import-dropzone`),i=document.getElementById(`import-file-input`),a=document.getElementById(`import-file-remove-btn`);e?.addEventListener(`click`,Cn),t?.addEventListener(`click`,Cn),yn.addEventListener(`click`,e=>{e.target===yn&&Cn()}),document.addEventListener(`keydown`,e=>{e.key===`Escape`&&yn&&!yn.classList.contains(`hidden`)&&Cn()}),document.querySelectorAll(`.import-type-tab`).forEach(e=>{e.addEventListener(`click`,()=>{let t=e.getAttribute(`data-type`);t&&t!==bn&&(bn=t,xn=null,wn(),Tn())})}),r?.addEventListener(`click`,e=>{e.target!==a&&i?.click()}),i?.addEventListener(`change`,e=>{let t=e.target.files?.[0];t&&En(t)}),r?.addEventListener(`dragover`,e=>{e.preventDefault(),e.stopPropagation(),r.classList.add(`drag-over`)}),r?.addEventListener(`dragleave`,e=>{e.preventDefault(),e.stopPropagation(),r.classList.remove(`drag-over`)}),r?.addEventListener(`drop`,e=>{e.preventDefault(),e.stopPropagation(),r.classList.remove(`drag-over`);let t=e.dataTransfer?.files?.[0];t&&En(t)}),a?.addEventListener(`click`,e=>{e.stopPropagation(),xn=null,wn(),Tn()}),n?.addEventListener(`click`,Dn)}function An(){Mn()}async function jn(e){e.innerHTML=`
|
|
2463
|
+
<div class="status-dashboard" style="opacity: 0;">
|
|
2464
|
+
<!-- Top Section: Left (Title + Endpoint + 6 Cards) | Right (Dotted Matrix Canvas) -->
|
|
2465
|
+
<div class="status-dashboard-hero">
|
|
2466
|
+
<!-- Left Column -->
|
|
2467
|
+
<div class="drixio-hero-left">
|
|
2468
|
+
<div class="drixio-hero-header skeleton-target">
|
|
2469
|
+
<h1 class="status-dashboard-db-title" id="hero-db-name">Loading...</h1>
|
|
2470
|
+
<div class="status-dashboard-endpoint">
|
|
2471
|
+
<span class="status-dashboard-endpoint-text" id="hero-conn-uri">Connecting to database instance...</span>
|
|
2472
|
+
<button type="button" class="status-dashboard-copy-btn" id="btn-copy-conn-uri" title="Copy Connection URI">
|
|
2473
|
+
<span class="material-symbols-outlined" style="font-size: 13px;">content_copy</span>
|
|
2474
|
+
<span id="btn-copy-conn-label">Copy</span>
|
|
2475
|
+
</button>
|
|
2476
|
+
</div>
|
|
2477
|
+
</div>
|
|
2478
|
+
|
|
2479
|
+
<!-- 6 Supabase-style Compact Status Tiles (2x3 Grid) -->
|
|
2480
|
+
<div class="status-dashboard-tiles-grid" id="status-cards-container">
|
|
2481
|
+
<!-- Skeleton Cards -->
|
|
2482
|
+
${[,,,,,,].fill(`
|
|
2483
|
+
<div class="status-dashboard-status-tile skeleton-target" style="background: rgba(255,255,255,0.02); border: 1px solid var(--color-border);">
|
|
2484
|
+
<div class="status-dashboard-tile-icon" style="background: rgba(255,255,255,0.05); color: transparent;">
|
|
2485
|
+
<span class="material-symbols-outlined">circle</span>
|
|
2486
|
+
</div>
|
|
2487
|
+
<div class="status-dashboard-tile-main">
|
|
2488
|
+
<div class="status-dashboard-tile-label" style="width: 40px; height: 10px; background: rgba(255,255,255,0.1); border-radius: 4px;"></div>
|
|
2489
|
+
<div class="status-dashboard-tile-value-row">
|
|
2490
|
+
<span class="status-dashboard-tile-value" style="width: 80px; height: 16px; background: rgba(255,255,255,0.15); border-radius: 4px;"></span>
|
|
2491
|
+
</div>
|
|
2492
|
+
</div>
|
|
2493
|
+
</div>`).join(``)}
|
|
2494
|
+
</div>
|
|
2495
|
+
</div>
|
|
2496
|
+
|
|
2497
|
+
<!-- Right Column: Supabase Dotted Matrix Canvas with Primary DB Node -->
|
|
2498
|
+
<div class="status-dashboard-hero-right skeleton-target">
|
|
2499
|
+
<div class="status-dashboard-canvas-wrapper">
|
|
2500
|
+
<div class="status-dashboard-canvas-dots"></div>
|
|
2501
|
+
|
|
2502
|
+
<div class="status-dashboard-canvas-actions">
|
|
2503
|
+
<button type="button" class="status-dashboard-canvas-actions-btn" id="btn-canvas-center" title="Center Topology">
|
|
2504
|
+
<span class="material-symbols-outlined">center_focus_strong</span>
|
|
2505
|
+
</button>
|
|
2506
|
+
</div>
|
|
2507
|
+
|
|
2508
|
+
<!-- Floating Primary DB Node -->
|
|
2509
|
+
<div class="status-dashboard-canvas-node" id="primary-db-node" style="opacity: 0;">
|
|
2510
|
+
<div class="node-main">
|
|
2511
|
+
<div class="node-icon-box" id="node-engine-avatar">
|
|
2512
|
+
<span class="material-symbols-outlined" id="node-engine-icon">database</span>
|
|
2513
|
+
</div>
|
|
2514
|
+
<div class="node-text">
|
|
2515
|
+
<div class="node-title" id="node-db-title">Primary Database</div>
|
|
2516
|
+
<div class="node-subtitle" id="node-db-desc">Initializing Engine</div>
|
|
2517
|
+
<div class="node-location" id="node-db-path">Connecting...</div>
|
|
2518
|
+
</div>
|
|
2519
|
+
<div class="node-badge" id="node-latency-pill">
|
|
2520
|
+
<span class="node-badge-dot"></span>
|
|
2521
|
+
<span id="node-latency-text">---</span>
|
|
2522
|
+
</div>
|
|
2523
|
+
</div>
|
|
2524
|
+
|
|
2525
|
+
<div class="node-metrics-bar">
|
|
2526
|
+
<span id="node-stat-cpu">CPU --%</span>
|
|
2527
|
+
<span class="sep">·</span>
|
|
2528
|
+
<span id="node-stat-disk">Disk --</span>
|
|
2529
|
+
<span class="sep">·</span>
|
|
2530
|
+
<span id="node-stat-ram">RAM --%</span>
|
|
2531
|
+
<span class="sep">·</span>
|
|
2532
|
+
<span id="node-stat-conns">-- conn</span>
|
|
2533
|
+
</div>
|
|
2534
|
+
</div>
|
|
2535
|
+
</div>
|
|
2536
|
+
</div>
|
|
2537
|
+
</div>
|
|
2538
|
+
|
|
2539
|
+
<!-- Activity Ribbon -->
|
|
2540
|
+
<div class="status-dashboard-ribbon skeleton-target">
|
|
2541
|
+
<div class="status-dashboard-ribbon-left">
|
|
2542
|
+
<div class="status-dashboard-health-stat">
|
|
2543
|
+
<span class="drixio-health-dot"></span>
|
|
2544
|
+
<span class="drixio-health-label" id="ribbon-health-label">Connecting...</span>
|
|
2545
|
+
</div>
|
|
2546
|
+
<span class="status-dashboard-ribbon-sep">•</span>
|
|
2547
|
+
<div class="drixio-records-stat">
|
|
2548
|
+
<span id="ribbon-records-count">Loading Records...</span>
|
|
2549
|
+
</div>
|
|
2550
|
+
<span class="status-dashboard-ribbon-sep">•</span>
|
|
2551
|
+
<div class="drixio-updated-stat">
|
|
2552
|
+
<span>Updated: <span id="header-last-updated" class="font-mono">Waiting</span></span>
|
|
2553
|
+
</div>
|
|
2554
|
+
</div>
|
|
2555
|
+
|
|
2556
|
+
<div class="status-dashboard-ribbon-right">
|
|
2557
|
+
<button type="button" id="status-live-toggle" class="status-dashboard-ribbon-btn" title="Toggle Auto-refresh">
|
|
2558
|
+
<span class="material-symbols-outlined live-dot-icon">radio_button_checked</span>
|
|
2559
|
+
<span id="status-live-text">Live (3s)</span>
|
|
2560
|
+
</button>
|
|
2561
|
+
<button type="button" id="status-refresh-btn" class="status-dashboard-ribbon-btn" title="Refresh Now">
|
|
2562
|
+
<span class="material-symbols-outlined" style="font-size: 14px;">refresh</span>
|
|
2563
|
+
<span>Refresh</span>
|
|
2564
|
+
</button>
|
|
2565
|
+
</div>
|
|
2566
|
+
</div>
|
|
2567
|
+
|
|
2568
|
+
<!-- Analytics Split View (Top Tables & Row Count Distribution) -->
|
|
2569
|
+
<div class="drixio-analytics-grid">
|
|
2570
|
+
<div id="status-toptables-container" class="drixio-panel hidden skeleton-target">
|
|
2571
|
+
<!-- Top tables injected here -->
|
|
2572
|
+
</div>
|
|
2573
|
+
<div id="status-distribution-container" class="drixio-panel hidden skeleton-target">
|
|
2574
|
+
<!-- Distribution Donut injected here -->
|
|
2575
|
+
</div>
|
|
2576
|
+
</div>
|
|
2577
|
+
|
|
2578
|
+
<!-- Quick Operations (6 Clean Action Cards) -->
|
|
2579
|
+
<div class="drixio-actions-panel skeleton-target">
|
|
2580
|
+
<div class="drixio-panel-header">
|
|
2581
|
+
<div class="flex items-center gap-2">
|
|
2582
|
+
<span class="material-symbols-outlined text-primary" style="font-size: 17px;">bolt</span>
|
|
2583
|
+
<h3 class="drixio-panel-title">Quick Operations</h3>
|
|
2584
|
+
</div>
|
|
2585
|
+
<span class="drixio-panel-tag">Database Actions</span>
|
|
2586
|
+
</div>
|
|
2587
|
+
<div class="drixio-actions-grid">
|
|
2588
|
+
<!-- 1. Export Full Backup -->
|
|
2589
|
+
<button type="button" class="drixio-action-card stagger-in" id="btn-quick-export-backup" title="Export full database backup (.sql)">
|
|
2590
|
+
<div class="action-icon-box" style="color: #3b82f6;">
|
|
2591
|
+
<span class="material-symbols-outlined">download</span>
|
|
2592
|
+
</div>
|
|
2593
|
+
<div class="action-content">
|
|
2594
|
+
<div class="action-title">Export Full Backup</div>
|
|
2595
|
+
<div class="action-desc">Download complete SQL schema & data dump</div>
|
|
2596
|
+
</div>
|
|
2597
|
+
</button>
|
|
2598
|
+
|
|
2599
|
+
<!-- 2. Export Schema DDL -->
|
|
2600
|
+
<button type="button" class="drixio-action-card stagger-in" id="btn-quick-export-ddl" title="Export schema DDL definitions (.sql)">
|
|
2601
|
+
<div class="action-icon-box" style="color: #06b6d4;">
|
|
2602
|
+
<span class="material-symbols-outlined">description</span>
|
|
2603
|
+
</div>
|
|
2604
|
+
<div class="action-content">
|
|
2605
|
+
<div class="action-title">Export Schema DDL</div>
|
|
2606
|
+
<div class="action-desc">Download CREATE TABLE DDL definitions</div>
|
|
2607
|
+
</div>
|
|
2608
|
+
</button>
|
|
2609
|
+
|
|
2610
|
+
<!-- 3. Import & Restore -->
|
|
2611
|
+
<button type="button" class="drixio-action-card stagger-in" id="btn-quick-import-backup" title="Import SQL script or JSON data">
|
|
2612
|
+
<div class="action-icon-box" style="color: #10b981;">
|
|
2613
|
+
<span class="material-symbols-outlined">upload</span>
|
|
2614
|
+
</div>
|
|
2615
|
+
<div class="action-content">
|
|
2616
|
+
<div class="action-title">Import & Restore</div>
|
|
2617
|
+
<div class="action-desc">Restore tables from SQL script or JSON dump</div>
|
|
2618
|
+
</div>
|
|
2619
|
+
</button>
|
|
2620
|
+
|
|
2621
|
+
<!-- 4. Data Dictionary -->
|
|
2622
|
+
<button type="button" class="drixio-action-card stagger-in" id="btn-quick-export-dict" title="Download database markdown data dictionary">
|
|
2623
|
+
<div class="action-icon-box" style="color: #8b5cf6;">
|
|
2624
|
+
<span class="material-symbols-outlined">menu_book</span>
|
|
2625
|
+
</div>
|
|
2626
|
+
<div class="action-content">
|
|
2627
|
+
<div class="action-title">Data Dictionary</div>
|
|
2628
|
+
<div class="action-desc">Generate markdown schema documentation</div>
|
|
2629
|
+
</div>
|
|
2630
|
+
</button>
|
|
2631
|
+
|
|
2632
|
+
<!-- 5. SQL Console -->
|
|
2633
|
+
<button type="button" class="drixio-action-card stagger-in" id="btn-quick-open-console" title="Open SQL Query Console">
|
|
2634
|
+
<div class="action-icon-box" style="color: #f59e0b;">
|
|
2635
|
+
<span class="material-symbols-outlined">terminal</span>
|
|
2636
|
+
</div>
|
|
2637
|
+
<div class="action-content">
|
|
2638
|
+
<div class="action-title">SQL Console</div>
|
|
2639
|
+
<div class="action-desc">Write, execute and test arbitrary queries</div>
|
|
2640
|
+
</div>
|
|
2641
|
+
</button>
|
|
2642
|
+
|
|
2643
|
+
<!-- 6. Visual ERD -->
|
|
2644
|
+
<button type="button" class="drixio-action-card stagger-in" id="btn-quick-open-erd" title="View Entity Relationship Diagram">
|
|
2645
|
+
<div class="action-icon-box" style="color: #ec4899;">
|
|
2646
|
+
<span class="material-symbols-outlined">hub</span>
|
|
2647
|
+
</div>
|
|
2648
|
+
<div class="action-content">
|
|
2649
|
+
<div class="action-title">Visual ERD</div>
|
|
2650
|
+
<div class="action-desc">Interactive table relationship canvas</div>
|
|
2651
|
+
</div>
|
|
2652
|
+
</button>
|
|
2653
|
+
</div>
|
|
2654
|
+
</div>
|
|
2655
|
+
|
|
2656
|
+
<!-- Collapsible Engine Specifications Drawer -->
|
|
2657
|
+
<div class="drixio-runtime-panel skeleton-target">
|
|
2658
|
+
<div class="drixio-runtime-header" id="runtime-toggle-header" role="button" tabindex="0">
|
|
2659
|
+
<div class="flex items-center gap-2">
|
|
2660
|
+
<span class="material-symbols-outlined text-soft" style="font-size: 16px;">tune</span>
|
|
2661
|
+
<span class="drixio-runtime-title">Engine Specifications & Runtime Environment</span>
|
|
2662
|
+
</div>
|
|
2663
|
+
<div class="flex items-center gap-2">
|
|
2664
|
+
<span class="drixio-runtime-chip" id="runtime-chip-engine">Database Engine</span>
|
|
2665
|
+
<span class="material-symbols-outlined text-soft" id="runtime-expand-arrow" style="font-size: 16px; transition: transform 0.2s ease;">expand_more</span>
|
|
2666
|
+
</div>
|
|
2667
|
+
</div>
|
|
2668
|
+
<div class="drixio-runtime-body hidden" id="runtime-details-body">
|
|
2669
|
+
<div class="drixio-runtime-grid">
|
|
2670
|
+
<div class="runtime-item">
|
|
2671
|
+
<span class="runtime-key">Engine Type</span>
|
|
2672
|
+
<span class="runtime-val" id="rt-engine">-</span>
|
|
2673
|
+
</div>
|
|
2674
|
+
<div class="runtime-item">
|
|
2675
|
+
<span class="runtime-key">Engine Version</span>
|
|
2676
|
+
<span class="runtime-val font-mono" id="rt-version">-</span>
|
|
2677
|
+
</div>
|
|
2678
|
+
<div class="runtime-item">
|
|
2679
|
+
<span class="runtime-key">Database Target</span>
|
|
2680
|
+
<span class="runtime-val font-mono" id="rt-dbname">-</span>
|
|
2681
|
+
</div>
|
|
2682
|
+
<div class="runtime-item">
|
|
2683
|
+
<span class="runtime-key">Uptime</span>
|
|
2684
|
+
<span class="runtime-val font-mono" id="rt-uptime">-</span>
|
|
2685
|
+
</div>
|
|
2686
|
+
<div class="runtime-item">
|
|
2687
|
+
<span class="runtime-key">Active Connections</span>
|
|
2688
|
+
<span class="runtime-val font-mono" id="rt-conns">-</span>
|
|
2689
|
+
</div>
|
|
2690
|
+
<div class="runtime-item">
|
|
2691
|
+
<span class="runtime-key">Total Tables</span>
|
|
2692
|
+
<span class="runtime-val font-mono" id="rt-tables">-</span>
|
|
2693
|
+
</div>
|
|
2694
|
+
<div class="runtime-item">
|
|
2695
|
+
<span class="runtime-key">Storage Size</span>
|
|
2696
|
+
<span class="runtime-val font-mono" id="rt-storage">-</span>
|
|
2697
|
+
</div>
|
|
2698
|
+
<div class="runtime-item">
|
|
2699
|
+
<span class="runtime-key">Memory Footprint</span>
|
|
2700
|
+
<span class="runtime-val font-mono" id="rt-memory">-</span>
|
|
2701
|
+
</div>
|
|
2702
|
+
</div>
|
|
2703
|
+
</div>
|
|
2704
|
+
</div>
|
|
2705
|
+
|
|
2706
|
+
<!-- Feature Prompts (Postgres / MySQL) -->
|
|
2707
|
+
<div id="feature-prompts-container" class="flex-col gap-4 hidden"></div>
|
|
2708
|
+
</div>
|
|
2709
|
+
`,window.gsap?(gsap.to(`.status-dashboard`,{opacity:1,duration:.3,ease:`power2.out`}),gsap.to(`.skeleton-target`,{opacity:.6,yoyo:!0,repeat:-1,duration:1,ease:`sine.inOut`})):document.querySelector(`.status-dashboard`).style.opacity=`1`,await Mn();let t=document.getElementById(`btn-copy-conn-uri`);t&&t.addEventListener(`click`,()=>{let e=document.getElementById(`hero-conn-uri`),t=document.getElementById(`btn-copy-conn-label`);e&&e.textContent&&e.textContent!==`Connecting...`&&(navigator.clipboard.writeText(e.textContent),t&&(t.textContent=`Copied!`,setTimeout(()=>{t&&(t.textContent=`Copy`)},1800)),typeof window.showToast==`function`&&window.showToast(`Connection URI copied to clipboard!`,`success`))});let n=document.getElementById(`btn-quick-export-backup`);n&&n.addEventListener(`click`,()=>{window.open(`/api/database/export`,`_blank`)});let r=document.getElementById(`btn-quick-export-ddl`);r&&r.addEventListener(`click`,()=>{window.open(`/api/database/schema-only`,`_blank`)});let i=document.getElementById(`btn-quick-import-backup`);i&&i.addEventListener(`click`,()=>{Sn(`database`)});let a=document.getElementById(`btn-quick-export-dict`);a&&a.addEventListener(`click`,()=>{window.open(`/api/database/dictionary`,`_blank`)});let o=document.getElementById(`btn-quick-open-console`);o&&o.addEventListener(`click`,()=>{typeof window.handleSwitchTab==`function`&&window.handleSwitchTab(`console-btn`)});let s=document.getElementById(`btn-quick-open-erd`);s&&s.addEventListener(`click`,()=>{typeof window.handleSwitchTab==`function`&&window.handleSwitchTab(`erd-btn`)});let c=document.getElementById(`runtime-toggle-header`),l=document.getElementById(`runtime-details-body`),u=document.getElementById(`runtime-expand-arrow`);c&&l&&c.addEventListener(`click`,()=>{l.classList.contains(`hidden`)?(l.classList.remove(`hidden`),u&&(u.style.transform=`rotate(180deg)`)):(l.classList.add(`hidden`),u&&(u.style.transform=`rotate(0deg)`))});let d=document.getElementById(`status-refresh-btn`);d&&d.addEventListener(`click`,()=>{let e=d.querySelector(`.material-symbols-outlined`);if(e){e.style.transition=`transform 0.4s ease`;let t=parseInt(e.dataset.rot||`0`,10)+360;e.style.transform=`rotate(${t}deg)`,e.dataset.rot=`${t}`}Mn()});let f=!0,p=document.getElementById(`status-live-toggle`);p&&p.addEventListener(`click`,()=>{f=!f;let e=p.querySelector(`.live-dot-icon`),t=document.getElementById(`status-live-text`);f?(e&&(e.style.color=`var(--color-primary)`),t&&(t.textContent=`Live (3s)`)):(e&&(e.style.color=`var(--color-text-soft)`),t&&(t.textContent=`Paused`))}),window.statusPollingInterval&&clearInterval(window.statusPollingInterval),window.statusPollingInterval=setInterval(()=>{window.AppState.currentTab===`status-btn`&&f&&Mn()},3e3)}async function Mn(){let e=performance.now();try{let[t,n]=await Promise.all([fetch(`/api/status`),fetch(`/api/tables/stats`)]),r=await t.json(),i=await n.json(),a=Math.round(performance.now()-e);r.success?Nn(r.data,i.data,a):Pn(r.error||`Failed to load status`)}catch(e){Pn(e instanceof Error?e.message:String(e))}}function Nn(e,t={},n=null){let r=document.getElementById(`hero-db-name`),i=document.getElementById(`hero-conn-uri`);if(!r)return;let a=``;a=e.dbType===`sqlite`?`sqlite://${e.dbName||`drixio.db`}`:e.dbType===`postgres`?`postgresql://localhost:5432/${e.dbName||`postgres`}`:e.dbType===`mysql`?`mysql://localhost:3306/${e.dbName||`mysql`}`:`${e.dbType||`database`}://${e.dbName||``}`,r.textContent=e.dbName||`Database`,i&&(i.textContent=a);let o=document.getElementById(`node-db-title`),s=document.getElementById(`node-db-desc`),c=document.getElementById(`node-db-path`),l=document.getElementById(`node-engine-avatar`),u=document.getElementById(`node-engine-icon`),d=document.getElementById(`node-latency-text`),f=document.getElementById(`node-stat-cpu`),p=document.getElementById(`node-stat-ram`),m=document.getElementById(`node-stat-disk`),h=document.getElementById(`node-stat-conns`);o&&(o.textContent=e.dbName||`Primary Database`),s&&(s.textContent=`${(e.dbType||`SQL`).toUpperCase()} Serverless Engine`),c&&(c.textContent=a),l&&u&&(e.dbType===`sqlite`?(u.textContent=`database`,l.style.background=`rgba(37, 99, 235, 0.12)`,l.style.color=`var(--color-primary)`):e.dbType===`postgres`?(u.textContent=`dns`,l.style.background=`rgba(99, 102, 241, 0.15)`,l.style.color=`#818cf8`):(u.textContent=`database`,l.style.background=`rgba(245, 158, 11, 0.15)`,l.style.color=`#f59e0b`)),d&&(d.textContent=n===null?`< 1ms`:`${n}ms`),f&&(f.textContent=`CPU ${e.osCpuUsage===void 0?`< 1%`:e.osCpuUsage.toFixed(1)+`%`}`),m&&(m.textContent=`Disk ${yt(e.sizeBytes)}`),p&&(e.osMemUsed&&e.osMemTotal?p.textContent=`RAM ${Math.round(e.osMemUsed/e.osMemTotal*100)}%`:p.textContent=`RAM N/A`),h&&(h.textContent=`${e.activeConnections||1} conn`);let g=document.getElementById(`header-last-updated`);if(g){let e=new Date,t=`${e.getHours().toString().padStart(2,`0`)}:${e.getMinutes().toString().padStart(2,`0`)}:${e.getSeconds().toString().padStart(2,`0`)}`;g.textContent=n===null?t:`${t} (${n}ms)`}let _=vn(t),v=document.getElementById(`ribbon-records-count`);v&&(v.textContent=`${(_||0).toLocaleString()} Total Records`),_n(e,_);let y=document.getElementById(`rt-engine`),b=document.getElementById(`rt-version`),x=document.getElementById(`rt-dbname`),S=document.getElementById(`rt-uptime`),C=document.getElementById(`rt-conns`),w=document.getElementById(`rt-tables`),T=document.getElementById(`rt-storage`),E=document.getElementById(`rt-memory`),D=document.getElementById(`runtime-chip-engine`);y&&(y.textContent=(e.dbType||`UNKNOWN`).toUpperCase()),b&&(b.textContent=e.version?`v${e.version}`:`N/A`),x&&(x.textContent=e.dbName||`N/A`),S&&(S.textContent=bt(e.uptime));let O=e.activeConnections||(e.dbType,1);C&&(C.textContent=`${O} active connection(s)`),w&&(w.textContent=`${e.totalTables??0} tables`),T&&(T.textContent=yt(e.sizeBytes)),E&&(e.osMemUsed&&e.osMemTotal?E.textContent=`${yt(e.osMemUsed)} / ${yt(e.osMemTotal)}`:E.textContent=`Managed by host system`),D&&(D.textContent=`${(e.dbType||`SQL`).toUpperCase()} ${e.version?`v${e.version}`:``}`);let k=document.getElementById(`feature-prompts-container`);k&&(e.dbType===`sqlite`?(k.classList.add(`hidden`),k.classList.remove(`flex`),k.innerHTML=``):(k.innerHTML=`
|
|
2710
|
+
<div class="feature-prompt stagger-in">
|
|
2711
|
+
<span class="material-symbols-outlined icon">info</span>
|
|
2712
|
+
<div class="feature-prompt-content">
|
|
2713
|
+
<div class="prompt-title">Query Performance Profiling</div>
|
|
2714
|
+
<div class="prompt-desc">This metric requires database plugins to be enabled on your server (e.g. <code>pg_stat_statements</code> for PostgreSQL, or <code>Performance Schema</code> for MySQL).</div>
|
|
2715
|
+
</div>
|
|
2716
|
+
<button type="button" class="feature-prompt-close" onclick="this.closest('#feature-prompts-container').classList.add('hidden')" title="Dismiss">
|
|
2717
|
+
<span class="material-symbols-outlined">close</span>
|
|
2718
|
+
</button>
|
|
2719
|
+
</div>
|
|
2720
|
+
`,k.classList.remove(`hidden`),k.classList.add(`flex`)));let A=document.getElementById(`ribbon-health-label`);A&&(A.textContent=e.status===`connected`?`100.0% Connection Health`:`Offline / Disconnected`),window.gsap&&!window._statusAnimatedIn&&(window._statusAnimatedIn=!0,gsap.killTweensOf(`.skeleton-target`),gsap.set(`.skeleton-target`,{clearProps:`opacity`}),gsap.fromTo(`.drixio-hero-header`,{opacity:0,x:-20},{opacity:1,x:0,duration:.5,ease:`power2.out`}),gsap.fromTo(`.status-dashboard-status-tile`,{opacity:0,y:15},{opacity:1,y:0,duration:.4,stagger:.05,ease:`back.out(1.2)`}),gsap.to(`#primary-db-node`,{opacity:1,duration:.5,ease:`power2.inOut`}),gsap.fromTo(`.drixio-action-card`,{opacity:0,y:10},{opacity:1,y:0,duration:.4,stagger:.05,ease:`power2.out`,delay:.2}))}function Pn(e){if(document.getElementById(`status-cards-container`)){window.gsap&&gsap.killTweensOf(`.skeleton-target`);let t=document.querySelector(`.status-dashboard`);t&&(t.innerHTML=`
|
|
2721
|
+
<div class="drixio-empty-analytics" style="margin-top: 60px;">
|
|
2722
|
+
<div class="drixio-empty-icon" style="background: rgba(239, 68, 68, 0.1); color: var(--color-error); width: 56px; height: 56px; border-radius: 50%;">
|
|
2723
|
+
<span class="material-symbols-outlined" style="font-size: 28px;">error</span>
|
|
2724
|
+
</div>
|
|
2725
|
+
<div class="drixio-empty-title" style="font-size: 18px; margin-top: 16px;">Connection Failed</div>
|
|
2726
|
+
<div class="drixio-empty-desc" style="max-width: 400px; margin-top: 8px;">
|
|
2727
|
+
Unable to fetch database status. Please check if your database engine is running and accessible.<br><br>
|
|
2728
|
+
<code style="color: var(--color-error); background: rgba(239,68,68,0.05); padding: 4px 8px; border-radius: 4px;">${e}</code>
|
|
2729
|
+
</div>
|
|
2730
|
+
<div class="drixio-empty-actions" style="margin-top: 24px;">
|
|
2731
|
+
<button type="button" class="btn-primary" onclick="window.refreshStatusDashboard && window.refreshStatusDashboard()">
|
|
2732
|
+
<span class="material-symbols-outlined" style="font-size: 16px;">refresh</span>
|
|
2733
|
+
<span>Try Again</span>
|
|
2734
|
+
</button>
|
|
2735
|
+
</div>
|
|
2736
|
+
</div>
|
|
2737
|
+
`)}}var Fn=`drixio_saved_connections`;function In(){try{let e=localStorage.getItem(Fn);if(!e)return[];let t=JSON.parse(e);return Array.isArray(t)?t:[]}catch{return[]}}function Ln(e){if(!e||!e.url)return In();let t=In(),n=e.url.trim(),r=e.name;r||=e.dialect===`sqlite`?n.replace(/^file:/,``).split(/[/\\]/).pop()||`sqlite.db`:n.split(`/`).pop()?.split(`?`)[0]||`database`;let i=t.findIndex(e=>e.url.trim().toLowerCase()===n.toLowerCase()),a={id:i>=0?t[i].id:`conn_${Date.now()}_${Math.random().toString(36).slice(2,6)}`,name:r,dialect:e.dialect||`sqlite`,url:n,isRemote:!!e.isRemote,badgeLabel:e.badgeLabel||(e.isRemote?`REMOTE`:`LOCAL`),lastConnected:Date.now()};return i>=0?t[i]={...t[i],...a}:t.unshift(a),localStorage.setItem(Fn,JSON.stringify(t)),t}function Rn(e){let t=In().filter(t=>t.id!==e);return localStorage.setItem(Fn,JSON.stringify(t)),t}async function zn(e){if(!e||!e.url)return!1;typeof window.showToast==`function`&&window.showToast(`Connecting to ${e.name}...`,`info`);try{let t=await(await fetch(`/api/connect`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({url:e.url,saveToEnv:!1})})).json();if(!t.success)throw Error(t.error||`Failed to connect`);Ln({...e,lastConnected:Date.now()}),window.AppState&&(window.AppState.dbType=t.data.dbType,window.AppState.isRemote=e.isRemote,window.AppState.badgeLabel=e.badgeLabel,window.AppState.currentTable=null,window.AppState.currentTableBtnElement=null);let n=document.getElementById(`db-name`);n&&(n.textContent=t.data.dbName||e.name);let r=document.getElementById(`env-badge`);return r&&(r.textContent=e.badgeLabel,r.className=`env-badge ${e.isRemote?`remote`:`local`}`,r.classList.remove(`hidden`)),typeof window.initSidebar==`function`&&await window.initSidebar(!0),typeof window.setStudioConnectionMode==`function`&&window.setStudioConnectionMode(!0),window.AppState?.currentTab===`connect-btn`&&typeof window.handleSwitchTab==`function`?window.handleSwitchTab(`data-btn`):typeof window.renderCurrentView==`function`&&window.renderCurrentView(``,!0),typeof window.showToast==`function`&&window.showToast(`Switched to database: ${e.name}`,`success`),!0}catch(e){return typeof window.showToast==`function`&&window.showToast(`Connection error: ${e.message}`,`error`),!1}}function Bn(e){let t=`sqlite`,n={sqlite:`create`,postgres:`existing`,mysql:`existing`},r={postgres:`uri`,mysql:`uri`},i={postgres:{uri:`postgresql://postgres:postgres@localhost:5432/postgres`,host:`localhost`,port:`5432`,user:`postgres`,password:``,dbname:`postgres`},mysql:{uri:`mysql://root:password@localhost:3306/mysql`,host:`localhost`,port:`3306`,user:`root`,password:``,dbname:`mysql`}},a={createName:`drixio.sqlite`,existingPath:`./drixio.sqlite`},o=!0,s={state:`idle`,message:`Click "Test Connection" to verify connectivity before proceeding.`,latencyMs:null,error:``},c={sqlite:{name:`SQLite`,badge:`Local File`,icon:`description`,desc:`Zero-config embedded file database.`},postgres:{name:`PostgreSQL`,badge:`Server Instance`,icon:`database`,desc:`Enterprise relational database server.`},mysql:{name:`MySQL`,badge:`Server Instance`,icon:`storage`,desc:`High-performance relational database.`}},l={sqlite:{create:{title:`About Create New (SQLite)`,desc:`Initializes a fresh, zero-configuration SQLite database file directly in your project root. No server setup or credentials required.`,action:`Will initialize a new SQLite database file and prepare schema.`},existing:{title:`About Connect Existing (SQLite)`,desc:`Connects to an existing SQLite database file (.sqlite, .db, .sqlite3) in your workspace to inspect schemas and browse data.`,action:`Will verify existing file on disk and inspect tables.`}},postgres:{create:{title:`About Create New (PostgreSQL)`,desc:`Connects to your PostgreSQL server instance using administrative credentials and executes "CREATE DATABASE" to set up your project database.`,action:`Will connect to PostgreSQL server and run CREATE DATABASE.`},existing:{title:`About Connect Existing (PostgreSQL)`,desc:`Connects to an existing PostgreSQL database instance via connection URI or host parameters. Introspects schemas, tables, and relations.`,action:`Will establish client connection and map schema tables.`}},mysql:{create:{title:`About Create New (MySQL)`,desc:`Connects to your MySQL / MariaDB server instance using administrative credentials and executes "CREATE DATABASE" to initialize your database.`,action:`Will connect to MySQL server and run CREATE DATABASE.`},existing:{title:`About Connect Existing (MySQL)`,desc:`Connects to an existing MySQL / MariaDB database service via connection URI or host parameters. Maps tables and indexes.`,action:`Will connect to MySQL database and inspect tables.`}}},u={sqlite:[{label:`drixio.sqlite`,val:`drixio.sqlite`,path:`./drixio.sqlite`},{label:`data.db`,val:`data.db`,path:`./data.db`}],postgres:[{label:`Local Default`,uri:`postgresql://postgres:postgres@localhost:5432/postgres`},{label:`Supabase Format`,uri:`postgresql://postgres.xxx:secret@aws-0-region.pooler.supabase.com:6543/postgres`}],mysql:[{label:`Local Default`,uri:`mysql://root:password@localhost:3306/mysql`},{label:`No Pass`,uri:`mysql://root:@localhost:3306/mydb`}]},d=e=>{if(!e||typeof e!=`string`)return null;let n=e.trim();n=n.replace(/^["']|["']$/g,``),n.includes(`=`)&&(n=n.split(`=`).slice(1).join(`=`).trim().replace(/^["']|["']$/g,``));try{let e=n;n.includes(`://`)||(e=`${t}://${n}`);let r=new URL(e),i=r.protocol.replace(`:`,``).toLowerCase(),a=i.startsWith(`post`)?`postgres`:i.startsWith(`my`)?`mysql`:i,o=r.hostname||`localhost`,s=a===`mysql`?`3306`:`5432`,c=r.port||s,l=a===`mysql`?`root`:`postgres`,u=decodeURIComponent(r.username||l),d=decodeURIComponent(r.password||``),f=r.pathname.replace(/^\//,``),p=a===`mysql`?`mysql`:`postgres`;return{dialect:a,host:o,port:c,user:u,password:d,dbName:decodeURIComponent(f.split(`?`)[0]||p)}}catch{return null}},f=()=>{let e=n[t];if(t===`sqlite`){if(e===`create`){let e=a.createName.trim()||`drixio.sqlite`;return!e.endsWith(`.sqlite`)&&!e.endsWith(`.db`)&&!e.endsWith(`.sqlite3`)&&(e+=`.sqlite`),`./${e}`}return a.existingPath.trim()||`./drixio.sqlite`}if(t===`postgres`||t===`mysql`){if(r[t]===`uri`){let e=i[t].uri.trim();if(e)return e}let n=i[t],a=n.host.trim()||`localhost`,o=t===`postgres`?`5432`:`3306`,s=n.port.trim()||o,c=t===`postgres`?`postgres`:`root`,l=n.user.trim()||c,u=n.password||``,d=e===`create`?`new_db`:t===`postgres`?`postgres`:`mysql`,f=n.dbname.trim()||d,p=u?`${encodeURIComponent(l)}:${encodeURIComponent(u)}@`:l?`${encodeURIComponent(l)}@`:``;return`${t}://${p}${a}:${s}/${f}`}return``},p=()=>{let e=n[t];if(t===`sqlite`)return e===`create`?a.createName.trim()||`drixio.sqlite`:(a.existingPath.trim()||`drixio.sqlite`).split(/[/\\]/).pop()||`drixio.sqlite`;if(r[t]===`uri`){let e=d(i[t].uri);if(e?.dbName)return e.dbName}let o=e===`create`?`new_db`:t===`postgres`?`postgres`:`mysql`;return i[t].dbname.trim()||o},m=()=>{if(t===`sqlite`)return`<workspace root>/${p()}`;if(r[t]===`uri`){let e=d(i[t].uri);if(e?.host)return`${e.host}:${e.port||(t===`postgres`?`5432`:`3306`)}`}let e=i[t].host.trim()||`localhost`,n=t===`postgres`?`5432`:`3306`;return`${e}:${i[t].port.trim()||n}`},h=()=>{let n=e.querySelector(`#preview-conn-url`),i=e.querySelector(`#preview-db-name`),a=e.querySelector(`#preview-location`),s=e.querySelector(`#preview-env-output`),c=f();n&&(n.textContent=c),i&&(i.textContent=p()),a&&(a.textContent=m()),s&&(o?(s.textContent=`DATABASE_URL="${c}"`,s.classList.remove(`disabled`)):(s.textContent=`(Will not write to .env)`,s.classList.add(`disabled`))),t!==`sqlite`&&r[t]===`uri`&&g()},g=()=>{let n=e.querySelector(`#uri-parsed-chips`);if(!n)return;let r=i[t].uri,a=d(r);if(!a){n.innerHTML=`
|
|
2738
|
+
<span class="parsed-chip error">
|
|
2739
|
+
<span class="material-symbols-outlined chip-icon">error</span>
|
|
2740
|
+
<span>Invalid or incomplete URI</span>
|
|
2741
|
+
</span>
|
|
2742
|
+
`;return}n.innerHTML=`
|
|
2743
|
+
<span class="parsed-chip">
|
|
2744
|
+
<span class="chip-label">Host:</span>
|
|
2745
|
+
<code class="chip-val">${a.host}:${a.port}</code>
|
|
2746
|
+
</span>
|
|
2747
|
+
<span class="parsed-chip">
|
|
2748
|
+
<span class="chip-label">User:</span>
|
|
2749
|
+
<code class="chip-val">${a.user||`none`}</code>
|
|
2750
|
+
</span>
|
|
2751
|
+
<span class="parsed-chip">
|
|
2752
|
+
<span class="chip-label">Database:</span>
|
|
2753
|
+
<code class="chip-val">${a.dbName}</code>
|
|
2754
|
+
</span>
|
|
2755
|
+
<span class="parsed-chip success">
|
|
2756
|
+
<span class="material-symbols-outlined chip-icon">check_circle</span>
|
|
2757
|
+
<span>Parsed</span>
|
|
2758
|
+
</span>
|
|
2759
|
+
`},_=()=>{let t=e.querySelector(`#test-status-badge`),n=e.querySelector(`#test-status-msg`),r=e.querySelector(`#run-test-btn`);!t||!n||!r||(s.state===`testing`?(t.className=`test-badge testing`,t.innerHTML=`
|
|
2760
|
+
<span class="material-symbols-outlined animate-spin" style="font-size: 13px;">sync</span>
|
|
2761
|
+
<span>Testing Connection...</span>
|
|
2762
|
+
`,n.textContent=`Contacting database target...`,r.disabled=!0):s.state===`success`?(t.className=`test-badge success`,t.innerHTML=`
|
|
2763
|
+
<span class="status-dot green"></span>
|
|
2764
|
+
<span>Reachable ${s.latencyMs===null?``:`(${s.latencyMs}ms)`}</span>
|
|
2765
|
+
`,n.textContent=s.message,r.disabled=!1):s.state===`error`?(t.className=`test-badge error`,t.innerHTML=`
|
|
2766
|
+
<span class="status-dot red"></span>
|
|
2767
|
+
<span>Connection Failed</span>
|
|
2768
|
+
`,n.textContent=s.error||`Unable to establish connection.`,r.disabled=!1):(t.className=`test-badge idle`,t.innerHTML=`
|
|
2769
|
+
<span class="status-dot gray"></span>
|
|
2770
|
+
<span>Not Tested</span>
|
|
2771
|
+
`,n.textContent=`Click "Test Connection" to verify connectivity before proceeding.`,r.disabled=!1))},v=async()=>{s={state:`testing`,message:``,latencyMs:null,error:``},_();let e=n[t],r=null;if(e===`create`&&(t===`postgres`||t===`mysql`)){let e=i[t];r={mode:`create`,dbType:t,host:e.host.trim()||`localhost`,port:e.port.trim()||(t===`mysql`?`3306`:`5432`),user:e.user.trim()||(t===`mysql`?`root`:`postgres`),password:e.password||``,dbName:e.dbname.trim()||`new_db`}}else if(t===`sqlite`&&e===`create`){let e=a.createName.trim()||`drixio.sqlite`;!e.endsWith(`.sqlite`)&&!e.endsWith(`.db`)&&!e.endsWith(`.sqlite3`)&&(e+=`.sqlite`),r={mode:`create`,dbType:`sqlite`,url:`./${e}`}}else{let e=``;e=t===`sqlite`?a.existingPath.trim()||`./drixio.sqlite`:f(),r={mode:`existing`,dbType:t,url:e}}try{let e=await(await fetch(`/api/test-connect`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(r)})).json();if(!e.success)throw Error(e.error||`Connection test failed`);s={state:`success`,message:e.data?.message||`Database target reachable and verified.`,latencyMs:e.data?.latencyMs??1,error:``}}catch(e){s={state:`error`,message:``,latencyMs:null,error:e.message}}_()},y=()=>{let r=n[t],i=l[t]?.[r]||{title:``,desc:``,action:``},a=u[t]||[],s=In();e.innerHTML=`
|
|
2772
|
+
<div class="connect-fullpage">
|
|
2773
|
+
<!-- 2-Column Workbench: Left = Configuration, Right = Live Diagnostics, Overview & Recent Connections -->
|
|
2774
|
+
<div class="connect-workbench-layout">
|
|
2775
|
+
<!-- Left Column: Unified Configuration Panel -->
|
|
2776
|
+
<section class="connect-config-pane">
|
|
2777
|
+
<!-- Step 1: Engine Selection -->
|
|
2778
|
+
<div class="connect-form-section">
|
|
2779
|
+
<div class="connect-section-header">
|
|
2780
|
+
<span class="connect-step-tag">1</span>
|
|
2781
|
+
<span>Select Database Dialect</span>
|
|
2782
|
+
</div>
|
|
2783
|
+
<div class="connect-engine-grid">
|
|
2784
|
+
${[`sqlite`,`postgres`,`mysql`].map(e=>{let n=c[e],r=t===e;return`
|
|
2785
|
+
<div class="connect-engine-card ${r?`active`:``}" data-type="${e}">
|
|
2786
|
+
<div class="connect-engine-card-top">
|
|
2787
|
+
<div class="connect-engine-card-icon">
|
|
2788
|
+
<span class="material-symbols-outlined">${n.icon}</span>
|
|
2789
|
+
</div>
|
|
2790
|
+
<div class="connect-engine-card-status">
|
|
2791
|
+
${r?`<span class="engine-active-pill"><span class="material-symbols-outlined">check</span>Active</span>`:`<span class="engine-select-pill">Select</span>`}
|
|
2792
|
+
</div>
|
|
2793
|
+
</div>
|
|
2794
|
+
<div class="connect-engine-card-content">
|
|
2795
|
+
<div class="connect-engine-card-header">
|
|
2796
|
+
<span class="engine-title">${n.name}</span>
|
|
2797
|
+
<span class="engine-badge ${e===`sqlite`?`badge-local`:`badge-server`}">${n.badge}</span>
|
|
2798
|
+
</div>
|
|
2799
|
+
<span class="engine-desc">${n.desc}</span>
|
|
2800
|
+
</div>
|
|
2801
|
+
</div>
|
|
2802
|
+
`}).join(``)}
|
|
2803
|
+
</div>
|
|
2804
|
+
</div>
|
|
2805
|
+
|
|
2806
|
+
<!-- Step 2: Configuration & Parameters with Integrated Mode Tabs -->
|
|
2807
|
+
<div class="connect-form-section">
|
|
2808
|
+
<div class="connect-section-header-row">
|
|
2809
|
+
<div class="connect-section-header">
|
|
2810
|
+
<span class="connect-step-tag">2</span>
|
|
2811
|
+
<span>Connection Details</span>
|
|
2812
|
+
</div>
|
|
2813
|
+
|
|
2814
|
+
<div class="connect-mode-segmented">
|
|
2815
|
+
<button type="button" class="connect-mode-btn ${r===`existing`?`active`:``}" data-mode="existing">
|
|
2816
|
+
<span class="material-symbols-outlined">link</span>
|
|
2817
|
+
<span>Connect Existing</span>
|
|
2818
|
+
</button>
|
|
2819
|
+
<button type="button" class="connect-mode-btn ${r===`create`?`active`:``}" data-mode="create">
|
|
2820
|
+
<span class="material-symbols-outlined">add_circle</span>
|
|
2821
|
+
<span>Create New</span>
|
|
2822
|
+
</button>
|
|
2823
|
+
</div>
|
|
2824
|
+
</div>
|
|
2825
|
+
|
|
2826
|
+
<!-- Inline Mode Explanation Callout -->
|
|
2827
|
+
<div class="connect-mode-callout">
|
|
2828
|
+
<span class="material-symbols-outlined icon-info">info</span>
|
|
2829
|
+
<span class="callout-desc">${i.desc}</span>
|
|
2830
|
+
</div>
|
|
2831
|
+
|
|
2832
|
+
<div class="connect-form-body">
|
|
2833
|
+
${b(t,r)}
|
|
2834
|
+
|
|
2835
|
+
<!-- Quick Presets -->
|
|
2836
|
+
${a.length>0?`
|
|
2837
|
+
<div class="connect-presets-group">
|
|
2838
|
+
<span class="connect-presets-label">Quick Presets:</span>
|
|
2839
|
+
<div class="connect-presets-chips">
|
|
2840
|
+
${a.map(e=>`
|
|
2841
|
+
<button type="button" class="connect-preset-chip" data-fill="${e.uri||(r===`create`?e.val:e.path)}">
|
|
2842
|
+
<span class="material-symbols-outlined chip-bolt-icon">bolt</span>
|
|
2843
|
+
<code>${e.label}</code>
|
|
2844
|
+
</button>
|
|
2845
|
+
`).join(``)}
|
|
2846
|
+
</div>
|
|
2847
|
+
</div>
|
|
2848
|
+
`:``}
|
|
2849
|
+
|
|
2850
|
+
<!-- Save to .env Checkbox -->
|
|
2851
|
+
<label class="connect-checkbox-row">
|
|
2852
|
+
<input type="checkbox" id="connect-save-env" ${o?`checked`:``} />
|
|
2853
|
+
<div class="connect-checkbox-info">
|
|
2854
|
+
<span class="checkbox-title">Persist connection URL to <code class="env-code">.env</code> (DATABASE_URL)</span>
|
|
2855
|
+
<span class="checkbox-desc">Enables automatic connection restoration when Drixio Studio or CLI boots up.</span>
|
|
2856
|
+
</div>
|
|
2857
|
+
</label>
|
|
2858
|
+
|
|
2859
|
+
<!-- Error Box -->
|
|
2860
|
+
<div id="connect-error-msg" class="connect-msg-box error hidden"></div>
|
|
2861
|
+
|
|
2862
|
+
<!-- Submit Action Button -->
|
|
2863
|
+
<div class="connect-action-row">
|
|
2864
|
+
<button type="button" id="connect-submit-btn" class="connect-submit-btn">
|
|
2865
|
+
<span class="material-symbols-outlined">${r===`create`?`rocket_launch`:`cable`}</span>
|
|
2866
|
+
<span>${r===`create`?`Create & Connect Database`:`Connect to Database`}</span>
|
|
2867
|
+
</button>
|
|
2868
|
+
</div>
|
|
2869
|
+
</div>
|
|
2870
|
+
</div>
|
|
2871
|
+
</section>
|
|
2872
|
+
|
|
2873
|
+
<!-- Right Column: Live Diagnostics, Preview & Recent Connections -->
|
|
2874
|
+
<section class="connect-preview-pane">
|
|
2875
|
+
<!-- Card 1: Connection Health & Live Test -->
|
|
2876
|
+
<div class="connect-card-box">
|
|
2877
|
+
<div class="card-box-header">
|
|
2878
|
+
<div class="card-box-title">
|
|
2879
|
+
<span class="material-symbols-outlined icon-blue">network_check</span>
|
|
2880
|
+
<span>Connection Diagnostics</span>
|
|
2881
|
+
</div>
|
|
2882
|
+
<button type="button" id="run-test-btn" class="connect-test-btn">
|
|
2883
|
+
<span class="material-symbols-outlined">bolt</span>
|
|
2884
|
+
<span>Test Connection</span>
|
|
2885
|
+
</button>
|
|
2886
|
+
</div>
|
|
2887
|
+
<div class="card-box-body">
|
|
2888
|
+
<div class="test-status-row">
|
|
2889
|
+
<div id="test-status-badge" class="test-badge idle">
|
|
2890
|
+
<span class="status-dot gray"></span>
|
|
2891
|
+
<span>Not Tested</span>
|
|
2892
|
+
</div>
|
|
2893
|
+
</div>
|
|
2894
|
+
<p id="test-status-msg" class="test-status-text">Click "Test Connection" to verify database reachability before proceeding.</p>
|
|
2895
|
+
</div>
|
|
2896
|
+
</div>
|
|
2897
|
+
|
|
2898
|
+
<!-- Card 2: Pre-flight Resolved Details -->
|
|
2899
|
+
<div class="connect-card-box">
|
|
2900
|
+
<div class="card-box-header">
|
|
2901
|
+
<div class="card-box-title">
|
|
2902
|
+
<span class="material-symbols-outlined icon-blue">tune</span>
|
|
2903
|
+
<span>Pre-flight Overview</span>
|
|
2904
|
+
</div>
|
|
2905
|
+
<button type="button" id="copy-preview-btn" class="connect-copy-icon-btn" title="Copy Connection URL">
|
|
2906
|
+
<span class="material-symbols-outlined">content_copy</span>
|
|
2907
|
+
</button>
|
|
2908
|
+
</div>
|
|
2909
|
+
|
|
2910
|
+
<div class="card-box-body">
|
|
2911
|
+
<div class="overview-item">
|
|
2912
|
+
<span class="overview-label">Target URL</span>
|
|
2913
|
+
<pre class="overview-code-box"><code id="preview-conn-url">${f()}</code></pre>
|
|
2914
|
+
</div>
|
|
2915
|
+
|
|
2916
|
+
<div class="overview-grid-2">
|
|
2917
|
+
<div class="overview-item">
|
|
2918
|
+
<span class="overview-label">Target Database</span>
|
|
2919
|
+
<span id="preview-db-name" class="overview-val">${p()}</span>
|
|
2920
|
+
</div>
|
|
2921
|
+
<div class="overview-item">
|
|
2922
|
+
<span class="overview-label">Storage / Host</span>
|
|
2923
|
+
<span id="preview-location" class="overview-val">${m()}</span>
|
|
2924
|
+
</div>
|
|
2925
|
+
</div>
|
|
2926
|
+
|
|
2927
|
+
<div class="overview-item">
|
|
2928
|
+
<span class="overview-label">Planned Operation</span>
|
|
2929
|
+
<span class="overview-action-badge">${i.action}</span>
|
|
2930
|
+
</div>
|
|
2931
|
+
|
|
2932
|
+
<div class="overview-item">
|
|
2933
|
+
<span class="overview-label">.env Integration</span>
|
|
2934
|
+
<code id="preview-env-output" class="overview-env-code">DATABASE_URL="${f()}"</code>
|
|
2935
|
+
</div>
|
|
2936
|
+
</div>
|
|
2937
|
+
</div>
|
|
2938
|
+
|
|
2939
|
+
<!-- Card 3: Recent Connections -->
|
|
2940
|
+
<div class="connect-card-box">
|
|
2941
|
+
<div class="card-box-header">
|
|
2942
|
+
<div class="card-box-title">
|
|
2943
|
+
<span class="material-symbols-outlined icon-blue">history</span>
|
|
2944
|
+
<span>Recent Connections</span>
|
|
2945
|
+
</div>
|
|
2946
|
+
<span class="saved-conn-count-badge">${s.length} saved</span>
|
|
2947
|
+
</div>
|
|
2948
|
+
<div class="card-box-body saved-conns-body">
|
|
2949
|
+
${s.length>0?`
|
|
2950
|
+
<div class="saved-conn-list">
|
|
2951
|
+
${s.map(e=>{let t=e.dialect===`postgres`?`database`:e.dialect===`mysql`?`storage`:`description`;return`
|
|
2952
|
+
<div class="saved-conn-item" data-id="${e.id}">
|
|
2953
|
+
<div class="saved-conn-main">
|
|
2954
|
+
<div class="saved-conn-icon">
|
|
2955
|
+
<span class="material-symbols-outlined">${t}</span>
|
|
2956
|
+
</div>
|
|
2957
|
+
<div class="saved-conn-info">
|
|
2958
|
+
<div class="saved-conn-name-row">
|
|
2959
|
+
<span class="saved-conn-name" title="${e.name}">${e.name}</span>
|
|
2960
|
+
<span class="saved-conn-badge ${e.isRemote?`remote`:`local`}">${e.badgeLabel||(e.isRemote?`REMOTE`:`LOCAL`)}</span>
|
|
2961
|
+
</div>
|
|
2962
|
+
<span class="saved-conn-url" title="${e.url}">${e.url}</span>
|
|
2963
|
+
</div>
|
|
2964
|
+
</div>
|
|
2965
|
+
<div class="saved-conn-actions">
|
|
2966
|
+
<button type="button" class="btn-use-conn" data-id="${e.id}" title="Load parameters into editor">
|
|
2967
|
+
<span class="material-symbols-outlined">edit</span>
|
|
2968
|
+
</button>
|
|
2969
|
+
<button type="button" class="btn-direct-connect" data-id="${e.id}" title="Connect immediately">
|
|
2970
|
+
<span class="material-symbols-outlined">login</span>
|
|
2971
|
+
<span>Connect</span>
|
|
2972
|
+
</button>
|
|
2973
|
+
<button type="button" class="btn-del-conn" data-id="${e.id}" title="Remove from saved">
|
|
2974
|
+
<span class="material-symbols-outlined">close</span>
|
|
2975
|
+
</button>
|
|
2976
|
+
</div>
|
|
2977
|
+
</div>
|
|
2978
|
+
`}).join(``)}
|
|
2979
|
+
</div>
|
|
2980
|
+
`:`
|
|
2981
|
+
<div class="saved-conn-empty">
|
|
2982
|
+
<span class="material-symbols-outlined empty-icon">cloud_off</span>
|
|
2983
|
+
<span class="empty-title">No saved connections yet</span>
|
|
2984
|
+
<p class="empty-desc">Successfully connected databases will be remembered here for 1-click switching.</p>
|
|
2985
|
+
</div>
|
|
2986
|
+
`}
|
|
2987
|
+
</div>
|
|
2988
|
+
</div>
|
|
2989
|
+
</section>
|
|
2990
|
+
</div>
|
|
2991
|
+
</div>
|
|
2992
|
+
`,S()},b=(e,t)=>{if(e===`sqlite`)return t===`create`?`
|
|
2993
|
+
<div class="connect-field">
|
|
2994
|
+
<label class="connect-label" for="connect-sqlite-name">Database File Name</label>
|
|
2995
|
+
<div class="connect-input-wrap">
|
|
2996
|
+
<input type="text" id="connect-sqlite-name" class="connect-input mono" placeholder="drixio.sqlite" value="${a.createName}" />
|
|
2997
|
+
</div>
|
|
2998
|
+
<p class="connect-hint">New database file will be initialized in your project root (e.g. <code>./drixio.sqlite</code>).</p>
|
|
2999
|
+
</div>
|
|
3000
|
+
`:`
|
|
3001
|
+
<div class="connect-field">
|
|
3002
|
+
<label class="connect-label" for="connect-sqlite-path">Database File Path / URI</label>
|
|
3003
|
+
<div class="connect-input-wrap">
|
|
3004
|
+
<input type="text" id="connect-sqlite-path" class="connect-input mono" placeholder="./drixio.sqlite" value="${a.existingPath}" />
|
|
3005
|
+
</div>
|
|
3006
|
+
<p class="connect-hint">Relative or absolute path (e.g. <code>./drixio.sqlite</code>, <code>./data.db</code>, or <code>file:./test.db</code>).</p>
|
|
3007
|
+
</div>
|
|
3008
|
+
`;let n=e===`postgres`,o=r[e],s=i[e],c=n?`5432`:`3306`,l=n?`postgres`:`root`,u=t===`create`,d=u?`my_project`:n?`postgres`:`mysql`;return`
|
|
3009
|
+
<!-- Segmented Input Method Selector -->
|
|
3010
|
+
<div class="connect-input-method-row">
|
|
3011
|
+
<div class="connect-method-segmented">
|
|
3012
|
+
<button type="button" class="connect-method-btn ${o===`uri`?`active`:``}" data-method="uri">
|
|
3013
|
+
<span class="material-symbols-outlined">link</span>
|
|
3014
|
+
<span>Connection URI</span>
|
|
3015
|
+
</button>
|
|
3016
|
+
<button type="button" class="connect-method-btn ${o===`fields`?`active`:``}" data-method="fields">
|
|
3017
|
+
<span class="material-symbols-outlined">tune</span>
|
|
3018
|
+
<span>Host & Parameters</span>
|
|
3019
|
+
</button>
|
|
3020
|
+
</div>
|
|
3021
|
+
<span class="connect-method-hint">${o===`uri`?`Paste URI from Supabase, Neon, or Docker`:`Configure host, port, user and credentials manually`}</span>
|
|
3022
|
+
</div>
|
|
3023
|
+
|
|
3024
|
+
${o===`uri`?`
|
|
3025
|
+
<!-- Option A: Clean, Single Connection URI Box -->
|
|
3026
|
+
<div class="connect-uri-card">
|
|
3027
|
+
<div class="connect-field">
|
|
3028
|
+
<div class="connect-label-row">
|
|
3029
|
+
<label class="connect-label" for="connect-server-uri">Connection String (URI)</label>
|
|
3030
|
+
<button type="button" class="connect-text-btn" id="btn-paste-uri">
|
|
3031
|
+
<span class="material-symbols-outlined">content_paste</span>
|
|
3032
|
+
<span>Paste</span>
|
|
3033
|
+
</button>
|
|
3034
|
+
</div>
|
|
3035
|
+
<div class="connect-input-wrap has-action">
|
|
3036
|
+
<input type="text" id="connect-server-uri" class="connect-input mono" placeholder="${n?`postgresql://postgres:password@localhost:5432/mydb`:`mysql://root:password@localhost:3306/mydb`}" value="${s.uri}" />
|
|
3037
|
+
<button type="button" class="connect-input-clear-btn" id="btn-clear-uri" title="Clear input">
|
|
3038
|
+
<span class="material-symbols-outlined">close</span>
|
|
3039
|
+
</button>
|
|
3040
|
+
</div>
|
|
3041
|
+
<p class="connect-hint">Supports standard connection strings with query options (e.g. <code>sslmode=require</code>).</p>
|
|
3042
|
+
</div>
|
|
3043
|
+
|
|
3044
|
+
<!-- Live Parsed Breakdown Chips -->
|
|
3045
|
+
<div class="uri-parsed-bar" id="uri-parsed-chips">
|
|
3046
|
+
<!-- Filled dynamically by updateParsedChipsUI() -->
|
|
3047
|
+
</div>
|
|
3048
|
+
</div>
|
|
3049
|
+
`:`
|
|
3050
|
+
<!-- Option B: Clean Form Fields Grid -->
|
|
3051
|
+
<div class="connect-fields-group">
|
|
3052
|
+
<!-- Host & Port Grid -->
|
|
3053
|
+
<div class="connect-grid-2">
|
|
3054
|
+
<div class="connect-field">
|
|
3055
|
+
<label class="connect-label" for="connect-server-host">Server Host</label>
|
|
3056
|
+
<div class="connect-input-wrap">
|
|
3057
|
+
<input type="text" id="connect-server-host" class="connect-input mono" placeholder="localhost" value="${s.host}" />
|
|
3058
|
+
</div>
|
|
3059
|
+
<p class="connect-hint">Hostname or IP address (e.g. <code>localhost</code>, <code>127.0.0.1</code>).</p>
|
|
3060
|
+
</div>
|
|
3061
|
+
<div class="connect-field">
|
|
3062
|
+
<label class="connect-label" for="connect-server-port">Port</label>
|
|
3063
|
+
<div class="connect-input-wrap">
|
|
3064
|
+
<input type="text" id="connect-server-port" class="connect-input mono" placeholder="${c}" value="${s.port}" />
|
|
3065
|
+
</div>
|
|
3066
|
+
<p class="connect-hint">Default is <code>${c}</code>.</p>
|
|
3067
|
+
</div>
|
|
3068
|
+
</div>
|
|
3069
|
+
|
|
3070
|
+
<!-- Username & Password Grid -->
|
|
3071
|
+
<div class="connect-grid-2">
|
|
3072
|
+
<div class="connect-field">
|
|
3073
|
+
<label class="connect-label" for="connect-server-user">Username</label>
|
|
3074
|
+
<div class="connect-input-wrap">
|
|
3075
|
+
<input type="text" id="connect-server-user" class="connect-input mono" placeholder="${l}" value="${s.user}" />
|
|
3076
|
+
</div>
|
|
3077
|
+
<p class="connect-hint">Database user account name.</p>
|
|
3078
|
+
</div>
|
|
3079
|
+
<div class="connect-field">
|
|
3080
|
+
<label class="connect-label" for="connect-server-pass">Password</label>
|
|
3081
|
+
<div class="connect-input-wrap has-action">
|
|
3082
|
+
<input type="password" id="connect-server-pass" class="connect-input mono" placeholder="Leave empty if none" value="${s.password}" />
|
|
3083
|
+
<button type="button" class="connect-input-eye-btn" id="toggle-pass-visibility" title="Toggle password visibility">
|
|
3084
|
+
<span class="material-symbols-outlined">visibility</span>
|
|
3085
|
+
</button>
|
|
3086
|
+
</div>
|
|
3087
|
+
<p class="connect-hint">Account password (optional if none).</p>
|
|
3088
|
+
</div>
|
|
3089
|
+
</div>
|
|
3090
|
+
|
|
3091
|
+
<!-- Database Name -->
|
|
3092
|
+
<div class="connect-field">
|
|
3093
|
+
<label class="connect-label" for="connect-server-dbname">${u?`New Database Name to Create`:`Target Database Name`}</label>
|
|
3094
|
+
<div class="connect-input-wrap">
|
|
3095
|
+
<input type="text" id="connect-server-dbname" class="connect-input mono" placeholder="e.g. ${d}" value="${s.dbname}" />
|
|
3096
|
+
</div>
|
|
3097
|
+
<p class="connect-hint">${u?`Connects to server instance to execute <code>CREATE DATABASE</code>.`:`Target database to inspect and manage.`}</p>
|
|
3098
|
+
</div>
|
|
3099
|
+
</div>
|
|
3100
|
+
`}
|
|
3101
|
+
`},x=()=>{s.state!==`idle`&&(s={state:`idle`,message:`Click "Test Connection" to verify connectivity before proceeding.`,latencyMs:null,error:``},_())},S=()=>{e.querySelectorAll(`.connect-engine-card`).forEach(e=>{e.onclick=()=>{let n=e.dataset.type;n!==t&&(t=n,x(),y())}}),e.querySelectorAll(`.connect-mode-btn`).forEach(e=>{e.onclick=()=>{let r=e.dataset.mode;n[t]!==r&&(n[t]=r,x(),y())}}),e.querySelectorAll(`.connect-method-btn`).forEach(e=>{e.onclick=()=>{let n=e.dataset.method;r[t]!==n&&(r[t]=n,x(),y())}});let s=e.querySelector(`#connect-sqlite-name`);s&&(s.oninput=()=>{a.createName=s.value,x(),h()});let c=e.querySelector(`#connect-sqlite-path`);c&&(c.oninput=()=>{a.existingPath=c.value,x(),h()});let l=e.querySelector(`#connect-server-uri`);if(l){l.oninput=()=>{let e=l.value;i[t].uri=e;let n=d(e);n&&(i[t].host=n.host,i[t].port=n.port,i[t].user=n.user,i[t].password=n.password,i[t].dbname=n.dbName),x(),h()};let n=e.querySelector(`#btn-paste-uri`);n&&(n.onclick=async()=>{try{if(navigator.clipboard?.readText){let e=await navigator.clipboard.readText();e&&(l.value=e.trim(),l.dispatchEvent(new Event(`input`)),window.showToast&&window.showToast(`Pasted from clipboard!`,`info`))}else l.focus()}catch{l.focus()}});let r=e.querySelector(`#btn-clear-uri`);r&&(r.onclick=()=>{l.value=``,l.dispatchEvent(new Event(`input`)),l.focus()})}let u=e.querySelector(`#connect-server-host`),m=e.querySelector(`#connect-server-port`),g=e.querySelector(`#connect-server-user`),b=e.querySelector(`#connect-server-pass`),S=e.querySelector(`#connect-server-dbname`),C=()=>{u&&(i[t].host=u.value),m&&(i[t].port=m.value),g&&(i[t].user=g.value),b&&(i[t].password=b.value),S&&(i[t].dbname=S.value);let e=i[t],n=e.password?`${encodeURIComponent(e.user)}:${encodeURIComponent(e.password)}@`:e.user?`${encodeURIComponent(e.user)}@`:``;i[t].uri=`${t}://${n}${e.host}:${e.port}/${e.dbname}`,x(),h()};[u,m,g,b,S].forEach(e=>{e&&(e.oninput=C)});let w=e.querySelector(`#toggle-pass-visibility`);w&&b&&(w.onclick=()=>{let e=b.type===`password`;b.type=e?`text`:`password`;let t=w.querySelector(`.material-symbols-outlined`);t&&(t.textContent=e?`visibility_off`:`visibility`)}),e.querySelectorAll(`.connect-preset-chip`).forEach(e=>{e.onclick=()=>{let r=e.dataset.fill,o=n[t];if(t===`sqlite`)o===`create`?(a.createName=r.replace(`./`,``),s&&(s.value=a.createName)):(a.existingPath=r,c&&(c.value=a.existingPath));else{i[t].uri=r;let e=d(r);e&&(i[t].host=e.host,i[t].port=e.port,i[t].user=e.user,i[t].password=e.password,i[t].dbname=e.dbName),l&&(l.value=r),u&&e&&(u.value=e.host),m&&e&&(m.value=e.port),g&&e&&(g.value=e.user),b&&e&&(b.value=e.password),S&&e&&(S.value=e.dbName)}x(),h(),window.showToast&&window.showToast(`Preset loaded!`,`info`)}});let T=e.querySelector(`#connect-save-env`);T&&(T.onchange=()=>{o=T.checked,h()});let E=e.querySelector(`#run-test-btn`);E&&(E.onclick=()=>{v()});let D=e.querySelector(`#copy-preview-btn`);D&&(D.onclick=()=>{let e=f();navigator.clipboard&&(navigator.clipboard.writeText(e),window.showToast&&window.showToast(`Copied connection URL to clipboard!`,`info`))}),e.querySelectorAll(`.btn-use-conn`).forEach(e=>{e.onclick=r=>{r.stopPropagation();let o=e.dataset.id,s=In().find(e=>e.id===o);if(s){if(t=s.dialect||`sqlite`,n[t]=`existing`,t===`sqlite`)a.existingPath=s.url;else{i[t].uri=s.url;let e=d(s.url);e&&(i[t].host=e.host,i[t].port=e.port,i[t].user=e.user,i[t].password=e.password,i[t].dbname=e.dbName)}x(),y(),window.showToast&&window.showToast(`Loaded parameters for "${s.name}"`,`info`)}}}),e.querySelectorAll(`.btn-direct-connect`).forEach(e=>{e.onclick=async t=>{t.stopPropagation();let n=e.dataset.id,r=In().find(e=>e.id===n);r&&(e.disabled=!0,await zn(r),e.disabled=!1)}}),e.querySelectorAll(`.btn-del-conn`).forEach(e=>{e.onclick=t=>{t.stopPropagation();let n=e.dataset.id;Rn(n),y(),window.showToast&&window.showToast(`Removed from saved connections.`,`info`)}});let O=e.querySelector(`#connect-submit-btn`),k=e.querySelector(`#connect-error-msg`);O&&(O.onclick=async()=>{let e=n[t],r=null;if(e===`create`&&(t===`postgres`||t===`mysql`)){let e=i[t],n=e.dbname.trim();if(!n){k&&(k.textContent=`Please enter a new database name to create.`,k.classList.remove(`hidden`));return}r={mode:`create`,dbType:t,host:e.host.trim()||`localhost`,port:e.port.trim()||(t===`mysql`?`3306`:`5432`),user:e.user.trim()||(t===`mysql`?`root`:`postgres`),password:e.password||``,dbName:n,saveToEnv:o}}else if(t===`sqlite`&&e===`create`){let e=a.createName.trim()||`drixio.sqlite`;!e.endsWith(`.sqlite`)&&!e.endsWith(`.db`)&&!e.endsWith(`.sqlite3`)&&(e+=`.sqlite`),r={url:`./${e}`,createIfNotExist:!0,saveToEnv:o}}else{let e=``;if(e=t===`sqlite`?a.existingPath.trim()||``:f(),!e){k&&(k.textContent=`Please enter valid connection parameters.`,k.classList.remove(`hidden`));return}r={url:e,createIfNotExist:!1,saveToEnv:o}}let s=O.innerHTML;O.disabled=!0,O.innerHTML=`
|
|
3102
|
+
<span class="material-symbols-outlined animate-spin" style="font-size: 18px;">sync</span>
|
|
3103
|
+
<span>Connecting...</span>
|
|
3104
|
+
`,k&&k.classList.add(`hidden`);try{let e=await(await fetch(`/api/connect`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify(r)})).json();if(!e.success)throw Error(e.error||`Failed to connect`);let n=t!==`sqlite`,i=n?`REMOTE`:`LOCAL`,a=e.data?.dbName||p(),o=e.data?.targetUrl||r.url;Ln({name:a,dialect:e.data?.dbType||t,url:o,isRemote:n,badgeLabel:i});let s=document.getElementById(`db-name`);s&&(s.textContent=a);let c=document.getElementById(`env-badge`);c&&(c.textContent=i,c.className=`env-badge ${n?`remote`:`local`}`,c.classList.remove(`hidden`)),window.showToast&&window.showToast(`Database connected successfully!`,`success`),typeof window.initSidebar==`function`&&await window.initSidebar(!0),typeof window.setStudioConnectionMode==`function`&&window.setStudioConnectionMode(!0),typeof window.handleSwitchTab==`function`&&window.handleSwitchTab(`data-btn`)}catch(e){O.disabled=!1,O.innerHTML=s,k&&(k.textContent=e.message,k.classList.remove(`hidden`)),window.showToast&&window.showToast(e.message,`error`)}}),h(),_()};y()}var Vn=()=>{let e=localStorage.getItem(`drixio-theme`),t=window.matchMedia&&window.matchMedia(`(prefers-color-scheme: dark)`).matches,n=e=>{let t=document.getElementById(`theme-icon`),n=document.getElementById(`theme-text`);t&&(t.textContent=e?`light_mode`:`dark_mode`),n&&(n.textContent=e?`Light Mode`:`Dark Mode`)};e===`dark`||!e&&t?(document.documentElement.setAttribute(`data-theme`,`dark`),n(!0)):n(!1);let r=document.getElementById(`theme-toggle`);r&&r.addEventListener(`click`,()=>{let e=document.documentElement.getAttribute(`data-theme`)===`dark`,t=e?`light`:`dark`;document.documentElement.setAttribute(`data-theme`,t),localStorage.setItem(`drixio-theme`,t),n(!e)})},Hn=()=>{let e=new Map;window.showToast=function(t,n=`success`){let r=document.getElementById(`toast-container`);if(!r)return;let i=Date.now(),a=`${n}:${t}`,o=e.get(a);if(o&&i-o<1500)return;if(e.set(a,i),e.size>50)for(let[t,n]of e.entries())i-n>1e4&&e.delete(t);for(;r.children.length>=3;){let e=r.firstElementChild;e&&e.remove()}let s=document.createElement(`div`);s.className=`toast ${n}`;let c=`check_circle`;n===`error`?c=`error`:n===`warning`?c=`warning`:n===`info`&&(c=`info`),s.innerHTML=`<span class="material-symbols-outlined">${c}</span> <span>${t}</span>`,r.appendChild(s),setTimeout(()=>{s.parentNode&&s.parentNode.removeChild(s)},3e3)}},Un=()=>{document.addEventListener(`mouseup`,()=>{window.DataGrid&&window.DataGrid.selection&&(window.DataGrid.selection.isDragging=!1),window.SchemaGrid&&window.SchemaGrid.selection&&(window.SchemaGrid.selection.isDragging=!1)}),document.addEventListener(`contextmenu`,e=>{let t=window.AppState?.currentTab===`data-btn`,n=window.AppState?.currentTab===`schema-btn`;if(!t&&!n||t&&!window.DataGrid||n&&!window.SchemaGrid)return;let r=e.target.closest(`td.row-header, td.data-cell`);if(!r||e.target.tagName===`INPUT`||e.target.tagName===`SELECT`||e.target.tagName===`TEXTAREA`)return;e.preventDefault();let i=parseInt(r.dataset.rowIdx);if(isNaN(i))return;let a=[i],o=t?window.DataGrid:window.SchemaGrid;if(o&&o.selection&&o.selection.startRow!==-1){let e=o.selection,t=Math.min(e.startRow,e.endRow),n=Math.max(e.startRow,e.endRow);if(i>=t&&i<=n){a=[];for(let e=t;e<=n;e++)a.push(e)}}let s=e=>{if(!t||!window.DataGrid)return null;let n=window.DataGrid.rows||[],i=window.DataGrid.pkColumn,a=null;if(e<n.length&&n[e]){a={...n[e]};let t=i?a[i]:null;t!=null&&window.DataGrid.pendingEdits?.[t]&&Object.assign(a,window.DataGrid.pendingEdits[t])}else{let t=r.dataset?.insertIndex===void 0?e-n.length:parseInt(r.dataset.insertIndex);window.DataGrid.pendingInserts?.[t]&&(a={...window.DataGrid.pendingInserts[t]})}return a},c=async e=>{let t=e.map(e=>s(e)).filter(Boolean);if(t.length===0)return;let n=t.length===1?t[0]:t,r=JSON.stringify(n,null,2);try{await navigator.clipboard.writeText(r)}catch{let e=document.createElement(`textarea`);e.value=r,document.body.appendChild(e),e.select(),document.execCommand(`copy`),e.remove()}window.showToast?.(`Copied ${t.length} row(s) as JSON!`,`success`)},l=async e=>{let t=e.map(e=>s(e)).filter(Boolean);if(t.length===0)return;let n=window.AppState?.currentTable||`table_name`,r=window.AppState?.dbType||`sqlite`,i=e=>r===`mysql`?`\`${e}\``:`"${e}"`,a=e=>{if(e==null)return`NULL`;if(typeof e==`number`)return String(e);if(typeof e==`boolean`)return e?`TRUE`:`FALSE`;if(typeof e==`object`)return`'${JSON.stringify(e).replace(/'/g,`''`)}'`;let t=String(e);return t.toUpperCase()===`NULL`?`NULL`:`'${t.replace(/'/g,`''`)}'`},o=window.DataGrid?.schema||[],c=o.length>0?o.map(e=>e.name):Object.keys(t[0]),l=t.map(e=>{let t=c.map(t=>a(e[t]));return`INSERT INTO ${i(n)} (${c.map(i).join(`, `)}) VALUES (${t.join(`, `)});`}).join(`
|
|
3105
|
+
`);try{await navigator.clipboard.writeText(l)}catch{let e=document.createElement(`textarea`);e.value=l,document.body.appendChild(e),e.select(),document.execCommand(`copy`),e.remove()}window.showToast?.(`Copied ${t.length} row(s) as SQL INSERT!`,`success`)},u=async()=>{let e=r.textContent||``;try{await navigator.clipboard.writeText(e)}catch{let t=document.createElement(`textarea`);t.value=e,document.body.appendChild(t),t.select(),document.execCommand(`copy`),t.remove()}window.showToast?.(`Copied cell value!`,`success`)},d=e=>{t?(window.DataGrid.currentTransaction=[],e===`delete`?a.forEach(e=>je(e)):e===`duplicate`&&(Ne(a,window.DataGrid.schema.map(e=>e.name)),setTimeout(()=>{let e=document.getElementById(`data-grid-container-${window.AppState.currentTable}`);e&&(e.scrollTop=e.scrollHeight)},50)),window.DataGrid.currentTransaction.length>0&&window.DataGrid.history.push(window.DataGrid.currentTransaction),window.DataGrid.currentTransaction=null):n&&(window.SchemaGrid.currentTransaction=[],e===`delete`?a.forEach(e=>Je(e)):e===`duplicate`&&(Xe(a,[`name`,`type`,`isPk`,`nullable`,`defaultValue`,`Index`]),setTimeout(()=>{let e=document.getElementById(`schema-grid-container-${window.AppState.currentTable}`);e&&(e.scrollTop=e.scrollHeight)},50)),window.SchemaGrid.currentTransaction.length>0&&window.SchemaGrid.history.push(window.SchemaGrid.currentTransaction),window.SchemaGrid.currentTransaction=null)},f=[];r.classList.contains(`data-cell`)&&r.textContent?.trim()&&(f.push({label:`Copy Cell Value`,icon:`content_paste`,action:()=>u()}),f.push(`divider`)),t&&f.push({label:a.length>1?`Copy ${a.length} Rows as JSON`:`Copy Row as JSON`,icon:`data_object`,action:()=>c(a)},{label:a.length>1?`Copy ${a.length} Rows as SQL`:`Copy Row as SQL (INSERT)`,icon:`code`,action:()=>l(a)},`divider`),f.push({label:a.length>1?`Duplicate ${a.length} Rows`:`Duplicate Row`,icon:`content_copy`,action:()=>d(`duplicate`)},`divider`,{label:a.length>1?`Delete ${a.length} Rows`:`Delete Row`,icon:`delete`,danger:!0,action:()=>d(`delete`)}),A(e,f)}),document.addEventListener(`keydown`,e=>{let t=window.AppState?.currentTab===`data-btn`,n=window.AppState?.currentTab===`schema-btn`;if(e.key===`F5`){t&&window.DataGrid?.refreshData?(e.preventDefault(),window.DataGrid.refreshData()):n&&document.getElementById(`btn-refresh-schema`)&&(e.preventDefault(),document.getElementById(`btn-refresh-schema`).click());return}if(!t&&!n)return;if(e.key.toLowerCase()===`s`&&(e.ctrlKey||e.metaKey)){e.preventDefault(),(e.target.tagName===`INPUT`||e.target.tagName===`SELECT`||e.target.tagName===`TEXTAREA`)&&e.target.blur(),setTimeout(()=>{t&&window.saveDataGridEdits?.(),n&&window.saveSchemaEdits?.()},50);return}if(e.target.tagName===`INPUT`||e.target.tagName===`SELECT`||e.target.tagName===`TEXTAREA`)return;if(e.key===`Escape`){let e=t?window.DataGrid:window.SchemaGrid;if(!e||!e.selection||e.selection.startRow===-1)return;if(e.selection.startRow!==e.selection.endRow||e.selection.startCol!==e.selection.endCol||e.selection.isRowSelection){e.selection.endRow=e.selection.startRow,e.selection.endCol=e.selection.startCol,e.selection.isRowSelection=!1,e.selection.isDraggingRow=!1;let n=window.AppState?.currentTable;he(t?`data-grid-table-${n}`:`schema-grid-table-${n}`,e);return}}if(e.key.toLowerCase()===`a`&&(e.ctrlKey||e.metaKey)){let n=t?window.DataGrid:window.SchemaGrid;if(!n)return;e.preventDefault();let r=window.AppState?.currentTable;if(!r)return;ge(t?`data-grid-table-${r}`:`schema-grid-table-${r}`,n,t?n.schema?.length||1:6);return}if([`ArrowUp`,`ArrowDown`,`ArrowLeft`,`ArrowRight`].includes(e.key)){let n=t?window.DataGrid:window.SchemaGrid;if(!n||!n.selection)return;e.preventDefault();let r=n.selection;if(r.startRow===-1)return;let i=t?n.schema.length:5,a=window.AppState?.currentTable,o=document.querySelectorAll(t?`#data-grid-table-${a} tbody tr`:`#schema-grid-table-${a} tbody tr`),s=o.length>0?o.length-1:0,c=e.shiftKey?r.endRow:r.startRow,l=e.shiftKey?r.endCol:r.startCol;e.key===`ArrowUp`&&(c=Math.max(0,c-1)),e.key===`ArrowDown`&&(c=Math.min(s,c+1)),e.key===`ArrowLeft`&&(l=Math.max(0,l-1)),e.key===`ArrowRight`&&(l=Math.min(i-1,l+1)),e.shiftKey?(r.endRow=c,r.endCol=l):(r.startRow=c,r.endRow=c,r.startCol=l,r.endCol=l),he(t?`data-grid-table-${a}`:`schema-grid-table-${a}`,n),setTimeout(()=>{let e=document.querySelector(t?`#data-grid-table-${a} td.data-cell[data-row-idx="${c}"][data-col-idx="${l}"]`:`#schema-grid-table-${a} td.data-cell[data-row-idx="${c}"][data-col-idx="${l}"]`);if(e){let n=document.getElementById(t?`data-grid-container-${a}`:`schema-grid-container-${a}`);if(n){let t=e.getBoundingClientRect(),r=n.getBoundingClientRect();t.bottom>r.bottom?n.scrollTop+=t.bottom-r.bottom+5:t.top<r.top+30&&(n.scrollTop-=r.top+30-t.top),t.right>r.right?n.scrollLeft+=t.right-r.right+5:t.left<r.left+50&&(n.scrollLeft-=r.left+50-t.left)}}},5);return}if((e.key===` `||e.code===`Space`)&&t&&window.DataGrid?.selection?.startRow!==-1){let t=window.DataGrid.selection;if(t.startRow===t.endRow&&t.startCol===t.endCol){let n=window.AppState?.currentTable,r=document.querySelector(`#data-grid-table-${n} td.data-cell[data-row-idx="${t.startRow}"][data-col-idx="${t.startCol}"]`);if(r&&!r.querySelector(`input, select`)){let t=r.dataset.col,n=(window.DataGrid.schema?.find(e=>e.name===t))?.type?.toUpperCase()||``;if(n.includes(`BOOL`)||n===`TINYINT(1)`){e.preventDefault();let t=r.dataset.original!==void 0&&r.dataset.original!==`-`?r.dataset.original:r.textContent.trim(),n=t===`1`||t.toLowerCase()===`true`?`0`:`1`;window.DataGrid.currentTransaction=[],Ae(r,n,window.DataGrid.schema.map(e=>e.name)),window.DataGrid.currentTransaction.length>0&&window.DataGrid.history.push(window.DataGrid.currentTransaction),window.DataGrid.currentTransaction=null;return}}}}let r=e.key.length===1&&!e.ctrlKey&&!e.metaKey&&!e.altKey,i=e.key===`F2`;if(e.key===`Enter`||r||i){if(e.repeat)return;let n=t?window.DataGrid:window.SchemaGrid;if(!n||!n.selection||n.selection.startRow===-1)return;let a=window.AppState?.currentTable;if(!a)return;let o=Math.min(n.selection.startRow,n.selection.endRow),s=Math.min(n.selection.startCol,n.selection.endCol),c=document.querySelector(t?`#data-grid-table-${a} td.data-cell[data-row-idx="${o}"][data-col-idx="${s}"]`:`#schema-grid-table-${a} td.data-cell[data-row-idx="${o}"][data-col-idx="${s}"]`);if(c&&!c.classList.contains(`manage-indexes-cell`)&&!c.querySelector(`input, select`)){e.preventDefault();let t=new MouseEvent(`dblclick`,{bubbles:!0,cancelable:!0,view:window});c.dispatchEvent(t);let n=c.querySelector(`input`);if(n){if(r)n.value=e.key,n.setSelectionRange(e.key.length,e.key.length);else if(i){let e=n.value.length;n.setSelectionRange(e,e)}}else{let t=c.querySelector(`select`);if(t&&r){let n=Array.from(t.options).find(t=>t.text.toLowerCase().startsWith(e.key.toLowerCase()));n&&(t.value=n.value,t.dispatchEvent(new Event(`change`,{bubbles:!0})))}}return}}if(e.key===`Tab`){let n=t?window.DataGrid:window.SchemaGrid;if(!n||!n.selection||n.selection.startRow===-1)return;e.preventDefault();let r=window.AppState?.currentTable,i=Math.min(n.selection.startRow,n.selection.endRow),a=Math.min(n.selection.startCol,n.selection.endCol),o=t?n.schema.length:5,s=document.querySelectorAll(t?`#data-grid-table-${r} tbody tr`:`#schema-grid-table-${r} tbody tr`),c=s.length>0?s.length-1:0;e.shiftKey?(--a,a<0&&(a=o-1,i=Math.max(0,i-1))):(a+=1,a>=o&&(a=0,i=Math.min(c,i+1))),n.selection.startRow=i,n.selection.endRow=i,n.selection.startCol=a,n.selection.endCol=a,he(t?`data-grid-table-${r}`:`schema-grid-table-${r}`,n),setTimeout(()=>{let e=document.querySelector(t?`#data-grid-table-${r} td.data-cell[data-row-idx="${i}"][data-col-idx="${a}"]`:`#schema-grid-table-${r} td.data-cell[data-row-idx="${i}"][data-col-idx="${a}"]`);if(e){let n=document.getElementById(t?`data-grid-container-${r}`:`schema-grid-container-${r}`);if(n){let t=e.getBoundingClientRect(),r=n.getBoundingClientRect();t.bottom>r.bottom?n.scrollTop+=t.bottom-r.bottom+5:t.top<r.top+30&&(n.scrollTop-=r.top+30-t.top),t.right>r.right?n.scrollLeft+=t.right-r.right+5:t.left<r.left+50&&(n.scrollLeft-=r.left+50-t.left)}}},5);return}if(e.key===`Delete`||e.key===`Backspace`){let e=t?window.DataGrid:window.SchemaGrid;if(!e||!e.selection||e.selection.startRow===-1)return;let r=e.selection,i=Math.min(r.startRow,r.endRow),a=Math.max(r.startRow,r.endRow),o=Math.min(r.startCol,r.endCol),s=Math.max(r.startCol,r.endCol),c=t?window.DataGrid?.schema?.length||1:6,l=r.isRowSelection===!0||o===0&&s>=c-1;if(t)if(l){window.DataGrid.currentTransaction=[];for(let e=i;e<=a;e++)je(e);window.DataGrid.currentTransaction.length>0&&window.DataGrid.history.push(window.DataGrid.currentTransaction),window.DataGrid.currentTransaction=null,window.updateSidebarDirtyState?.()}else window.DataGrid.currentTransaction=[],document.querySelectorAll(`#data-grid-table-${window.AppState.currentTable} .cell-in-range`).forEach(e=>Ae(e,``,window.DataGrid.schema.map(e=>e.name))),window.DataGrid.currentTransaction.length>0&&window.DataGrid.history.push(window.DataGrid.currentTransaction),window.DataGrid.currentTransaction=null;else if(n)if(l){window.SchemaGrid.currentTransaction=[];for(let e=i;e<=a;e++)Je(e);window.SchemaGrid.currentTransaction.length>0&&window.SchemaGrid.history.push(window.SchemaGrid.currentTransaction),window.SchemaGrid.currentTransaction=null,window.updateSidebarDirtyState?.()}else{window.SchemaGrid.currentTransaction=[];let e=[`name`,`type`,`isPk`,`nullable`,`defaultValue`];document.querySelectorAll(`#schema-grid-table-${window.AppState.currentTable} .cell-in-range`).forEach(t=>{(t.dataset.insertIndex!==void 0||t.dataset.colKey===`name`)&&qe(t,``,e)}),window.SchemaGrid.currentTransaction.length>0&&window.SchemaGrid.history.push(window.SchemaGrid.currentTransaction),window.SchemaGrid.currentTransaction=null}}if(e.key.toLowerCase()===`s`&&(e.ctrlKey||e.metaKey)&&(e.preventDefault(),t&&window.saveDataGridEdits(),n&&window.saveSchemaEdits()),e.key.toLowerCase()===`c`&&(e.ctrlKey||e.metaKey)){let e=(t?window.DataGrid:window.SchemaGrid).selection;if(e.startRow===-1)return;let n=Math.min(e.startRow,e.endRow),r=Math.max(e.startRow,e.endRow),i=Math.min(e.startCol,e.endCol),a=Math.max(e.startCol,e.endCol),o=``;for(let e=n;e<=r;e++){let n=[];for(let r=i;r<=a;r++){let i=document.querySelector(t?`#data-grid-table-${window.AppState.currentTable} td.data-cell[data-row-idx="${e}"][data-col-idx="${r}"]`:`#schema-grid-table-${window.AppState.currentTable} td.data-cell[data-row-idx="${e}"][data-col-idx="${r}"]`);if(i){let e=i.textContent.replace(/^null$|^\+ New$/,``);n.push(e)}}o+=n.join(` `)+`
|
|
3106
|
+
`}navigator.clipboard.writeText(o.trimEnd()),document.querySelectorAll(`.cell-in-range`).forEach(e=>{e.style.backgroundColor=`var(--color-copy-flash)`,setTimeout(()=>e.style.backgroundColor=``,150)})}if(e.key===`-`&&(e.ctrlKey||e.metaKey)){e.preventDefault();let r=t?window.DataGrid:window.SchemaGrid,i=r.selection;if(i.startRow===-1)return;let a=Math.min(i.startRow,i.endRow),o=Math.max(i.startRow,i.endRow);if(t){r.currentTransaction=[];for(let e=a;e<=o;e++)je(e);r.currentTransaction.length>0&&r.history.push(r.currentTransaction),r.currentTransaction=null}else if(n){r.currentTransaction=[];for(let e=a;e<=o;e++)Je(e);r.currentTransaction.length>0&&r.history.push(r.currentTransaction),r.currentTransaction=null}}if(e.key.toLowerCase()===`z`&&(e.ctrlKey||e.metaKey)){if(e.preventDefault(),t){let e=window.DataGrid.history?.pop();if(e){let t=window.DataGrid.schema.map(e=>e.name);for(let n=e.length-1;n>=0;n--){let r=e[n];r.type===`delete`?Me(r.rowIdx,r.pk):Ae(r.td,r.oldVal,t,!1)}}}else if(n){let e=window.SchemaGrid.history?.pop();if(e){let t=[`name`,`type`,`isPk`,`nullable`,`defaultValue`];for(let n=e.length-1;n>=0;n--){let r=e[n];r.type===`delete`?Ye(r.rowIdx,r.pk):qe(r.td,r.oldVal,t,!1)}}}}}),document.addEventListener(`paste`,e=>{let t=window.AppState?.currentTab===`data-btn`,n=window.AppState?.currentTab===`schema-btn`;if(!t&&!n)return;let r=t?window.DataGrid:window.SchemaGrid;if(!r||e.target.tagName===`INPUT`||e.target.tagName===`SELECT`||e.target.tagName===`TEXTAREA`)return;let i=r.selection;if(i.startRow===-1)return;let a=e.clipboardData.getData(`text`);if(!a)return;e.preventDefault();let o=a.split(/\r?\n/).map(e=>e.split(` `)),s=Math.min(i.startRow,i.endRow),c=Math.min(i.startCol,i.endCol);if(r.currentTransaction=[],t){let e=r.schema.map(e=>e.name);o.forEach(t=>{t.forEach((t,n)=>{let r=c+n,i=document.querySelector(`#data-grid-table-${window.AppState.currentTable} td.data-cell[data-row-idx="${s}"][data-col-idx="${r}"]`);i&&Ae(i,t,e)}),s++}),r.currentTransaction.length>0&&r.history.push(r.currentTransaction),r.currentTransaction=null}else if(n){let e=[`name`,`type`,`isPk`,`nullable`,`defaultValue`];o.forEach(t=>{t.forEach((t,n)=>{let r=c+n,i=document.querySelector(`#schema-grid-table-${window.AppState.currentTable} td.data-cell[data-row-idx="${s}"][data-col-idx="${r}"]`);i&&(i.dataset.insertIndex!==void 0||i.dataset.colKey===`name`)&&qe(i,t,e)}),s++}),r.currentTransaction.length>0&&r.history.push(r.currentTransaction),r.currentTransaction=null}})},Wn=null;function Gn(){Wn&&Wn.close(),Wn=_t({id:`help-modal-overlay`,icon:`help`,iconColor:`primary`,title:`Shortcuts & Documentation`,subtitle:`Drixio Studio Edition Tips & Resources`,badge:`Help`,width:`640px`,body:`
|
|
3107
|
+
<div class="help-modal-body" style="padding: 0;">
|
|
3108
|
+
<div class="shortcuts-group">
|
|
3109
|
+
<h4 class="shortcuts-group-title">Global Shortcuts</h4>
|
|
3110
|
+
<div class="shortcut-row">
|
|
3111
|
+
<span class="shortcut-desc">Quick Search / Focus Table Filter</span>
|
|
3112
|
+
<div class="shortcut-keys"><kbd>Ctrl</kbd> + <kbd>K</kbd> / <kbd>/</kbd></div>
|
|
3113
|
+
</div>
|
|
3114
|
+
<div class="shortcut-row">
|
|
3115
|
+
<span class="shortcut-desc">Close Modal / Dismiss Popover</span>
|
|
3116
|
+
<div class="shortcut-keys"><kbd>Esc</kbd></div>
|
|
3117
|
+
</div>
|
|
3118
|
+
</div>
|
|
3119
|
+
|
|
3120
|
+
<div class="shortcuts-group">
|
|
3121
|
+
<h4 class="shortcuts-group-title">Data Grid & Editor</h4>
|
|
3122
|
+
<div class="shortcut-row">
|
|
3123
|
+
<span class="shortcut-desc">Inline Edit Cell</span>
|
|
3124
|
+
<div class="shortcut-keys"><kbd>Double Click</kbd></div>
|
|
3125
|
+
</div>
|
|
3126
|
+
<div class="shortcut-row">
|
|
3127
|
+
<span class="shortcut-desc">Save Cell Edit</span>
|
|
3128
|
+
<div class="shortcut-keys"><kbd>Enter</kbd></div>
|
|
3129
|
+
</div>
|
|
3130
|
+
<div class="shortcut-row">
|
|
3131
|
+
<span class="shortcut-desc">Run Query in SQL Console</span>
|
|
3132
|
+
<div class="shortcut-keys"><kbd>Ctrl</kbd> + <kbd>Enter</kbd></div>
|
|
3133
|
+
</div>
|
|
3134
|
+
</div>
|
|
3135
|
+
|
|
3136
|
+
<div class="help-links-group">
|
|
3137
|
+
<a href="https://github.com/TerKSDev/drixio" target="_blank" rel="noopener noreferrer" class="help-link-card">
|
|
3138
|
+
<span class="material-symbols-outlined">code</span>
|
|
3139
|
+
<div>
|
|
3140
|
+
<span class="link-card-title">GitHub Repository</span>
|
|
3141
|
+
<span class="link-card-desc">Source code, releases, and roadmap</span>
|
|
3142
|
+
</div>
|
|
3143
|
+
<span class="material-symbols-outlined card-arrow">open_in_new</span>
|
|
3144
|
+
</a>
|
|
3145
|
+
<a href="https://github.com/TerKSDev/drixio/issues" target="_blank" rel="noopener noreferrer" class="help-link-card">
|
|
3146
|
+
<span class="material-symbols-outlined">bug_report</span>
|
|
3147
|
+
<div>
|
|
3148
|
+
<span class="link-card-title">Report an Issue</span>
|
|
3149
|
+
<span class="link-card-desc">Found a bug or have a suggestion?</span>
|
|
3150
|
+
</div>
|
|
3151
|
+
<span class="material-symbols-outlined card-arrow">open_in_new</span>
|
|
3152
|
+
</a>
|
|
3153
|
+
</div>
|
|
3154
|
+
</div>
|
|
3155
|
+
`,footer:`
|
|
3156
|
+
<span class="help-version-tag">Drixio Studio Edition</span>
|
|
3157
|
+
<button type="button" id="help-modal-done-btn" class="btn-secondary" style="padding: 5px 14px; font-size: 12px; cursor: pointer;">Close</button>
|
|
3158
|
+
`,onClose:()=>{Wn=null}});let e=Wn.footer.querySelector(`#help-modal-done-btn`);e&&(e.onclick=()=>Wn.close())}var H=null;function Kn(){let e=document.getElementById(`breadcrumb-db-crumb`);H=document.getElementById(`db-switcher-dropdown`),!(!e||!H)&&(e.addEventListener(`click`,t=>{t.stopPropagation(),H.classList.contains(`hidden`)?(qn(),H.classList.remove(`hidden`),e.classList.add(`is-open`)):(H.classList.add(`hidden`),e.classList.remove(`is-open`))}),document.addEventListener(`click`,t=>{H&&!H.contains(t.target)&&!e.contains(t.target)&&(H.classList.add(`hidden`),e.classList.remove(`is-open`))}),document.addEventListener(`keydown`,t=>{t.key===`Escape`&&H&&!H.classList.contains(`hidden`)&&(H.classList.add(`hidden`),e.classList.remove(`is-open`))}))}function qn(){if(!H&&(H=document.getElementById(`db-switcher-dropdown`),!H))return;let e=In(),t=document.getElementById(`db-name`)?.textContent?.trim().toLowerCase()||``,n=e=>{switch(e){case`postgres`:return`dns`;case`mysql`:return`storage`;default:return`data_object`}};H.innerHTML=`
|
|
3159
|
+
<div class="db-switcher-header">
|
|
3160
|
+
<span class="db-switcher-title">DATABASES</span>
|
|
3161
|
+
<span class="db-switcher-count">${e.length}</span>
|
|
3162
|
+
</div>
|
|
3163
|
+
|
|
3164
|
+
<div class="db-switcher-list">
|
|
3165
|
+
${e.length===0?`<div class="db-switcher-empty">No saved databases yet. Connect or create one below!</div>`:e.map(e=>{let r=e.name.toLowerCase()===t||e.url.toLowerCase().includes(t);return`
|
|
3166
|
+
<div class="db-switcher-item ${r?`active`:``}" data-id="${e.id}">
|
|
3167
|
+
<div class="db-item-icon-wrap ${e.dialect}">
|
|
3168
|
+
<span class="material-symbols-outlined db-item-icon">${n(e.dialect)}</span>
|
|
3169
|
+
</div>
|
|
3170
|
+
<div class="db-item-info">
|
|
3171
|
+
<div class="db-item-name-row">
|
|
3172
|
+
<span class="db-item-name">${Jn(e.name)}</span>
|
|
3173
|
+
${r?`<span class="db-active-badge">Active</span>`:``}
|
|
3174
|
+
</div>
|
|
3175
|
+
<span class="db-item-meta">${Jn(e.badgeLabel||(e.isRemote?`REMOTE`:`LOCAL`))}</span>
|
|
3176
|
+
</div>
|
|
3177
|
+
${r?`<span class="material-symbols-outlined db-active-check">check</span>`:`<button type="button" class="db-remove-btn" title="Forget database" data-id="${e.id}">✕</button>`}
|
|
3178
|
+
</div>
|
|
3179
|
+
`}).join(``)}
|
|
3180
|
+
</div>
|
|
3181
|
+
|
|
3182
|
+
<div class="db-switcher-divider"></div>
|
|
3183
|
+
|
|
3184
|
+
<div class="db-switcher-footer">
|
|
3185
|
+
<button type="button" id="db-switcher-new-btn" class="db-switcher-new-btn">
|
|
3186
|
+
<span class="material-symbols-outlined">add</span>
|
|
3187
|
+
<span>Connect or Create New Database...</span>
|
|
3188
|
+
</button>
|
|
3189
|
+
</div>
|
|
3190
|
+
`,H.querySelectorAll(`.db-switcher-item`).forEach(t=>{t.addEventListener(`click`,async n=>{if(n.target.closest(`.db-remove-btn`))return;let r=t.getAttribute(`data-id`),i=e.find(e=>e.id===r);i&&(H.classList.add(`hidden`),document.getElementById(`breadcrumb-db-crumb`)?.classList.remove(`is-open`),await zn(i))})}),H.querySelectorAll(`.db-remove-btn`).forEach(e=>{e.addEventListener(`click`,t=>{t.stopPropagation();let n=e.getAttribute(`data-id`);n&&(Rn(n),qn())})});let r=document.getElementById(`db-switcher-new-btn`);r&&r.addEventListener(`click`,e=>{e.stopPropagation(),H.classList.add(`hidden`),document.getElementById(`breadcrumb-db-crumb`)?.classList.remove(`is-open`),typeof window.handleSwitchTab==`function`&&window.handleSwitchTab(`connect-btn`)})}function Jn(e){return e?String(e).replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`).replace(/"/g,`"`):``}function Yn(e){if(e===void 0)throw ReferenceError(`this hasn't been initialised - super() hasn't been called`);return e}function Xn(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,e.__proto__=t}var Zn={autoSleep:120,force3D:`auto`,nullTargetWarn:1,units:{lineHeight:``}},Qn={duration:.5,overwrite:!1,delay:0},$n,er,U,tr=1e8,W=1/tr,nr=Math.PI*2,rr=nr/4,ir=0,ar=Math.sqrt,or=Math.cos,sr=Math.sin,G=function(e){return typeof e==`string`},K=function(e){return typeof e==`function`},cr=function(e){return typeof e==`number`},lr=function(e){return e===void 0},ur=function(e){return typeof e==`object`},dr=function(e){return e!==!1},fr=function(){return typeof window<`u`},pr=function(e){return K(e)||G(e)},mr=typeof ArrayBuffer==`function`&&ArrayBuffer.isView||function(){},hr=Array.isArray,gr=/random\([^)]+\)/g,_r=/,\s*/g,vr=/(?:-?\.?\d|\.)+/gi,yr=/[-+=.]*\d+[.e\-+]*\d*[e\-+]*\d*/g,br=/[-+=.]*\d+[.e-]*\d*[a-z%]*/g,xr=/[-+=.]*\d+\.?\d*(?:e-|e\+)?\d*/gi,Sr=/[+-]=-?[.\d]+/,Cr=/[^,'"\[\]\s]+/gi,wr=/^[+\-=e\s\d]*\d+[.\d]*([a-z]*|%)\s*$/i,q,Tr,Er,Dr,Or={},kr={},Ar,jr=function(e){return(kr=si(e,Or))&&wo},Mr=function(e,t){return console.warn(`Invalid property`,e,`set to`,t,`Missing plugin? gsap.registerPlugin()`)},Nr=function(e,t){return!t&&console.warn(e)},Pr=function(e,t){return e&&(Or[e]=t)&&kr&&(kr[e]=t)||Or},Fr=function(){return 0},Ir={suppressEvents:!0,isStart:!0,kill:!1},Lr={suppressEvents:!0,kill:!1},Rr={suppressEvents:!0},zr={},Br=[],Vr={},Hr,Ur={},Wr={},Gr=30,Kr=[],qr=``,Jr=function(e){var t=e[0],n,r;if(ur(t)||K(t)||(e=[e]),!(n=(t._gsap||{}).harness)){for(r=Kr.length;r--&&!Kr[r].targetTest(t););n=Kr[r]}for(r=e.length;r--;)e[r]&&(e[r]._gsap||(e[r]._gsap=new Pa(e[r],n)))||e.splice(r,1);return e},Yr=function(e){return e._gsap||Jr(Wi(e))[0]._gsap},Xr=function(e,t,n){return(n=e[t])&&K(n)?e[t]():lr(n)&&e.getAttribute&&e.getAttribute(t)||n},Zr=function(e,t){return(e=e.split(`,`)).forEach(t)||e},J=function(e){return Math.round(e*1e5)/1e5||0},Y=function(e){return Math.round(e*1e7)/1e7||0},Qr=function(e,t){var n=t.charAt(0),r=parseFloat(t.substr(2));return e=parseFloat(e),n===`+`?e+r:n===`-`?e-r:n===`*`?e*r:e/r},$r=function(e,t){for(var n=t.length,r=0;e.indexOf(t[r])<0&&++r<n;);return r<n},ei=function(){var e=Br.length,t=Br.slice(0),n,r;for(Vr={},Br.length=0,n=0;n<e;n++)r=t[n],r&&r._lazy&&(r.render(r._lazy[0],r._lazy[1],!0)._lazy=0)},ti=function(e){return!!(e._initted||e._startAt||e.add)},ni=function(e,t,n,r){Br.length&&!er&&ei(),e.render(t,n,r||!!(er&&t<0&&ti(e))),Br.length&&!er&&ei()},ri=function(e){var t=parseFloat(e);return(t||t===0)&&(e+``).match(Cr).length<2?t:G(e)?e.trim():e},ii=function(e){return e},ai=function(e,t){for(var n in t)n in e||(e[n]=t[n]);return e},oi=function(e){return function(t,n){for(var r in n)r in t||r===`duration`&&e||r===`ease`||(t[r]=n[r])}},si=function(e,t){for(var n in t)e[n]=t[n];return e},ci=function e(t,n){for(var r in n)r!==`__proto__`&&r!==`constructor`&&r!==`prototype`&&(t[r]=ur(n[r])?e(t[r]||(t[r]={}),n[r]):n[r]);return t},li=function(e,t){var n={},r;for(r in e)r in t||(n[r]=e[r]);return n},ui=function(e){var t=e.parent||q,n=e.keyframes?oi(hr(e.keyframes)):ai;if(dr(e.inherit))for(;t;)n(e,t.vars.defaults),t=t.parent||t._dp;return e},di=function(e,t){for(var n=e.length,r=n===t.length;r&&n--&&e[n]===t[n];);return n<0},fi=function(e,t,n,r,i){n===void 0&&(n=`_first`),r===void 0&&(r=`_last`);var a=e[r],o;if(i)for(o=t[i];a&&a[i]>o;)a=a._prev;return a?(t._next=a._next,a._next=t):(t._next=e[n],e[n]=t),t._next?t._next._prev=t:e[r]=t,t._prev=a,t.parent=t._dp=e,t},pi=function(e,t,n,r){n===void 0&&(n=`_first`),r===void 0&&(r=`_last`);var i=t._prev,a=t._next;i?i._next=a:e[n]===t&&(e[n]=a),a?a._prev=i:e[r]===t&&(e[r]=i),t._next=t._prev=t.parent=null},mi=function(e,t){e.parent&&(!t||e.parent.autoRemoveChildren)&&e.parent.remove&&e.parent.remove(e),e._act=0},hi=function(e,t){if(e&&(!t||t._end>e._dur||t._start<0))for(var n=e;n;)n._dirty=1,n=n.parent;return e},gi=function(e){for(var t=e.parent;t&&t.parent;)t._dirty=1,t.totalDuration(),t=t.parent;return e},_i=function(e,t,n,r){return e._startAt&&(er?e._startAt.revert(Lr):e.vars.immediateRender&&!e.vars.autoRevert||e._startAt.render(t,!0,r))},vi=function e(t){return!t||t._ts&&e(t.parent)},yi=function(e){return e._repeat?bi(e._tTime,e=e.duration()+e._rDelay)*e:0},bi=function(e,t){var n=Math.floor(e=Y(e/t));return e&&n===e?n-1:n},xi=function(e,t){return(e-t._start)*t._ts+(t._ts>=0?0:t._dirty?t.totalDuration():t._tDur)},Si=function(e){return e._end=Y(e._start+(e._tDur/Math.abs(e._ts||e._rts||W)||0))},Ci=function(e,t){var n=e._dp;return n&&n.smoothChildTiming&&e._ts&&(e._start=Y(n._time-(e._ts>0?t/e._ts:((e._dirty?e.totalDuration():e._tDur)-t)/-e._ts)),Si(e),n._dirty||hi(n,e)),e},wi=function(e,t){var n;if((t._time||!t._dur&&t._initted||t._start<e._time&&(t._dur||!t.add))&&(n=xi(e.rawTime(),t),(!t._dur||Ri(0,t.totalDuration(),n)-t._tTime>W)&&t.render(n,!0)),hi(e,t)._dp&&e._initted&&e._time>=e._dur&&e._ts){if(e._dur<e.duration())for(n=e;n._dp;)n.rawTime()>=0&&n.totalTime(n._tTime),n=n._dp;e._zTime=-W}},Ti=function(e,t,n,r){return t.parent&&mi(t),t._start=Y((cr(n)?n:n||e!==q?Fi(e,n,t):e._time)+t._delay),t._end=Y(t._start+(t.totalDuration()/Math.abs(t.timeScale())||0)),fi(e,t,`_first`,`_last`,e._sort?`_start`:0),ki(t)||(e._recent=t),r||wi(e,t),e._ts<0&&Ci(e,e._tTime),e},Ei=function(e,t){return(Or.ScrollTrigger||Mr(`scrollTrigger`,t))&&Or.ScrollTrigger.create(t,e)},Di=function(e,t,n,r,i){if(Ua(e,t,i),!e._initted)return 1;if(!n&&e._pt&&!er&&(e._dur&&e.vars.lazy!==!1||!e._dur&&e.vars.lazy)&&Hr!==xa.frame)return Br.push(e),e._lazy=[i,r],1},Oi=function e(t){var n=t.parent;return n&&n._ts&&n._initted&&!n._lock&&(n.rawTime()<0||e(n))},ki=function(e){var t=e.data;return t===`isFromStart`||t===`isStart`},Ai=function(e,t,n,r){var i=e.ratio,a=t<0||!t&&(!e._start&&Oi(e)&&!(!e._initted&&ki(e))||(e._ts<0||e._dp._ts<0)&&!ki(e))?0:1,o=e._rDelay,s=0,c,l,u;if(o&&e._repeat&&(s=Ri(0,e._tDur,t),l=bi(s,o),e._yoyo&&l&1&&(a=1-a),l!==bi(e._tTime,o)&&(i=1-a,e.vars.repeatRefresh&&e._initted&&e.invalidate())),a!==i||er||r||e._zTime===W||!t&&e._zTime){if(!e._initted&&Di(e,t,r,n,s))return;for(u=e._zTime,e._zTime=t||(n?W:0),n||=t&&!u,e.ratio=a,e._from&&(a=1-a),e._time=0,e._tTime=s,c=e._pt;c;)c.r(a,c.d),c=c._next;t<0&&_i(e,t,n,!0),e._onUpdate&&!n&&sa(e,`onUpdate`),s&&e._repeat&&!n&&e.parent&&sa(e,`onRepeat`),(t>=e._tDur||t<0)&&e.ratio===a&&(a&&mi(e,1),!n&&!er&&(sa(e,a?`onComplete`:`onReverseComplete`,!0),e._prom&&e._prom()))}else e._zTime||=t},ji=function(e,t,n){var r;if(n>t)for(r=e._first;r&&r._start<=n;){if(r.data===`isPause`&&r._start>t)return r;r=r._next}else for(r=e._last;r&&r._start>=n;){if(r.data===`isPause`&&r._start<t)return r;r=r._prev}},Mi=function(e,t,n,r){var i=e._repeat,a=Y(t)||0,o=e._tTime/e._tDur;return o&&!r&&(e._time*=a/e._dur),e._dur=a,e._tDur=i?i<0?1e10:Y(a*(i+1)+e._rDelay*i):a,o>0&&!r&&Ci(e,e._tTime=e._tDur*o),e.parent&&Si(e),n||hi(e.parent,e),e},Ni=function(e){return e instanceof Ia?hi(e):Mi(e,e._dur)},Pi={_start:0,endTime:Fr,totalDuration:Fr},Fi=function e(t,n,r){var i=t.labels,a=t._recent||Pi,o=t.duration()>=tr?a.endTime(!1):t._dur,s,c,l;return G(n)&&(isNaN(n)||n in i)?(c=n.charAt(0),l=n.substr(-1)===`%`,s=n.indexOf(`=`),c===`<`||c===`>`?(s>=0&&(n=n.replace(/=/,``)),(c===`<`?a._start:a.endTime(a._repeat>=0))+(parseFloat(n.substr(1))||0)*(l?(s<0?a:r).totalDuration()/100:1)):s<0?(n in i||(i[n]=o),i[n]):(c=parseFloat(n.charAt(s-1)+n.substr(s+1)),l&&r&&(c=c/100*(hr(r)?r[0]:r).totalDuration()),s>1?e(t,n.substr(0,s-1),r)+c:o+c)):n==null?o:+n},Ii=function(e,t,n){var r=cr(t[1]),i=(r?2:1)+(e<2?0:1),a=t[i],o,s;if(r&&(a.duration=t[1]),a.parent=n,e){for(o=a,s=n;s&&!(`immediateRender`in o);)o=s.vars.defaults||{},s=dr(s.vars.inherit)&&s.parent;a.immediateRender=dr(o.immediateRender),e<2?a.runBackwards=1:a.startAt=t[i-1]}return new Q(t[0],a,t[i+1])},Li=function(e,t){return e||e===0?t(e):t},Ri=function(e,t,n){return n<e?e:n>t?t:n},zi=function(e,t){return!G(e)||!(t=wr.exec(e))?``:t[1]},Bi=function(e,t,n){return Li(n,function(n){return Ri(e,t,n)})},Vi=[].slice,Hi=function(e,t){return e&&ur(e)&&`length`in e&&(!t&&!e.length||e.length-1 in e&&ur(e[0]))&&!e.nodeType&&e!==Tr},Ui=function(e,t,n){return n===void 0&&(n=[]),e.forEach(function(e){var r;return G(e)&&!t||Hi(e,1)?(r=n).push.apply(r,Wi(e)):n.push(e)})||n},Wi=function(e,t,n){return U&&!t&&U.selector?U.selector(e):G(e)&&!n&&(Er||!Sa())?Vi.call((t||Dr).querySelectorAll(e),0):hr(e)?Ui(e,n):Hi(e)?Vi.call(e,0):e?[e]:[]},Gi=function(e){return e=Wi(e)[0]||Nr(`Invalid scope`)||{},function(t){var n=e.current||e.nativeElement||e;return Wi(t,n.querySelectorAll?n:n===e?Nr(`Invalid scope`)||Dr.createElement(`div`):e)}},Ki=function(e){return e.sort(function(){return .5-Math.random()})},qi=function(e){if(K(e))return e;var t=ur(e)?e:{each:e},n=ka(t.ease),r=t.from||0,i=parseFloat(t.base)||0,a={},o=r>0&&r<1,s=isNaN(r)||o,c=t.axis,l=r,u=r;return G(r)?l=u={center:.5,edges:.5,end:1}[r]||0:!o&&s&&(l=r[0],u=r[1]),function(e,o,d){var f=(d||t).length,p=a[f],m,h,g,_,v,y,b,x,S;if(!p){if(S=t.grid===`auto`?0:(t.grid||[1,tr])[1],!S){for(b=-tr;b<(b=d[S++].getBoundingClientRect().left)&&S<f;);S<f&&S--}for(p=a[f]=[],m=s?Math.min(S,f)*l-.5:r%S,h=S===tr?0:s?f*u/S-.5:r/S|0,b=0,x=tr,y=0;y<f;y++)g=y%S-m,_=h-(y/S|0),p[y]=v=c?Math.abs(c===`y`?_:g):ar(g*g+_*_),v>b&&(b=v),v<x&&(x=v);r===`random`&&Ki(p),p.max=b-x,p.min=x,p.v=f=(parseFloat(t.amount)||parseFloat(t.each)*(S>f?f-1:c?c===`y`?f/S:S:Math.max(S,f/S))||0)*(r===`edges`?-1:1),p.b=f<0?i-f:i,p.u=zi(t.amount||t.each)||0,n=n&&f<0?Oa(n):n}return f=(p[e]-p.min)/p.max||0,Y(p.b+(n?n(f):f)*p.v)+p.u}},Ji=function(e){var t=10**((e+``).split(`.`)[1]||``).length;return function(n){var r=Y(Math.round(parseFloat(n)/e)*e*t);return(r-r%1)/t+(cr(n)?0:zi(n))}},Yi=function(e,t){var n=hr(e),r,i;return!n&&ur(e)&&(r=n=e.radius||tr,e.values?(e=Wi(e.values),(i=!cr(e[0]))&&(r*=r)):e=Ji(e.increment)),Li(t,n?K(e)?function(t){return i=e(t),Math.abs(i-t)<=r?i:t}:function(t){for(var n=parseFloat(i?t.x:t),a=parseFloat(i?t.y:0),o=tr,s=0,c=e.length,l,u;c--;)i?(l=e[c].x-n,u=e[c].y-a,l=l*l+u*u):l=Math.abs(e[c]-n),l<o&&(o=l,s=c);return s=!r||o<=r?e[s]:t,i||s===t||cr(t)?s:s+zi(t)}:Ji(e))},Xi=function(e,t,n,r){return Li(hr(e)?!t:n===!0?!!(n=0):!r,function(){return hr(e)?e[~~(Math.random()*e.length)]:(n||=1e-5)&&(r=n<1?10**((n+``).length-2):1)&&Math.floor(Math.round((e-n/2+Math.random()*(t-e+n*.99))/n)*n*r)/r})},Zi=function(){var e=[...arguments];return function(t){return e.reduce(function(e,t){return t(e)},t)}},Qi=function(e,t){return function(n){return e(parseFloat(n))+(t||zi(n))}},$i=function(e,t,n){return ia(e,t,0,1,n)},ea=function(e,t,n){return Li(n,function(n){return e[~~t(n)]})},ta=function e(t,n,r){var i=n-t;return hr(t)?ea(t,e(0,t.length),n):Li(r,function(e){return(i+(e-t)%i)%i+t})},na=function e(t,n,r){var i=n-t,a=i*2;return hr(t)?ea(t,e(0,t.length-1),n):Li(r,function(e){return e=(a+(e-t)%a)%a||0,t+(e>i?a-e:e)})},ra=function(e){return e.replace(gr,function(e){var t=e.indexOf(`[`)+1,n=e.substring(t||7,t?e.indexOf(`]`):e.length-1).split(_r);return Xi(t?n:+n[0],t?0:+n[1],+n[2]||1e-5)})},ia=function(e,t,n,r,i){var a=t-e,o=r-n;return Li(i,function(t){return n+((t-e)/a*o||0)})},aa=function e(t,n,r,i){var a=isNaN(t+n)?0:function(e){return(1-e)*t+e*n};if(!a){var o=G(t),s={},c,l,u,d,f;if(r===!0&&(i=1)&&(r=null),o)t={p:t},n={p:n};else if(hr(t)&&!hr(n)){for(u=[],d=t.length,f=d-2,l=1;l<d;l++)u.push(e(t[l-1],t[l]));d--,a=function(e){e*=d;var t=Math.min(f,~~e);return u[t](e-t)},r=n}else i||(t=si(hr(t)?[]:{},t));if(!u){for(c in n)Ra.call(s,t,c,`get`,n[c]);a=function(e){return io(e,s)||(o?t.p:t)}}}return Li(r,a)},oa=function(e,t,n){var r=e.labels,i=tr,a,o,s;for(a in r)o=r[a]-t,o<0==!!n&&o&&i>(o=Math.abs(o))&&(s=a,i=o);return s},sa=function(e,t,n){var r=e.vars,i=r[t],a=U,o=e._ctx,s,c,l;if(i)return s=r[t+`Params`],c=r.callbackScope||e,n&&Br.length&&ei(),o&&(U=o),l=s?i.apply(c,s):i.call(c),U=a,l},ca=function(e){return mi(e),e.scrollTrigger&&e.scrollTrigger.kill(!!er),e.progress()<1&&sa(e,`onInterrupt`),e},la,ua=[],da=function(e){if(e)if(e=!e.name&&e.default||e,fr()||e.headless){var t=e.name,n=K(e),r=t&&!n&&e.init?function(){this._props=[]}:e,i={init:Fr,render:io,add:Ra,kill:oo,modifier:ao,rawVars:0},a={targetTest:0,get:0,getSetter:eo,aliases:{},register:0};if(Sa(),e!==r){if(Ur[t])return;ai(r,ai(li(e,i),a)),si(r.prototype,si(i,li(e,a))),Ur[r.prop=t]=r,e.targetTest&&(Kr.push(r),zr[t]=1),t=(t===`css`?`CSS`:t.charAt(0).toUpperCase()+t.substr(1))+`Plugin`}Pr(t,r),e.register&&e.register(wo,r,lo)}else ua.push(e)},X=255,fa={aqua:[0,X,X],lime:[0,X,0],silver:[192,192,192],black:[0,0,0],maroon:[128,0,0],teal:[0,128,128],blue:[0,0,X],navy:[0,0,128],white:[X,X,X],olive:[128,128,0],yellow:[X,X,0],orange:[X,165,0],gray:[128,128,128],purple:[128,0,128],green:[0,128,0],red:[X,0,0],pink:[X,192,203],cyan:[0,X,X],transparent:[X,X,X,0]},pa=function(e,t,n){return e+=e<0?1:e>1?-1:0,(e*6<1?t+(n-t)*e*6:e<.5?n:e*3<2?t+(n-t)*(2/3-e)*6:t)*X+.5|0},ma=function(e,t,n){var r=e?cr(e)?[e>>16,e>>8&X,e&X]:0:fa.black,i,a,o,s,c,l,u,d,f,p;if(!r){if(e.substr(-1)===`,`&&(e=e.substr(0,e.length-1)),fa[e])r=fa[e];else if(e.charAt(0)===`#`){if(e.length<6&&(i=e.charAt(1),a=e.charAt(2),o=e.charAt(3),e=`#`+i+i+a+a+o+o+(e.length===5?e.charAt(4)+e.charAt(4):``)),e.length===9)return r=parseInt(e.substr(1,6),16),[r>>16,r>>8&X,r&X,parseInt(e.substr(7),16)/255];e=parseInt(e.substr(1),16),r=[e>>16,e>>8&X,e&X]}else if(e.substr(0,3)===`hsl`){if(r=p=e.match(vr),!t)s=r[0]%360/360,c=r[1]/100,l=r[2]/100,a=l<=.5?l*(c+1):l+c-l*c,i=l*2-a,r.length>3&&(r[3]*=1),r[0]=pa(s+1/3,i,a),r[1]=pa(s,i,a),r[2]=pa(s-1/3,i,a);else if(~e.indexOf(`=`))return r=e.match(yr),n&&r.length<4&&(r[3]=1),r}else r=e.match(vr)||fa.transparent;r=r.map(Number)}return t&&!p&&(i=r[0]/X,a=r[1]/X,o=r[2]/X,u=Math.max(i,a,o),d=Math.min(i,a,o),l=(u+d)/2,u===d?s=c=0:(f=u-d,c=l>.5?f/(2-u-d):f/(u+d),s=u===i?(a-o)/f+(a<o?6:0):u===a?(o-i)/f+2:(i-a)/f+4,s*=60),r[0]=~~(s+.5),r[1]=~~(c*100+.5),r[2]=~~(l*100+.5)),n&&r.length<4&&(r[3]=1),r},ha=function(e){var t=[],n=[],r=-1;return e.split(_a).forEach(function(e){var i=e.match(br)||[];t.push.apply(t,i),n.push(r+=i.length+1)}),t.c=n,t},ga=function(e,t,n){var r=``,i=(e+r).match(_a),a=t?`hsla(`:`rgba(`,o=0,s,c,l,u;if(!i)return e;if(i=i.map(function(e){return(e=ma(e,t,1))&&a+(t?e[0]+`,`+e[1]+`%,`+e[2]+`%,`+e[3]:e.join(`,`))+`)`}),n&&(l=ha(e),s=n.c,s.join(r)!==l.c.join(r)))for(c=e.replace(_a,`1`).split(br),u=c.length-1;o<u;o++)r+=c[o]+(~s.indexOf(o)?i.shift()||a+`0,0,0,0)`:(l.length?l:i.length?i:n).shift());if(!c)for(c=e.split(_a),u=c.length-1;o<u;o++)r+=c[o]+i[o];return r+c[u]},_a=function(){var e=`(?:\\b(?:(?:rgb|rgba|hsl|hsla)\\(.+?\\))|\\B#(?:[0-9a-f]{3,4}){1,2}\\b`,t;for(t in fa)e+=`|`+t+`\\b`;return RegExp(e+`)`,`gi`)}(),va=/hsl[a]?\(/,ya=function(e){var t=e.join(` `),n;if(_a.lastIndex=0,_a.test(t))return n=va.test(t),e[1]=ga(e[1],n),e[0]=ga(e[0],n,ha(e[1])),!0},ba,xa=function(){var e=Date.now,t=500,n=33,r=e(),i=r,a=1e3/240,o=a,s=[],c,l,u,d,f,p,m=function u(m){var h=e()-i,g=m===!0,_,v,y,b;if((h>t||h<0)&&(r+=h-n),i+=h,y=i-r,_=y-o,(_>0||g)&&(b=++d.frame,f=y-d.time*1e3,d.time=y/=1e3,o+=_+(_>=a?4:a-_),v=1),g||(c=l(u)),v)for(p=0;p<s.length;p++)s[p](y,f,b,m)};return d={time:0,frame:0,tick:function(){m(!0)},deltaRatio:function(e){return f/(1e3/(e||60))},wake:function(){Ar&&(!Er&&fr()&&(Tr=Er=window,Dr=Tr.document||{},Or.gsap=wo,(Tr.gsapVersions||=[]).push(wo.version),jr(kr||Tr.GreenSockGlobals||!Tr.gsap&&Tr||{}),ua.forEach(da)),u=typeof requestAnimationFrame<`u`&&requestAnimationFrame,c&&d.sleep(),l=u||function(e){return setTimeout(e,o-d.time*1e3+1|0)},ba=1,m(2))},sleep:function(){(u?cancelAnimationFrame:clearTimeout)(c),ba=0,l=Fr},lagSmoothing:function(e,r){t=e||1/0,n=Math.min(r||33,t)},fps:function(e){a=1e3/(e||240),o=d.time*1e3+a},add:function(e,t,n){var r=t?function(t,n,i,a){e(t,n,i,a),d.remove(r)}:e;return d.remove(e),s[n?`unshift`:`push`](r),Sa(),r},remove:function(e,t){~(t=s.indexOf(e))&&s.splice(t,1)&&p>=t&&p--},_listeners:s},d}(),Sa=function(){return!ba&&xa.wake()},Z={},Ca=/^[\d.\-M][\d.\-,\s]/,wa=/["']/g,Ta=function(e){for(var t={},n=e.substr(1,e.length-3).split(`:`),r=n[0],i=1,a=n.length,o,s,c;i<a;i++)s=n[i],o=i===a-1?s.length:s.lastIndexOf(`,`),c=s.substr(0,o),t[r]=isNaN(c)?c.replace(wa,``).trim():+c,r=s.substr(o+1).trim();return t},Ea=function(e){var t=e.indexOf(`(`)+1,n=e.indexOf(`)`),r=e.indexOf(`(`,t);return e.substring(t,~r&&r<n?e.indexOf(`)`,n+1):n)},Da=function(e){var t=(e+``).split(`(`),n=Z[t[0]];return n&&t.length>1&&n.config?n.config.apply(null,~e.indexOf(`{`)?[Ta(t[1])]:Ea(e).split(`,`).map(ri)):Z._CE&&Ca.test(e)?Z._CE(``,e):n},Oa=function(e){return function(t){return 1-e(1-t)}},ka=function(e,t){return e&&(K(e)?e:Z[e]||Da(e))||t},Aa=function(e,t,n,r){n===void 0&&(n=function(e){return 1-t(1-e)}),r===void 0&&(r=function(e){return e<.5?t(e*2)/2:1-t((1-e)*2)/2});var i={easeIn:t,easeOut:n,easeInOut:r},a;return Zr(e,function(e){for(var t in Z[e]=Or[e]=i,Z[a=e.toLowerCase()]=n,i)Z[a+(t===`easeIn`?`.in`:t===`easeOut`?`.out`:`.inOut`)]=Z[e+`.`+t]=i[t]}),i},ja=function(e){return function(t){return t<.5?(1-e(1-t*2))/2:.5+e((t-.5)*2)/2}},Ma=function e(t,n,r){var i=n>=1?n:1,a=(r||(t?.3:.45))/(n<1?n:1),o=a/nr*(Math.asin(1/i)||0),s=function(e){return e===1?1:i*2**(-10*e)*sr((e-o)*a)+1},c=t===`out`?s:t===`in`?function(e){return 1-s(1-e)}:ja(s);return a=nr/a,c.config=function(n,r){return e(t,n,r)},c},Na=function e(t,n){n===void 0&&(n=1.70158);var r=function(e){return e?--e*e*((n+1)*e+n)+1:0},i=t===`out`?r:t===`in`?function(e){return 1-r(1-e)}:ja(r);return i.config=function(n){return e(t,n)},i};Zr(`Linear,Quad,Cubic,Quart,Quint,Strong`,function(e,t){var n=t<5?t+1:t;Aa(e+`,Power`+(n-1),t?function(e){return e**+n}:function(e){return e},function(e){return 1-(1-e)**n},function(e){return e<.5?(e*2)**n/2:1-((1-e)*2)**n/2})}),Z.Linear.easeNone=Z.none=Z.Linear.easeIn,Aa(`Elastic`,Ma(`in`),Ma(`out`),Ma()),(function(e,t){var n=1/t,r=2*n,i=2.5*n,a=function(a){return a<n?e*a*a:a<r?e*(a-1.5/t)**2+.75:a<i?e*(a-=2.25/t)*a+.9375:e*(a-2.625/t)**2+.984375};Aa(`Bounce`,function(e){return 1-a(1-e)},a)})(7.5625,2.75),Aa(`Expo`,function(e){return 2**(10*(e-1))*e+e*e*e*e*e*e*(1-e)}),Aa(`Circ`,function(e){return-(ar(1-e*e)-1)}),Aa(`Sine`,function(e){return e===1?1:-or(e*rr)+1}),Aa(`Back`,Na(`in`),Na(`out`),Na()),Z.SteppedEase=Z.steps=Or.SteppedEase={config:function(e,t){e===void 0&&(e=1);var n=1/e,r=e+ +!t,i=+!!t,a=1-W;return function(e){return((r*Ri(0,a,e)|0)+i)*n}}},Qn.ease=Z[`quad.out`],Zr(`onComplete,onUpdate,onStart,onRepeat,onReverseComplete,onInterrupt`,function(e){return qr+=e+`,`+e+`Params,`});var Pa=function(e,t){this.id=ir++,e._gsap=this,this.target=e,this.harness=t,this.get=t?t.get:Xr,this.set=t?t.getSetter:eo},Fa=function(){function e(e){this.vars=e,this._delay=+e.delay||0,(this._repeat=e.repeat===1/0?-2:e.repeat||0)&&(this._rDelay=e.repeatDelay||0,this._yoyo=!!e.yoyo||!!e.yoyoEase),this._ts=1,Mi(this,+e.duration,1,1),this.data=e.data,U&&(this._ctx=U,U.data.push(this)),ba||xa.wake()}var t=e.prototype;return t.delay=function(e){return e||e===0?(this.parent&&this.parent.smoothChildTiming&&this.startTime(this._start+e-this._delay),this._delay=e,this):this._delay},t.duration=function(e){return arguments.length?this.totalDuration(this._repeat>0?e+(e+this._rDelay)*this._repeat:e):this.totalDuration()&&this._dur},t.totalDuration=function(e){return arguments.length?(this._dirty=0,Mi(this,this._repeat<0?e:(e-this._repeat*this._rDelay)/(this._repeat+1))):this._tDur},t.totalTime=function(e,t){if(Sa(),!arguments.length)return this._tTime;var n=this._dp;if(n&&n.smoothChildTiming&&this._ts){for(Ci(this,e),!n._dp||n.parent||wi(n,this);n&&n.parent;)n.parent._time!==n._start+(n._ts>=0?n._tTime/n._ts:(n.totalDuration()-n._tTime)/-n._ts)&&n.totalTime(n._tTime,!0),n=n.parent;!this.parent&&this._dp.autoRemoveChildren&&(this._ts>0&&e<this._tDur||this._ts<0&&e>0||!this._tDur&&!e)&&Ti(this._dp,this,this._start-this._delay)}return(this._tTime!==e||!this._dur&&!t||this._initted&&Math.abs(this._zTime)===W||!this._initted&&this._dur&&e||!e&&!this._initted&&(this.add||this._ptLookup))&&(this._ts||(this._pTime=e),ni(this,e,t)),this},t.time=function(e,t){return arguments.length?this.totalTime(Math.min(this.totalDuration(),e+yi(this))%(this._dur+this._rDelay)||(e?this._dur:0),t):this._time},t.totalProgress=function(e,t){return arguments.length?this.totalTime(this.totalDuration()*e,t):this.totalDuration()?Math.min(1,this._tTime/this._tDur):this.rawTime()>=0&&this._initted?1:0},t.progress=function(e,t){return arguments.length?this.totalTime(this.duration()*(this._yoyo&&!(this.iteration()&1)?1-e:e)+yi(this),t):this.duration()?Math.min(1,this._time/this._dur):+(this.rawTime()>0)},t.iteration=function(e,t){var n=this.duration()+this._rDelay;return arguments.length?this.totalTime(this._time+(e-1)*n,t):this._repeat?bi(this._tTime,n)+1:1},t.timeScale=function(e,t){if(!arguments.length)return this._rts===-W?0:this._rts;if(this._rts===e)return this;var n=this.parent&&this._ts?xi(this.parent._time,this):this._tTime;return this._rts=+e||0,this._ts=this._ps||e===-W?0:this._rts,this.totalTime(Ri(-Math.abs(this._delay),this.totalDuration(),n),t!==!1),Si(this),gi(this)},t.paused=function(e){return arguments.length?(this._ps!==e&&(this._ps=e,e?(this._pTime=this._tTime||Math.max(-this._delay,this.rawTime()),this._ts=this._act=0):(Sa(),this._ts=this._rts,this.totalTime(this.parent&&!this.parent.smoothChildTiming?this.rawTime():this._tTime||this._pTime,this.progress()===1&&Math.abs(this._zTime)!==W&&(this._tTime-=W)))),this):this._ps},t.startTime=function(e){if(arguments.length){this._start=Y(e);var t=this.parent||this._dp;return t&&(t._sort||!this.parent)&&Ti(t,this,this._start-this._delay),this}return this._start},t.endTime=function(e){return this._start+(dr(e)?this.totalDuration():this.duration())/Math.abs(this._ts||1)},t.rawTime=function(e){var t=this.parent||this._dp;return t?e&&(!this._ts||this._repeat&&this._time&&this.totalProgress()<1)?this._tTime%(this._dur+this._rDelay):this._ts?xi(t.rawTime(e),this):this._tTime:this._tTime},t.revert=function(e){e===void 0&&(e=Rr);var t=er;return er=e,ti(this)&&(this.timeline&&this.timeline.revert(e),this.totalTime(-.01,e.suppressEvents)),this.data!==`nested`&&e.kill!==!1&&this.kill(),er=t,this},t.globalTime=function(e){for(var t=this,n=arguments.length?e:t.rawTime();t;)n=t._start+n/(Math.abs(t._ts)||1),t=t._dp;return!this.parent&&this._sat?this._sat.globalTime(e):n},t.repeat=function(e){return arguments.length?(this._repeat=e===1/0?-2:e,Ni(this)):this._repeat===-2?1/0:this._repeat},t.repeatDelay=function(e){if(arguments.length){var t=this._time;return this._rDelay=e,Ni(this),t?this.time(t):this}return this._rDelay},t.yoyo=function(e){return arguments.length?(this._yoyo=e,this):this._yoyo},t.seek=function(e,t){return this.totalTime(Fi(this,e),dr(t))},t.restart=function(e,t){return this.play().totalTime(e?-this._delay:0,dr(t)),this._dur||(this._zTime=-W),this},t.play=function(e,t){return e!=null&&this.seek(e,t),this.reversed(!1).paused(!1)},t.reverse=function(e,t){return e!=null&&this.seek(e||this.totalDuration(),t),this.reversed(!0).paused(!1)},t.pause=function(e,t){return e!=null&&this.seek(e,t),this.paused(!0)},t.resume=function(){return this.paused(!1)},t.reversed=function(e){return arguments.length?(!!e!==this.reversed()&&this.timeScale(-this._rts||(e?-W:0)),this):this._rts<0},t.invalidate=function(){return this._initted=this._act=0,this._zTime=-W,this},t.isActive=function(){var e=this.parent||this._dp,t=this._start,n;return!!(!e||this._ts&&this._initted&&e.isActive()&&(n=e.rawTime(!0))>=t&&n<this.endTime(!0)-W)},t.eventCallback=function(e,t,n){var r=this.vars;return arguments.length>1?(t?(r[e]=t,n&&(r[e+`Params`]=n),e===`onUpdate`&&(this._onUpdate=t)):delete r[e],this):r[e]},t.then=function(e){var t=this,n=t._prom;return new Promise(function(r){var i=K(e)?e:ii,a=function(){var e=t.then;t.then=null,n&&n(),K(i)&&(i=i(t))&&(i.then||i===t)&&(t.then=e),r(i),t.then=e};t._initted&&t.totalProgress()===1&&t._ts>=0||!t._tTime&&t._ts<0?a():t._prom=a})},t.kill=function(){ca(this)},e}();ai(Fa.prototype,{_time:0,_start:0,_end:0,_tTime:0,_tDur:0,_dirty:0,_repeat:0,_yoyo:!1,parent:null,_initted:!1,_rDelay:0,_ts:1,_dp:0,ratio:0,_zTime:-W,_prom:0,_ps:!1,_rts:1});var Ia=function(e){Xn(t,e);function t(t,n){var r;return t===void 0&&(t={}),r=e.call(this,t)||this,r.labels={},r.smoothChildTiming=!!t.smoothChildTiming,r.autoRemoveChildren=!!t.autoRemoveChildren,r._sort=dr(t.sortChildren),q&&Ti(t.parent||q,Yn(r),n),t.reversed&&r.reverse(),t.paused&&r.paused(!0),t.scrollTrigger&&Ei(Yn(r),t.scrollTrigger),r}var n=t.prototype;return n.to=function(e,t,n){return Ii(0,arguments,this),this},n.from=function(e,t,n){return Ii(1,arguments,this),this},n.fromTo=function(e,t,n,r){return Ii(2,arguments,this),this},n.set=function(e,t,n){return t.duration=0,t.parent=this,ui(t).repeatDelay||(t.repeat=0),t.immediateRender=!!t.immediateRender,new Q(e,t,Fi(this,n),1),this},n.call=function(e,t,n){return Ti(this,Q.delayedCall(0,e,t),n)},n.staggerTo=function(e,t,n,r,i,a,o){return n.duration=t,n.stagger=n.stagger||r,n.onComplete=a,n.onCompleteParams=o,n.parent=this,new Q(e,n,Fi(this,i)),this},n.staggerFrom=function(e,t,n,r,i,a,o){return n.runBackwards=1,ui(n).immediateRender=dr(n.immediateRender),this.staggerTo(e,t,n,r,i,a,o)},n.staggerFromTo=function(e,t,n,r,i,a,o,s){return r.startAt=n,ui(r).immediateRender=dr(r.immediateRender),this.staggerTo(e,t,r,i,a,o,s)},n.render=function(e,t,n){var r=this._time,i=this._dirty?this.totalDuration():this._tDur,a=this._dur,o=e<=0?0:Y(e),s=this._zTime<0!=e<0&&(this._initted||!a),c,l,u,d,f,p,m,h,g,_,v,y;if(this!==q&&o>i&&e>=0&&(o=i),o!==this._tTime||n||s){if(r!==this._time&&a&&(o+=this._time-r,e+=this._time-r),c=o,g=this._start,h=this._ts,p=!h,s&&(a||(r=this._zTime),(e||!t)&&(this._zTime=e)),this._repeat){if(v=this._yoyo,f=a+this._rDelay,this._repeat<-1&&e<0)return this.totalTime(f*100+e,t,n);if(c=Y(o%f),o===i?(d=this._repeat,c=a):(_=Y(o/f),d=~~_,d&&d===_&&(c=a,d--),c>a&&(c=a)),_=bi(this._tTime,f),!r&&this._tTime&&_!==d&&this._tTime-_*f-this._dur<=0&&(_=d),v&&d&1&&(c=a-c,y=1),d!==_&&!this._lock){var b=v&&_&1,x=b===(v&&d&1);if(d<_&&(b=!b),r=b?0:o%a?a:o,this._lock=1,this.render(r||(y?0:Y(d*f)),t,!a)._lock=0,this._tTime=o,!t&&this.parent&&sa(this,`onRepeat`),this.vars.repeatRefresh&&!y&&(this.invalidate()._lock=1,_=d),r&&r!==this._time||p!==!this._ts||this.vars.onRepeat&&!this.parent&&!this._act||(a=this._dur,i=this._tDur,x&&(this._lock=2,r=b?a:-1e-4,this.render(r,!0),this.vars.repeatRefresh&&!y&&this.invalidate()),this._lock=0,!this._ts&&!p))return this}}if(this._hasPause&&!this._forcing&&this._lock<2&&(m=ji(this,Y(r),Y(c)),m&&(o-=c-(c=m._start))),this._tTime=o,this._time=c,this._act=!!h,this._initted||(this._onUpdate=this.vars.onUpdate,this._initted=1,this._zTime=e,r=0),!r&&o&&a&&!t&&!_&&(sa(this,`onStart`),this._tTime!==o))return this;if(c>=r&&e>=0)for(l=this._first;l;){if(u=l._next,(l._act||c>=l._start)&&l._ts&&m!==l){if(l.parent!==this)return this.render(e,t,n);if(l.render(l._ts>0?(c-l._start)*l._ts:(l._dirty?l.totalDuration():l._tDur)+(c-l._start)*l._ts,t,n),c!==this._time||!this._ts&&!p){m=0,u&&(o+=this._zTime=-W);break}}l=u}else{l=this._last;for(var S=e<0?e:c;l;){if(u=l._prev,(l._act||S<=l._end)&&l._ts&&m!==l){if(l.parent!==this)return this.render(e,t,n);if(l.render(l._ts>0?(S-l._start)*l._ts:(l._dirty?l.totalDuration():l._tDur)+(S-l._start)*l._ts,t,n||er&&ti(l)),c!==this._time||!this._ts&&!p){m=0,u&&(o+=this._zTime=S?-W:W);break}}l=u}}if(m&&!t&&(this.pause(),m.render(c>=r?0:-W)._zTime=c>=r?1:-1,this._ts))return this._start=g,Si(this),this.render(e,t,n);this._onUpdate&&!t&&sa(this,`onUpdate`,!0),(o===i&&this._tTime>=this.totalDuration()||!o&&r)&&(g===this._start||Math.abs(h)!==Math.abs(this._ts))&&(this._lock||((e||!a)&&(o===i&&this._ts>0||!o&&this._ts<0)&&mi(this,1),!t&&!(e<0&&!r)&&(o||r||!i)&&(sa(this,o===i&&e>=0?`onComplete`:`onReverseComplete`,!0),this._prom&&!(o<i&&this.timeScale()>0)&&this._prom())))}return this},n.add=function(e,t){var n=this;if(cr(t)||(t=Fi(this,t,e)),!(e instanceof Fa)){if(hr(e))return e.forEach(function(e){return n.add(e,t)}),this;if(G(e))return this.addLabel(e,t);if(K(e))e=Q.delayedCall(0,e);else return this}return this===e?this:Ti(this,e,t)},n.getChildren=function(e,t,n,r){e===void 0&&(e=!0),t===void 0&&(t=!0),n===void 0&&(n=!0),r===void 0&&(r=-tr);for(var i=[],a=this._first;a;)a._start>=r&&(a instanceof Q?t&&i.push(a):(n&&i.push(a),e&&i.push.apply(i,a.getChildren(!0,t,n)))),a=a._next;return i},n.getById=function(e){for(var t=this.getChildren(1,1,1),n=t.length;n--;)if(t[n].vars.id===e)return t[n]},n.remove=function(e){return G(e)?this.removeLabel(e):K(e)?this.killTweensOf(e):(e.parent===this&&pi(this,e),e===this._recent&&(this._recent=this._last),hi(this))},n.totalTime=function(t,n){return arguments.length?(this._forcing=1,!this._dp&&this._ts&&(this._start=Y(xa.time-(this._ts>0?t/this._ts:(this.totalDuration()-t)/-this._ts))),e.prototype.totalTime.call(this,t,n),this._forcing=0,this):this._tTime},n.addLabel=function(e,t){return this.labels[e]=Fi(this,t),this},n.removeLabel=function(e){return delete this.labels[e],this},n.addPause=function(e,t,n){var r=Q.delayedCall(0,t||Fr,n);return r.data=`isPause`,this._hasPause=1,Ti(this,r,Fi(this,e))},n.removePause=function(e){var t=this._first;for(e=Fi(this,e);t;)t._start===e&&t.data===`isPause`&&mi(t),t=t._next},n.killTweensOf=function(e,t,n){for(var r=this.getTweensOf(e,n),i=r.length;i--;)Va!==r[i]&&r[i].kill(e,t);return this},n.getTweensOf=function(e,t){for(var n=[],r=Wi(e),i=this._first,a=cr(t),o;i;)i instanceof Q?$r(i._targets,r)&&(a?(!Va||i._initted&&i._ts)&&i.globalTime(0)<=t&&i.globalTime(i.totalDuration())>t:!t||i.isActive())&&n.push(i):(o=i.getTweensOf(r,t)).length&&n.push.apply(n,o),i=i._next;return n},n.tweenTo=function(e,t){t||={};var n=this,r=Fi(n,e),i=t,a=i.startAt,o=i.onStart,s=i.onStartParams,c=i.immediateRender,l,u=Q.to(n,ai({ease:t.ease||`none`,lazy:!1,immediateRender:!1,time:r,overwrite:`auto`,duration:t.duration||Math.abs((r-(a&&`time`in a?a.time:n._time))/n.timeScale())||W,onStart:function(){if(n.pause(),!l){var e=t.duration||Math.abs((r-(a&&`time`in a?a.time:n._time))/n.timeScale());u._dur!==e&&Mi(u,e,0,1).render(u._time,!0,!0),l=1}o&&o.apply(u,s||[])}},t));return c?u.render(0):u},n.tweenFromTo=function(e,t,n){return this.tweenTo(t,ai({startAt:{time:Fi(this,e)}},n))},n.recent=function(){return this._recent},n.nextLabel=function(e){return e===void 0&&(e=this._time),oa(this,Fi(this,e))},n.previousLabel=function(e){return e===void 0&&(e=this._time),oa(this,Fi(this,e),1)},n.currentLabel=function(e){return arguments.length?this.seek(e,!0):this.previousLabel(this._time+W)},n.shiftChildren=function(e,t,n){n===void 0&&(n=0);var r=this._first,i=this.labels,a;for(e=Y(e);r;)r._start>=n&&(r._start+=e,r._end+=e),r=r._next;if(t)for(a in i)i[a]>=n&&(i[a]+=e);return hi(this)},n.invalidate=function(t){var n=this._first;for(this._lock=0;n;)n.invalidate(t),n=n._next;return e.prototype.invalidate.call(this,t)},n.clear=function(e){e===void 0&&(e=!0);for(var t=this._first,n;t;)n=t._next,this.remove(t),t=n;return this._dp&&(this._time=this._tTime=this._pTime=0),e&&(this.labels={}),hi(this)},n.totalDuration=function(e){var t=0,n=this,r=n._last,i=tr,a,o,s;if(arguments.length)return n.timeScale((n._repeat<0?n.duration():n.totalDuration())/(n.reversed()?-e:e));if(n._dirty){for(s=n.parent;r;)a=r._prev,r._dirty&&r.totalDuration(),o=r._start,o>i&&n._sort&&r._ts&&!n._lock?(n._lock=1,Ti(n,r,o-r._delay,1)._lock=0):i=o,o<0&&r._ts&&(t-=o,(!s&&!n._dp||s&&s.smoothChildTiming)&&(n._start+=Y(o/n._ts),n._time-=o,n._tTime-=o),n.shiftChildren(-o,!1,-1/0),i=0),r._end>t&&r._ts&&(t=r._end),r=a;Mi(n,n===q&&n._time>t?n._time:t,1,1),n._dirty=0}return n._tDur},t.updateRoot=function(e){if(q._ts&&(ni(q,xi(e,q)),Hr=xa.frame),xa.frame>=Gr){Gr+=Zn.autoSleep||120;var t=q._first;if((!t||!t._ts)&&Zn.autoSleep&&xa._listeners.length<2){for(;t&&!t._ts;)t=t._next;t||xa.sleep()}}},t}(Fa);ai(Ia.prototype,{_lock:0,_hasPause:0,_forcing:0});var La=function(e,t,n,r,i,a,o){var s=new lo(this._pt,e,t,0,1,ro,null,i),c=0,l=0,u,d,f,p,m,h,g,_;for(s.b=n,s.e=r,n+=``,r+=``,(g=~r.indexOf(`random(`))&&(r=ra(r)),a&&(_=[n,r],a(_,e,t),n=_[0],r=_[1]),d=n.match(xr)||[];u=xr.exec(r);)p=u[0],m=r.substring(c,u.index),f?f=(f+1)%5:m.substr(-5)===`rgba(`&&(f=1),p!==d[l++]&&(h=parseFloat(d[l-1])||0,s._pt={_next:s._pt,p:m||l===1?m:`,`,s:h,c:p.charAt(1)===`=`?Qr(h,p)-h:parseFloat(p)-h,m:f&&f<4?Math.round:0},c=xr.lastIndex);return s.c=c<r.length?r.substring(c,r.length):``,s.fp=o,(Sr.test(r)||g)&&(s.e=0),this._pt=s,s},Ra=function(e,t,n,r,i,a,o,s,c,l){K(r)&&(r=r(i||0,e,a));var u=e[t],d=n===`get`?K(u)?c?e[t.indexOf(`set`)||!K(e[`get`+t.substr(3)])?t:`get`+t.substr(3)](c):e[t]():u:n,f=K(u)?c?Qa:Za:Xa,p;if(G(r)&&(~r.indexOf(`random(`)&&(r=ra(r)),r.charAt(1)===`=`&&(p=Qr(d,r)+(zi(d)||0),(p||p===0)&&(r=p))),!l||d!==r||Ha)return!isNaN(d*r)&&r!==``?(p=new lo(this._pt,e,t,+d||0,r-(d||0),typeof u==`boolean`?no:to,0,f),c&&(p.fp=c),o&&p.modifier(o,this,e),this._pt=p):(!u&&!(t in e)&&Mr(t,r),La.call(this,e,t,d,r,f,s||Zn.stringFilter,c))},za=function(e,t,n,r,i){if(K(e)&&(e=qa(e,i,t,n,r)),!ur(e)||e.style&&e.nodeType||hr(e)||mr(e))return G(e)?qa(e,i,t,n,r):e;var a={},o;for(o in e)a[o]=qa(e[o],i,t,n,r);return a},Ba=function(e,t,n,r,i,a){var o,s,c,l;if(Ur[e]&&(o=new Ur[e]).init(i,o.rawVars?t[e]:za(t[e],r,i,a,n),n,r,a)!==!1&&(n._pt=s=new lo(n._pt,i,e,0,1,o.render,o,0,o.priority),n!==la))for(c=n._ptLookup[n._targets.indexOf(i)],l=o._props.length;l--;)c[o._props[l]]=s;return o},Va,Ha,Ua=function e(t,n,r){var i=t.vars,a=i.ease,o=i.startAt,s=i.immediateRender,c=i.lazy,l=i.onUpdate,u=i.runBackwards,d=i.yoyoEase,f=i.keyframes,p=i.autoRevert,m=t._dur,h=t._startAt,g=t._targets,_=t.parent,v=_&&_.data===`nested`?_.vars.targets:g,y=t._overwrite===`auto`&&!$n,b=t.timeline,x=i.easeReverse||d,S,C,w,T,E,D,O,k,A,j,M,N,P;if(b&&(!f||!a)&&(a=`none`),t._ease=ka(a,Qn.ease),t._rEase=x&&(ka(x)||t._ease),t._from=!b&&!!i.runBackwards,t._from&&(t.ratio=1),!b||f&&!i.stagger){if(k=g[0]?Yr(g[0]).harness:0,N=k&&i[k.prop],S=li(i,zr),h&&(h._zTime<0&&h.progress(1),n<0&&u&&s&&!p?h.render(-1,!0):h.revert(u&&m?Lr:Ir),h._lazy=0),o){if(mi(t._startAt=Q.set(g,ai({data:`isStart`,overwrite:!1,parent:_,immediateRender:!0,lazy:!h&&dr(c),startAt:null,delay:0,onUpdate:l&&function(){return sa(t,`onUpdate`)},stagger:0},o))),t._startAt._dp=0,t._startAt._sat=t,n<0&&(er||!s&&!p)&&t._startAt.revert(Lr),s&&m&&n<=0&&r<=0){n&&(t._zTime=n);return}}else if(u&&m&&!h){if(n&&(s=!1),w=ai({overwrite:!1,data:`isFromStart`,lazy:s&&!h&&dr(c),immediateRender:s,stagger:0,parent:_},S),N&&(w[k.prop]=N),mi(t._startAt=Q.set(g,w)),t._startAt._dp=0,t._startAt._sat=t,n<0&&(er?t._startAt.revert(Lr):t._startAt.render(-1,!0)),t._zTime=n,!s)e(t._startAt,W,W);else if(!n)return}for(t._pt=t._ptCache=0,c=m&&dr(c)||c&&!m,C=0;C<g.length;C++){if(E=g[C],O=E._gsap||Jr(g)[C]._gsap,t._ptLookup[C]=j={},Vr[O.id]&&Br.length&&ei(),M=v===g?C:v.indexOf(E),k&&(A=new k).init(E,N||S,t,M,v)!==!1&&(t._pt=T=new lo(t._pt,E,A.name,0,1,A.render,A,0,A.priority),A._props.forEach(function(e){j[e]=T}),A.priority&&(D=1)),!k||N)for(w in S)Ur[w]&&(A=Ba(w,S,t,M,E,v))?A.priority&&(D=1):j[w]=T=Ra.call(t,E,w,`get`,S[w],M,v,0,i.stringFilter);t._op&&t._op[C]&&t.kill(E,t._op[C]),y&&t._pt&&(Va=t,q.killTweensOf(E,j,t.globalTime(n)),P=!t.parent,Va=0),t._pt&&c&&(Vr[O.id]=1)}D&&co(t),t._onInit&&t._onInit(t)}t._onUpdate=l,t._initted=(!t._op||t._pt)&&!P,f&&n<=0&&b.render(tr,!0,!0)},Wa=function(e,t,n,r,i,a,o,s){var c=(e._pt&&e._ptCache||(e._ptCache={}))[t],l,u,d,f;if(!c)for(c=e._ptCache[t]=[],d=e._ptLookup,f=e._targets.length;f--;){if(l=d[f][t],l&&l.d&&l.d._pt)for(l=l.d._pt;l&&l.p!==t&&l.fp!==t;)l=l._next;if(!l)return Ha=1,e.vars[t]=`+=0`,Ua(e,o),Ha=0,s?Nr(t+` not eligible for reset. Try splitting into individual properties`):1;c.push(l)}for(f=c.length;f--;)u=c[f],l=u._pt||u,l.s=(r||r===0)&&!i?r:l.s+(r||0)+a*l.c,l.c=n-l.s,u.e&&=J(n)+zi(u.e),u.b&&=l.s+zi(u.b)},Ga=function(e,t){var n=e[0]?Yr(e[0]).harness:0,r=n&&n.aliases,i,a,o,s;if(!r)return t;for(a in i=si({},t),r)if(a in i)for(s=r[a].split(`,`),o=s.length;o--;)i[s[o]]=i[a];return i},Ka=function(e,t,n,r){var i=t.ease||r||`power1.inOut`,a,o;if(hr(t))o=n[e]||(n[e]=[]),t.forEach(function(e,n){return o.push({t:n/(t.length-1)*100,v:e,e:i})});else for(a in t)o=n[a]||(n[a]=[]),a===`ease`||o.push({t:parseFloat(e),v:t[a],e:i})},qa=function(e,t,n,r,i){return K(e)?e.call(t,n,r,i):G(e)&&~e.indexOf(`random(`)?ra(e):e},Ja=qr+`repeat,repeatDelay,yoyo,repeatRefresh,yoyoEase,easeReverse,autoRevert`,Ya={};Zr(Ja+`,id,stagger,delay,duration,paused,scrollTrigger`,function(e){return Ya[e]=1});var Q=function(e){Xn(t,e);function t(t,n,r,i){var a;typeof n==`number`&&(r.duration=n,n=r,r=null),a=e.call(this,i?n:ui(n))||this;var o=a.vars,s=o.duration,c=o.delay,l=o.immediateRender,u=o.stagger,d=o.overwrite,f=o.keyframes,p=o.defaults,m=o.scrollTrigger,h=n.parent||q,g=(hr(t)||mr(t)?cr(t[0]):`length`in n)?[t]:Wi(t),_,v,y,b,x,S,C,w;if(a._targets=g.length?Jr(g):Nr(`GSAP target `+t+` not found. https://gsap.com`,!Zn.nullTargetWarn)||[],a._ptLookup=[],a._overwrite=d,f||u||pr(s)||pr(c)){n=a.vars;var T=n.easeReverse||n.yoyoEase;if(_=a.timeline=new Ia({data:`nested`,defaults:p||{},targets:h&&h.data===`nested`?h.vars.targets:g}),_.kill(),_.parent=_._dp=Yn(a),_._start=0,u||pr(s)||pr(c)){if(b=g.length,C=u&&qi(u),ur(u))for(x in u)~Ja.indexOf(x)&&(w||={},w[x]=u[x]);for(v=0;v<b;v++)y=li(n,Ya),y.stagger=0,T&&(y.easeReverse=T),w&&si(y,w),S=g[v],y.duration=+qa(s,Yn(a),v,S,g),y.delay=(+qa(c,Yn(a),v,S,g)||0)-a._delay,!u&&b===1&&y.delay&&(a._delay=c=y.delay,a._start+=c,y.delay=0),_.to(S,y,C?C(v,S,g):0),_._ease=Z.none;_.duration()?s=c=0:a.timeline=0}else if(f){ui(ai(_.vars.defaults,{ease:`none`})),_._ease=ka(f.ease||n.ease||`none`);var E=0,D,O,k;if(hr(f))f.forEach(function(e){return _.to(g,e,`>`)}),_.duration();else{for(x in y={},f)x===`ease`||x===`easeEach`||Ka(x,f[x],y,f.easeEach);for(x in y)for(D=y[x].sort(function(e,t){return e.t-t.t}),E=0,v=0;v<D.length;v++)O=D[v],k={ease:O.e,duration:(O.t-(v?D[v-1].t:0))/100*s},k[x]=O.v,_.to(g,k,E),E+=k.duration;_.duration()<s&&_.to({},{duration:s-_.duration()})}}s||a.duration(s=_.duration())}else a.timeline=0;return d===!0&&!$n&&(Va=Yn(a),q.killTweensOf(g),Va=0),Ti(h,Yn(a),r),n.reversed&&a.reverse(),n.paused&&a.paused(!0),(l||!s&&!f&&a._start===Y(h._time)&&dr(l)&&vi(Yn(a))&&h.data!==`nested`)&&(a._tTime=-W,a.render(Math.max(0,-c)||0)),m&&Ei(Yn(a),m),a}var n=t.prototype;return n.render=function(e,t,n){var r=this._time,i=this._tDur,a=this._dur,o=e<0,s=e>i-W&&!o?i:e<W?0:e,c,l,u,d,f,p,m,h;if(!a)Ai(this,e,t,n);else if(s!==this._tTime||!e||n||!this._initted&&this._tTime||this._startAt&&this._zTime<0!==o||this._lazy){if(c=s,h=this.timeline,this._repeat){if(d=a+this._rDelay,this._repeat<-1&&o)return this.totalTime(d*100+e,t,n);if(c=Y(s%d),s===i?(u=this._repeat,c=a):(f=Y(s/d),u=~~f,u&&u===f?(c=a,u--):c>a&&(c=a)),p=this._yoyo&&u&1,p&&(c=a-c),f=bi(this._tTime,d),c===r&&!n&&this._initted&&u===f)return this._tTime=s,this;u!==f&&this.vars.repeatRefresh&&!p&&!this._lock&&c!==d&&this._initted&&(this._lock=n=1,this.render(Y(d*u),!0).invalidate()._lock=0)}if(!this._initted){if(Di(this,o?e:c,n,t,s))return this._tTime=0,this;if(r!==this._time&&!(n&&this.vars.repeatRefresh&&u!==f))return this;if(a!==this._dur)return this.render(e,t,n)}if(this._rEase){var g=c<r;if(g!==this._inv){var _=g?r:a-r;this._inv=g,this._from&&(this.ratio=1-this.ratio),this._invRatio=this.ratio,this._invTime=r,this._invRecip=_?(g?-1:1)/_:0,this._invScale=g?-this.ratio:1-this.ratio,this._invEase=g?this._rEase:this._ease}this.ratio=m=this._invRatio+this._invScale*this._invEase((c-this._invTime)*this._invRecip)}else this.ratio=m=this._ease(c/a);if(this._from&&(this.ratio=m=1-m),this._tTime=s,this._time=c,!this._act&&this._ts&&(this._act=1,this._lazy=0),!r&&s&&!t&&!f&&(sa(this,`onStart`),this._tTime!==s))return this;for(l=this._pt;l;)l.r(m,l.d),l=l._next;h&&h.render(e<0?e:h._dur*h._ease(c/this._dur),t,n)||this._startAt&&(this._zTime=e),this._onUpdate&&!t&&(o&&_i(this,e,t,n),sa(this,`onUpdate`)),this._repeat&&u!==f&&this.vars.onRepeat&&!t&&this.parent&&sa(this,`onRepeat`),(s===this._tDur||!s)&&this._tTime===s&&(o&&!this._onUpdate&&_i(this,e,!0,!0),(e||!a)&&(s===this._tDur&&this._ts>0||!s&&this._ts<0)&&mi(this,1),!t&&!(o&&!r)&&(s||r||p)&&(sa(this,s===i?`onComplete`:`onReverseComplete`,!0),this._prom&&!(s<i&&this.timeScale()>0)&&this._prom()))}return this},n.targets=function(){return this._targets},n.invalidate=function(t){return(!t||!this.vars.runBackwards)&&(this._startAt=0),this._pt=this._op=this._onUpdate=this._lazy=this.ratio=0,this._ptLookup=[],this.timeline&&this.timeline.invalidate(t),e.prototype.invalidate.call(this,t)},n.resetTo=function(e,t,n,r,i){ba||xa.wake(),this._ts||this.play();var a=Math.min(this._dur,(this._dp._time-this._start)*this._ts),o;return this._initted||Ua(this,a),o=this._ease(a/this._dur),Wa(this,e,t,n,r,o,a,i)?this.resetTo(e,t,n,r,1):(Ci(this,0),this.parent||fi(this._dp,this,`_first`,`_last`,this._dp._sort?`_start`:0),this.render(0))},n.kill=function(e,t){if(t===void 0&&(t=`all`),!e&&(!t||t===`all`))return this._lazy=this._pt=0,this.parent?ca(this):this.scrollTrigger&&this.scrollTrigger.kill(!!er),this;if(this.timeline){var n=this.timeline.totalDuration();return this.timeline.killTweensOf(e,t,Va&&Va.vars.overwrite!==!0)._first||ca(this),this.parent&&n!==this.timeline.totalDuration()&&Mi(this,this._dur*this.timeline._tDur/n,0,1),this}var r=this._targets,i=e?Wi(e):r,a=this._ptLookup,o=this._pt,s,c,l,u,d,f,p;if((!t||t===`all`)&&di(r,i))return t===`all`&&(this._pt=0),ca(this);for(s=this._op=this._op||[],t!==`all`&&(G(t)&&(d={},Zr(t,function(e){return d[e]=1}),t=d),t=Ga(r,t)),p=r.length;p--;)if(~i.indexOf(r[p]))for(d in c=a[p],t===`all`?(s[p]=t,u=c,l={}):(l=s[p]=s[p]||{},u=t),u)f=c&&c[d],f&&((!(`kill`in f.d)||f.d.kill(d)===!0)&&pi(this,f,`_pt`),delete c[d]),l!==`all`&&(l[d]=1);return this._initted&&!this._pt&&o&&ca(this),this},t.to=function(e,n){return new t(e,n,arguments[2])},t.from=function(e,t){return Ii(1,arguments)},t.delayedCall=function(e,n,r,i){return new t(n,0,{immediateRender:!1,lazy:!1,overwrite:!1,delay:e,onComplete:n,onReverseComplete:n,onCompleteParams:r,onReverseCompleteParams:r,callbackScope:i})},t.fromTo=function(e,t,n){return Ii(2,arguments)},t.set=function(e,n){return n.duration=0,n.repeatDelay||(n.repeat=0),new t(e,n)},t.killTweensOf=function(e,t,n){return q.killTweensOf(e,t,n)},t}(Fa);ai(Q.prototype,{_targets:[],_lazy:0,_startAt:0,_op:0,_onInit:0}),Zr(`staggerTo,staggerFrom,staggerFromTo`,function(e){Q[e]=function(){var t=new Ia,n=Vi.call(arguments,0);return n.splice(e===`staggerFromTo`?5:4,0,0),t[e].apply(t,n)}});var Xa=function(e,t,n){return e[t]=n},Za=function(e,t,n){return e[t](n)},Qa=function(e,t,n,r){return e[t](r.fp,n)},$a=function(e,t,n){return e.setAttribute(t,n)},eo=function(e,t){return K(e[t])?Za:lr(e[t])&&e.setAttribute?$a:Xa},to=function(e,t){return t.set(t.t,t.p,Math.round((t.s+t.c*e)*1e6)/1e6,t)},no=function(e,t){return t.set(t.t,t.p,!!(t.s+t.c*e),t)},ro=function(e,t){var n=t._pt,r=``;if(!e&&t.b)r=t.b;else if(e===1&&t.e)r=t.e;else{for(;n;)r=n.p+(n.m?n.m(n.s+n.c*e):Math.round((n.s+n.c*e)*1e4)/1e4)+r,n=n._next;r+=t.c}t.set(t.t,t.p,r,t)},io=function(e,t){for(var n=t._pt;n;)n.r(e,n.d),n=n._next},ao=function(e,t,n,r){for(var i=this._pt,a;i;)a=i._next,i.p===r&&i.modifier(e,t,n),i=a},oo=function(e){for(var t=this._pt,n,r;t;)r=t._next,t.p===e&&!t.op||t.op===e?pi(this,t,`_pt`):t.dep||(n=1),t=r;return!n},so=function(e,t,n,r){r.mSet(e,t,r.m.call(r.tween,n,r.mt),r)},co=function(e){for(var t=e._pt,n,r,i,a;t;){for(n=t._next,r=i;r&&r.pr>t.pr;)r=r._next;(t._prev=r?r._prev:a)?t._prev._next=t:i=t,(t._next=r)?r._prev=t:a=t,t=n}e._pt=i},lo=function(){function e(e,t,n,r,i,a,o,s,c){this.t=t,this.s=r,this.c=i,this.p=n,this.r=a||to,this.d=o||this,this.set=s||Xa,this.pr=c||0,this._next=e,e&&(e._prev=this)}var t=e.prototype;return t.modifier=function(e,t,n){this.mSet=this.mSet||this.set,this.set=so,this.m=e,this.mt=n,this.tween=t},e}();Zr(qr+`parent,duration,ease,delay,overwrite,runBackwards,startAt,yoyo,immediateRender,repeat,repeatDelay,data,paused,reversed,lazy,callbackScope,stringFilter,id,yoyoEase,stagger,inherit,repeatRefresh,keyframes,autoRevert,scrollTrigger,easeReverse`,function(e){return zr[e]=1}),Or.TweenMax=Or.TweenLite=Q,Or.TimelineLite=Or.TimelineMax=Ia,q=new Ia({sortChildren:!1,defaults:Qn,autoRemoveChildren:!0,id:`root`,smoothChildTiming:!0}),Zn.stringFilter=ya;var uo=[],fo={},po=[],mo=0,ho=0,go=function(e){return(fo[e]||po).map(function(e){return e()})},_o=function(){var e=Date.now(),t=[];e-mo>2&&(go(`matchMediaInit`),uo.forEach(function(e){var n=e.queries,r=e.conditions,i,a,o,s;for(a in n)i=Tr.matchMedia(n[a]).matches,i&&(o=1),i!==r[a]&&(r[a]=i,s=1);s&&(e.revert(),o&&t.push(e))}),go(`matchMediaRevert`),t.forEach(function(e){return e.onMatch(e,function(t){return e.add(null,t)})}),mo=e,go(`matchMedia`))},vo=function(){function e(e,t){this.selector=t&&Gi(t),this.data=[],this._r=[],this.isReverted=!1,this.id=ho++,e&&this.add(e)}var t=e.prototype;return t.add=function(e,t,n){K(e)&&(n=t,t=e,e=K);var r=this,i=function(){var e=U,i=r.selector,a;return e&&e!==r&&e.data.push(r),n&&(r.selector=Gi(n)),U=r,a=t.apply(r,arguments),K(a)&&r._r.push(a),U=e,r.selector=i,r.isReverted=!1,a};return r.last=i,e===K?i(r,function(e){return r.add(null,e)}):e?r[e]=i:i},t.ignore=function(e){var t=U;U=null,e(this),U=t},t.getTweens=function(){var t=[];return this.data.forEach(function(n){return n instanceof e?t.push.apply(t,n.getTweens()):n instanceof Q&&!(n.parent&&n.parent.data===`nested`)&&t.push(n)}),t},t.clear=function(){this._r.length=this.data.length=0},t.kill=function(e,t){var n=this;if(e?(function(){for(var t=n.getTweens(),r=n.data.length,i;r--;)i=n.data[r],i.data===`isFlip`&&(i.revert(),i.getChildren(!0,!0,!1).forEach(function(e){return t.splice(t.indexOf(e),1)}));for(t.map(function(e){return{g:e._dur||e._delay||e._sat&&!e._sat.vars.immediateRender?e.globalTime(0):-1/0,t:e}}).sort(function(e,t){return t.g-e.g||-1/0}).forEach(function(t){return t.t.revert(e)}),r=n.data.length;r--;)i=n.data[r],i instanceof Ia?i.data!==`nested`&&(i.scrollTrigger&&i.scrollTrigger.revert(),i.kill()):!(i instanceof Q)&&i.revert&&i.revert(e);n._r.forEach(function(t){return t(e,n)}),n.isReverted=!0})():this.data.forEach(function(e){return e.kill&&e.kill()}),this.clear(),t)for(var r=uo.length;r--;)uo[r].id===this.id&&uo.splice(r,1)},t.revert=function(e){this.kill(e||{})},e}(),yo=function(){function e(e){this.contexts=[],this.scope=e,U&&U.data.push(this)}var t=e.prototype;return t.add=function(e,t,n){ur(e)||(e={matches:e});var r=new vo(0,n||this.scope),i=r.conditions={},a,o,s;for(o in U&&!r.selector&&(r.selector=U.selector),this.contexts.push(r),t=r.add(`onMatch`,t),r.queries=e,e)o===`all`?s=1:(a=Tr.matchMedia(e[o]),a&&(uo.indexOf(r)<0&&uo.push(r),(i[o]=a.matches)&&(s=1),a.addListener?a.addListener(_o):a.addEventListener(`change`,_o)));return s&&t(r,function(e){return r.add(null,e)}),this},t.revert=function(e){this.kill(e||{})},t.kill=function(e){this.contexts.forEach(function(t){return t.kill(e,!0)})},e}(),bo={registerPlugin:function(){[...arguments].forEach(function(e){return da(e)})},timeline:function(e){return new Ia(e)},getTweensOf:function(e,t){return q.getTweensOf(e,t)},getProperty:function(e,t,n,r){G(e)&&(e=Wi(e)[0]);var i=Yr(e||{}).get,a=n?ii:ri;return n===`native`&&(n=``),e&&(t?a((Ur[t]&&Ur[t].get||i)(e,t,n,r)):function(t,n,r){return a((Ur[t]&&Ur[t].get||i)(e,t,n,r))})},quickSetter:function(e,t,n){if(e=Wi(e),e.length>1){var r=e.map(function(e){return wo.quickSetter(e,t,n)}),i=r.length;return function(e){for(var t=i;t--;)r[t](e)}}e=e[0]||{};var a=Ur[t],o=Yr(e),s=o.harness&&(o.harness.aliases||{})[t]||t,c=a?function(t){var r=new a;la._pt=0,r.init(e,n?t+n:t,la,0,[e]),r.render(1,r),la._pt&&io(1,la)}:o.set(e,s);return a?c:function(t){return c(e,s,n?t+n:t,o,1)}},quickTo:function(e,t,n){var r,i=wo.to(e,ai((r={},r[t]=`+=0.1`,r.paused=!0,r.stagger=0,r),n||{})),a=function(e,n,r){return i.resetTo(t,e,n,r)};return a.tween=i,a},isTweening:function(e){return q.getTweensOf(e,!0).length>0},defaults:function(e){return e&&e.ease&&(e.ease=ka(e.ease,Qn.ease)),ci(Qn,e||{})},config:function(e){return ci(Zn,e||{})},registerEffect:function(e){var t=e.name,n=e.effect,r=e.plugins,i=e.defaults,a=e.extendTimeline;(r||``).split(`,`).forEach(function(e){return e&&!Ur[e]&&!Or[e]&&Nr(t+` effect requires `+e+` plugin.`)}),Wr[t]=function(e,t,r){return n(Wi(e),ai(t||{},i),r)},a&&(Ia.prototype[t]=function(e,n,r){return this.add(Wr[t](e,ur(n)?n:(r=n)&&{},this),r)})},registerEase:function(e,t){Z[e]=ka(t)},parseEase:function(e,t){return arguments.length?ka(e,t):Z},getById:function(e){return q.getById(e)},exportRoot:function(e,t){e===void 0&&(e={});var n=new Ia(e),r,i;for(n.smoothChildTiming=dr(e.smoothChildTiming),q.remove(n),n._dp=0,n._time=n._tTime=q._time,r=q._first;r;)i=r._next,(t||!(!r._dur&&r instanceof Q&&r.vars.onComplete===r._targets[0]))&&Ti(n,r,r._start-r._delay),r=i;return Ti(q,n,0),n},context:function(e,t){return e?new vo(e,t):U},matchMedia:function(e){return new yo(e)},matchMediaRefresh:function(){return uo.forEach(function(e){var t=e.conditions,n,r;for(r in t)t[r]&&(t[r]=!1,n=1);n&&e.revert()})||_o()},addEventListener:function(e,t){var n=fo[e]||(fo[e]=[]);~n.indexOf(t)||n.push(t)},removeEventListener:function(e,t){var n=fo[e],r=n&&n.indexOf(t);r>=0&&n.splice(r,1)},utils:{wrap:ta,wrapYoyo:na,distribute:qi,random:Xi,snap:Yi,normalize:$i,getUnit:zi,clamp:Bi,splitColor:ma,toArray:Wi,selector:Gi,mapRange:ia,pipe:Zi,unitize:Qi,interpolate:aa,shuffle:Ki},install:jr,effects:Wr,ticker:xa,updateRoot:Ia.updateRoot,plugins:Ur,globalTimeline:q,core:{PropTween:lo,globals:Pr,Tween:Q,Timeline:Ia,Animation:Fa,getCache:Yr,_removeLinkedListItem:pi,reverting:function(){return er},context:function(e){return e&&U&&(U.data.push(e),e._ctx=U),U},suppressOverwrites:function(e){return $n=e}}};Zr(`to,from,fromTo,delayedCall,set,killTweensOf`,function(e){return bo[e]=Q[e]}),xa.add(Ia.updateRoot),la=bo.to({},{duration:0});var xo=function(e,t){for(var n=e._pt;n&&n.p!==t&&n.op!==t&&n.fp!==t;)n=n._next;return n},So=function(e,t){var n=e._targets,r,i,a;for(r in t)for(i=n.length;i--;)a=e._ptLookup[i][r],(a&&=a.d)&&(a._pt&&(a=xo(a,r)),a&&a.modifier&&a.modifier(t[r],e,n[i],r))},Co=function(e,t){return{name:e,headless:1,rawVars:1,init:function(e,n,r){r._onInit=function(e){var r,i;if(G(n)&&(r={},Zr(n,function(e){return r[e]=1}),n=r),t){for(i in r={},n)r[i]=t(n[i]);n=r}So(e,n)}}}},wo=bo.registerPlugin({name:`attr`,init:function(e,t,n,r,i){var a,o,s;for(a in this.tween=n,t)s=e.getAttribute(a)||``,o=this.add(e,`setAttribute`,(s||0)+``,t[a],r,i,0,0,a),o.op=a,o.b=s,this._props.push(a)},render:function(e,t){for(var n=t._pt;n;)er?n.set(n.t,n.p,n.b,n):n.r(e,n.d),n=n._next}},{name:`endArray`,headless:1,init:function(e,t){for(var n=t.length;n--;)this.add(e,n,e[n]||0,t[n],0,0,0,0,0,1)}},Co(`roundProps`,Ji),Co(`modifiers`),Co(`snap`,Yi))||bo;Q.version=Ia.version=wo.version=`3.15.0`,Ar=1,fr()&&Sa(),Z.Power0,Z.Power1,Z.Power2,Z.Power3,Z.Power4,Z.Linear,Z.Quad,Z.Cubic,Z.Quart,Z.Quint,Z.Strong,Z.Elastic,Z.Back,Z.SteppedEase,Z.Bounce,Z.Sine,Z.Expo,Z.Circ;var To,Eo,Do,Oo,ko,Ao,jo,Mo=function(){return typeof window<`u`},No={},Po=180/Math.PI,Fo=Math.PI/180,Io=Math.atan2,Lo=1e8,Ro=/([A-Z])/g,zo=/(left|right|width|margin|padding|x)/i,Bo=/[\s,\(]\S/,Vo={autoAlpha:`opacity,visibility`,scale:`scaleX,scaleY`,alpha:`opacity`},Ho=function(e,t){return t.set(t.t,t.p,Math.round((t.s+t.c*e)*1e4)/1e4+t.u,t)},Uo=function(e,t){return t.set(t.t,t.p,e===1?t.e:Math.round((t.s+t.c*e)*1e4)/1e4+t.u,t)},Wo=function(e,t){return t.set(t.t,t.p,e?Math.round((t.s+t.c*e)*1e4)/1e4+t.u:t.b,t)},Go=function(e,t){return t.set(t.t,t.p,e===1?t.e:e?Math.round((t.s+t.c*e)*1e4)/1e4+t.u:t.b,t)},Ko=function(e,t){var n=t.s+t.c*e;t.set(t.t,t.p,~~(n+(n<0?-.5:.5))+t.u,t)},qo=function(e,t){return t.set(t.t,t.p,e?t.e:t.b,t)},Jo=function(e,t){return t.set(t.t,t.p,e===1?t.e:t.b,t)},Yo=function(e,t,n){return e.style[t]=n},Xo=function(e,t,n){return e.style.setProperty(t,n)},Zo=function(e,t,n){return e._gsap[t]=n},Qo=function(e,t,n){return e._gsap.scaleX=e._gsap.scaleY=n},$o=function(e,t,n,r,i){var a=e._gsap;a.scaleX=a.scaleY=n,a.renderTransform(i,a)},es=function(e,t,n,r,i){var a=e._gsap;a[t]=n,a.renderTransform(i,a)},$=`transform`,ts=$+`Origin`,ns=function e(t,n){var r=this,i=this.target,a=i.style,o=i._gsap;if(t in No&&a){if(this.tfm=this.tfm||{},t!==`transform`)t=Vo[t]||t,~t.indexOf(`,`)?t.split(`,`).forEach(function(e){return r.tfm[e]=xs(i,e)}):this.tfm[t]=o.x?o[t]:xs(i,t),t===ts&&(this.tfm.zOrigin=o.zOrigin);else return Vo.transform.split(`,`).forEach(function(t){return e.call(r,t,n)});if(this.props.indexOf($)>=0)return;o.svg&&(this.svgo=i.getAttribute(`data-svg-origin`),this.props.push(ts,n,``)),t=$}(a||n)&&this.props.push(t,n,a[t])},rs=function(e){e.translate&&(e.removeProperty(`translate`),e.removeProperty(`scale`),e.removeProperty(`rotate`))},is=function(){var e=this.props,t=this.target,n=t.style,r=t._gsap,i,a;for(i=0;i<e.length;i+=3)e[i+1]?e[i+1]===2?t[e[i]](e[i+2]):t[e[i]]=e[i+2]:e[i+2]?n[e[i]]=e[i+2]:n.removeProperty(e[i].substr(0,2)===`--`?e[i]:e[i].replace(Ro,`-$1`).toLowerCase());if(this.tfm){for(a in this.tfm)r[a]=this.tfm[a];r.svg&&(r.renderTransform(),t.setAttribute(`data-svg-origin`,this.svgo||``)),i=jo(),(!i||!i.isStart)&&!n[$]&&(rs(n),r.zOrigin&&n[ts]&&(n[ts]+=` `+r.zOrigin+`px`,r.zOrigin=0,r.renderTransform()),r.uncache=1)}},as=function(e,t){var n={target:e,props:[],revert:is,save:ns};return e._gsap||wo.core.getCache(e),t&&e.style&&e.nodeType&&t.split(`,`).forEach(function(e){return n.save(e)}),n},os,ss=function(e,t){var n=Eo.createElementNS?Eo.createElementNS((t||`http://www.w3.org/1999/xhtml`).replace(/^https/,`http`),e):Eo.createElement(e);return n&&n.style?n:Eo.createElement(e)},cs=function e(t,n,r){var i=getComputedStyle(t);return i[n]||i.getPropertyValue(n.replace(Ro,`-$1`).toLowerCase())||i.getPropertyValue(n)||!r&&e(t,us(n)||n,1)||``},ls=`O,Moz,ms,Ms,Webkit`.split(`,`),us=function(e,t,n){var r=(t||ko).style,i=5;if(e in r&&!n)return e;for(e=e.charAt(0).toUpperCase()+e.substr(1);i--&&!(ls[i]+e in r););return i<0?null:(i===3?`ms`:i>=0?ls[i]:``)+e},ds=function(){Mo()&&window.document&&(To=window,Eo=To.document,Do=Eo.documentElement,ko=ss(`div`)||{style:{}},ss(`div`),$=us($),ts=$+`Origin`,ko.style.cssText=`border-width:0;line-height:0;position:absolute;padding:0`,os=!!us(`perspective`),jo=wo.core.reverting,Oo=1)},fs=function(e){var t=e.ownerSVGElement,n=ss(`svg`,t&&t.getAttribute(`xmlns`)||`http://www.w3.org/2000/svg`),r=e.cloneNode(!0),i;r.style.display=`block`,n.appendChild(r),Do.appendChild(n);try{i=r.getBBox()}catch{}return n.removeChild(r),Do.removeChild(n),i},ps=function(e,t){for(var n=t.length;n--;)if(e.hasAttribute(t[n]))return e.getAttribute(t[n])},ms=function(e){var t,n;try{t=e.getBBox()}catch{t=fs(e),n=1}return t&&(t.width||t.height)||n||(t=fs(e)),t&&!t.width&&!t.x&&!t.y?{x:+ps(e,[`x`,`cx`,`x1`])||0,y:+ps(e,[`y`,`cy`,`y1`])||0,width:0,height:0}:t},hs=function(e){return!!(e.getCTM&&(!e.parentNode||e.ownerSVGElement)&&ms(e))},gs=function(e,t){if(t){var n=e.style,r;t in No&&t!==ts&&(t=$),n.removeProperty?(r=t.substr(0,2),(r===`ms`||t.substr(0,6)===`webkit`)&&(t=`-`+t),n.removeProperty(r===`--`?t:t.replace(Ro,`-$1`).toLowerCase())):n.removeAttribute(t)}},_s=function(e,t,n,r,i,a){var o=new lo(e._pt,t,n,0,1,a?Jo:qo);return e._pt=o,o.b=r,o.e=i,e._props.push(n),o},vs={deg:1,rad:1,turn:1},ys={grid:1,flex:1},bs=function e(t,n,r,i){var a=parseFloat(r)||0,o=(r+``).trim().substr((a+``).length)||`px`,s=ko.style,c=zo.test(n),l=t.tagName.toLowerCase()===`svg`,u=(l?`client`:`offset`)+(c?`Width`:`Height`),d=100,f=i===`px`,p=i===`%`,m,h,g,_;if(i===o||!a||vs[i]||vs[o])return a;if(o!==`px`&&!f&&(a=e(t,n,r,`px`)),_=t.getCTM&&hs(t),(p||o===`%`)&&(No[n]||~n.indexOf(`adius`)))return m=_?t.getBBox()[c?`width`:`height`]:t[u],J(p?a/m*d:a/100*m);if(s[c?`width`:`height`]=d+(f?o:i),h=i!==`rem`&&~n.indexOf(`adius`)||i===`em`&&t.appendChild&&!l?t:t.parentNode,_&&(h=(t.ownerSVGElement||{}).parentNode),(!h||h===Eo||!h.appendChild)&&(h=Eo.body),g=h._gsap,g&&p&&g.width&&c&&g.time===xa.time&&!g.uncache)return J(a/g.width*d);if(p&&(n===`height`||n===`width`)){var v=t.style[n];t.style[n]=d+i,m=t[u],v?t.style[n]=v:gs(t,n)}else(p||o===`%`)&&!ys[cs(h,`display`)]&&(s.position=cs(t,`position`)),h===t&&(s.position=`static`),h.appendChild(ko),m=ko[u],h.removeChild(ko),s.position=`absolute`;return c&&p&&(g=Yr(h),g.time=xa.time,g.width=h[u]),J(f?m*a/d:m&&a?d/m*a:0)},xs=function(e,t,n,r){var i;return Oo||ds(),t in Vo&&t!==`transform`&&(t=Vo[t],~t.indexOf(`,`)&&(t=t.split(`,`)[0])),No[t]&&t!==`transform`?(i=Ns(e,r),i=t===`transformOrigin`?i.svg?i.origin:Ps(cs(e,ts))+` `+i.zOrigin+`px`:i[t]):(i=e.style[t],(!i||i===`auto`||r||~(i+``).indexOf(`calc(`))&&(i=Es[t]&&Es[t](e,t,n)||cs(e,t)||Xr(e,t)||+(t===`opacity`))),n&&!~(i+``).trim().indexOf(` `)?bs(e,t,i,n)+n:i},Ss=function(e,t,n,r){if(!n||n===`none`){var i=us(t,e,1),a=i&&cs(e,i,1);a&&a!==n?(t=i,n=a):t===`borderColor`&&(n=cs(e,`borderTopColor`))}var o=new lo(this._pt,e.style,t,0,1,ro),s=0,c=0,l,u,d,f,p,m,h,g,_,v,y,b;if(o.b=n,o.e=r,n+=``,r+=``,r.substring(0,6)===`var(--`&&(r=cs(e,r.substring(4,r.indexOf(`)`)))),r===`auto`&&(m=e.style[t],e.style[t]=r,r=cs(e,t)||r,m?e.style[t]=m:gs(e,t)),l=[n,r],ya(l),n=l[0],r=l[1],d=n.match(br)||[],b=r.match(br)||[],b.length){for(;u=br.exec(r);)h=u[0],_=r.substring(s,u.index),p?p=(p+1)%5:(_.substr(-5)===`rgba(`||_.substr(-5)===`hsla(`)&&(p=1),h!==(m=d[c++]||``)&&(f=parseFloat(m)||0,y=m.substr((f+``).length),h.charAt(1)===`=`&&(h=Qr(f,h)+y),g=parseFloat(h),v=h.substr((g+``).length),s=br.lastIndex-v.length,v||(v=v||Zn.units[t]||y,s===r.length&&(r+=v,o.e+=v)),y!==v&&(f=bs(e,t,m,v)||0),o._pt={_next:o._pt,p:_||c===1?_:`,`,s:f,c:g-f,m:p&&p<4||t===`zIndex`?Math.round:0});o.c=s<r.length?r.substring(s,r.length):``}else o.r=t===`display`&&r===`none`?Jo:qo;return Sr.test(r)&&(o.e=0),this._pt=o,o},Cs={top:`0%`,bottom:`100%`,left:`0%`,right:`100%`,center:`50%`},ws=function(e){var t=e.split(` `),n=t[0],r=t[1]||`50%`;return(n===`top`||n===`bottom`||r===`left`||r===`right`)&&(e=n,n=r,r=e),t[0]=Cs[n]||n,t[1]=Cs[r]||r,t.join(` `)},Ts=function(e,t){if(t.tween&&t.tween._time===t.tween._dur){var n=t.t,r=n.style,i=t.u,a=n._gsap,o,s,c;if(i===`all`||i===!0)r.cssText=``,s=1;else for(i=i.split(`,`),c=i.length;--c>-1;)o=i[c],No[o]&&(s=1,o=o===`transformOrigin`?ts:$),gs(n,o);s&&(gs(n,$),a&&(a.svg&&n.removeAttribute(`transform`),r.scale=r.rotate=r.translate=`none`,Ns(n,1),a.uncache=1,rs(r)))}},Es={clearProps:function(e,t,n,r,i){if(i.data!==`isFromStart`){var a=e._pt=new lo(e._pt,t,n,0,0,Ts);return a.u=r,a.pr=-10,a.tween=i,e._props.push(n),1}}},Ds=[1,0,0,1,0,0],Os={},ks=function(e){return e===`matrix(1, 0, 0, 1, 0, 0)`||e===`none`||!e},As=function(e){var t=cs(e,$);return ks(t)?Ds:t.substr(7).match(yr).map(J)},js=function(e,t){var n=e._gsap||Yr(e),r=e.style,i=As(e),a,o,s,c;return n.svg&&e.getAttribute(`transform`)?(s=e.transform.baseVal.consolidate().matrix,i=[s.a,s.b,s.c,s.d,s.e,s.f],i.join(`,`)===`1,0,0,1,0,0`?Ds:i):(i===Ds&&!e.offsetParent&&e!==Do&&!n.svg&&(s=r.display,r.display=`block`,a=e.parentNode,(!a||!e.offsetParent&&!e.getBoundingClientRect().width)&&(c=1,o=e.nextElementSibling,Do.appendChild(e)),i=As(e),s?r.display=s:gs(e,`display`),c&&(o?a.insertBefore(e,o):a?a.appendChild(e):Do.removeChild(e))),t&&i.length>6?[i[0],i[1],i[4],i[5],i[12],i[13]]:i)},Ms=function(e,t,n,r,i,a){var o=e._gsap,s=i||js(e,!0),c=o.xOrigin||0,l=o.yOrigin||0,u=o.xOffset||0,d=o.yOffset||0,f=s[0],p=s[1],m=s[2],h=s[3],g=s[4],_=s[5],v=t.split(` `),y=parseFloat(v[0])||0,b=parseFloat(v[1])||0,x,S,C,w;n?s!==Ds&&(S=f*h-p*m)&&(C=h/S*y+b*(-m/S)+(m*_-h*g)/S,w=y*(-p/S)+f/S*b-(f*_-p*g)/S,y=C,b=w):(x=ms(e),y=x.x+(~v[0].indexOf(`%`)?y/100*x.width:y),b=x.y+(~(v[1]||v[0]).indexOf(`%`)?b/100*x.height:b)),r||r!==!1&&o.smooth?(g=y-c,_=b-l,o.xOffset=u+(g*f+_*m)-g,o.yOffset=d+(g*p+_*h)-_):o.xOffset=o.yOffset=0,o.xOrigin=y,o.yOrigin=b,o.smooth=!!r,o.origin=t,o.originIsAbsolute=!!n,e.style[ts]=`0px 0px`,a&&(_s(a,o,`xOrigin`,c,y),_s(a,o,`yOrigin`,l,b),_s(a,o,`xOffset`,u,o.xOffset),_s(a,o,`yOffset`,d,o.yOffset)),e.setAttribute(`data-svg-origin`,y+` `+b)},Ns=function(e,t){var n=e._gsap||new Pa(e);if(`x`in n&&!t&&!n.uncache)return n;var r=e.style,i=n.scaleX<0,a=`px`,o=`deg`,s=getComputedStyle(e),c=cs(e,ts)||`0`,l=u=d=m=h=g=_=v=y=0,u,d,f=p=1,p,m,h,g,_,v,y,b,x,S,C,w,T,E,D,O,k,A,j,M,N,P,F,I,L,R,ee,te;return n.svg=!!(e.getCTM&&hs(e)),s.translate&&((s.translate!==`none`||s.scale!==`none`||s.rotate!==`none`)&&(r[$]=(s.translate===`none`?``:`translate3d(`+(s.translate+` 0 0`).split(` `).slice(0,3).join(`, `)+`) `)+(s.rotate===`none`?``:`rotate(`+s.rotate+`) `)+(s.scale===`none`?``:`scale(`+s.scale.split(` `).join(`,`)+`) `)+(s[$]===`none`?``:s[$])),r.scale=r.rotate=r.translate=`none`),S=js(e,n.svg),n.svg&&(n.uncache?(N=e.getBBox(),c=n.xOrigin-N.x+`px `+(n.yOrigin-N.y)+`px`,M=``):M=!t&&e.getAttribute(`data-svg-origin`),Ms(e,M||c,!!M||n.originIsAbsolute,n.smooth!==!1,S)),b=n.xOrigin||0,x=n.yOrigin||0,S!==Ds&&(E=S[0],D=S[1],O=S[2],k=S[3],l=A=S[4],u=j=S[5],S.length===6?(f=Math.sqrt(E*E+D*D),p=Math.sqrt(k*k+O*O),m=E||D?Io(D,E)*Po:0,_=O||k?Io(O,k)*Po+m:0,_&&(p*=Math.abs(Math.cos(_*Fo))),n.svg&&(l-=b-(b*E+x*O),u-=x-(b*D+x*k))):(te=S[6],R=S[7],F=S[8],I=S[9],L=S[10],ee=S[11],l=S[12],u=S[13],d=S[14],C=Io(te,L),h=C*Po,C&&(w=Math.cos(-C),T=Math.sin(-C),M=A*w+F*T,N=j*w+I*T,P=te*w+L*T,F=A*-T+F*w,I=j*-T+I*w,L=te*-T+L*w,ee=R*-T+ee*w,A=M,j=N,te=P),C=Io(-O,L),g=C*Po,C&&(w=Math.cos(-C),T=Math.sin(-C),M=E*w-F*T,N=D*w-I*T,P=O*w-L*T,ee=k*T+ee*w,E=M,D=N,O=P),C=Io(D,E),m=C*Po,C&&(w=Math.cos(C),T=Math.sin(C),M=E*w+D*T,N=A*w+j*T,D=D*w-E*T,j=j*w-A*T,E=M,A=N),h&&Math.abs(h)+Math.abs(m)>359.9&&(h=m=0,g=180-g),f=J(Math.sqrt(E*E+D*D+O*O)),p=J(Math.sqrt(j*j+te*te)),C=Io(A,j),_=Math.abs(C)>2e-4?C*Po:0,y=ee?1/(ee<0?-ee:ee):0),n.svg&&(M=e.getAttribute(`transform`),n.forceCSS=e.setAttribute(`transform`,``)||!ks(cs(e,$)),M&&e.setAttribute(`transform`,M))),Math.abs(_)>90&&Math.abs(_)<270&&(i?(f*=-1,_+=m<=0?180:-180,m+=m<=0?180:-180):(p*=-1,_+=_<=0?180:-180)),t||=n.uncache,n.x=l-((n.xPercent=l&&(!t&&n.xPercent||(Math.round(e.offsetWidth/2)===Math.round(-l)?-50:0)))?e.offsetWidth*n.xPercent/100:0)+a,n.y=u-((n.yPercent=u&&(!t&&n.yPercent||(Math.round(e.offsetHeight/2)===Math.round(-u)?-50:0)))?e.offsetHeight*n.yPercent/100:0)+a,n.z=d+a,n.scaleX=J(f),n.scaleY=J(p),n.rotation=J(m)+o,n.rotationX=J(h)+o,n.rotationY=J(g)+o,n.skewX=_+o,n.skewY=v+o,n.transformPerspective=y+a,(n.zOrigin=parseFloat(c.split(` `)[2])||!t&&n.zOrigin||0)&&(r[ts]=Ps(c)),n.xOffset=n.yOffset=0,n.force3D=Zn.force3D,n.renderTransform=n.svg?Vs:os?Bs:Is,n.uncache=0,n},Ps=function(e){return(e=e.split(` `))[0]+` `+e[1]},Fs=function(e,t,n){var r=zi(t);return J(parseFloat(t)+parseFloat(bs(e,`x`,n+`px`,r)))+r},Is=function(e,t){t.z=`0px`,t.rotationY=t.rotationX=`0deg`,t.force3D=0,Bs(e,t)},Ls=`0deg`,Rs=`0px`,zs=`) `,Bs=function(e,t){var n=t||this,r=n.xPercent,i=n.yPercent,a=n.x,o=n.y,s=n.z,c=n.rotation,l=n.rotationY,u=n.rotationX,d=n.skewX,f=n.skewY,p=n.scaleX,m=n.scaleY,h=n.transformPerspective,g=n.force3D,_=n.target,v=n.zOrigin,y=``,b=g===`auto`&&e&&e!==1||g===!0;if(v&&(u!==Ls||l!==Ls)){var x=parseFloat(l)*Fo,S=Math.sin(x),C=Math.cos(x),w;x=parseFloat(u)*Fo,w=Math.cos(x),a=Fs(_,a,S*w*-v),o=Fs(_,o,-Math.sin(x)*-v),s=Fs(_,s,C*w*-v+v)}h!==Rs&&(y+=`perspective(`+h+zs),(r||i)&&(y+=`translate(`+r+`%, `+i+`%) `),(b||a!==Rs||o!==Rs||s!==Rs)&&(y+=s!==Rs||b?`translate3d(`+a+`, `+o+`, `+s+`) `:`translate(`+a+`, `+o+zs),c!==Ls&&(y+=`rotate(`+c+zs),l!==Ls&&(y+=`rotateY(`+l+zs),u!==Ls&&(y+=`rotateX(`+u+zs),(d!==Ls||f!==Ls)&&(y+=`skew(`+d+`, `+f+zs),(p!==1||m!==1)&&(y+=`scale(`+p+`, `+m+zs),_.style[$]=y||`translate(0, 0)`},Vs=function(e,t){var n=t||this,r=n.xPercent,i=n.yPercent,a=n.x,o=n.y,s=n.rotation,c=n.skewX,l=n.skewY,u=n.scaleX,d=n.scaleY,f=n.target,p=n.xOrigin,m=n.yOrigin,h=n.xOffset,g=n.yOffset,_=n.forceCSS,v=parseFloat(a),y=parseFloat(o),b,x,S,C,w;s=parseFloat(s),c=parseFloat(c),l=parseFloat(l),l&&(l=parseFloat(l),c+=l,s+=l),s||c?(s*=Fo,c*=Fo,b=Math.cos(s)*u,x=Math.sin(s)*u,S=Math.sin(s-c)*-d,C=Math.cos(s-c)*d,c&&(l*=Fo,w=Math.tan(c-l),w=Math.sqrt(1+w*w),S*=w,C*=w,l&&(w=Math.tan(l),w=Math.sqrt(1+w*w),b*=w,x*=w)),b=J(b),x=J(x),S=J(S),C=J(C)):(b=u,C=d,x=S=0),(v&&!~(a+``).indexOf(`px`)||y&&!~(o+``).indexOf(`px`))&&(v=bs(f,`x`,a,`px`),y=bs(f,`y`,o,`px`)),(p||m||h||g)&&(v=J(v+p-(p*b+m*S)+h),y=J(y+m-(p*x+m*C)+g)),(r||i)&&(w=f.getBBox(),v=J(v+r/100*w.width),y=J(y+i/100*w.height)),w=`matrix(`+b+`,`+x+`,`+S+`,`+C+`,`+v+`,`+y+`)`,f.setAttribute(`transform`,w),_&&(f.style[$]=w)},Hs=function(e,t,n,r,i){var a=360,o=G(i),s=parseFloat(i)*(o&&~i.indexOf(`rad`)?Po:1)-r,c=r+s+`deg`,l,u;return o&&(l=i.split(`_`)[1],l===`short`&&(s%=a,s!==s%(a/2)&&(s+=s<0?a:-a)),l===`cw`&&s<0?s=(s+a*Lo)%a-~~(s/a)*a:l===`ccw`&&s>0&&(s=(s-a*Lo)%a-~~(s/a)*a)),e._pt=u=new lo(e._pt,t,n,r,s,Uo),u.e=c,u.u=`deg`,e._props.push(n),u},Us=function(e,t){for(var n in t)e[n]=t[n];return e},Ws=function(e,t,n){var r=Us({},n._gsap),i=`perspective,force3D,transformOrigin,svgOrigin`,a=n.style,o,s,c,l,u,d,f,p;for(s in r.svg?(c=n.getAttribute(`transform`),n.setAttribute(`transform`,``),a[$]=t,o=Ns(n,1),gs(n,$),n.setAttribute(`transform`,c)):(c=getComputedStyle(n)[$],a[$]=t,o=Ns(n,1),a[$]=c),No)c=r[s],l=o[s],c!==l&&i.indexOf(s)<0&&(f=zi(c),p=zi(l),u=f===p?parseFloat(c):bs(n,s,c,p),d=parseFloat(l),e._pt=new lo(e._pt,o,s,u,d-u,Ho),e._pt.u=p||0,e._props.push(s));Us(o,r)};Zr(`padding,margin,Width,Radius`,function(e,t){var n=`Top`,r=`Right`,i=`Bottom`,a=`Left`,o=(t<3?[n,r,i,a]:[n+a,n+r,i+r,i+a]).map(function(n){return t<2?e+n:`border`+n+e});Es[t>1?`border`+e:e]=function(e,t,n,r,i){var a,s;if(arguments.length<4)return a=o.map(function(t){return xs(e,t,n)}),s=a.join(` `),s.split(a[0]).length===5?a[0]:s;a=(r+``).split(` `),s={},o.forEach(function(e,t){return s[e]=a[t]=a[t]||a[(t-1)/2|0]}),e.init(t,s,i)}});var Gs={name:`css`,register:ds,targetTest:function(e){return e.style&&e.nodeType},init:function(e,t,n,r,i){var a=this._props,o=e.style,s=n.vars.startAt,c,l,u,d,f,p,m,h,g,_,v,y,b,x,S,C,w;for(m in Oo||ds(),this.styles=this.styles||as(e),C=this.styles.props,this.tween=n,t)if(m!==`autoRound`&&(l=t[m],!(Ur[m]&&Ba(m,t,n,r,e,i)))){if(f=typeof l,p=Es[m],f===`function`&&(l=l.call(n,r,e,i),f=typeof l),f===`string`&&~l.indexOf(`random(`)&&(l=ra(l)),p)p(this,e,m,l,n)&&(S=1);else if(m.substr(0,2)===`--`)c=(getComputedStyle(e).getPropertyValue(m)+``).trim(),l+=``,_a.lastIndex=0,_a.test(c)||(h=zi(c),g=zi(l),g?h!==g&&(c=bs(e,m,c,g)+g):h&&(l+=h)),this.add(o,`setProperty`,c,l,r,i,0,0,m),a.push(m),C.push(m,0,o[m]);else if(f!==`undefined`){if(s&&m in s?(c=typeof s[m]==`function`?s[m].call(n,r,e,i):s[m],G(c)&&~c.indexOf(`random(`)&&(c=ra(c)),zi(c+``)||c===`auto`||(c+=Zn.units[m]||zi(xs(e,m))||``),(c+``).charAt(1)===`=`&&(c=xs(e,m))):c=xs(e,m),d=parseFloat(c),_=f===`string`&&l.charAt(1)===`=`&&l.substr(0,2),_&&(l=l.substr(2)),u=parseFloat(l),m in Vo&&(m===`autoAlpha`&&(d===1&&xs(e,`visibility`)===`hidden`&&u&&(d=0),C.push(`visibility`,0,o.visibility),_s(this,o,`visibility`,d?`inherit`:`hidden`,u?`inherit`:`hidden`,!u)),m!==`scale`&&m!==`transform`&&(m=Vo[m],~m.indexOf(`,`)&&(m=m.split(`,`)[0]))),v=m in No,v){if(this.styles.save(m),w=l,f===`string`&&l.substring(0,6)===`var(--`){if(l=cs(e,l.substring(4,l.indexOf(`)`))),l.substring(0,5)===`calc(`){var T=e.style.perspective;e.style.perspective=l,l=cs(e,`perspective`),T?e.style.perspective=T:gs(e,`perspective`)}u=parseFloat(l)}if(y||(b=e._gsap,b.renderTransform&&!t.parseTransform||Ns(e,t.parseTransform),x=t.smoothOrigin!==!1&&b.smooth,y=this._pt=new lo(this._pt,o,$,0,1,b.renderTransform,b,0,-1),y.dep=1),m===`scale`)this._pt=new lo(this._pt,b,`scaleY`,b.scaleY,(_?Qr(b.scaleY,_+u):u)-b.scaleY||0,Ho),this._pt.u=0,a.push(`scaleY`,m),m+=`X`;else if(m===`transformOrigin`){C.push(ts,0,o[ts]),l=ws(l),b.svg?Ms(e,l,0,x,0,this):(g=parseFloat(l.split(` `)[2])||0,g!==b.zOrigin&&_s(this,b,`zOrigin`,b.zOrigin,g),_s(this,o,m,Ps(c),Ps(l)));continue}else if(m===`svgOrigin`){Ms(e,l,1,x,0,this);continue}else if(m in Os){Hs(this,b,m,d,_?Qr(d,_+l):l);continue}else if(m===`smoothOrigin`){_s(this,b,`smooth`,b.smooth,l);continue}else if(m===`force3D`){b[m]=l;continue}else if(m===`transform`){Ws(this,l,e);continue}}else m in o||(m=us(m)||m);if(v||(u||u===0)&&(d||d===0)&&!Bo.test(l)&&m in o)h=(c+``).substr((d+``).length),u||=0,g=zi(l)||(m in Zn.units?Zn.units[m]:h),h!==g&&(d=bs(e,m,c,g)),this._pt=new lo(this._pt,v?b:o,m,d,(_?Qr(d,_+u):u)-d,!v&&(g===`px`||m===`zIndex`)&&t.autoRound!==!1?Ko:Ho),this._pt.u=g||0,v&&w!==l?(this._pt.b=c,this._pt.e=w,this._pt.r=Go):h!==g&&g!==`%`&&(this._pt.b=c,this._pt.r=Wo);else if(m in o)Ss.call(this,e,m,c,_?_+l:l);else if(m in e)this.add(e,m,c||e[m],_?_+l:l,r,i);else if(m!==`parseTransform`){Mr(m,l);continue}v||(m in o?C.push(m,0,o[m]):typeof e[m]==`function`?C.push(m,2,e[m]()):C.push(m,1,c||e[m])),a.push(m)}}S&&co(this)},render:function(e,t){if(t.tween._time||!jo())for(var n=t._pt;n;)n.r(e,n.d),n=n._next;else t.styles.revert()},get:xs,aliases:Vo,getSetter:function(e,t,n){var r=Vo[t];return r&&r.indexOf(`,`)<0&&(t=r),t in No&&t!==ts&&(e._gsap.x||xs(e,`x`))?n&&Ao===n?t===`scale`?Qo:Zo:(Ao=n||{})&&(t===`scale`?$o:es):e.style&&!lr(e.style[t])?Yo:~t.indexOf(`-`)?Xo:eo(e,t)},core:{_removeProperty:gs,_getMatrix:js}};wo.utils.checkPrefix=us,wo.core.getStyleSaver=as,(function(e,t,n,r){var i=Zr(e+`,`+t+`,`+n,function(e){No[e]=1});Zr(t,function(e){Zn.units[e]=`deg`,Os[e]=1}),Vo[i[13]]=e+`,`+t,Zr(r,function(e){var t=e.split(`:`);Vo[t[1]]=i[t[0]]})})(`x,y,z,scale,scaleX,scaleY,xPercent,yPercent`,`rotation,rotationX,rotationY,skewX,skewY`,`transform,transformOrigin,svgOrigin,force3D,smoothOrigin,transformPerspective`,`0:translateX,1:translateY,2:translateZ,8:rotate,8:rotationZ,8:rotateZ,9:rotateX,10:rotateY`),Zr(`x,y,z,top,right,bottom,left,width,height,fontSize,padding,margin,perspective`,function(e){Zn.units[e]=`px`}),wo.registerPlugin(Gs);var Ks=wo.registerPlugin(Gs)||wo;Ks.core.Tween;var qs=null,Js=null,Ys=null,Xs=!1,Zs=[],Qs=0;function $s(){if(document.getElementById(`command-palette-overlay`))return;qs=document.createElement(`div`),qs.id=`command-palette-overlay`,qs.className=`command-palette-overlay`,qs.innerHTML=`
|
|
3191
|
+
<div class="command-palette-modal" id="command-palette-modal">
|
|
3192
|
+
<div class="command-palette-header">
|
|
3193
|
+
<span class="material-symbols-outlined search-icon">search</span>
|
|
3194
|
+
<input
|
|
3195
|
+
type="text"
|
|
3196
|
+
class="command-palette-input"
|
|
3197
|
+
id="command-palette-input"
|
|
3198
|
+
placeholder="Search tables, views, or commands..."
|
|
3199
|
+
autocomplete="off"
|
|
3200
|
+
spellcheck="false"
|
|
3201
|
+
/>
|
|
3202
|
+
<span class="command-palette-close-badge" id="command-palette-esc-btn">ESC</span>
|
|
3203
|
+
</div>
|
|
3204
|
+
<div class="command-palette-results" id="command-palette-results"></div>
|
|
3205
|
+
<div class="command-palette-footer">
|
|
3206
|
+
<div class="command-palette-footer-keys">
|
|
3207
|
+
<span class="command-palette-footer-key-item"><kbd>↑</kbd><kbd>↓</kbd> navigate</span>
|
|
3208
|
+
<span class="command-palette-footer-key-item"><kbd>↵</kbd> select</span>
|
|
3209
|
+
<span class="command-palette-footer-key-item"><kbd>esc</kbd> close</span>
|
|
3210
|
+
</div>
|
|
3211
|
+
<span class="font-mono text-soft">Drixio Command</span>
|
|
3212
|
+
</div>
|
|
3213
|
+
</div>
|
|
3214
|
+
`,document.body.appendChild(qs),Js=document.getElementById(`command-palette-input`),Ys=document.getElementById(`command-palette-results`),document.getElementById(`command-palette-modal`);let e=document.getElementById(`command-palette-esc-btn`);qs.addEventListener(`click`,e=>{e.target===qs&&tc()}),e?.addEventListener(`click`,tc),Js.addEventListener(`input`,()=>{rc(Js.value.trim())}),Js.addEventListener(`keydown`,e=>{e.key===`ArrowDown`?(e.preventDefault(),Zs.length>0&&(Qs=(Qs+1)%Zs.length,ic())):e.key===`ArrowUp`?(e.preventDefault(),Zs.length>0&&(Qs=(Qs-1+Zs.length)%Zs.length,ic())):e.key===`Enter`?(e.preventDefault(),Zs[Qs]&&oc(Zs[Qs])):e.key===`Escape`&&(e.preventDefault(),tc())}),window.openCommandPalette=ec,window.closeCommandPalette=tc}function ec(){qs||$s(),Xs=!0,Qs=0,Js.value=``,qs.classList.add(`is-open`);let e=document.getElementById(`command-palette-modal`);window.gsap&&e&&gsap.fromTo(e,{opacity:0,scale:.95,y:-15},{opacity:1,scale:1,y:0,duration:.2,ease:`power2.out`}),rc(``),setTimeout(()=>Js?.focus(),50)}function tc(){if(!Xs||!qs)return;Xs=!1;let e=document.getElementById(`command-palette-modal`);window.gsap&&e?gsap.to(e,{opacity:0,scale:.96,y:-10,duration:.15,ease:`power2.in`,onComplete:()=>{qs.classList.remove(`is-open`)}}):qs.classList.remove(`is-open`)}function nc(){let e=[];return document.querySelectorAll(`.table-btn`).forEach(t=>{let n=t.dataset.table;n&&e.push({group:`Tables`,id:`table-${n}`,icon:`table`,title:n,desc:`Open table in Data Editor`,badge:`Table`,action:()=>{window.AppState.currentTable=n,window.AppState.currentTableBtnElement=t,document.querySelectorAll(`.table-btn`).forEach(e=>e.classList.remove(`active`)),t.classList.add(`active`),window.handleSwitchTab(`data-btn`)}})}),e.push({group:`Views`,id:`view-sql`,icon:`terminal`,title:`SQL Console`,desc:`Execute raw SQL queries & analyze execution plans`,badge:`Console`,action:()=>window.handleSwitchTab(`sql-btn`)},{group:`Views`,id:`view-status`,icon:`monitoring`,title:`Database Status & Metrics`,desc:`View real-time database metrics, performance and storage`,badge:`Dashboard`,action:()=>window.handleSwitchTab(`status-btn`)},{group:`Views`,id:`view-erd`,icon:`account_tree`,title:`Visual ERD Diagram`,desc:`Interactive schema relation diagram and canvas`,badge:`Diagram`,action:()=>window.handleSwitchTab(`erd-btn`)},{group:`Views`,id:`view-connect`,icon:`cable`,title:`Connect Database`,desc:`Configure connection string or switch database instances`,badge:`Connection`,action:()=>window.handleSwitchTab(`connect-btn`)},{group:`Views`,id:`view-schema`,icon:`schema`,title:`Schema Viewer`,desc:`Inspect columns, indexes, and constraints`,badge:`Schema`,action:()=>window.handleSwitchTab(`schema-btn`)}),e.push({group:`Actions`,id:`action-theme`,icon:`dark_mode`,title:`Toggle Color Theme`,desc:`Switch between light and dark visual modes`,badge:`Theme`,action:()=>document.getElementById(`theme-toggle`)?.click()},{group:`Actions`,id:`action-create-table`,icon:`add_box`,title:`Create New Table`,desc:`Design and create a new database table schema`,badge:`Schema`,action:()=>window.openCreateTableModal?.()},{group:`Actions`,id:`action-export-db`,icon:`download`,title:`Export Full Database Backup`,desc:`Download complete SQL schema & data dump`,badge:`.sql`,action:()=>window.open(`/api/database/export`,`_blank`)},{group:`Actions`,id:`action-export-ddl`,icon:`description`,title:`Export Schema DDL Only`,desc:`Download CREATE TABLE statements without rows`,badge:`.sql`,action:()=>window.open(`/api/database/schema-only`,`_blank`)},{group:`Actions`,id:`action-export-dict`,icon:`menu_book`,title:`Export Data Dictionary`,desc:`Download schema markdown documentation`,badge:`.md`,action:()=>window.open(`/api/database/dictionary`,`_blank`)},{group:`Actions`,id:`action-import-data`,icon:`upload`,title:`Import & Restore Data`,desc:`Import SQL script or JSON dataset into database`,badge:`Import`,action:()=>Sn(`records`)},{group:`Actions`,id:`action-refresh`,icon:`refresh`,title:`Refresh Data & Tables`,desc:`Reload database schema and table rows`,badge:`Reload`,action:()=>{document.getElementById(`header-refresh-btn`)?.click()}}),e}function rc(e){let t=e.toLowerCase(),n=nc();if(Zs=t?n.filter(e=>e.title.toLowerCase().includes(t)||e.desc.toLowerCase().includes(t)||e.badge.toLowerCase().includes(t)||e.group.toLowerCase().includes(t)):n,Qs=Math.min(Qs,Math.max(0,Zs.length-1)),Zs.length===0){Ys.innerHTML=`
|
|
3215
|
+
<div class="command-palette-empty">
|
|
3216
|
+
<span class="material-symbols-outlined">search_off</span>
|
|
3217
|
+
<div class="command-palette-empty-text">No matching commands or tables found</div>
|
|
3218
|
+
</div>
|
|
3219
|
+
`;return}let r={};Zs.forEach((e,t)=>{r[e.group]||(r[e.group]=[]),r[e.group].push({item:e,index:t})});let i=``;for(let[e,t]of Object.entries(r)){i+=`<div class="command-palette-group-title">${e}</div>`;for(let{item:e,index:n}of t)i+=`
|
|
3220
|
+
<div class="command-palette-item ${n===Qs?`is-selected`:``}" data-idx="${n}">
|
|
3221
|
+
<div class="command-palette-item-left">
|
|
3222
|
+
<div class="command-palette-item-icon">
|
|
3223
|
+
<span class="material-symbols-outlined">${e.icon}</span>
|
|
3224
|
+
</div>
|
|
3225
|
+
<div class="command-palette-item-info">
|
|
3226
|
+
<div class="command-palette-item-title">${vt(e.title)}</div>
|
|
3227
|
+
<div class="command-palette-item-desc">${vt(e.desc)}</div>
|
|
3228
|
+
</div>
|
|
3229
|
+
</div>
|
|
3230
|
+
<span class="command-palette-item-badge">${e.badge}</span>
|
|
3231
|
+
</div>
|
|
3232
|
+
`}Ys.innerHTML=i,Ys.querySelectorAll(`.command-palette-item`).forEach(e=>{e.addEventListener(`click`,()=>{let t=parseInt(e.dataset.idx,10);Zs[t]&&oc(Zs[t])}),e.addEventListener(`mouseenter`,()=>{Qs=parseInt(e.dataset.idx,10),ic()})}),ac()}function ic(){Ys.querySelectorAll(`.command-palette-item`).forEach(e=>{let t=parseInt(e.dataset.idx,10);e.classList.toggle(`is-selected`,t===Qs)}),ac()}function ac(){let e=Ys.querySelector(`.command-palette-item.is-selected`);e&&e.scrollIntoView({block:`nearest`})}function oc(e){tc(),setTimeout(()=>{try{e.action()}catch(e){console.error(`Command execution failed:`,e)}},100)}window.gsap=Ks;var sc=document.getElementById(`header-container`),cc=document.getElementById(`sidebar-container`),lc=document.getElementById(`tab-container`);sc.innerHTML=e,cc.innerHTML=t,lc.innerHTML=n;var uc=document.getElementById(`export-btn`),dc=document.getElementById(`export-dropdown`),fc=document.getElementById(`import-btn`);fc&&fc.addEventListener(`click`,e=>{e.stopPropagation(),dc&&(dc.classList.add(`hidden`),uc?.classList.remove(`is-open`)),Sn(`records`)});var pc=document.getElementById(`header-connect-btn`);pc&&pc.addEventListener(`click`,()=>{window.handleSwitchTab(`connect-btn`)}),Kn(),$s();var mc=document.getElementById(`header-search-pill`);mc&&mc.addEventListener(`click`,()=>{ec()}),document.addEventListener(`keydown`,e=>{(e.ctrlKey||e.metaKey)&&e.key.toLowerCase()===`k`&&(e.preventDefault(),ec())});var hc=document.getElementById(`header-help-btn`);if(hc&&hc.addEventListener(`click`,()=>{Gn()}),uc&&dc){uc.addEventListener(`click`,e=>{e.stopPropagation();let t=dc.classList.contains(`hidden`);dc.classList.toggle(`hidden`),uc.classList.toggle(`is-open`,t);let n=window.AppState.currentTab===`data-btn`,r=window.AppState.currentTab===`console-btn`;document.querySelectorAll(`.data-tab-only`).forEach(e=>{e.style.display=n?`list-item`:`none`}),document.querySelectorAll(`.console-tab-only`).forEach(e=>{e.style.display=r?`list-item`:`none`})}),document.addEventListener(`click`,e=>{!uc.contains(e.target)&&!dc.contains(e.target)&&(dc.classList.add(`hidden`),uc.classList.remove(`is-open`))});let e=(e,t,n=!1)=>{if(dc.classList.add(`hidden`),uc.classList.remove(`is-open`),e===`/api/query/export`){let e=document.querySelector(`#sql-editor textarea`)||document.querySelector(`#sql-editor`),n=``;if(e&&e.value&&(n=e.value),!n&&window.ViewCache&&window.ViewCache.lastQuery&&(n=window.ViewCache.lastQuery),!n){alert(`No SQL query found to export.`);return}fetch(`/api/query/export?format=${t}`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({sql:n})}).then(e=>e.ok?e.blob():e.json().then(e=>{throw Error(e.error||`Export failed`)})).then(e=>{let n=URL.createObjectURL(e),r=document.createElement(`a`);r.href=n,r.download=`query_result_${new Date().getTime()}.${t}`,document.body.appendChild(r),r.click(),r.remove()}).catch(e=>alert(`Failed to export query: `+e.message));return}let r=`${e}?format=${t}`;if(n&&window.DataGrid){let e=Ee?Ee():``,t=window.DataGrid.sortState?.col||``,n=window.DataGrid.sortState?.asc??!0;r+=`&where=${encodeURIComponent(e)}&orderCol=${t}&orderAsc=${n}`}window.open(r,`_blank`)};document.getElementById(`export-data-csv-btn`)?.addEventListener(`click`,()=>{window.AppState.currentTable&&e(`/api/tables/${window.AppState.currentTable}/export`,`csv`)}),document.getElementById(`export-data-json-btn`)?.addEventListener(`click`,()=>{window.AppState.currentTable&&e(`/api/tables/${window.AppState.currentTable}/export`,`json`)}),document.getElementById(`export-data-filtered-csv-btn`)?.addEventListener(`click`,()=>{window.AppState.currentTable&&e(`/api/tables/${window.AppState.currentTable}/export`,`csv`,!0)}),document.getElementById(`export-data-filtered-json-btn`)?.addEventListener(`click`,()=>{window.AppState.currentTable&&e(`/api/tables/${window.AppState.currentTable}/export`,`json`,!0)}),document.getElementById(`export-schema-dict-btn`)?.addEventListener(`click`,()=>{window.open(`/api/database/dictionary`,`_blank`),dc.classList.add(`hidden`),uc.classList.remove(`is-open`)}),document.getElementById(`export-schema-sql-btn`)?.addEventListener(`click`,()=>{window.open(`/api/database/schema-only`,`_blank`),dc.classList.add(`hidden`),uc.classList.remove(`is-open`)}),document.getElementById(`export-schema-snapshot-btn`)?.addEventListener(`click`,()=>{window.open(`/api/schema/snapshot`,`_blank`),dc.classList.add(`hidden`),uc.classList.remove(`is-open`)}),document.getElementById(`export-console-csv-btn`)?.addEventListener(`click`,()=>{e(`/api/query/export`,`csv`)}),document.getElementById(`export-console-json-btn`)?.addEventListener(`click`,()=>{e(`/api/query/export`,`json`)}),document.getElementById(`export-erd-json-btn`)?.addEventListener(`click`,()=>{let e=localStorage.getItem(`drixio_erd_drafts`);if(!e){alert(`No ERD layout found to export.`);return}let t=new Blob([e],{type:`application/json`}),n=URL.createObjectURL(t),r=document.createElement(`a`);r.href=n,r.download=`erd_layout_${new Date().getTime()}.json`,r.click(),dc.classList.add(`hidden`),uc.classList.remove(`is-open`)}),document.getElementById(`export-db-sql-btn`)?.addEventListener(`click`,()=>{window.open(`/api/database/export`,`_blank`),dc.classList.add(`hidden`),uc.classList.remove(`is-open`)})}window.AppState={currentTable:null,currentTab:`data-btn`,currentTableBtnElement:null,dbType:null},window.setStudioConnectionMode=function(e){let t=document.getElementById(`data-btn`),n=document.getElementById(`schema-btn`),r=document.getElementById(`erd-btn`),i=document.getElementById(`status-btn`),a=document.getElementById(`connect-btn`),o=document.getElementById(`export-btn`)?.closest(`.header-dropdown-wrap`),s=document.getElementById(`import-btn`),c=document.getElementById(`slash`),l=document.getElementById(`current-table`),u=document.getElementById(`db-name`),d=document.getElementById(`env-badge`);e?(t?.classList.remove(`hidden`),n?.classList.remove(`hidden`),r?.classList.remove(`hidden`),i?.classList.remove(`hidden`),a?.classList.add(`hidden`),o&&o.classList.remove(`hidden`),s&&s.classList.remove(`hidden`),c&&c.classList.remove(`hidden`),l&&l.classList.remove(`hidden`),d&&d.classList.remove(`hidden`),window.AppState.currentTab===`connect-btn`&&window.handleSwitchTab(`data-btn`)):(t?.classList.add(`hidden`),n?.classList.add(`hidden`),r?.classList.add(`hidden`),i?.classList.add(`hidden`),a?.classList.remove(`hidden`),o&&o.classList.add(`hidden`),s&&s.classList.add(`hidden`),c&&c.classList.add(`hidden`),l&&l.classList.add(`hidden`),d&&d.classList.add(`hidden`),u&&(u.textContent=`No Database`),window.AppState.currentTab!==`connect-btn`&&window.AppState.currentTab!==`sql-btn`&&window.handleSwitchTab(`connect-btn`))},a().then(e=>{if(e&&e.success&&e.data){window.AppState.dbType=e.data.dbType,window.AppState.isRemote=!!e.data.isRemote,window.AppState.badgeLabel=e.data.badgeLabel;let t=document.getElementById(`env-badge`);if(t&&(t.textContent=e.data.badgeLabel||(e.data.isRemote?`REMOTE`:`LOCAL`),t.className=`env-badge ${e.data.isRemote?`remote`:`local`}`),e.data.dbName){let t=document.getElementById(`db-name`);t&&(t.textContent=e.data.dbName)}if(e.data.appVersion){let t=document.getElementById(`sidebar-version-tag`);t&&(t.textContent=`v${e.data.appVersion}`)}e.data.connected&&e.data.dbType&&e.data.dbType!==`none`?(window.setStudioConnectionMode(!0),e.data.targetUrl&&Ln({name:e.data.dbName,dialect:e.data.dbType,url:e.data.targetUrl,isRemote:!!e.data.isRemote,badgeLabel:e.data.badgeLabel})):window.setStudioConnectionMode(!1)}else window.setStudioConnectionMode(!1)}),window.TableStates={},window.ViewCache={},window.updateSidebarDirtyState=function(){document.querySelectorAll(`.table-btn`).forEach(e=>{let t=e.dataset.table,n=e.querySelector(`span`).textContent,r=t||n.replace(/\s*\*$/,``),i=window.TableStates[r],a=!1;if(i){if(i.dataGrid){let e=i.dataGrid;(Object.keys(e.pendingEdits||{}).length>0||e.pendingInserts&&e.pendingInserts.some(e=>Object.keys(e).length>0)||e.pendingDeletes&&e.pendingDeletes.size>0)&&(a=!0)}if(i.schemaGrid){let e=i.schemaGrid,t=e.pendingIndexEdits&&(e.pendingIndexEdits.added.length>0||e.pendingIndexEdits.dropped.length>0);(Object.keys(e.pendingEdits||{}).length>0||e.pendingInserts&&e.pendingInserts.some(e=>Object.keys(e).length>0)||e.pendingDeletes&&e.pendingDeletes.size>0||t)&&(a=!0)}}let o=e.querySelector(`span`),s=o.textContent.replace(/\s*\*$/,``),c=e.querySelector(`.dirty-indicator-dot`);o.textContent=s,a?(o.style.fontWeight=`600`,c&&c.classList.remove(`hidden`)):(o.style.fontWeight=`500`,c&&c.classList.add(`hidden`))})};var gc={data:`data-btn`,schema:`schema-btn`,sql:`sql-btn`,erd:`erd-btn`,status:`status-btn`,connect:`connect-btn`},_c={"data-btn":`data`,"schema-btn":`schema`,"sql-btn":`sql`,"erd-btn":`erd`,"status-btn":`status`,"connect-btn":`connect`};function vc(){let e=window.location.hash.replace(/^#\/?/,``).trim();if(!e)return{route:null,table:null};let t=e.split(`/`);return{route:t[0]?t[0].toLowerCase():null,table:t[1]?decodeURIComponent(t[1]):null}}window.parseHash=vc;function yc(e,t){let n=_c[e]||`data`,r=t===void 0?window.AppState?.currentTable:t,i=`#${n}`;r&&(e===`data-btn`||e===`schema-btn`)&&(i=`#${n}/${encodeURIComponent(r)}`),window.location.hash!==i&&history.replaceState(null,``,i)}window.updateUrlHash=yc,window.getInitialRouteTab=function(){let{route:e}=vc();return e&&gc[e]?gc[e]:null},window.handleSwitchTab=function(e,t=!0){document.querySelectorAll(`.tab-btn`).forEach(e=>e.classList.remove(`isCurrentTab`));let n=document.getElementById(e);n&&n.classList.add(`isCurrentTab`),window.AppState.currentTab=e;let r=document.getElementById(`sidebar-container`);if(e===`data-btn`||e===`schema-btn`?r?.classList.remove(`hidden`):r?.classList.add(`hidden`),e===`erd-btn`||e===`sql-btn`||e===`status-btn`||e===`connect-btn`)window.AppState.currentTable=null,window.AppState.currentTableBtnElement=null,document.querySelectorAll(`.table-btn`).forEach(e=>e.classList.remove(`active`));else if(e===`data-btn`||e===`schema-btn`)if(window.AppState.currentTable)window.updateSidebarActiveTable?.(window.AppState.currentTable);else{let e=document.querySelector(`.table-btn`);e&&(window.AppState.currentTable=e.dataset.table||e.querySelector(`span`).textContent.replace(/\s*\*$/,``),window.AppState.currentTableBtnElement=e,e.classList.add(`active`))}t&&yc(e),window.renderCurrentView()},window.addEventListener(`hashchange`,()=>{let{route:e,table:t}=vc();if(e&&gc[e]){let n=gc[e];if(t&&(n===`data-btn`||n===`schema-btn`)){window.AppState.currentTable=t;let e=document.querySelector(`.table-btn[data-table="${t}"]`);window.AppState.currentTableBtnElement=e||null}(window.AppState.currentTab!==n||t&&window.AppState.currentTable!==t)&&window.handleSwitchTab(n,!1)}}),window.renderEmptyState=function(e){e.innerHTML=`
|
|
3233
|
+
<div class="empty-state">
|
|
3234
|
+
<div class="icon-container">
|
|
3235
|
+
<span class="material-symbols-outlined">database</span>
|
|
3236
|
+
</div>
|
|
3237
|
+
<h2>No Table Selected</h2>
|
|
3238
|
+
<p>Select a table from the sidebar to view its data, schema, or run SQL queries.</p>
|
|
3239
|
+
<button type="button" id="btn-empty-create-table" class="header-btn primary" style="margin-top: 18px; padding: 8px 18px; gap: 6px; font-size: 13px; cursor: pointer;">
|
|
3240
|
+
<span class="material-symbols-outlined" style="font-size: 18px;">add</span>
|
|
3241
|
+
<span>Create Table</span>
|
|
3242
|
+
</button>
|
|
3243
|
+
</div>
|
|
3244
|
+
`;let t=e.querySelector(`#btn-empty-create-table`);t&&(t.onclick=()=>{window.openCreateTableModal?.()})},window.renderCurrentView=function(e=``,t=!1){let n=[`erd-btn`,`sql-btn`,`status-btn`,`connect-btn`].includes(window.AppState.currentTab),r=document.getElementById(`main-content`);document.querySelectorAll(`.view-container`).forEach(e=>e.style.display=`none`);let i=n?`view-${window.AppState.currentTab}`:`view-${window.AppState.currentTab}-${window.AppState.currentTable}`,a=document.getElementById(i);if(a||(a=document.createElement(`div`),a.id=i,a.className=`view-container`,a.style.width=`100%`,a.style.flex=`1`,a.style.display=`flex`,a.style.flexDirection=`column`,a.style.overflow=`hidden`,r.appendChild(a)),a.style.display=`flex`,!window.AppState.currentTable&&!n){window.renderEmptyState(a);return}let o=window.AppState.currentTable;o&&!window.TableStates[o]&&(window.TableStates[o]={dataGrid:null,schemaGrid:null}),window.AppState.currentTab===`data-btn`?(window.TableStates[o].dataGrid&&(window.DataGrid=window.TableStates[o].dataGrid),!window.TableStates[o].dataGrid||!a.hasChildNodes()?Ge(o,window.AppState.currentTableBtnElement,e,t,a):window.updateSidebarActiveTable?.(o)):window.AppState.currentTab===`schema-btn`?(window.TableStates[o].schemaGrid&&(window.SchemaGrid=window.TableStates[o].schemaGrid),!window.TableStates[o].schemaGrid||!a.hasChildNodes()?rt(o,window.AppState.currentTableBtnElement,a):window.updateSidebarActiveTable?.(o)):window.AppState.currentTab===`sql-btn`?a.hasChildNodes()||kt(o,window.AppState.currentTableBtnElement,a):window.AppState.currentTab===`erd-btn`?a.hasChildNodes()||pn(a):window.AppState.currentTab===`status-btn`?a.hasChildNodes()?An():jn(a):window.AppState.currentTab===`connect-btn`&&(a.hasChildNodes()||Bn(a));let s=document.getElementById(`table-icon`),c=document.getElementById(`table-name`);s&&c&&(o?(s.textContent=`table_chart`,c.textContent=o):window.AppState.currentTab===`erd-btn`?(s.textContent=`account_tree`,c.textContent=`ER Diagram`):window.AppState.currentTab===`sql-btn`?(s.textContent=`terminal`,c.textContent=`SQL Console`):window.AppState.currentTab===`status-btn`?(s.textContent=`monitoring`,c.textContent=`Dashboard`):window.AppState.currentTab===`connect-btn`?(s.textContent=`cable`,c.textContent=`Connect Database`):(s.textContent=`table_chart`,c.textContent=`none`)),document.querySelectorAll(`.table-btn`).forEach(e=>e.classList.remove(`active`)),window.AppState.currentTableBtnElement&&window.AppState.currentTableBtnElement.classList.add(`active`)},window.saveDataGridEdits=ke,window.saveSchemaEdits=Ke,document.addEventListener(`DOMContentLoaded`,()=>{ie(),Vn(),Hn(),Un();let e=document.getElementById(`header-refresh-btn`);e&&e.addEventListener(`click`,()=>{let t=e.querySelector(`.material-symbols-outlined`);t?.classList.add(`animate-spin`);let n=window.AppState.currentTab,r=window.AppState.currentTable;if(r){let e=`view-${n}-${r}`,t=document.getElementById(e);t&&(t.innerHTML=``)}window.renderCurrentView(``,!0),setTimeout(()=>{t?.classList.remove(`animate-spin`),window.showToast&&window.showToast(`Data refreshed`,`success`)},350)})});
|