From 2adc1eae389acbed73d4313d5b0f91e90ee2e118 Mon Sep 17 00:00:00 2001 From: Aaron Po Date: Sat, 27 Jun 2026 23:25:59 -0400 Subject: [PATCH] update pipeline results viewer --- tooling/pipeline-results-viewer/index.html | 59 ++- .../pipeline-results-viewer/package-lock.json | 407 +++++++++++++++++- tooling/pipeline-results-viewer/package.json | 2 +- tooling/pipeline-results-viewer/src/main.ts | 90 +++- tooling/pipeline-results-viewer/src/style.css | 28 -- .../pipeline-results-viewer/src/style.scss | 267 ++++++++++++ 6 files changed, 799 insertions(+), 54 deletions(-) delete mode 100644 tooling/pipeline-results-viewer/src/style.css create mode 100644 tooling/pipeline-results-viewer/src/style.scss diff --git a/tooling/pipeline-results-viewer/index.html b/tooling/pipeline-results-viewer/index.html index fedae0d..52a4ba9 100644 --- a/tooling/pipeline-results-viewer/index.html +++ b/tooling/pipeline-results-viewer/index.html @@ -11,13 +11,62 @@

Pipeline Results Viewer

+
+ + +
-
- - +
+
+ + +
+

+
+
+ -

-
diff --git a/tooling/pipeline-results-viewer/package-lock.json b/tooling/pipeline-results-viewer/package-lock.json index 69d6ce6..c29c296 100644 --- a/tooling/pipeline-results-viewer/package-lock.json +++ b/tooling/pipeline-results-viewer/package-lock.json @@ -8,11 +8,11 @@ "name": "pipeline-results-viewer", "version": "0.0.0", "dependencies": { - "95css": "^0.4.2", "sql.js": "^1.14.1" }, "devDependencies": { "@types/sql.js": "^1.4.11", + "sass": "^1.101.0", "typescript": "^6.0.3", "vite": "^8.0.12" } @@ -80,6 +80,316 @@ "url": "https://github.com/sponsors/Boshen" } }, + "node_modules/@parcel/watcher": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", + "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.6", + "@parcel/watcher-darwin-arm64": "2.5.6", + "@parcel/watcher-darwin-x64": "2.5.6", + "@parcel/watcher-freebsd-x64": "2.5.6", + "@parcel/watcher-linux-arm-glibc": "2.5.6", + "@parcel/watcher-linux-arm-musl": "2.5.6", + "@parcel/watcher-linux-arm64-glibc": "2.5.6", + "@parcel/watcher-linux-arm64-musl": "2.5.6", + "@parcel/watcher-linux-x64-glibc": "2.5.6", + "@parcel/watcher-linux-x64-musl": "2.5.6", + "@parcel/watcher-win32-arm64": "2.5.6", + "@parcel/watcher-win32-ia32": "2.5.6", + "@parcel/watcher-win32-x64": "2.5.6" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", + "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", + "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", + "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", + "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", + "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", + "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", + "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", + "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", + "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", + "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", + "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", + "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", + "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", @@ -383,11 +693,21 @@ "@types/node": "*" } }, - "node_modules/95css": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/95css/-/95css-0.4.2.tgz", - "integrity": "sha512-lzlM1rTgaGCxQL66qZTTzH7H+fHOMwwOZQsry3FcgoeUTrPp3mFReY4LUFPiTywC+Qeve0OH2VxrjDhHgRyu5A==", - "license": "MIT" + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } }, "node_modules/detect-libc": { "version": "2.1.2", @@ -432,6 +752,38 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/immutable": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.8.tgz", + "integrity": "sha512-TM5YqrGeTsVIPPpILzeqZ8D2Zc2TvNgSDi88zPF2a4cyqQdWV/wVWBDRDbNzzrLeRWScrFcOX9lW2iX6GOtUDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/lightningcss": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", @@ -712,6 +1064,14 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -761,6 +1121,20 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/readdirp": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", + "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/rolldown": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", @@ -795,6 +1169,27 @@ "@rolldown/binding-win32-x64-msvc": "1.0.3" } }, + "node_modules/sass": { + "version": "1.101.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.101.0.tgz", + "integrity": "sha512-OL3GoQyoUdDt843DpVmDO6y2k1sc5IhUDSpu8XucEI+35neq5QivZ1iuegnpraEVTJXlQGK1gl27zKcTLEPbQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^5.0.0", + "immutable": "^5.1.5", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=20.19.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", diff --git a/tooling/pipeline-results-viewer/package.json b/tooling/pipeline-results-viewer/package.json index 9b8d92b..251ba18 100644 --- a/tooling/pipeline-results-viewer/package.json +++ b/tooling/pipeline-results-viewer/package.json @@ -10,11 +10,11 @@ }, "devDependencies": { "@types/sql.js": "^1.4.11", + "sass": "^1.101.0", "typescript": "^6.0.3", "vite": "^8.0.12" }, "dependencies": { - "95css": "^0.4.2", "sql.js": "^1.14.1" } } diff --git a/tooling/pipeline-results-viewer/src/main.ts b/tooling/pipeline-results-viewer/src/main.ts index 75e70cd..90242e8 100644 --- a/tooling/pipeline-results-viewer/src/main.ts +++ b/tooling/pipeline-results-viewer/src/main.ts @@ -1,27 +1,41 @@ -import "95css/css/95css.css"; -import initSqlJs, { type Database } from "sql.js"; +import initSqlJs, { type Database, type SqlValue } from "sql.js"; import sqlWasmUrl from "sql.js/dist/sql-wasm.wasm?url"; -import "./style.css"; +import "./style.scss"; const DB_URL = "/biergarten.sqlite"; +const tabs = document.querySelectorAll("[role='tab']"); +const panels = document.querySelectorAll("[role='tabpanel']"); + const tableSelect = document.querySelector("#table-select")!; const tableContainer = document.querySelector("#table-container")!; const status = document.querySelector("#status")!; -const renderTable = (db: Database, name: string) => { - const result = db.exec(`SELECT * FROM "${name}"`); - tableContainer.replaceChildren(); +const queryInput = document.querySelector("#query-input")!; +const queryRun = document.querySelector("#query-run")!; +const queryStatus = document.querySelector("#query-status")!; +const queryContainer = + document.querySelector("#query-container")!; - if (result.length === 0) { - tableContainer.append( - Object.assign(document.createElement("p"), { textContent: "No rows." }), - ); - return; - } +for (const tab of tabs) { + tab.addEventListener("click", () => { + for (const otherTab of tabs) { + const isActive = otherTab === tab; + otherTab.classList.toggle("is-active", isActive); + otherTab.setAttribute("aria-selected", String(isActive)); + } + for (const panel of panels) { + panel.hidden = panel.id !== tab.getAttribute("aria-controls"); + } + }); +} - const { columns, values } = result[0]; +const renderResultTable = ( + container: HTMLDivElement, + columns: string[], + values: SqlValue[][], +) => { const table = document.createElement("table"); const head = table.createTHead().insertRow(); @@ -41,7 +55,47 @@ const renderTable = (db: Database, name: string) => { } } - tableContainer.append(table); + container.append(table); +}; + +const renderTable = (db: Database, name: string) => { + const result = db.exec(`SELECT * FROM "${name}"`); + tableContainer.replaceChildren(); + + if (result.length === 0) { + tableContainer.append( + Object.assign(document.createElement("p"), { textContent: "No rows." }), + ); + return; + } + + const { columns, values } = result[0]; + renderResultTable(tableContainer, columns, values); +}; + +const runQuery = (db: Database) => { + const sql = queryInput.value.trim(); + queryContainer.replaceChildren(); + + if (!sql) { + queryStatus.textContent = "Enter a query to run."; + return; + } + + try { + const results = db.exec(sql); + + if (results.length === 0) { + queryStatus.textContent = "Query executed. No rows returned."; + return; + } + + const { columns, values } = results[results.length - 1]; + renderResultTable(queryContainer, columns, values); + queryStatus.textContent = `${values.length} row${values.length === 1 ? "" : "s"} returned.`; + } catch (error) { + queryStatus.textContent = `Error: ${error instanceof Error ? error.message : String(error)}`; + } }; async function main(): Promise { @@ -78,6 +132,14 @@ async function main(): Promise { } status.textContent = ""; + + queryRun.addEventListener("click", () => runQuery(db)); + queryInput.addEventListener("keydown", (event) => { + if ((event.metaKey || event.ctrlKey) && event.key === "Enter") { + event.preventDefault(); + runQuery(db); + } + }); } main().catch((error: unknown) => { diff --git a/tooling/pipeline-results-viewer/src/style.css b/tooling/pipeline-results-viewer/src/style.css deleted file mode 100644 index 850191d..0000000 --- a/tooling/pipeline-results-viewer/src/style.css +++ /dev/null @@ -1,28 +0,0 @@ -body { - margin: 0; - padding: 24px; -} - -#window { - width: 100%; - max-width: 1600px; - margin: 0 auto; -} - -#table-container { - overflow-x: auto; - margin-top: 8px; -} - -table { - border-collapse: collapse; -} - -th, -td { - border: 1px solid #808080; - padding: 4px 8px; - text-align: left; - font-size: 14px; - white-space: nowrap; -} diff --git a/tooling/pipeline-results-viewer/src/style.scss b/tooling/pipeline-results-viewer/src/style.scss new file mode 100644 index 0000000..abbba7f --- /dev/null +++ b/tooling/pipeline-results-viewer/src/style.scss @@ -0,0 +1,267 @@ +$gray-100: #dedede; +$gray-300: #c3c3c3; +$gray-500: #808080; +$white: #ffffff; +$black: #000000; +$navy: #000080; +$blue: #1084d0; +$teal: #008080; + +$color-surface: $gray-300; +$color-surface-alt: $gray-100; +$color-surface-sunken: $white; +$color-border-highlight: $gray-100; +$color-border-lowlight: $gray-500; +$color-border-light: $white; +$color-border-dark: $black; +$color-text: $black; +$color-text-on-accent: $white; +$color-text-status: $navy; +$color-accent-start: $navy; +$color-accent-end: $blue; +$color-desktop-bg: $teal; +$color-row-stripe: $gray-100; +$color-shadow: rgba($black, 0.4); +$color-focus-ring: $black; + +$bevel-size: 2px; +$font-stack: Tahoma, "MS Sans Serif", Verdana, sans-serif; + +@mixin bevel-raised($size: $bevel-size) { + border-style: solid; + border-width: $size; + border-top-color: $color-border-highlight; + border-left-color: $color-border-highlight; + border-right-color: $color-border-dark; + border-bottom-color: $color-border-dark; + box-shadow: + inset $size $size 0 0 $color-border-light, + inset (-$size) (-$size) 0 0 $color-border-lowlight; +} + +@mixin bevel-sunken($size: $bevel-size) { + border-style: solid; + border-width: $size; + border-top-color: $color-border-lowlight; + border-left-color: $color-border-lowlight; + border-right-color: $color-border-light; + border-bottom-color: $color-border-light; + box-shadow: + inset $size $size 0 0 $color-border-dark, + inset (-$size) (-$size) 0 0 $color-surface-alt; +} + +* { + box-sizing: border-box; +} + +body { + margin: 0; + padding: 24px; + background: $color-desktop-bg; + font-family: $font-stack; + color: $color-text; + min-height: 100vh; +} + +#window { + width: 100%; + max-width: 1600px; + margin: 0 auto; + background: $color-surface; + padding: 6px; + display: flex; + flex-direction: column; + gap: 6px; + @include bevel-raised(3px); + box-shadow: 8px 8px 0 0 $color-shadow; +} + +.dialog-header { + background: linear-gradient(90deg, $color-accent-start, $color-accent-end); + color: $color-text-on-accent; + padding: 6px 8px; + display: flex; + align-items: center; + + p { + margin: 0; + font-size: 14px; + font-weight: bold; + letter-spacing: 0.5px; + text-transform: uppercase; + } +} + +.tab-strip { + display: flex; + gap: 4px; + padding: 8px 8px 0; +} + +.tab { + position: relative; + top: 3px; + margin-bottom: -2px; + font-family: $font-stack; + font-size: 12px; + font-weight: bold; + text-transform: uppercase; + padding: 6px 14px; + background: $color-surface; + color: $color-text; + border-style: solid; + border-width: 2px; + border-top-color: $color-border-light; + border-left-color: $color-border-light; + border-right-color: $color-border-dark; + border-bottom-color: $color-border-dark; + border-radius: 0; + cursor: pointer; + + &.is-active { + top: 0; + z-index: 1; + border-bottom-color: $color-surface; + } + + &:focus { + outline: 1px dotted $color-focus-ring; + outline-offset: -4px; + } +} + +.dialog-body { + border-top: 2px solid $color-border-dark; + padding: 12px; +} + +.tab-panel { + display: flex; + flex-direction: column; + gap: 12px; + + &[hidden] { + display: none; + } +} + +.field-group { + display: flex; + align-items: center; + gap: 8px; + + label { + font-size: 12px; + font-weight: bold; + text-transform: uppercase; + } + + &.field-group--stacked { + flex-direction: column; + align-items: stretch; + } +} + +select#table-select { + font-family: $font-stack; + font-size: 13px; + padding: 3px 6px; + background: $color-surface-sunken; + color: $color-text; + border-radius: 0; + @include bevel-sunken(); + + &:focus { + outline: 1px dotted $color-focus-ring; + outline-offset: 2px; + } +} + +textarea#query-input { + font-family: $font-stack; + font-size: 13px; + padding: 6px; + background: $color-surface-sunken; + color: $color-text; + border-radius: 0; + resize: vertical; + @include bevel-sunken(); + + &:focus { + outline: 1px dotted $color-focus-ring; + outline-offset: 2px; + } +} + +.btn { + align-self: flex-start; + font-family: $font-stack; + font-size: 12px; + font-weight: bold; + text-transform: uppercase; + padding: 6px 16px; + background: $color-surface; + color: $color-text; + cursor: pointer; + @include bevel-raised(); + + &:active { + @include bevel-sunken(); + } + + &:focus { + outline: 1px dotted $color-focus-ring; + outline-offset: -4px; + } +} + +#status, +#query-status { + margin: 0; + font-size: 12px; + font-style: italic; + color: $color-text-status; +} + +#table-container, +#query-container { + background: $color-surface-sunken; + padding: 4px; + overflow-x: auto; + @include bevel-sunken(); +} + +table { + border-collapse: collapse; + margin: 0 auto; + font-family: $font-stack; +} + +th, +td { + border: 1px solid $color-border-lowlight; + padding: 4px 8px; + text-align: left; + font-size: 13px; +} + +th { + background: $color-surface; + white-space: nowrap; + font-weight: bold; + text-transform: uppercase; + @include bevel-raised(1px); +} + +td { + min-width: 120px; + max-width: 800px; + white-space: normal; + word-break: break-word; + background: $color-surface-sunken; +} + +tbody tr:nth-child(even) td { + background: $color-row-stripe; +}