Skip to content

Commit f2e54a9

Browse files
author
Peng Ren
committed
Add env variables support and fix some bugs
1 parent 8c3cd0b commit f2e54a9

13 files changed

Lines changed: 737 additions & 162 deletions

media/connection-editor.css

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,22 @@ textarea.editable {
224224
margin-top: 0;
225225
}
226226

227+
.hint-icon {
228+
display: inline-flex;
229+
align-items: center;
230+
justify-content: center;
231+
width: 14px;
232+
height: 14px;
233+
border-radius: 50%;
234+
border: 1px solid var(--vscode-descriptionForeground);
235+
font-size: 10px;
236+
font-weight: 700;
237+
color: var(--vscode-descriptionForeground);
238+
cursor: help;
239+
margin-left: 4px;
240+
vertical-align: middle;
241+
}
242+
227243
.params-wrap {
228244
margin-top: 4px;
229245
border: 1px solid var(--vscode-panel-border, var(--vscode-input-border));

media/connection-editor.html

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,38 @@ <h2>${heading}</h2>
123123
</div>
124124
</div>
125125

126+
<div class="field field-full">
127+
<div class="section-header">
128+
<div class="section-title">Environment Variables
129+
<span class="hint-icon" title="These are NOT part of the connection string. They will be passed as environment variables to the runtime when executing SQL through this connection.">?</span>
130+
</div>
131+
<button
132+
id="addEnvVar"
133+
class="secondary icon-btn"
134+
type="button"
135+
title="Add environment variable"
136+
aria-label="Add environment variable"
137+
>
138+
+
139+
</button>
140+
</div>
141+
<div class="params-wrap">
142+
<table>
143+
<thead>
144+
<tr>
145+
<th>Variable</th>
146+
<th>Value</th>
147+
<th>Action</th>
148+
</tr>
149+
</thead>
150+
<tbody id="envVarsBody"></tbody>
151+
</table>
152+
</div>
153+
<div id="envVarsEmpty" class="param-empty">
154+
No environment variables.
155+
</div>
156+
</div>
157+
126158
<div class="field field-full">
127159
<label>Connection String</label>
128160
<div class="inline-field">

media/connection-editor.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,9 @@ document.getElementById("driver").addEventListener("input", function () {
190190
const paramsBody = document.getElementById("additionalParamsBody");
191191
const paramsEmpty = document.getElementById("additionalParamsEmpty");
192192

193+
const envVarsBody = document.getElementById("envVarsBody");
194+
const envVarsEmpty = document.getElementById("envVarsEmpty");
195+
193196
// Handle database type selection
194197
const dbTypeWidget = document.getElementById("dbTypeWidget");
195198
const dbTypeHidden = document.getElementById("databaseType");
@@ -434,6 +437,75 @@ function collectAdditionalParameters() {
434437
return result;
435438
}
436439

440+
// --- Environment Variables ---
441+
function refreshEnvVarsEmptyState() {
442+
envVarsEmpty.style.display =
443+
envVarsBody.children.length === 0 ? "block" : "none";
444+
}
445+
446+
function createEnvVarRow(key = "", value = "") {
447+
const row = document.createElement("tr");
448+
449+
const keyCell = document.createElement("td");
450+
const keyInput = document.createElement("input");
451+
keyInput.className = "param-input";
452+
keyInput.value = key;
453+
keyCell.appendChild(keyInput);
454+
455+
const valueCell = document.createElement("td");
456+
const valueInput = document.createElement("input");
457+
valueInput.className = "param-input";
458+
valueInput.value = value;
459+
valueCell.appendChild(valueInput);
460+
461+
const actionCell = document.createElement("td");
462+
const deleteBtn = document.createElement("button");
463+
deleteBtn.type = "button";
464+
deleteBtn.className = "secondary icon-btn";
465+
deleteBtn.textContent = "x";
466+
deleteBtn.title = "Delete environment variable";
467+
deleteBtn.setAttribute("aria-label", "Delete environment variable");
468+
deleteBtn.addEventListener("click", () => {
469+
row.remove();
470+
refreshEnvVarsEmptyState();
471+
});
472+
actionCell.appendChild(deleteBtn);
473+
474+
row.appendChild(keyCell);
475+
row.appendChild(valueCell);
476+
row.appendChild(actionCell);
477+
envVarsBody.appendChild(row);
478+
refreshEnvVarsEmptyState();
479+
}
480+
481+
function loadInitialEnvVars() {
482+
const initialEnvVars = initial.connection.envVars || {};
483+
Object.entries(initialEnvVars).forEach(([key, value]) => {
484+
createEnvVarRow(String(key || ""), String(value || ""));
485+
});
486+
refreshEnvVarsEmptyState();
487+
}
488+
489+
function collectEnvVars() {
490+
const result = {};
491+
const rows = Array.from(envVarsBody.querySelectorAll("tr"));
492+
for (const row of rows) {
493+
const inputs = row.querySelectorAll("input");
494+
if (inputs.length < 2) {
495+
continue;
496+
}
497+
498+
const key = inputs[0].value.trim();
499+
if (!key) {
500+
continue;
501+
}
502+
503+
result[key] = inputs[1].value;
504+
}
505+
506+
return result;
507+
}
508+
437509
function updateConnectionString() {
438510
const host = document.getElementById("host").value.trim() || "localhost";
439511
const port = document.getElementById("port").value.trim();
@@ -525,6 +597,7 @@ async function copyConnectionString() {
525597
}
526598

527599
loadInitialParams();
600+
loadInitialEnvVars();
528601
updateConnectionString();
529602

530603
// When user directly edits connection string, mark it to preserve the value
@@ -537,6 +610,10 @@ document.getElementById("addParam").addEventListener("click", () => {
537610
updateConnectionString();
538611
});
539612

613+
document.getElementById("addEnvVar").addEventListener("click", () => {
614+
createEnvVarRow("", "");
615+
});
616+
540617
document
541618
.getElementById("copyConnectionString")
542619
.addEventListener("click", () => {
@@ -616,6 +693,7 @@ saveBtn.addEventListener("click", () => {
616693
connectionString: realConnectionString,
617694
databaseType: selectedDbType || undefined,
618695
additionalParameters,
696+
envVars: collectEnvVars(),
619697
};
620698

621699
vscode.postMessage({ command: "save", data });

media/sql-executor.css

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -810,6 +810,111 @@ body.vscode-high-contrast-light .json-number {
810810
background: var(--text-secondary);
811811
}
812812

813+
/* History Tab Styling */
814+
.results-history-wrap {
815+
flex: 1;
816+
min-height: 0;
817+
position: relative;
818+
}
819+
820+
.history-container {
821+
height: 100%;
822+
overflow-y: auto;
823+
padding: 8px;
824+
display: flex;
825+
flex-direction: column;
826+
gap: 8px;
827+
}
828+
829+
.history-entry {
830+
background-color: var(--bg-secondary);
831+
border: 1px solid var(--border-color);
832+
border-left: 3px solid var(--primary-color);
833+
border-radius: 4px;
834+
padding: 8px 10px;
835+
display: flex;
836+
flex-direction: column;
837+
gap: 6px;
838+
transition: background-color 0.2s;
839+
}
840+
841+
.history-entry:hover {
842+
background-color: rgba(255, 255, 255, 0.08);
843+
}
844+
845+
.history-entry.history-dql {
846+
border-left-color: var(--success-color);
847+
}
848+
849+
.history-entry.history-ddl {
850+
border-left-color: #0e8cd6;
851+
}
852+
853+
.history-entry.history-dml {
854+
border-left-color: #f0ad4e;
855+
}
856+
857+
.history-entry.has-error {
858+
border-left-color: var(--danger-color);
859+
background-color: rgba(241, 76, 76, 0.1);
860+
}
861+
862+
.history-header {
863+
display: flex;
864+
gap: 10px;
865+
align-items: center;
866+
font-size: 11px;
867+
flex-wrap: wrap;
868+
}
869+
870+
.history-time {
871+
color: var(--text-secondary);
872+
font-weight: 500;
873+
min-width: 80px;
874+
}
875+
876+
.history-duration {
877+
color: var(--text-secondary);
878+
min-width: 40px;
879+
}
880+
881+
.history-result {
882+
flex: 1;
883+
font-weight: 500;
884+
}
885+
886+
.history-error {
887+
color: var(--danger-color);
888+
font-weight: 600;
889+
}
890+
891+
.history-success {
892+
color: var(--success-color);
893+
font-weight: 600;
894+
}
895+
896+
.history-sql {
897+
font-family: "Consolas", "Monaco", monospace;
898+
font-size: 11px;
899+
color: var(--text-secondary);
900+
word-break: break-word;
901+
line-height: 1.4;
902+
max-height: 3em;
903+
overflow: hidden;
904+
text-overflow: ellipsis;
905+
background-color: rgba(0, 0, 0, 0.2);
906+
border-radius: 2px;
907+
padding: 4px 6px;
908+
white-space: pre-wrap;
909+
}
910+
911+
.placeholder {
912+
color: var(--text-secondary);
913+
font-style: italic;
914+
text-align: center;
915+
padding: 20px;
916+
}
917+
813918
@media (max-width: 860px) {
814919
.header {
815920
flex-direction: column;

media/sql-executor.html

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<head>
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6-
<title>SQL4ALL Query</title>
6+
<title>SQL4ALL Execute</title>
77
<link rel="stylesheet" href="${cssPath}" />
88
<link rel="stylesheet" href="${cmCssPath}" />
99
</head>
@@ -75,7 +75,7 @@
7575
<button
7676
id="runBtn"
7777
class="btn btn-small btn-icon btn-primary"
78-
title="Run Query (Ctrl/Cmd+Enter)"
78+
title="Execute SQL (Ctrl/Cmd+Enter)"
7979
>
8080
<span class="icon" aria-hidden="true">
8181
<svg viewBox="0 0 16 16" focusable="false">
@@ -113,7 +113,6 @@
113113

114114
<section class="results-pane">
115115
<div class="results-header">
116-
<div class="section-title">Query Results</div>
117116
<div class="results-tabs" role="tablist" aria-label="Result views">
118117
<button
119118
id="resultsTabTable"
@@ -135,6 +134,16 @@
135134
>
136135
JSON
137136
</button>
137+
<button
138+
id="resultsTabHistory"
139+
class="results-tab"
140+
role="tab"
141+
aria-selected="false"
142+
aria-controls="resultsHistoryWrap"
143+
type="button"
144+
>
145+
History
146+
</button>
138147
</div>
139148
<div class="result-actions">
140149
<span id="resultCount" class="result-count">Rows: 0</span>
@@ -187,15 +196,26 @@
187196
{"message": "Run SQL to view results"}</pre
188197
>
189198
</div>
199+
<div
200+
id="resultsHistoryWrap"
201+
class="results-history-wrap results-panel"
202+
role="tabpanel"
203+
aria-labelledby="resultsTabHistory"
204+
>
205+
<div id="historyContainer" class="history-container">
206+
<p class="placeholder">Execution history will appear here.</p>
207+
</div>
208+
</div>
190209
</div>
191210
<div id="resultsStatus" class="results-status results-status-neutral">
192-
Ready to query.
211+
Ready to execute.
193212
</div>
194213
</section>
195214
</div>
196215

197216
<script src="${cmJsPath}"></script>
198217
<script src="${cmSqlPath}"></script>
218+
<script src="${sqlUtilsJsPath}"></script>
199219
<script src="${jsPath}"></script>
200220
</body>
201221
</html>

0 commit comments

Comments
 (0)