qalita 2.3.2__py3-none-any.whl → 2.5.2__py3-none-any.whl
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.
- qalita/__main__.py +213 -9
- qalita/commands/{agent.py → worker.py} +89 -89
- qalita/internal/config.py +26 -19
- qalita/internal/utils.py +1 -1
- qalita/web/app.py +97 -14
- qalita/web/blueprints/context.py +13 -60
- qalita/web/blueprints/dashboard.py +35 -76
- qalita/web/blueprints/helpers.py +154 -63
- qalita/web/blueprints/sources.py +29 -61
- qalita/web/blueprints/{agents.py → workers.py} +108 -185
- qalita-2.5.2.dist-info/METADATA +66 -0
- qalita-2.5.2.dist-info/RECORD +24 -0
- {qalita-2.3.2.dist-info → qalita-2.5.2.dist-info}/WHEEL +1 -1
- qalita-2.5.2.dist-info/entry_points.txt +2 -0
- qalita/web/blueprints/studio.py +0 -1294
- qalita/web/public/chatgpt.svg +0 -3
- qalita/web/public/claude.png +0 -0
- qalita/web/public/favicon.ico +0 -0
- qalita/web/public/gemini.png +0 -0
- qalita/web/public/logo-no-slogan.png +0 -0
- qalita/web/public/logo-white-no-slogan.svg +0 -11
- qalita/web/public/mistral.svg +0 -1
- qalita/web/public/noise.webp +0 -0
- qalita/web/public/ollama.png +0 -0
- qalita/web/public/platform.png +0 -0
- qalita/web/public/sources-logos/alloy-db.png +0 -0
- qalita/web/public/sources-logos/amazon-athena.png +0 -0
- qalita/web/public/sources-logos/amazon-rds.png +0 -0
- qalita/web/public/sources-logos/api.svg +0 -2
- qalita/web/public/sources-logos/avro.svg +0 -20
- qalita/web/public/sources-logos/azure-database-mysql.png +0 -0
- qalita/web/public/sources-logos/azure-database-postgresql.png +0 -0
- qalita/web/public/sources-logos/azure-sql-database.png +0 -0
- qalita/web/public/sources-logos/azure-sql-managed-instance.png +0 -0
- qalita/web/public/sources-logos/azure-synapse-analytics.png +0 -0
- qalita/web/public/sources-logos/azure_blob.svg +0 -1
- qalita/web/public/sources-logos/bigquery.png +0 -0
- qalita/web/public/sources-logos/cassandra.svg +0 -254
- qalita/web/public/sources-logos/clickhouse.png +0 -0
- qalita/web/public/sources-logos/cloud-sql.png +0 -0
- qalita/web/public/sources-logos/cockroach-db.png +0 -0
- qalita/web/public/sources-logos/csv.svg +0 -1
- qalita/web/public/sources-logos/database.svg +0 -3
- qalita/web/public/sources-logos/databricks.png +0 -0
- qalita/web/public/sources-logos/duckdb.png +0 -0
- qalita/web/public/sources-logos/elasticsearch.svg +0 -1
- qalita/web/public/sources-logos/excel.svg +0 -1
- qalita/web/public/sources-logos/file.svg +0 -1
- qalita/web/public/sources-logos/folder.svg +0 -6
- qalita/web/public/sources-logos/gcs.png +0 -0
- qalita/web/public/sources-logos/hdfs.svg +0 -1
- qalita/web/public/sources-logos/ibm-db2.png +0 -0
- qalita/web/public/sources-logos/json.png +0 -0
- qalita/web/public/sources-logos/maria-db.png +0 -0
- qalita/web/public/sources-logos/mongodb.svg +0 -1
- qalita/web/public/sources-logos/mssql.svg +0 -1
- qalita/web/public/sources-logos/mysql.svg +0 -7
- qalita/web/public/sources-logos/oracle.svg +0 -4
- qalita/web/public/sources-logos/parquet.svg +0 -16
- qalita/web/public/sources-logos/picture.png +0 -0
- qalita/web/public/sources-logos/postgresql.svg +0 -22
- qalita/web/public/sources-logos/questdb.png +0 -0
- qalita/web/public/sources-logos/redshift.png +0 -0
- qalita/web/public/sources-logos/s3.svg +0 -34
- qalita/web/public/sources-logos/sap-hana.png +0 -0
- qalita/web/public/sources-logos/sftp.png +0 -0
- qalita/web/public/sources-logos/single-store.png +0 -0
- qalita/web/public/sources-logos/snowflake.png +0 -0
- qalita/web/public/sources-logos/sqlite.svg +0 -104
- qalita/web/public/sources-logos/sqlserver.png +0 -0
- qalita/web/public/sources-logos/starburst.png +0 -0
- qalita/web/public/sources-logos/stream.png +0 -0
- qalita/web/public/sources-logos/teradata.png +0 -0
- qalita/web/public/sources-logos/timescale.png +0 -0
- qalita/web/public/sources-logos/xls.svg +0 -1
- qalita/web/public/sources-logos/xlsx.svg +0 -1
- qalita/web/public/sources-logos/yugabyte-db.png +0 -0
- qalita/web/public/studio-logo.svg +0 -10
- qalita/web/public/studio.css +0 -304
- qalita/web/public/studio.png +0 -0
- qalita/web/public/styles.css +0 -682
- qalita/web/templates/dashboard.html +0 -373
- qalita/web/templates/navbar.html +0 -40
- qalita/web/templates/sources/added.html +0 -57
- qalita/web/templates/sources/edit.html +0 -411
- qalita/web/templates/sources/select-source.html +0 -128
- qalita/web/templates/studio/agent-panel.html +0 -828
- qalita/web/templates/studio/context-panel.html +0 -300
- qalita/web/templates/studio/index.html +0 -79
- qalita/web/templates/studio/navbar.html +0 -14
- qalita/web/templates/studio/view-panel.html +0 -529
- qalita-2.3.2.dist-info/METADATA +0 -58
- qalita-2.3.2.dist-info/RECORD +0 -101
- qalita-2.3.2.dist-info/entry_points.txt +0 -3
- {qalita-2.3.2.dist-info → qalita-2.5.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,300 +0,0 @@
|
|
|
1
|
-
<div id="context_panel_root" style="position:relative;">
|
|
2
|
-
<div id="backend_status_dot" title="Checking backend..."
|
|
3
|
-
style="position:absolute; top:10px; right:10px; width:10px; height:10px; border-radius:50%; background:#9ca3af; box-shadow:0 0 0 1px #e5e7eb;">
|
|
4
|
-
</div>
|
|
5
|
-
<div id="ctx_error" style="display:none; margin:0 0 8px 0; padding:8px 10px; border:1px solid #fecaca; background:#fef2f2; color:#b91c1c; border-radius:6px; font-size:12px;"></div>
|
|
6
|
-
<div class="right" style="display:flex;align-items:center;gap:8px;">
|
|
7
|
-
<p style="margin: 0px; min-width: 130px;">Platform Context</p>
|
|
8
|
-
<select id="ctx_select" class="studio-input" style="margin-top: 0px; cursor:pointer;"></select>
|
|
9
|
-
</div>
|
|
10
|
-
<div id="projects_container"
|
|
11
|
-
style="margin-top:10px; display:grid; grid-template-columns: repeat( auto-fit, minmax(200px, 1fr) ); gap:10px;">
|
|
12
|
-
</div>
|
|
13
|
-
<div id="issues_container"
|
|
14
|
-
style="margin-top:10px; display:grid; grid-template-columns: repeat( auto-fit, minmax(200px, 1fr) ); gap:10px;">
|
|
15
|
-
</div>
|
|
16
|
-
<div id="setup_platform_card" class="studio-setup-card" style="display:none;">
|
|
17
|
-
<div style="display:flex; align-items:flex-start; gap:10px;">
|
|
18
|
-
<div>
|
|
19
|
-
<div class="studio-setup-title">Setup platform</div>
|
|
20
|
-
<div class="studio-setup-text">
|
|
21
|
-
Your current context doesn't provide QALITA platform credentials.<br><br>
|
|
22
|
-
To use contextual data quality metadata, configure your Platform endpoint and token.
|
|
23
|
-
</div>
|
|
24
|
-
<div class="studio-setup-actions">
|
|
25
|
-
<a class="studio-setup-doc-btn" href="https://cloud.platform.qalita.io/signup" target="_blank"
|
|
26
|
-
rel="noopener noreferrer">Create a free account</a>
|
|
27
|
-
</div>
|
|
28
|
-
<div class="studio-setup-actions">
|
|
29
|
-
<a class="studio-setup-doc-btn outlined" href="https://doc.qalita.io/docs/studio" target="_blank"
|
|
30
|
-
rel="noopener noreferrer">Open documentation</a>
|
|
31
|
-
</div>
|
|
32
|
-
</div>
|
|
33
|
-
</div>
|
|
34
|
-
</div>
|
|
35
|
-
</div>
|
|
36
|
-
<script>
|
|
37
|
-
(function () {
|
|
38
|
-
function setCtxError(msg) {
|
|
39
|
-
try {
|
|
40
|
-
var el = document.getElementById('ctx_error');
|
|
41
|
-
if (!el) return;
|
|
42
|
-
if (!msg) { el.style.display = 'none'; el.textContent = ''; return; }
|
|
43
|
-
el.textContent = String(msg);
|
|
44
|
-
el.style.display = 'block';
|
|
45
|
-
} catch (e) { /* noop */ }
|
|
46
|
-
}
|
|
47
|
-
function showSetupCardIfNeeded(status) {
|
|
48
|
-
try {
|
|
49
|
-
var card = document.getElementById('setup_platform_card');
|
|
50
|
-
if (!card) return;
|
|
51
|
-
var configured = !!(status && status.configured);
|
|
52
|
-
var endpointPresent = !!(status && status.endpoint_present);
|
|
53
|
-
var tokenPresent = !!(status && status.token_present);
|
|
54
|
-
// Show card if either endpoint or token is missing
|
|
55
|
-
if (!endpointPresent || !tokenPresent) {
|
|
56
|
-
card.style.display = 'block';
|
|
57
|
-
} else {
|
|
58
|
-
card.style.display = 'none';
|
|
59
|
-
}
|
|
60
|
-
} catch (e) { /* noop */ }
|
|
61
|
-
}
|
|
62
|
-
function checkBackend() {
|
|
63
|
-
var dot = document.getElementById('backend_status_dot');
|
|
64
|
-
if (dot) { dot.style.background = '#9ca3af'; dot.title = 'Checking backend...'; }
|
|
65
|
-
// Use server-side proxy to avoid CORS and rely on current context
|
|
66
|
-
fetch('/studio/check-backend')
|
|
67
|
-
.then(function (r) { return r.json(); })
|
|
68
|
-
.then(function (j) {
|
|
69
|
-
var isOk = !!(j && j.ok);
|
|
70
|
-
var url = (j && j.url) ? String(j.url) : '';
|
|
71
|
-
var code = (j && j.status != null) ? String(j.status) : 'N/A';
|
|
72
|
-
if (dot) { dot.style.background = isOk ? '#10b981' : '#ef4444'; dot.title = (isOk ? 'Backend reachable: ' : 'Backend not reachable: ') + (url || '(unknown)') + ' (HTTP ' + code + ')'; }
|
|
73
|
-
setCtxError(isOk ? '' : ('Backend not reachable: ' + (url || '(unknown)') + ' (HTTP ' + code + ')'));
|
|
74
|
-
showSetupCardIfNeeded(j || {});
|
|
75
|
-
})
|
|
76
|
-
.catch(function () { if (dot) { dot.style.background = '#ef4444'; dot.title = 'Backend check failed'; } setCtxError('Backend check failed'); });
|
|
77
|
-
}
|
|
78
|
-
function renderIssues(items) {
|
|
79
|
-
var root = document.getElementById('issues_container');
|
|
80
|
-
if (!root) return;
|
|
81
|
-
root.innerHTML = '';
|
|
82
|
-
if (!Array.isArray(items) || !items.length) {
|
|
83
|
-
var e = document.createElement('div');
|
|
84
|
-
e.className = 'muted';
|
|
85
|
-
e.textContent = 'No issues';
|
|
86
|
-
root.appendChild(e);
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
var params = new URLSearchParams(window.location.search);
|
|
90
|
-
var selectedId = params.get('issue_id');
|
|
91
|
-
items.forEach(function (it) {
|
|
92
|
-
var id = String(it.id || it.issue_id || '');
|
|
93
|
-
var title = it.title || it.name || ('Issue ' + id);
|
|
94
|
-
var status = it.status || it.state || '';
|
|
95
|
-
var card = document.createElement('div');
|
|
96
|
-
card.className = 'card';
|
|
97
|
-
card.style.cursor = 'pointer';
|
|
98
|
-
card.style.padding = '8px 10px';
|
|
99
|
-
card.style.border = '1px solid #e5e7eb';
|
|
100
|
-
card.style.borderRadius = '8px';
|
|
101
|
-
card.style.background = '#ffffff';
|
|
102
|
-
card.innerHTML = '<div style="font-weight:600; color:#111827;">' + title + '</div>' +
|
|
103
|
-
'<div class="muted" style="font-size:12px;">' + (status ? ('Status: ' + status) : '') + '</div>' +
|
|
104
|
-
'<div class="muted" style="font-size:12px;">#' + id + '</div>';
|
|
105
|
-
try {
|
|
106
|
-
// Persist full issue object for agent context usage
|
|
107
|
-
sessionStorage.setItem('studio_issue_' + id, JSON.stringify(it));
|
|
108
|
-
} catch (e) { /* ignore */ }
|
|
109
|
-
if (selectedId && id === selectedId) {
|
|
110
|
-
card.style.boxShadow = '0 0 0 2px rgba(65,105,225,0.35)';
|
|
111
|
-
}
|
|
112
|
-
card.addEventListener('click', function () {
|
|
113
|
-
var p = new URLSearchParams(window.location.search);
|
|
114
|
-
var currentlySelected = (selectedId && selectedId === id);
|
|
115
|
-
if (currentlySelected) { p.delete('issue_id'); }
|
|
116
|
-
else { p.set('issue_id', id); }
|
|
117
|
-
var newUrl = window.location.pathname + (p.toString() ? ('?' + p.toString()) : '');
|
|
118
|
-
window.history.replaceState({}, '', newUrl);
|
|
119
|
-
try { window.dispatchEvent(new Event('popstate')); } catch (e) { }
|
|
120
|
-
try { if (!currentlySelected) { sessionStorage.setItem('studio_issue_' + id, JSON.stringify(it)); } } catch (e) { }
|
|
121
|
-
// update highlight
|
|
122
|
-
renderIssues(items);
|
|
123
|
-
// ensure local conversations synced only on select
|
|
124
|
-
if (!currentlySelected) {
|
|
125
|
-
try { fetch('/studio/sync-conversations?issue_id=' + encodeURIComponent(id)).then(function () { /*noop*/ }).catch(function () { }); } catch (e) { }
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
root.appendChild(card);
|
|
129
|
-
});
|
|
130
|
-
}
|
|
131
|
-
function getProjectInitials(name) {
|
|
132
|
-
try {
|
|
133
|
-
if (!name) return '?';
|
|
134
|
-
var parts = String(name).trim().split(/\s+/).filter(Boolean);
|
|
135
|
-
var initials = parts.slice(0, 2).map(function (p) { return p[0]; }).join('').toUpperCase();
|
|
136
|
-
return initials || '?';
|
|
137
|
-
} catch (e) { return '?'; }
|
|
138
|
-
}
|
|
139
|
-
function renderProjects(items) {
|
|
140
|
-
var root = document.getElementById('projects_container');
|
|
141
|
-
if (!root) return;
|
|
142
|
-
root.innerHTML = '';
|
|
143
|
-
if (!Array.isArray(items) || !items.length) {
|
|
144
|
-
var e = document.createElement('div');
|
|
145
|
-
e.className = 'muted';
|
|
146
|
-
e.textContent = 'No projects';
|
|
147
|
-
root.appendChild(e);
|
|
148
|
-
return;
|
|
149
|
-
}
|
|
150
|
-
var params = new URLSearchParams(window.location.search);
|
|
151
|
-
var selectedId = params.get('project_id');
|
|
152
|
-
items.forEach(function (it) {
|
|
153
|
-
var id = String(it.id || it.project_id || it.uid || it.key || '');
|
|
154
|
-
var title = it.name || it.title || it.key || ('Project ' + id);
|
|
155
|
-
var description = it.description || '';
|
|
156
|
-
var avatar = it.avatar || '';
|
|
157
|
-
var initials = getProjectInitials(title);
|
|
158
|
-
var avatarHtml = avatar
|
|
159
|
-
? '<img src="' + avatar + '" alt="' + initials + '" style="width:28px;height:28px;border-radius:50%;object-fit:cover;border:1px solid #e5e7eb;" />'
|
|
160
|
-
: '<div style="width:28px;height:28px;border-radius:50%;background:#f3f4f6;color:#374151;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:600;border:1px solid #e5e7eb;">' + initials + '</div>';
|
|
161
|
-
var card = document.createElement('div');
|
|
162
|
-
card.className = 'card';
|
|
163
|
-
card.style.cursor = 'pointer';
|
|
164
|
-
card.style.padding = '8px 10px';
|
|
165
|
-
card.style.border = '1px solid #e5e7eb';
|
|
166
|
-
card.style.borderRadius = '8px';
|
|
167
|
-
card.style.background = '#ffffff';
|
|
168
|
-
card.innerHTML = '<div style="display:flex;align-items:center;gap:10px;">' +
|
|
169
|
-
avatarHtml +
|
|
170
|
-
'<div style="min-width:0;">' +
|
|
171
|
-
'<div style="font-weight:600;color:#111827;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">' + title + '</div>' +
|
|
172
|
-
(description ? '<div class="muted" style="font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">' + description + '</div>' : '') +
|
|
173
|
-
'<div class="muted" style="font-size:12px;">#' + id + '</div>' +
|
|
174
|
-
'</div>' +
|
|
175
|
-
'</div>';
|
|
176
|
-
try {
|
|
177
|
-
sessionStorage.setItem('studio_project_' + id, JSON.stringify(it));
|
|
178
|
-
} catch (e) { /* ignore */ }
|
|
179
|
-
if (selectedId && id === selectedId) {
|
|
180
|
-
card.style.boxShadow = '0 0 0 2px rgba(65,105,225,0.35)';
|
|
181
|
-
}
|
|
182
|
-
card.addEventListener('click', function () {
|
|
183
|
-
var p = new URLSearchParams(window.location.search);
|
|
184
|
-
var currentlySelected = (selectedId && selectedId === id);
|
|
185
|
-
if (currentlySelected) { p.delete('project_id'); }
|
|
186
|
-
else { p.set('project_id', id); }
|
|
187
|
-
var newUrl = window.location.pathname + (p.toString()?('?' + p.toString()):'');
|
|
188
|
-
window.history.replaceState({}, '', newUrl);
|
|
189
|
-
try { window.dispatchEvent(new Event('popstate')); } catch(e){}
|
|
190
|
-
try { if (!currentlySelected) { sessionStorage.setItem('studio_project_' + id, JSON.stringify(it)); } } catch (e) { }
|
|
191
|
-
renderProjects(items);
|
|
192
|
-
// Reload issues to reflect potential project scoping on server
|
|
193
|
-
loadIssues();
|
|
194
|
-
try { if (window.StudioViewPanel && typeof window.StudioViewPanel.reload === 'function') { window.StudioViewPanel.reload(); } } catch(e){}
|
|
195
|
-
});
|
|
196
|
-
root.appendChild(card);
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
function loadIssues() {
|
|
200
|
-
var projectId = '';
|
|
201
|
-
try { projectId = new URLSearchParams(window.location.search).get('project_id') || ''; } catch (e) { projectId = ''; }
|
|
202
|
-
var url = '/context/issues' + (projectId ? ('?project_id=' + encodeURIComponent(projectId)) : '');
|
|
203
|
-
function extractIssueSourceId(it) {
|
|
204
|
-
try {
|
|
205
|
-
if (!it || typeof it !== 'object') return '';
|
|
206
|
-
if (it.source_id != null) return String(it.source_id);
|
|
207
|
-
if (it.sourceId != null) return String(it.sourceId);
|
|
208
|
-
if (it.source && (it.source.id != null || it.source.source_id != null)) return String(it.source.id != null ? it.source.id : it.source.source_id);
|
|
209
|
-
if (it.data && (it.data.source_id != null)) return String(it.data.source_id);
|
|
210
|
-
} catch (e) { }
|
|
211
|
-
return '';
|
|
212
|
-
}
|
|
213
|
-
function normalizeSourceIds(j) {
|
|
214
|
-
try {
|
|
215
|
-
var arr = [];
|
|
216
|
-
if (!j) return arr;
|
|
217
|
-
var root = (j.items != null ? j.items : (j.data != null ? j.data : (j.results != null ? j.results : j)));
|
|
218
|
-
if (!Array.isArray(root)) root = [root];
|
|
219
|
-
root.forEach(function (it) { if (it) { var id = String(it.id != null ? it.id : (it.source_id != null ? it.source_id : '')); if (id) arr.push(id); } });
|
|
220
|
-
return arr;
|
|
221
|
-
} catch (e) { return []; }
|
|
222
|
-
}
|
|
223
|
-
fetch(url)
|
|
224
|
-
.then(function (r) { return r.json(); })
|
|
225
|
-
.then(function (j) {
|
|
226
|
-
var items = (j && j.ok) ? (j.items || []) : [];
|
|
227
|
-
if (!j || !j.ok) { setCtxError((j && (j.message || j.error)) || 'Failed to load issues'); } else { setCtxError(''); }
|
|
228
|
-
if (!projectId) { renderIssues(items); return; }
|
|
229
|
-
// Client-side filter by project sources for robustness
|
|
230
|
-
var srcUrl = '/studio/sources?project_id=' + encodeURIComponent(projectId);
|
|
231
|
-
fetch(srcUrl)
|
|
232
|
-
.then(function (r) { return r.json(); })
|
|
233
|
-
.then(function (sj) {
|
|
234
|
-
var sourceIds = normalizeSourceIds(sj);
|
|
235
|
-
var set = {};
|
|
236
|
-
sourceIds.forEach(function (id) { set[String(id)] = true; });
|
|
237
|
-
var filtered = items.filter(function (it) { var sid = extractIssueSourceId(it); return sid && set[sid]; });
|
|
238
|
-
renderIssues(filtered);
|
|
239
|
-
})
|
|
240
|
-
.catch(function () { setCtxError('Failed to load project sources'); renderIssues([]); });
|
|
241
|
-
})
|
|
242
|
-
.catch(function () { setCtxError('Failed to load issues'); renderIssues([]); });
|
|
243
|
-
}
|
|
244
|
-
function loadProjects() {
|
|
245
|
-
function normalizeProjects(j) {
|
|
246
|
-
try {
|
|
247
|
-
if (!j) return [];
|
|
248
|
-
if (Array.isArray(j)) return j;
|
|
249
|
-
// Common wrappers
|
|
250
|
-
if (Array.isArray(j.items)) return j.items;
|
|
251
|
-
if (Array.isArray(j.data)) return j.data;
|
|
252
|
-
if (Array.isArray(j.results)) return j.results;
|
|
253
|
-
if (j.data && Array.isArray(j.data.items)) return j.data.items;
|
|
254
|
-
if (j.projects && Array.isArray(j.projects)) return j.projects;
|
|
255
|
-
// Single object cases
|
|
256
|
-
if (j.items && typeof j.items === 'object') return [j.items];
|
|
257
|
-
if (j.data && typeof j.data === 'object' && !Array.isArray(j.data)) return [j.data];
|
|
258
|
-
if (typeof j === 'object' && (j.id != null || j.name != null)) return [j];
|
|
259
|
-
} catch (e) { /* ignore */ }
|
|
260
|
-
return [];
|
|
261
|
-
}
|
|
262
|
-
fetch('/studio/projects')
|
|
263
|
-
.then(function (r) { return r.json(); })
|
|
264
|
-
.then(function (j) {
|
|
265
|
-
var items = normalizeProjects(j && (j.items != null ? j.items : j));
|
|
266
|
-
if (!j) { setCtxError('Failed to load projects'); } else { setCtxError(''); }
|
|
267
|
-
renderProjects(items);
|
|
268
|
-
})
|
|
269
|
-
.catch(function () { setCtxError('Failed to load projects'); renderProjects([]); });
|
|
270
|
-
}
|
|
271
|
-
function loadContexts() {
|
|
272
|
-
fetch('/contexts').then(r => r.json()).then(j => {
|
|
273
|
-
var sel = document.getElementById('ctx_select'); if (!sel) return;
|
|
274
|
-
sel.innerHTML = '';
|
|
275
|
-
var opt = document.createElement('option');
|
|
276
|
-
opt.value = ''; opt.textContent = 'Default context'; sel.appendChild(opt);
|
|
277
|
-
(j.items || []).forEach(function (it) {
|
|
278
|
-
var o = document.createElement('option');
|
|
279
|
-
o.value = it.path; o.textContent = it.name; sel.appendChild(o);
|
|
280
|
-
});
|
|
281
|
-
if (j.selected) { sel.value = j.selected; }
|
|
282
|
-
}).catch(() => { });
|
|
283
|
-
}
|
|
284
|
-
function onSelect() {
|
|
285
|
-
var sel = document.getElementById('ctx_select'); if (!sel) return;
|
|
286
|
-
fetch('/context/select', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ path: sel.value }) })
|
|
287
|
-
.then(() => { window.location.reload(); })
|
|
288
|
-
.catch(() => { window.location.reload(); });
|
|
289
|
-
}
|
|
290
|
-
document.addEventListener('DOMContentLoaded', function () {
|
|
291
|
-
loadContexts();
|
|
292
|
-
var sel = document.getElementById('ctx_select'); if (sel) sel.addEventListener('change', onSelect);
|
|
293
|
-
checkBackend();
|
|
294
|
-
loadProjects();
|
|
295
|
-
loadIssues();
|
|
296
|
-
// On navigation (back/forward), re-render selection highlight
|
|
297
|
-
window.addEventListener('popstate', function () { loadProjects(); loadIssues(); });
|
|
298
|
-
});
|
|
299
|
-
})();
|
|
300
|
-
</script>
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
|
|
4
|
-
<head>
|
|
5
|
-
<meta charset="utf-8" />
|
|
6
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
7
|
-
<title>QALITA Studio</title>
|
|
8
|
-
<link rel="icon" href="/static/studio.png" />
|
|
9
|
-
<link rel="stylesheet" href="/static/styles.css" />
|
|
10
|
-
<link rel="stylesheet" href="/static/studio.css" />
|
|
11
|
-
</head>
|
|
12
|
-
|
|
13
|
-
<body class="studio" style="background-repeat: no-repeat;">
|
|
14
|
-
{% include 'studio/navbar.html' %}
|
|
15
|
-
<div class="studio-panels" id="studio_panels">
|
|
16
|
-
<section class="panel" id="panel_context" style="flex: 0 0 18%; margin-top: 40px;">
|
|
17
|
-
{% include 'studio/context-panel.html' %}
|
|
18
|
-
</section>
|
|
19
|
-
<div class="resizer" data-resizer="left-center"></div>
|
|
20
|
-
<section class="panel" id="panel_view" style="flex: 0 0 49%;">
|
|
21
|
-
{% include 'studio/view-panel.html' %}
|
|
22
|
-
</section>
|
|
23
|
-
<div class="resizer" data-resizer="center-right"></div>
|
|
24
|
-
<section class="panel" id="panel_agent" style="display:flex; padding-right:20px; flex-direction:column; overflow:hidden; flex: 0 0 33%;">
|
|
25
|
-
{% include 'studio/agent-panel.html' %}
|
|
26
|
-
</section>
|
|
27
|
-
</div>
|
|
28
|
-
|
|
29
|
-
<script>
|
|
30
|
-
(function () {
|
|
31
|
-
const container = document.getElementById('studio_panels');
|
|
32
|
-
if (!container) return;
|
|
33
|
-
const panels = container.querySelectorAll('.panel');
|
|
34
|
-
const resizers = container.querySelectorAll('.resizer');
|
|
35
|
-
|
|
36
|
-
let active = null;
|
|
37
|
-
let startX = 0;
|
|
38
|
-
let startWidths = [];
|
|
39
|
-
|
|
40
|
-
function onMouseDown(e) {
|
|
41
|
-
active = e.target;
|
|
42
|
-
startX = e.clientX;
|
|
43
|
-
startWidths = Array.from(panels).map(p => p.getBoundingClientRect().width);
|
|
44
|
-
container.classList.add('dragging');
|
|
45
|
-
window.addEventListener('mousemove', onMouseMove);
|
|
46
|
-
window.addEventListener('mouseup', onMouseUp);
|
|
47
|
-
e.preventDefault();
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function onMouseMove(e) {
|
|
51
|
-
if (!active) return;
|
|
52
|
-
const dx = e.clientX - startX;
|
|
53
|
-
const min = 263;
|
|
54
|
-
if (active.dataset.resizer === 'left-center') {
|
|
55
|
-
let w0 = Math.max(min, startWidths[0] + dx);
|
|
56
|
-
let w1 = Math.max(min, startWidths[1] - dx);
|
|
57
|
-
panels[0].style.flex = '0 0 ' + w0 + 'px';
|
|
58
|
-
panels[1].style.flex = '0 0 ' + w1 + 'px';
|
|
59
|
-
} else if (active.dataset.resizer === 'center-right') {
|
|
60
|
-
let w1 = Math.max(min, startWidths[1] + dx);
|
|
61
|
-
let w2 = Math.max(min, startWidths[2] - dx);
|
|
62
|
-
panels[1].style.flex = '0 0 ' + w1 + 'px';
|
|
63
|
-
panels[2].style.flex = '0 0 ' + w2 + 'px';
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function onMouseUp() {
|
|
68
|
-
container.classList.remove('dragging');
|
|
69
|
-
window.removeEventListener('mousemove', onMouseMove);
|
|
70
|
-
window.removeEventListener('mouseup', onMouseUp);
|
|
71
|
-
active = null;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
resizers.forEach(r => r.addEventListener('mousedown', onMouseDown));
|
|
75
|
-
})();
|
|
76
|
-
</script>
|
|
77
|
-
</body>
|
|
78
|
-
|
|
79
|
-
</html>
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
<div class="topbar studio-navbar">
|
|
2
|
-
<div class="brand" style="max-width:100%; padding: 6px 23px;">
|
|
3
|
-
<div class="left">
|
|
4
|
-
<a href="/" style="display:flex;align-items:flex-end;gap:5px;text-decoration:none;color:inherit;">
|
|
5
|
-
<img src="/static/logo-white-no-slogan.svg" alt="Logo QALITA" width="25" height="25"
|
|
6
|
-
style="height:25px;" />
|
|
7
|
-
<span style="font-size: 20px; font-weight: 300; color: #ffffff;">Studio</span>
|
|
8
|
-
<span class="badge"
|
|
9
|
-
style="font-size: 10px; margin-bottom: 3px; background-color: #ffffff; color: #000000; border-radius: 4px; padding: 2px 8px;">Alpha
|
|
10
|
-
0</span>
|
|
11
|
-
</a>
|
|
12
|
-
</div>
|
|
13
|
-
</div>
|
|
14
|
-
</div>
|