@@ -212,31 +218,50 @@ function renderEndpoint(status) {
el('#ep-curl-snippet').textContent = snippet;
}
-function setupCopyButtons() {
- document.body.addEventListener('click', async (e) => {
- const btn = e.target.closest('.copy-btn');
- if (!btn) return;
- const targetSel = btn.dataset.copy;
- if (!targetSel) return;
- const target = el(targetSel);
- if (!target) return;
- const text = target.textContent;
- try {
- await navigator.clipboard.writeText(text);
- const original = btn.textContent;
- btn.classList.add('copied');
- btn.textContent = 'Copied';
- setTimeout(() => {
- btn.classList.remove('copied');
- btn.textContent = original;
- }, 1400);
- } catch {
- // Clipboard API may fail over plain HTTP; fall back to selection
+async function copyText(text, indicatorEl) {
+ try {
+ await navigator.clipboard.writeText(text);
+ if (indicatorEl) {
+ indicatorEl.classList.add('copied');
+ setTimeout(() => indicatorEl.classList.remove('copied'), 1200);
+ }
+ return true;
+ } catch {
+ // Plain HTTP fallback: select the text so the user can ⌘C
+ if (indicatorEl) {
const range = document.createRange();
- range.selectNode(target);
+ range.selectNode(indicatorEl);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
}
+ return false;
+ }
+}
+
+function setupCopyButtons() {
+ document.body.addEventListener('click', async (e) => {
+ // Inline icon copy with literal text (used for dynamically-rendered service rows)
+ const litBtn = e.target.closest('[data-copy-text]');
+ if (litBtn) {
+ await copyText(litBtn.dataset.copyText, litBtn);
+ return;
+ }
+ // Copy buttons (with svg icon) referenced by data-copy="selector"
+ const btn = e.target.closest('[data-copy]');
+ if (btn) {
+ const target = el(btn.dataset.copy);
+ if (target) {
+ await copyText(target.textContent, btn);
+ target.classList.add('copied');
+ setTimeout(() => target.classList.remove('copied'), 1200);
+ }
+ return;
+ }
+ // Self-copy: clicking the text itself
+ const selfCopy = e.target.closest('[data-copy-self]');
+ if (selfCopy) {
+ await copyText(selfCopy.textContent, selfCopy);
+ }
});
}
diff --git a/image/app/static/index.html b/image/app/static/index.html
index ae07bfd..df3266e 100644
--- a/image/app/static/index.html
+++ b/image/app/static/index.html
@@ -28,18 +28,24 @@
+
+ Updates to
eugr/spark-vllm-docker
+ — the upstream project that orchestrates vLLM on your Sparks (launch-cluster.sh, recipes, mods). These are
not firmware, OS, or model updates.
+
Checking for updates…
diff --git a/image/app/static/style.css b/image/app/static/style.css
index ca16881..f6ee0fe 100644
--- a/image/app/static/style.css
+++ b/image/app/static/style.css
@@ -97,7 +97,8 @@ main {
overflow-x: auto;
white-space: nowrap;
}
-.copy-btn {
+.copy-btn,
+.icon-btn {
appearance: none;
background: var(--surface-2);
border: 1px solid var(--border);
@@ -108,15 +109,27 @@ main {
cursor: pointer;
transition: color 0.15s, border-color 0.15s, background 0.15s;
flex-shrink: 0;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
}
-.copy-btn:hover { color: var(--text); border-color: #34343c; }
-.copy-btn.copied {
+.icon-btn { padding: 5px 7px; }
+.icon-btn svg { width: 14px; height: 14px; display: block; }
+.copy-btn:hover,
+.icon-btn:hover { color: var(--text); border-color: #34343c; }
+.copy-btn.copied,
+.icon-btn.copied {
color: var(--accent);
border-color: rgba(74, 222, 128, 0.4);
background: rgba(74, 222, 128, 0.08);
}
+.icon-btn.copied svg { color: var(--accent); }
.copy-btn.small { padding: 3px 8px; font-size: 11px; }
+.copyable { cursor: pointer; }
+.copyable:hover { outline: 1px solid rgba(96, 165, 250, 0.5); }
+.copyable.copied { outline: 1px solid var(--accent); background: rgba(74, 222, 128, 0.05); }
+
.ep-curl { margin-top: 8px; }
.ep-curl summary { cursor: pointer; padding: 4px 0; }
.ep-curl[open] summary { margin-bottom: 6px; }
@@ -274,10 +287,14 @@ main {
background: var(--surface);
border: 1px solid rgba(96, 165, 250, 0.4);
border-radius: var(--radius);
- padding: 10px 14px;
+ padding: 12px 14px;
margin-top: 18px;
font-size: 13px;
}
+.ub-context { margin-bottom: 8px; line-height: 1.5; }
+.ub-context a { color: var(--info); text-decoration: none; }
+.ub-context a:hover { text-decoration: underline; }
+.ub-context em { font-style: normal; color: var(--text); font-weight: 500; }
.update-banner.up-to-date {
border-color: var(--border);
color: var(--muted);
@@ -409,13 +426,25 @@ main {
.service-card .row {
display: flex;
+ align-items: center;
font-size: 12px;
color: var(--muted);
gap: 6px;
}
.service-card .row .k { width: 60px; flex-shrink: 0; }
-.service-card .row .v { color: var(--text); font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace; word-break: break-all; }
+.service-card .row .v {
+ color: var(--text);
+ font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;
+ word-break: break-all;
+ flex: 1;
+ padding: 2px 4px;
+ border-radius: 4px;
+}
.service-card .row .v.muted-v { color: var(--muted); font-family: inherit; }
+.service-card .row .v.copyable:hover { outline: 1px solid rgba(96, 165, 250, 0.5); }
+.service-card .row .v.copyable.copied { outline: 1px solid var(--accent); background: rgba(74, 222, 128, 0.05); }
+.service-card .row .icon-btn { padding: 3px 6px; }
+.service-card .row .icon-btn svg { width: 12px; height: 12px; }
.service-actions {
display: flex;
diff --git a/package/startos/versions/v0_1_0.ts b/package/startos/versions/v0_1_0.ts
index 4d0c9f9..be68a25 100644
--- a/package/startos/versions/v0_1_0.ts
+++ b/package/startos/versions/v0_1_0.ts
@@ -1,10 +1,10 @@
import { VersionInfo, IMPOSSIBLE } from '@start9labs/start-sdk'
export const v0_1_0 = VersionInfo.of({
- version: '0.2.3:0',
+ version: '0.2.4:0',
releaseNotes: {
en_US:
- 'Per-model Advanced settings + downloaded-model catalog flow. Each card now has an Advanced button: max context tokens, GPU memory %, and optimization toggles (fastsafetensors, prefix caching, FP8 KV cache). After a download finishes, a dialog appears to add the model to the catalog with those same knobs as launch defaults. Custom models can be deleted. Overrides persist in /data/models-overrides.yaml and survive package updates.',
+ 'Hotfix + UX polish: fixes parakeet/magpie status showing as "Unknown" (empty container-name env var no longer overrode the default). Copy buttons are now compact icons, and the values themselves are clickable to copy. Service cards show host + URL + model as separate copyable rows. The update banner now spells out what is being updated (spark-vllm-docker, the upstream LLM-cluster project).',
},
migrations: {
up: async ({ effects }) => {},