diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js
index 2112983..cf27e16 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js
+++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/app.js
@@ -526,6 +526,24 @@ const CANVAS_PALETTE = {
waterfallHue: [45, 18], waterfallSat: 86, waterfallLight: [92, 42], waterfallAlpha: [0.34, 0.82],
},
},
+ fire: {
+ dark: {
+ bg: "#130706",
+ spectrumLine: "#ff7a1f", spectrumFill: "rgba(255,122,31,0.14)",
+ spectrumGrid: "rgba(255,110,40,0.09)", spectrumLabel: "rgba(255,202,164,0.54)",
+ waveformLine: "rgba(255,134,54,0.94)", waveformPeak: "rgba(255,220,96,0.92)",
+ waveformGrid: "rgba(255,120,36,0.11)", waveformLabel: "rgba(255,214,176,0.66)",
+ waterfallHue: [8, 42], waterfallSat: 96, waterfallLight: [8, 58], waterfallAlpha: [0.26, 0.88],
+ },
+ light: {
+ bg: "#fff2e7",
+ spectrumLine: "#c24500", spectrumFill: "rgba(194,69,0,0.14)",
+ spectrumGrid: "rgba(125,52,0,0.09)", spectrumLabel: "rgba(90,38,0,0.56)",
+ waveformLine: "rgba(176,62,0,0.95)", waveformPeak: "rgba(224,132,0,0.90)",
+ waveformGrid: "rgba(125,52,0,0.10)", waveformLabel: "rgba(90,38,0,0.68)",
+ waterfallHue: [18, 48], waterfallSat: 90, waterfallLight: [92, 42], waterfallAlpha: [0.34, 0.84],
+ },
+ },
};
function currentStyle() {
@@ -540,7 +558,7 @@ function canvasPalette() {
function setStyle(style) {
const remapped = style === "nord" ? "arctic" : style === "monokai" ? "lime" : style;
- const valid = ["original", "arctic", "lime", "contrast", "neon-disco", "golden-rain"];
+ const valid = ["original", "arctic", "lime", "contrast", "neon-disco", "golden-rain", "fire"];
const next = valid.includes(remapped) ? remapped : "original";
if (next === "original") {
document.documentElement.removeAttribute("data-style");
@@ -4910,19 +4928,35 @@ function bmCategoryColorMap() {
return map;
}
-function createBookmarkChip(bm, colorMap) {
+function createBookmarkChip(bm, colorMap, options = {}) {
const span = document.createElement("span");
const freqStr = typeof bmFmtFreq === "function"
? bmFmtFreq(bm.freq_hz) : bm.freq_hz + "\u202fHz";
const esc = (s) => String(s)
.replace(/&/g, "&").replace(//g, ">");
span.className = "spectrum-bookmark-chip";
+ if (options.sideStack) {
+ span.classList.add("spectrum-bookmark-chip-side");
+ }
span.title = bm.name + " \u2014 " + freqStr + (bm.comment ? "\n" + bm.comment : "");
span.dataset.bmId = bm.id;
+ const labelHtml = options.sideStack
+ ? (
+ `` +
+ `` +
+ `${esc(freqStr)}` +
+ `` +
+ `${esc(bm.name)}`
+ )
+ : (
+ "\u00a0" + esc(bm.name)
+ );
span.innerHTML =
- "\u00a0" + esc(bm.name);
+ labelHtml;
const col = colorMap[bm.category || ""];
span.style.setProperty("--bm-cat-bg", col);
span.style.setProperty("--bm-cat-fg", bmContrastFg(col));
@@ -4948,7 +4982,7 @@ function updateSideBookmarkStack(container, bookmarks, colorMap) {
container.dataset.bmKey = nextKey;
container.innerHTML = "";
for (const bm of bookmarks) {
- container.appendChild(createBookmarkChip(bm, colorMap));
+ container.appendChild(createBookmarkChip(bm, colorMap, { sideStack: true }));
}
}
container.classList.add("bm-side-visible");
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html
index 56a924e..a52cf06 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html
+++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/index.html
@@ -46,6 +46,7 @@
+
@@ -273,6 +274,9 @@
+
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/bookmarks.js b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/bookmarks.js
index 1cda0fa..9006f10 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/bookmarks.js
+++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/plugins/bookmarks.js
@@ -51,32 +51,51 @@ async function bmFetch(categoryFilter) {
function bmApplyFilters() {
const text = (document.getElementById("bm-text-filter")?.value || "").trim().toLowerCase();
- const filtered = text
- ? bmList.filter((bm) =>
+ const modeFilter = (document.getElementById("bm-mode-filter")?.value || "").trim().toUpperCase();
+ let filtered = modeFilter
+ ? bmList.filter((bm) => String(bm.mode || "").toUpperCase() === modeFilter)
+ : bmList;
+ filtered = text
+ ? filtered.filter((bm) =>
(bm.name || "").toLowerCase().includes(text) ||
(bm.category || "").toLowerCase().includes(text) ||
(bm.comment || "").toLowerCase().includes(text)
)
- : bmList;
+ : filtered;
bmRender(filtered);
}
async function bmRefreshCategoryFilter(keepValue) {
const sel = document.getElementById("bm-category-filter");
- if (!sel) return;
+ const modeSel = document.getElementById("bm-mode-filter");
+ if (!sel && !modeSel) return;
try {
const resp = await fetch("/bookmarks");
if (!resp.ok) return;
const all = await resp.json();
- const cats = [...new Set(all.map((b) => b.category || "").filter(Boolean))].sort();
- while (sel.options.length > 1) sel.remove(1);
- cats.forEach((cat) => {
- const opt = document.createElement("option");
- opt.value = cat;
- opt.textContent = cat;
- sel.add(opt);
- });
- if (keepValue && cats.includes(keepValue)) sel.value = keepValue;
+ if (sel) {
+ const cats = [...new Set(all.map((b) => b.category || "").filter(Boolean))].sort();
+ while (sel.options.length > 1) sel.remove(1);
+ cats.forEach((cat) => {
+ const opt = document.createElement("option");
+ opt.value = cat;
+ opt.textContent = cat;
+ sel.add(opt);
+ });
+ if (keepValue && cats.includes(keepValue)) sel.value = keepValue;
+ }
+ if (modeSel) {
+ const keepMode = modeSel.value;
+ const modes = [...new Set(all.map((b) => String(b.mode || "").trim().toUpperCase()).filter(Boolean))].sort();
+ while (modeSel.options.length > 1) modeSel.remove(1);
+ modes.forEach((mode) => {
+ const opt = document.createElement("option");
+ opt.value = mode;
+ opt.textContent = mode;
+ modeSel.add(opt);
+ });
+ if (keepMode && modes.includes(keepMode)) modeSel.value = keepMode;
+ }
} catch (_) {}
}
@@ -301,6 +320,11 @@ async function bmApply(bm) {
bmFetch(e.target.value);
});
+ // Mode filter dropdown (client-side, no re-fetch)
+ document.getElementById("bm-mode-filter").addEventListener("change", () => {
+ bmApplyFilters();
+ });
+
// Text search filter (client-side, no re-fetch)
document.getElementById("bm-text-filter").addEventListener("input", () => {
bmApplyFilters();
diff --git a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css
index f6036a3..d6829f1 100644
--- a/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css
+++ b/src/trx-client/trx-frontend/trx-frontend-http/assets/web/style.css
@@ -1656,6 +1656,51 @@ button:focus-visible, input:focus-visible, select:focus-visible {
transform: none;
max-width: 100%;
}
+.spectrum-bookmark-chip-side {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 0.16rem;
+ width: 100%;
+ min-height: 0;
+ padding: 0.38rem 0.5rem 0.42rem;
+ border-radius: 0.55rem;
+ white-space: normal;
+ line-height: 1.1;
+}
+.spectrum-bookmark-side-head {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.32rem;
+ min-width: 0;
+ width: 100%;
+}
+.spectrum-bookmark-chip-side .bm-icon-svg {
+ flex: 0 0 auto;
+ width: 0.5rem;
+ height: 0.72rem;
+ opacity: 0.95;
+}
+.spectrum-bookmark-chip-side .spectrum-bookmark-freq,
+.spectrum-bookmark-chip-side .spectrum-bookmark-name {
+ display: block;
+ max-width: 100%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.spectrum-bookmark-chip-side .spectrum-bookmark-freq {
+ min-width: 0;
+ font-size: 0.54rem;
+ font-weight: 700;
+ letter-spacing: 0.04em;
+ text-transform: uppercase;
+ opacity: 0.9;
+}
+.spectrum-bookmark-chip-side .spectrum-bookmark-name {
+ width: 100%;
+ font-size: 0.64rem;
+ font-weight: 600;
+}
.bm-icon-svg path {
fill: var(--bm-cat-fg, #1a202c);
}
@@ -2261,3 +2306,61 @@ button:focus-visible, input:focus-visible, select:focus-visible {
--wavelength-fg: #87663a;
--spectrum-bg: #f5ecd9;
}
+
+/* ── Fire style ───────────────────────────────────────────────────────── */
+[data-style="fire"] {
+ --bg: #120706;
+ --card-bg: #1b0c0a;
+ --input-bg: #180907;
+ --border: #4c1a12;
+ --border-light: #7a2e1a;
+ --text: #ffe7d2;
+ --text-muted: #c78361;
+ --text-heading: #fff3e7;
+ --btn-bg: #2c110d;
+ --btn-border: #8f3a20;
+ --accent-green: #ff6f1f;
+ --accent-yellow: #ffb347;
+ --accent-red: #ff4a24;
+ --jog-hi: #381510;
+ --jog-lo: #24100c;
+ --jog-shadow: rgba(0,0,0,0.62);
+ --jog-inset: rgba(255,164,76,0.07);
+ --audio-level-bg: #1f0d0a;
+ --audio-level-border: #7a2e1a;
+ --audio-level-fill-start: #ff4a24;
+ --audio-level-fill-end: #ffb347;
+ --filter-bg: #2b120d;
+ --filter-fg: #ffe7d2;
+ --filter-border: #8f3a20;
+ --wavelength-fg: #d38d6a;
+ --spectrum-bg: #140907;
+}
+[data-style="fire"][data-theme="light"] {
+ --bg: #fff3ea;
+ --card-bg: #fff7f0;
+ --input-bg: #ffe9da;
+ --border: #efc7b1;
+ --border-light: #d9a487;
+ --text: #42180d;
+ --text-muted: #8a4b31;
+ --text-heading: #2f120a;
+ --btn-bg: #ffe2cf;
+ --btn-border: #cc8563;
+ --accent-green: #d24c12;
+ --accent-yellow: #d88400;
+ --accent-red: #c53114;
+ --jog-hi: #ffe2cf;
+ --jog-lo: #ffd5bc;
+ --jog-shadow: rgba(108,44,15,0.18);
+ --jog-inset: rgba(255,255,255,0.72);
+ --audio-level-bg: #ffe7d7;
+ --audio-level-border: #d9a487;
+ --audio-level-fill-start: #c53114;
+ --audio-level-fill-end: #d88400;
+ --filter-bg: #ffe2cf;
+ --filter-fg: #42180d;
+ --filter-border: #cc8563;
+ --wavelength-fg: #9a5a3a;
+ --spectrum-bg: #fff0e4;
+}